|
| 1 | +use rand::RngCore; |
| 2 | +use std::fs; |
| 3 | +use std::path::PathBuf; |
| 4 | + |
1 | 5 | use super::*;
|
2 | 6 |
|
3 | 7 | use crate::{
|
@@ -35,10 +39,42 @@ impl TestOpts {
|
35 | 39 | time_options: None,
|
36 | 40 | options: Options::new(),
|
37 | 41 | fail_fast: false,
|
| 42 | + output_postprocess_executable: None, |
| 43 | + output_postprocess_args: vec![], |
| 44 | + } |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +// These implementations of TempDir and tmpdir are forked from rust/library/std/src/sys_common/io.rs. |
| 49 | +struct TempDir(PathBuf); |
| 50 | + |
| 51 | +impl TempDir { |
| 52 | + fn join(&self, path: &str) -> PathBuf { |
| 53 | + let TempDir(ref p) = *self; |
| 54 | + p.join(path) |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +impl Drop for TempDir { |
| 59 | + fn drop(&mut self) { |
| 60 | + let TempDir(ref p) = *self; |
| 61 | + let result = fs::remove_dir_all(p); |
| 62 | + // Avoid panicking while panicking as this causes the process to |
| 63 | + // immediately abort, without displaying test results. |
| 64 | + if !thread::panicking() { |
| 65 | + result.unwrap(); |
38 | 66 | }
|
39 | 67 | }
|
40 | 68 | }
|
41 | 69 |
|
| 70 | +fn tmpdir() -> TempDir { |
| 71 | + let p = env::temp_dir(); |
| 72 | + let mut r = rand::thread_rng(); |
| 73 | + let ret = p.join(&format!("rust-{}", r.next_u32())); |
| 74 | + fs::create_dir(&ret).unwrap(); |
| 75 | + TempDir(ret) |
| 76 | +} |
| 77 | + |
42 | 78 | fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
|
43 | 79 | vec![
|
44 | 80 | TestDescAndFn {
|
@@ -478,6 +514,25 @@ fn parse_include_ignored_flag() {
|
478 | 514 | assert_eq!(opts.run_ignored, RunIgnored::Yes);
|
479 | 515 | }
|
480 | 516 |
|
| 517 | +#[test] |
| 518 | +fn parse_output_postprocess() { |
| 519 | + let args = vec![ |
| 520 | + "progname".to_string(), |
| 521 | + "filter".to_string(), |
| 522 | + "--output_postprocess_executable".to_string(), |
| 523 | + "/tmp/postprocess.sh".to_string(), |
| 524 | + "--output_postprocess_args".to_string(), |
| 525 | + "--test1=a".to_string(), |
| 526 | + "--output_postprocess_args=--test2=b".to_string(), |
| 527 | + ]; |
| 528 | + let opts = parse_opts(&args).unwrap().unwrap(); |
| 529 | + assert_eq!(opts.output_postprocess_executable, Some(PathBuf::from("/tmp/postprocess.sh"))); |
| 530 | + assert_eq!( |
| 531 | + opts.output_postprocess_args, |
| 532 | + vec!["--test1=a".to_string(), "--test2=b".to_string()] |
| 533 | + ); |
| 534 | +} |
| 535 | + |
481 | 536 | #[test]
|
482 | 537 | pub fn filter_for_ignored_option() {
|
483 | 538 | // When we run ignored tests the test filter should filter out all the
|
@@ -922,3 +977,73 @@ fn test_dyn_bench_returning_err_fails_when_run_as_test() {
|
922 | 977 | let result = rx.recv().unwrap().result;
|
923 | 978 | assert_eq!(result, TrFailed);
|
924 | 979 | }
|
| 980 | + |
| 981 | +#[test] |
| 982 | +fn test_output_postprocessing() { |
| 983 | + let desc = TestDescAndFn { |
| 984 | + desc: TestDesc { |
| 985 | + name: StaticTestName("whatever"), |
| 986 | + ignore: false, |
| 987 | + ignore_message: None, |
| 988 | + source_file: "", |
| 989 | + start_line: 0, |
| 990 | + start_col: 0, |
| 991 | + end_line: 0, |
| 992 | + end_col: 0, |
| 993 | + should_panic: ShouldPanic::No, |
| 994 | + compile_fail: false, |
| 995 | + no_run: false, |
| 996 | + test_type: TestType::Unknown, |
| 997 | + }, |
| 998 | + testfn: DynTestFn(Box::new(move || Ok(()))), |
| 999 | + }; |
| 1000 | + |
| 1001 | + let mut test_postprocessor: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); |
| 1002 | + if cfg!(target_os = "windows") { |
| 1003 | + test_postprocessor.push("src/testdata/postprocess.cmd"); |
| 1004 | + } else { |
| 1005 | + test_postprocessor.push("src/testdata/postprocess.sh"); |
| 1006 | + } |
| 1007 | + |
| 1008 | + let tmpdir = tmpdir(); |
| 1009 | + let output_path = &tmpdir.join("output.txt"); |
| 1010 | + |
| 1011 | + std::env::set_var("TEST_POSTPROCESSOR_OUTPUT_FILE", output_path); |
| 1012 | + |
| 1013 | + let opts = TestOpts { |
| 1014 | + run_tests: true, |
| 1015 | + output_postprocess_executable: Some(test_postprocessor), |
| 1016 | + output_postprocess_args: vec!["--test1=a".to_string(), "--test2=b".to_string()], |
| 1017 | + format: OutputFormat::Json, |
| 1018 | + ..TestOpts::new() |
| 1019 | + }; |
| 1020 | + run_tests_console(&opts, vec![desc]).unwrap(); |
| 1021 | + |
| 1022 | + // Read output and replace the decimal value at `"exec_time": 0.000084974` to make the text deterministic. |
| 1023 | + // This replacement could be done easier with a regex, but `std` has no regex. |
| 1024 | + let mut contents = |
| 1025 | + fs::read_to_string(output_path).expect("Test postprocessor did not create file"); |
| 1026 | + let replace_trigger = r#""exec_time": "#; |
| 1027 | + let replace_start = |
| 1028 | + contents.find(replace_trigger).expect("exec_time not found in the output JSON") |
| 1029 | + + replace_trigger.len(); |
| 1030 | + let replace_end = replace_start |
| 1031 | + + contents[replace_start..] |
| 1032 | + .find(' ') |
| 1033 | + .expect("No space found after the decimal value for exec_time"); |
| 1034 | + contents.replace_range(replace_start..replace_end, "AAA.BBB"); |
| 1035 | + |
| 1036 | + // Split output at line breaks to make the comparison platform-agnostic regarding newline style. |
| 1037 | + let contents_lines = contents.as_str().lines().collect::<Vec<&str>>(); |
| 1038 | + |
| 1039 | + let expected_lines = vec![ |
| 1040 | + r#"{ "type": "suite", "event": "started", "test_count": 1 }"#, |
| 1041 | + r#"{ "type": "test", "event": "started", "name": "whatever" }"#, |
| 1042 | + r#"{ "type": "test", "name": "whatever", "event": "ok" }"#, |
| 1043 | + r#"{ "type": "suite", "event": "ok", "passed": 1, "failed": 0, "ignored": 0, "measured": 0, "filtered_out": 0, "exec_time": AAA.BBB }"#, |
| 1044 | + r#"--test1=a"#, |
| 1045 | + r#"--test2=b"#, |
| 1046 | + ]; |
| 1047 | + |
| 1048 | + assert_eq!(contents_lines, expected_lines); |
| 1049 | +} |
0 commit comments