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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::io::{stderr, Write};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use tempfile::tempdir;
use walkdir::WalkDir;

use config::{Config, Mode};
use error::Result;
use formatting;
use steps::{build::BuildStepFactory, check_errors::CheckErrorsStepFactory, TestStepFactory};

lazy_static! {
    static ref TESTING_MUTEX: Mutex<()> = Mutex::new(());
}

pub fn run_tests(config: Config) {
    if let Err(error) = run_tests_with_writer(config, stderr()) {
        panic!("{}", error);
    }
}

pub fn run_tests_with_writer<W: Write>(config: Config, writer: W) -> Result<()> {
    TestPlan::new(config).execute(writer)
}

struct TestPlan {
    config: Config,
    steps: Vec<Box<TestStepFactory>>,
    crates: Vec<PathBuf>,
}

impl TestPlan {
    pub fn new(mut config: Config) -> Self {
        let crates = WalkDir::new(&config.base_dir)
            .max_depth(1)
            .min_depth(1)
            .into_iter()
            .filter_map(|entry| entry.ok())
            .filter(|entry| entry.path().is_dir())
            .map(|entry| entry.path().into())
            .collect();

        let mut steps: Vec<Box<TestStepFactory>> = match config.mode {
            Mode::BuildFail => vec![Box::new(CheckErrorsStepFactory::new())],
            Mode::BuildSuccess => vec![Box::new(BuildStepFactory::new())],
            Mode::Expand => vec![Box::new(BuildStepFactory::new())],
        };

        if config.additional_steps.len() > 0 {
            steps.append(&mut config.additional_steps);
        }

        TestPlan {
            config,
            crates,
            steps,
        }
    }

    pub fn execute<W: Write>(self, mut writer: W) -> Result<()> {
        let _lock = TESTING_MUTEX.lock().unwrap();

        let mut successful: usize = 0;
        let mut failed: usize = 0;

        let results: Vec<_> = self.crates
            .iter()
            .map(|ref crate_path| {
                if !(self.config.crates_filter)(crate_path) {
                    writeln!(
                        writer,
                        "testing crate {} ... IGNORED",
                        crate_path.to_string_lossy()
                    )?;

                    return Ok(());
                }

                match self.execute_steps(&crate_path) {
                    Ok(result) => {
                        writeln!(
                            writer,
                            "testing crate {} ... OK",
                            crate_path.to_string_lossy()
                        )?;

                        successful += 1;
                        Ok(result)
                    }

                    Err(error) => {
                        writeln!(
                            writer,
                            "testing crate {} ... FAILED",
                            crate_path.to_string_lossy()
                        )?;

                        failed += 1;
                        Err(error)
                    }
                }
            })
            .collect();

        for result in self.crates.iter().zip(results) {
            if let Err(error) = result.1 {
                writeln!(writer, "\n{}:", result.0.to_string_lossy())?;

                writeln!(
                    writer,
                    "{}",
                    formatting::prefix_each_line(error.to_string(), "  ")
                )?;
            }
        }

        if failed > 0 {
            bail!("{} tests failed", failed);
        } else {
            Ok(())
        }
    }

    fn execute_steps(&self, crate_path: &Path) -> Result<()> {
        let build_path = tempdir()?;

        let local_steps: Vec<_> = self.steps
            .iter()
            .map(|factory| factory.initialize(&self.config, crate_path))
            .collect();

        for step in local_steps {
            step?.execute(&self.config, build_path.as_ref())?;
        }

        Ok(())
    }
}