crate_compile_test/
runner.rs

1use std::io::Write;
2use std::sync::Mutex;
3
4use colored::*;
5use failure::Error;
6
7use config::Config;
8use error::{Result, TestingError};
9use plan::TestPlan;
10
11pub struct TestRunner<'a> {
12    tests: Vec<Test>,
13    output: Mutex<&'a mut Write>,
14}
15
16pub struct TestResult {
17    success: bool,
18}
19
20struct Test {
21    name: &'static str,
22    config: Box<Fn() -> Config>,
23}
24
25impl<'a> TestRunner<'a> {
26    pub fn new(output: &'a mut Write) -> Self {
27        TestRunner {
28            output: Mutex::new(output),
29            tests: vec![],
30        }
31    }
32
33    pub fn add<F: Fn() -> Config + 'static>(&mut self, name: &'static str, config: F) {
34        self.tests.push(Test {
35            name,
36            config: Box::new(config),
37        });
38    }
39
40    pub fn start(self) -> Result<TestResult> {
41        let mut overall_successful: usize = 0;
42        let mut overall_failed: usize = 0;
43        let mut overall_ignored: usize = 0;
44
45        for test in &self.tests {
46            writeln!(
47                self.output.lock().unwrap(),
48                "{}",
49                format!(r#"Running "{}""#, test.name).underline()
50            )?;
51
52            let plan = TestPlan::new((test.config)());
53
54            let mut successful: usize = 0;
55            let mut failed: usize = 0;
56            let mut ignored: usize = 0;
57
58            let errors: Vec<Error> = plan.crates()
59                .iter()
60                .map(|crate_path| -> Result<()> {
61                    if plan.is_crate_filtered_out(crate_path) {
62                        writeln!(
63                            self.output.lock().unwrap(),
64                            "  testing crate {} ... {}",
65                            crate_path.to_string_lossy().bold(),
66                            "IGNORED".yellow(),
67                        )?;
68
69                        ignored += 1;
70                        return Ok(());
71                    }
72
73                    match plan.execute_steps(&crate_path) {
74                        Ok(()) => {
75                            writeln!(
76                                self.output.lock().unwrap(),
77                                "  testing crate {} ... {}",
78                                crate_path.to_string_lossy().bold(),
79                                "OK".bright_green(),
80                            )?;
81
82                            successful += 1;
83                            Ok(())
84                        }
85
86                        Err(error) => {
87                            writeln!(
88                                self.output.lock().unwrap(),
89                                "  testing crate {} ... {}",
90                                crate_path.to_string_lossy().bold(),
91                                "FAILED".red()
92                            )?;
93
94                            failed += 1;
95                            bail!(TestingError::TestFailed {
96                                path: crate_path.clone(),
97                                error,
98                            });
99                        }
100                    }
101                })
102                .filter_map(|item| item.err())
103                .collect();
104
105            for error in errors {
106                writeln!(self.output.lock().unwrap(), "\n{}", error)?;
107            }
108
109            writeln!(self.output.lock().unwrap(), "")?;
110
111            overall_successful += successful;
112            overall_failed += failed;
113            overall_ignored += ignored;
114        }
115
116        writeln!(
117            self.output.lock().unwrap(),
118            "Summary: {} successful, {} failed, {} ignored.",
119            overall_successful.to_string().bright_green(),
120            overall_failed.to_string().red(),
121            overall_ignored.to_string().yellow(),
122        )?;
123
124        Ok(TestResult {
125            success: overall_failed == 0,
126        })
127    }
128}
129
130impl TestResult {
131    pub fn is_success(&self) -> bool {
132        self.success
133    }
134}