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 | TrAllowedFail, |
23 | TrBench(BenchSamples), |
24 | TrTimedFail, |
25 | } |
26 | |
27 | unsafe impl Send for TestResult {} |
28 | |
29 | /// Creates a `TestResult` depending on the raw result of test execution |
30 | /// and associated data. |
31 | pub fn calc_result<'a>( |
32 | desc: &TestDesc, |
33 | task_result: Result<(), &'a dyn Any>, |
34 | time_opts: &Option<time::TestTimeOptions>, |
35 | exec_time: &Option<time::TestExecTime>, |
36 | ) -> TestResult { |
37 | let result = match (&desc.should_panic, task_result) { |
38 | (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk, |
39 | (&ShouldPanic::YesWithMessage(msg), Err(ref err)) => { |
40 | let maybe_panic_str = err |
41 | .downcast_ref::<String>() |
42 | .map(|e| &**e) |
43 | .or_else(|| err.downcast_ref::<&'static str>().copied()); |
44 | |
45 | if maybe_panic_str.map(|e| e.contains(msg)).unwrap_or(false) { |
46 | TestResult::TrOk |
47 | } else if desc.allow_fail { |
48 | TestResult::TrAllowedFail |
49 | } else if let Some(panic_str) = maybe_panic_str { |
50 | TestResult::TrFailedMsg(format!( |
51 | r#"panic did not contain expected string |
52 | panic message: ` {:?}`, |
53 | expected substring: ` {:?}`"# , |
54 | panic_str, msg |
55 | )) |
56 | } else { |
57 | TestResult::TrFailedMsg(format!( |
58 | r#"expected panic with string value, |
59 | found non-string value: ` {:?}` |
60 | expected substring: ` {:?}`"# , |
61 | (**err).type_id(), |
62 | msg |
63 | )) |
64 | } |
65 | } |
66 | (&ShouldPanic::Yes, Ok(())) => { |
67 | TestResult::TrFailedMsg("test did not panic as expected" .to_string()) |
68 | } |
69 | _ if desc.allow_fail => TestResult::TrAllowedFail, |
70 | _ => TestResult::TrFailed, |
71 | }; |
72 | |
73 | // If test is already failed (or allowed to fail), do not change the result. |
74 | if result != TestResult::TrOk { |
75 | return result; |
76 | } |
77 | |
78 | // Check if test is failed due to timeout. |
79 | if let (Some(opts), Some(time)) = (time_opts, exec_time) { |
80 | if opts.error_on_excess && opts.is_critical(desc, time) { |
81 | return TestResult::TrTimedFail; |
82 | } |
83 | } |
84 | |
85 | result |
86 | } |
87 | |
88 | /// Creates a `TestResult` depending on the exit code of test subprocess. |
89 | pub fn get_result_from_exit_code( |
90 | desc: &TestDesc, |
91 | code: i32, |
92 | time_opts: &Option<time::TestTimeOptions>, |
93 | exec_time: &Option<time::TestExecTime>, |
94 | ) -> TestResult { |
95 | let result: TestResult = match (desc.allow_fail, code) { |
96 | (_, TR_OK) => TestResult::TrOk, |
97 | (true, TR_FAILED) => TestResult::TrAllowedFail, |
98 | (false, TR_FAILED) => TestResult::TrFailed, |
99 | (_, _) => TestResult::TrFailedMsg(format!("got unexpected return code {}" , code)), |
100 | }; |
101 | |
102 | // If test is already failed (or allowed to fail), do not change the result. |
103 | if result != TestResult::TrOk { |
104 | return result; |
105 | } |
106 | |
107 | // Check if test is failed due to timeout. |
108 | if let (Some(opts: &TestTimeOptions), Some(time: &TestExecTime)) = (time_opts, exec_time) { |
109 | if opts.error_on_excess && opts.is_critical(test:desc, exec_time:time) { |
110 | return TestResult::TrTimedFail; |
111 | } |
112 | } |
113 | |
114 | result |
115 | } |
116 | |