Skip to content

Commit

Permalink
refactor(cli): move test reporting into trait (#10408)
Browse files Browse the repository at this point in the history
  • Loading branch information
caspervonb authored Apr 29, 2021
1 parent b9a136c commit ec0d3b6
Showing 1 changed file with 154 additions and 88 deletions.
242 changes: 154 additions & 88 deletions cli/tools/test_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::sync::Arc;
use std::time::Instant;

#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -52,6 +53,141 @@ pub enum TestMessage {
},
}

trait TestReporter {
fn visit_message(&mut self, message: TestMessage);
fn done(&mut self);
}

struct PrettyTestReporter {
time: Instant,
failed: usize,
filtered_out: usize,
ignored: usize,
passed: usize,
measured: usize,
pending: usize,
failures: Vec<(String, String)>,
concurrent: bool,
}

impl PrettyTestReporter {
fn new(concurrent: bool) -> PrettyTestReporter {
PrettyTestReporter {
time: Instant::now(),
failed: 0,
filtered_out: 0,
ignored: 0,
passed: 0,
measured: 0,
pending: 0,
failures: Vec::new(),
concurrent,
}
}
}

impl TestReporter for PrettyTestReporter {
fn visit_message(&mut self, message: TestMessage) {
match &message {
TestMessage::Plan {
pending,
filtered,
only: _,
} => {
println!("running {} tests", pending);
self.pending += pending;
self.filtered_out += filtered;
}

TestMessage::Wait { name } => {
if !self.concurrent {
print!("test {} ...", name);
}
}

TestMessage::Result {
name,
duration,
result,
} => {
self.pending -= 1;

if self.concurrent {
print!("test {} ...", name);
}

match result {
TestResult::Ok => {
println!(
" {} {}",
colors::green("ok"),
colors::gray(format!("({}ms)", duration))
);

self.passed += 1;
}
TestResult::Ignored => {
println!(
" {} {}",
colors::yellow("ignored"),
colors::gray(format!("({}ms)", duration))
);

self.ignored += 1;
}
TestResult::Failed(error) => {
println!(
" {} {}",
colors::red("FAILED"),
colors::gray(format!("({}ms)", duration))
);

self.failed += 1;
self.failures.push((name.to_string(), error.to_string()));
}
}
}
}
}

fn done(&mut self) {
if !self.failures.is_empty() {
println!("\nfailures:\n");
for (name, error) in &self.failures {
println!("{}", name);
println!("{}", error);
println!();
}

println!("failures:\n");
for (name, _) in &self.failures {
println!("\t{}", name);
}
}

let status = if self.pending > 0 || !self.failures.is_empty() {
colors::red("FAILED").to_string()
} else {
colors::green("ok").to_string()
};

println!(
"\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n",
status,
self.passed,
self.failed,
self.ignored,
self.measured,
self.filtered_out,
colors::gray(format!("({}ms)", self.time.elapsed().as_millis())),
);
}
}

fn create_reporter(concurrent: bool) -> Box<dyn TestReporter + Send> {
Box::new(PrettyTestReporter::new(concurrent))
}

fn is_supported(p: &Path) -> bool {
use std::path::Component;
if let Some(Component::Normal(basename_os_str)) = p.components().next_back() {
Expand Down Expand Up @@ -141,6 +277,7 @@ pub async fn run_test_file(

let execute_result = worker.execute_module(&main_module).await;
execute_result?;

worker.execute("window.dispatchEvent(new Event('load'))")?;

let execute_result = worker.execute_module(&test_module).await;
Expand Down Expand Up @@ -250,129 +387,58 @@ pub async fn run_tests(
.buffer_unordered(concurrent_jobs)
.collect::<Vec<Result<Result<(), AnyError>, tokio::task::JoinError>>>();

let mut reporter = create_reporter(concurrent_jobs > 1);
let handler = {
tokio::task::spawn_blocking(move || {
let time = std::time::Instant::now();
let mut failed = 0;
let mut filtered_out = 0;
let mut ignored = 0;
let mut passed = 0;
let measured = 0;

let mut planned = 0;
let mut used_only = false;
let mut has_error = false;
let mut failures: Vec<(String, String)> = Vec::new();
let mut planned = 0;
let mut reported = 0;

for message in receiver.iter() {
match message {
match message.clone() {
TestMessage::Plan {
pending,
filtered,
filtered: _,
only,
} => {
println!("running {} tests", pending);

if only {
used_only = true;
}

planned += pending;
filtered_out += filtered;
}

TestMessage::Wait { name } => {
if concurrent_jobs == 1 {
print!("test {} ...", name);
}
}

TestMessage::Result {
name,
duration,
name: _,
duration: _,
result,
} => {
if concurrent_jobs != 1 {
print!("test {} ...", name);
}
reported += 1;

match result {
TestResult::Ok => {
println!(
" {} {}",
colors::green("ok"),
colors::gray(format!("({}ms)", duration))
);

passed += 1;
}
TestResult::Ignored => {
println!(
" {} {}",
colors::yellow("ignored"),
colors::gray(format!("({}ms)", duration))
);

ignored += 1;
}
TestResult::Failed(error) => {
println!(
" {} {}",
colors::red("FAILED"),
colors::gray(format!("({}ms)", duration))
);

failed += 1;
failures.push((name, error));
has_error = true;
}
if let TestResult::Failed(_) = result {
has_error = true;
}
}
_ => {}
}

reporter.visit_message(message);

if has_error && fail_fast {
break;
}
}

// If one of the workers panic then we can end up with less test results than what was
// planned.
// In that case we mark it as an error so that it will be reported as failed.
if planned > passed + ignored + failed {
if planned > reported {
has_error = true;
}

if !failures.is_empty() {
println!("\nfailures:\n");
for (name, error) in &failures {
println!("{}", name);
println!("{}", error);
println!();
}
reporter.done();

println!("failures:\n");
for (name, _) in &failures {
println!("\t{}", name);
}
if planned > reported {
has_error = true;
}

let status = if has_error {
colors::red("FAILED").to_string()
} else {
colors::green("ok").to_string()
};

println!(
"\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n",
status,
passed,
failed,
ignored,
measured,
filtered_out,
colors::gray(format!("({}ms)", time.elapsed().as_millis())),
);

if used_only {
println!(
"{} because the \"only\" option was used\n",
Expand Down

0 comments on commit ec0d3b6

Please sign in to comment.