      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115

    use std::any::Any;

    use super::bench::BenchSamples;
    use super::time;
    use super::types::TestDesc;
    use super::options::ShouldPanic;

    pub use self::TestResult::*;

    // Return codes for secondary process.
    // Start somewhere other than 0 so we know the return code means what we think
    // it means.
    pub const TR_OK: i32 = 50;
    pub const TR_FAILED: i32 = 51;

    #[derive(Debug, Clone, PartialEq)]
    pub enum TestResult {
        TrOk,
        TrFailed,
        TrFailedMsg(String),
        TrIgnored,
        TrAllowedFail,
        TrBench(BenchSamples),
        TrTimedFail,
    }

    unsafe impl Send for TestResult {}

    /// Creates a `TestResult` depending on the raw result of test execution
    /// and assotiated data.
    pub fn calc_result<'a>(
        desc: &TestDesc,
        task_result: Result<(), &'a (dyn Any + 'static + Send)>,
        time_opts: &Option<time::TestTimeOptions>,
        exec_time: &Option<time::TestExecTime>
    ) -> TestResult {
        let result = match (&desc.should_panic, task_result) {
            (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk,
            (&ShouldPanic::YesWithMessage(msg), Err(ref err)) => {
                let maybe_panic_str = err
                    .downcast_ref::<String>()
                    .map(|e| &**e)
                    .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e));

                if maybe_panic_str.map(|e| e.contains(msg)).unwrap_or(false) {
                    TestResult::TrOk
                } else if desc.allow_fail {
                    TestResult::TrAllowedFail
                } else if let Some(panic_str) = maybe_panic_str {
                    TestResult::TrFailedMsg(format!(
                        r#"panic did not contain expected string
          panic message: `{:?}`,
     expected substring: `{:?}`"#,
                        panic_str, msg
                    ))
                } else {
                    TestResult::TrFailedMsg(format!(
                        r#"expected panic with string value,
     found non-string value: `{:?}`
         expected substring: `{:?}`"#,
                        (**err).type_id(),
                        msg
                    ))
                }
            }
            (&ShouldPanic::Yes, Ok(())) => {
                TestResult::TrFailedMsg("test did not panic as expected".to_string())
            }
            _ if desc.allow_fail => TestResult::TrAllowedFail,
            _ => TestResult::TrFailed,
        };

        // If test is already failed (or allowed to fail), do not change the result.
        if result != TestResult::TrOk {
            return result;
        }

        // Check if test is failed due to timeout.
        if let (Some(opts), Some(time)) = (time_opts, exec_time) {
            if opts.error_on_excess && opts.is_critical(desc, time) {
                return TestResult::TrTimedFail;
            }
        }

        result
    }

    /// Creates a `TestResult` depending on the exit code of test subprocess.
    pub fn get_result_from_exit_code(
        desc: &TestDesc,
        code: i32,
        time_opts: &Option<time::TestTimeOptions>,
        exec_time: &Option<time::TestExecTime>,
    ) -> TestResult {
        let result = match (desc.allow_fail, code) {
            (_, TR_OK) => TestResult::TrOk,
            (true, TR_FAILED) => TestResult::TrAllowedFail,
            (false, TR_FAILED) => TestResult::TrFailed,
            (_, _) => TestResult::TrFailedMsg(format!("got unexpected return code {}", code)),
        };

        // If test is already failed (or allowed to fail), do not change the result.
        if result != TestResult::TrOk {
            return result;
        }

        // Check if test is failed due to timeout.
        if let (Some(opts), Some(time)) = (time_opts, exec_time) {
            if opts.error_on_excess && opts.is_critical(desc, time) {
                return TestResult::TrTimedFail;
            }
        }

        result
    }