cargo_all_features/
test_runner.rs

1use crate::types::FeatureList;
2use std::{error, path, process};
3use termcolor::WriteColor;
4
5pub struct TestRunner {
6    command: process::Command,
7    crate_name: String,
8    /// A comma separated list of features
9    features: String,
10    working_dir: path::PathBuf,
11    cargo_command: String,
12}
13
14fn split_slice<'a>(slice: &'a [String], item: &'a str) -> (&'a [String], &'a [String]) {
15    if let Some(pos) = slice.iter().position(|s| s == item) {
16        (&slice[..pos], &slice[pos..])
17    } else {
18        (slice, &[])
19    }
20}
21
22impl TestRunner {
23    pub fn new(
24        cargo_command: String,
25        crate_name: String,
26        feature_set: FeatureList,
27        cargo_args: &[String],
28        working_dir: path::PathBuf,
29    ) -> Self {
30        let mut command = process::Command::new(crate::cargo_cmd());
31
32        command.arg(cargo_command.clone());
33
34        let (cargo_args_b, cargo_args_a) = split_slice(cargo_args, "--");
35
36        // Pass through cargo args
37        // Example: `cargo all-features clippy --no-deps -- --package xyz`
38        // We take `clippy` and `--no-deps` for now
39        command.args(cargo_args_b.iter());
40
41        // We add `--no-default-features`
42        command.arg("--no-default-features");
43
44        // We add feature set `--features [any combination]`
45        let mut features = feature_set
46            .iter()
47            .fold(String::new(), |s, feature| s + feature + ",");
48
49        if !features.is_empty() {
50            features.remove(features.len() - 1);
51
52            command.arg("--features");
53            command.arg(&features);
54        }
55
56        // And last we pass `--` and `--package xyz` to command args
57        command.args(cargo_args_a.iter());
58
59        // We successfully constructed `cargo clippy --no-deps --no-default-features --features [any combination] -- --package xyz`
60        TestRunner {
61            crate_name,
62            command,
63            features,
64            working_dir,
65            cargo_command,
66        }
67    }
68
69    pub fn run(&mut self) -> Result<crate::TestOutcome, Box<dyn error::Error>> {
70        let mut stdout = termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto);
71        stdout
72            .set_color(
73                termcolor::ColorSpec::new()
74                    .set_fg(Some(termcolor::Color::Cyan))
75                    .set_bold(true),
76            )
77            .unwrap();
78        print!("     Running {} ", self.cargo_command);
79        stdout.reset().unwrap();
80        println!("crate={} features=[{}]", self.crate_name, self.features);
81
82        let output = self
83            .command
84            .stdout(process::Stdio::inherit())
85            .stderr(process::Stdio::inherit())
86            .current_dir(&self.working_dir)
87            .output()?;
88
89        Ok(if output.status.success() {
90            crate::TestOutcome::Pass
91        } else {
92            crate::TestOutcome::Fail(output.status)
93        })
94    }
95}