1 | use std::any::Any; |
2 | |
3 | use super::bench::BenchSamples; |
4 | use super::options::ShouldPanic; |
5 | use super::time; |
6 | use super::types::TestDesc; |
7 | |
8 | pub use self::TestResult::*; |
9 | |
10 | // Return codes for secondary process. |
11 | // Start somewhere other than 0 so we know the return code means what we think |
12 | // it means. |
13 | pub const TR_OK: i32 = 50; |
14 | pub const TR_FAILED: i32 = 51; |
15 | |
16 | #[derive(Debug, Clone, PartialEq)] |
17 | pub enum TestResult { |
18 | TrOk, |
19 | TrFailed, |
20 | TrFailedMsg(String), |
21 | TrIgnored, |
22 | TrBench(BenchSamples), |
23 | TrTimedFail, |
24 | } |
25 | |
26 | /// Creates a `TestResult` depending on the raw result of test execution |
27 | /// and associated data. |
28 | pub fn calc_result<'a>( |
29 | desc: &TestDesc, |
30 | task_result: Result<(), &'a (dyn Any + 'static + Send)>, |
31 | time_opts: &Option<time::TestTimeOptions>, |
32 | exec_time: &Option<time::TestExecTime>, |
33 | ) -> TestResult { |
34 | let result = match (&desc.should_panic, task_result) { |
35 | (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk, |
36 | (&ShouldPanic::YesWithMessage(msg), Err(err)) => { |
37 | let maybe_panic_str = err |
38 | .downcast_ref::<String>() |
39 | .map(|e| &**e) |
40 | .or_else(|| err.downcast_ref::<&'static str>().copied()); |
41 | |
42 | if maybe_panic_str.map(|e| e.contains(msg)).unwrap_or(false) { |
43 | TestResult::TrOk |
44 | } else if let Some(panic_str) = maybe_panic_str { |
45 | TestResult::TrFailedMsg(format!( |
46 | r#"panic did not contain expected string |
47 | panic message: `{panic_str:?}`, |
48 | expected substring: `{msg:?}`"# |
49 | )) |
50 | } else { |
51 | TestResult::TrFailedMsg(format!( |
52 | r#"expected panic with string value, |
53 | found non-string value: `{:?}` |
54 | expected substring: `{:?}`"# , |
55 | (*err).type_id(), |
56 | msg |
57 | )) |
58 | } |
59 | } |
60 | (&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => { |
61 | TestResult::TrFailedMsg("test did not panic as expected" .to_string()) |
62 | } |
63 | _ => TestResult::TrFailed, |
64 | }; |
65 | |
66 | // If test is already failed (or allowed to fail), do not change the result. |
67 | if result != TestResult::TrOk { |
68 | return result; |
69 | } |
70 | |
71 | // Check if test is failed due to timeout. |
72 | if let (Some(opts), Some(time)) = (time_opts, exec_time) { |
73 | if opts.error_on_excess && opts.is_critical(desc, time) { |
74 | return TestResult::TrTimedFail; |
75 | } |
76 | } |
77 | |
78 | result |
79 | } |
80 | |
81 | /// Creates a `TestResult` depending on the exit code of test subprocess. |
82 | pub fn get_result_from_exit_code( |
83 | desc: &TestDesc, |
84 | code: i32, |
85 | time_opts: &Option<time::TestTimeOptions>, |
86 | exec_time: &Option<time::TestExecTime>, |
87 | ) -> TestResult { |
88 | let result: TestResult = match code { |
89 | TR_OK => TestResult::TrOk, |
90 | TR_FAILED => TestResult::TrFailed, |
91 | _ => TestResult::TrFailedMsg(format!("got unexpected return code {code}" )), |
92 | }; |
93 | |
94 | // If test is already failed (or allowed to fail), do not change the result. |
95 | if result != TestResult::TrOk { |
96 | return result; |
97 | } |
98 | |
99 | // Check if test is failed due to timeout. |
100 | if let (Some(opts: &{unknown}), Some(time: &{unknown})) = (time_opts, exec_time) { |
101 | if opts.error_on_excess && opts.is_critical(desc, time) { |
102 | return TestResult::TrTimedFail; |
103 | } |
104 | } |
105 | |
106 | result |
107 | } |
108 | |