1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 23:34:47 -05:00

refactor(cli/tools/test_runner): make test reporters stateless (#11357)

This collects summary information in the event collector and 
passes it to the reporter instead of having this embedded in 
each reporter which leads to a lot of duplication.
This commit is contained in:
Casper Beyer 2021-07-14 06:11:58 +08:00 committed by GitHub
parent 9cb48bd8fe
commit 56635d3b52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -31,6 +31,7 @@ use std::path::PathBuf;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use std::time::Instant; use std::time::Instant;
use swc_common::comments::CommentKind; use swc_common::comments::CommentKind;
@ -66,36 +67,51 @@ pub struct TestEvent {
pub message: TestMessage, pub message: TestMessage,
} }
#[derive(Debug, Clone, Deserialize)]
pub struct TestSummary {
pub total: usize,
pub passed: usize,
pub failed: usize,
pub ignored: usize,
pub filtered_out: usize,
pub measured: usize,
pub failures: Vec<(String, String)>,
}
impl TestSummary {
fn new() -> TestSummary {
TestSummary {
total: 0,
passed: 0,
failed: 0,
ignored: 0,
filtered_out: 0,
measured: 0,
failures: Vec::new(),
}
}
fn has_failed(&self) -> bool {
self.failed > 0 || !self.failures.is_empty()
}
fn has_pending(&self) -> bool {
self.total - self.passed - self.failed - self.ignored > 0
}
}
trait TestReporter { trait TestReporter {
fn visit_event(&mut self, event: TestEvent); fn visit_event(&mut self, event: TestEvent);
fn done(&mut self); fn done(&mut self, summary: &TestSummary, elapsed: &Duration);
} }
struct PrettyTestReporter { struct PrettyTestReporter {
time: Instant,
failed: usize,
filtered_out: usize,
ignored: usize,
passed: usize,
measured: usize,
pending: usize,
failures: Vec<(String, String)>,
concurrent: bool, concurrent: bool,
} }
impl PrettyTestReporter { impl PrettyTestReporter {
fn new(concurrent: bool) -> PrettyTestReporter { fn new(concurrent: bool) -> PrettyTestReporter {
PrettyTestReporter { PrettyTestReporter { concurrent }
time: Instant::now(),
failed: 0,
filtered_out: 0,
ignored: 0,
passed: 0,
measured: 0,
pending: 0,
failures: Vec::new(),
concurrent,
}
} }
} }
@ -104,7 +120,7 @@ impl TestReporter for PrettyTestReporter {
match &event.message { match &event.message {
TestMessage::Plan { TestMessage::Plan {
pending, pending,
filtered, filtered: _,
only: _, only: _,
} => { } => {
if *pending == 1 { if *pending == 1 {
@ -112,9 +128,6 @@ impl TestReporter for PrettyTestReporter {
} else { } else {
println!("running {} tests from {}", pending, event.origin); println!("running {} tests from {}", pending, event.origin);
} }
self.pending += pending;
self.filtered_out += filtered;
} }
TestMessage::Wait { name } => { TestMessage::Wait { name } => {
@ -128,8 +141,6 @@ impl TestReporter for PrettyTestReporter {
duration, duration,
result, result,
} => { } => {
self.pending -= 1;
if self.concurrent { if self.concurrent {
print!("test {} ...", name); print!("test {} ...", name);
} }
@ -141,49 +152,44 @@ impl TestReporter for PrettyTestReporter {
colors::green("ok"), colors::green("ok"),
colors::gray(format!("({}ms)", duration)) colors::gray(format!("({}ms)", duration))
); );
self.passed += 1;
} }
TestResult::Ignored => { TestResult::Ignored => {
println!( println!(
" {} {}", " {} {}",
colors::yellow("ignored"), colors::yellow("ignored"),
colors::gray(format!("({}ms)", duration)) colors::gray(format!("({}ms)", duration))
); );
self.ignored += 1;
} }
TestResult::Failed(error) => {
TestResult::Failed(_) => {
println!( println!(
" {} {}", " {} {}",
colors::red("FAILED"), colors::red("FAILED"),
colors::gray(format!("({}ms)", duration)) colors::gray(format!("({}ms)", duration))
); );
self.failed += 1;
self.failures.push((name.to_string(), error.to_string()));
} }
} }
} }
} }
} }
fn done(&mut self) { fn done(&mut self, summary: &TestSummary, elapsed: &Duration) {
if !self.failures.is_empty() { if !summary.failures.is_empty() {
println!("\nfailures:\n"); println!("\nfailures:\n");
for (name, error) in &self.failures { for (name, error) in &summary.failures {
println!("{}", name); println!("{}", name);
println!("{}", error); println!("{}", error);
println!(); println!();
} }
println!("failures:\n"); println!("failures:\n");
for (name, _) in &self.failures { for (name, _) in &summary.failures {
println!("\t{}", name); println!("\t{}", name);
} }
} }
let status = if self.pending > 0 || !self.failures.is_empty() { let status = if summary.has_failed() || summary.has_pending() {
colors::red("FAILED").to_string() colors::red("FAILED").to_string()
} else { } else {
colors::green("ok").to_string() colors::green("ok").to_string()
@ -192,12 +198,12 @@ impl TestReporter for PrettyTestReporter {
println!( println!(
"\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n", "\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n",
status, status,
self.passed, summary.passed,
self.failed, summary.failed,
self.ignored, summary.ignored,
self.measured, summary.measured,
self.filtered_out, summary.filtered_out,
colors::gray(format!("({}ms)", self.time.elapsed().as_millis())), colors::gray(format!("({}ms)", elapsed.as_millis())),
); );
} }
} }
@ -511,69 +517,66 @@ pub async fn run_tests(
let mut reporter = create_reporter(concurrent_jobs > 1); let mut reporter = create_reporter(concurrent_jobs > 1);
let handler = { let handler = {
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let earlier = Instant::now();
let mut summary = TestSummary::new();
let mut used_only = false; let mut used_only = false;
let mut has_error = false;
let mut planned = 0;
let mut reported = 0;
let mut failed = 0;
for event in receiver.iter() { for event in receiver.iter() {
match event.message.clone() { match event.message.clone() {
TestMessage::Plan { TestMessage::Plan {
pending, pending,
filtered: _, filtered,
only, only,
} => { } => {
summary.total += pending;
summary.filtered_out += filtered;
if only { if only {
used_only = true; used_only = true;
} }
planned += pending;
} }
TestMessage::Result { TestMessage::Result {
name: _, name,
duration: _, duration: _,
result, result,
} => { } => match result {
reported += 1; TestResult::Ok => {
summary.passed += 1;
if let TestResult::Failed(_) = result {
has_error = true;
failed += 1;
} }
}
TestResult::Ignored => {
summary.ignored += 1;
}
TestResult::Failed(error) => {
summary.failed += 1;
summary.failures.push((name.clone(), error.clone()));
}
},
_ => {} _ => {}
} }
reporter.visit_event(event); reporter.visit_event(event);
if let Some(x) = fail_fast { if let Some(x) = fail_fast {
if failed >= x { if summary.failed >= x {
break; break;
} }
} }
} }
if planned > reported { let elapsed = Instant::now().duration_since(earlier);
has_error = true; reporter.done(&summary, &elapsed);
}
reporter.done();
if planned > reported {
has_error = true;
}
if used_only { if used_only {
println!( println!(
"{} because the \"only\" option was used\n", "{} because the \"only\" option was used\n",
colors::red("FAILED") colors::red("FAILED")
); );
has_error = true;
} }
has_error used_only || summary.failed > 0
}) })
}; };