tester/
test_result.rs

1use std::any::Any;
2
3use super::bench::BenchSamples;
4use super::options::ShouldPanic;
5use super::time;
6use super::types::TestDesc;
7
8pub 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.
13pub const TR_OK: i32 = 50;
14pub const TR_FAILED: i32 = 51;
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum TestResult {
18    TrOk,
19    TrFailed,
20    TrFailedMsg(String),
21    TrIgnored,
22    TrAllowedFail,
23    TrBench(BenchSamples),
24    TrTimedFail,
25}
26
27unsafe impl Send for TestResult {}
28
29/// Creates a `TestResult` depending on the raw result of test execution
30/// and associated data.
31pub 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.
89pub 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 = 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), Some(time)) = (time_opts, exec_time) {
109        if opts.error_on_excess && opts.is_critical(desc, time) {
110            return TestResult::TrTimedFail;
111        }
112    }
113
114    result
115}