use std::time::{Instant, SystemTime};
use crate::core::{ReportFormat, RunnerConfig, TestRun, TestSuite};
use crate::report::{self, TestReporter};
use crate::spec::Spec;
pub struct TestRunner {
config: RunnerConfig,
specs: Vec<Spec>,
}
impl TestRunner {
pub fn new(config: RunnerConfig) -> Self {
TestRunner { config, specs: Vec::new() }
}
pub fn add_spec(mut self, spec: Spec) -> Self {
self.specs.push(spec);
self
}
pub fn add_specs(mut self, specs: impl IntoIterator<Item = Spec>) -> Self {
self.specs.extend(specs);
self
}
pub fn run(mut self) -> TestRun {
let start_time = SystemTime::now();
let wall_start = Instant::now();
let mut suites = Vec::new();
let specs = std::mem::take(&mut self.specs);
for spec in specs {
let suite = self.run_spec(spec);
suites.push(suite);
}
let duration = wall_start.elapsed();
TestRun {
suites,
start_time,
end_time: SystemTime::now(),
duration,
}
}
fn run_spec(&self, spec: Spec) -> TestSuite {
spec.run_with_config(&self.config)
}
pub fn report(&self, run: &TestRun) -> String {
let reporter: Box<dyn TestReporter> = match self.config.format {
ReportFormat::Pretty => Box::new(report::PrettyReporter::new(self.config.verbose)),
ReportFormat::Tap => Box::new(report::TapReporter),
ReportFormat::Junit => Box::new(report::JunitReporter::new()),
ReportFormat::Json => Box::new(report::JsonReporter),
ReportFormat::Compact => Box::new(report::CompactReporter),
};
reporter.report(run)
}
}
pub fn run_tests(specs: impl IntoIterator<Item = Spec>) -> TestRun {
TestRunner::new(RunnerConfig::default())
.add_specs(specs)
.run()
}
pub fn run_and_exit(specs: impl IntoIterator<Item = Spec>) -> ! {
let config = RunnerConfig::default();
let run = run_tests(specs);
let report = render_report_with_config(&config, &run);
println!("{report}");
std::process::exit(if run.success() { 0 } else { 1 });
}
fn render_report_with_config(config: &RunnerConfig, run: &TestRun) -> String {
let reporter: Box<dyn TestReporter> = match config.format {
ReportFormat::Pretty => Box::new(report::PrettyReporter::new(config.verbose)),
ReportFormat::Tap => Box::new(report::TapReporter),
ReportFormat::Junit => Box::new(report::JunitReporter::new()),
ReportFormat::Json => Box::new(report::JsonReporter),
ReportFormat::Compact => Box::new(report::CompactReporter),
};
reporter.report(run)
}