mirror of
https://github.com/denoland/deno.git
synced 2025-03-04 01:44:26 -05:00
refactor(cli): move test reporting into trait (#10408)
This commit is contained in:
parent
b9a136c868
commit
ec0d3b6128
1 changed files with 154 additions and 88 deletions
|
@ -25,6 +25,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::Instant;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
@ -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 {
|
fn is_supported(p: &Path) -> bool {
|
||||||
use std::path::Component;
|
use std::path::Component;
|
||||||
if let Some(Component::Normal(basename_os_str)) = p.components().next_back() {
|
if let Some(Component::Normal(basename_os_str)) = p.components().next_back() {
|
||||||
|
@ -141,6 +277,7 @@ pub async fn run_test_file(
|
||||||
|
|
||||||
let execute_result = worker.execute_module(&main_module).await;
|
let execute_result = worker.execute_module(&main_module).await;
|
||||||
execute_result?;
|
execute_result?;
|
||||||
|
|
||||||
worker.execute("window.dispatchEvent(new Event('load'))")?;
|
worker.execute("window.dispatchEvent(new Event('load'))")?;
|
||||||
|
|
||||||
let execute_result = worker.execute_module(&test_module).await;
|
let execute_result = worker.execute_module(&test_module).await;
|
||||||
|
@ -250,129 +387,58 @@ pub async fn run_tests(
|
||||||
.buffer_unordered(concurrent_jobs)
|
.buffer_unordered(concurrent_jobs)
|
||||||
.collect::<Vec<Result<Result<(), AnyError>, tokio::task::JoinError>>>();
|
.collect::<Vec<Result<Result<(), AnyError>, tokio::task::JoinError>>>();
|
||||||
|
|
||||||
|
let mut reporter = create_reporter(concurrent_jobs > 1);
|
||||||
let handler = {
|
let handler = {
|
||||||
tokio::task::spawn_blocking(move || {
|
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 used_only = false;
|
||||||
let mut has_error = 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() {
|
for message in receiver.iter() {
|
||||||
match message {
|
match message.clone() {
|
||||||
TestMessage::Plan {
|
TestMessage::Plan {
|
||||||
pending,
|
pending,
|
||||||
filtered,
|
filtered: _,
|
||||||
only,
|
only,
|
||||||
} => {
|
} => {
|
||||||
println!("running {} tests", pending);
|
|
||||||
|
|
||||||
if only {
|
if only {
|
||||||
used_only = true;
|
used_only = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
planned += pending;
|
planned += pending;
|
||||||
filtered_out += filtered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TestMessage::Wait { name } => {
|
|
||||||
if concurrent_jobs == 1 {
|
|
||||||
print!("test {} ...", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TestMessage::Result {
|
TestMessage::Result {
|
||||||
name,
|
name: _,
|
||||||
duration,
|
duration: _,
|
||||||
result,
|
result,
|
||||||
} => {
|
} => {
|
||||||
if concurrent_jobs != 1 {
|
reported += 1;
|
||||||
print!("test {} ...", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
match result {
|
if let TestResult::Failed(_) = result {
|
||||||
TestResult::Ok => {
|
has_error = true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reporter.visit_message(message);
|
||||||
|
|
||||||
if has_error && fail_fast {
|
if has_error && fail_fast {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If one of the workers panic then we can end up with less test results than what was
|
if planned > reported {
|
||||||
// planned.
|
|
||||||
// In that case we mark it as an error so that it will be reported as failed.
|
|
||||||
if planned > passed + ignored + failed {
|
|
||||||
has_error = true;
|
has_error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !failures.is_empty() {
|
reporter.done();
|
||||||
println!("\nfailures:\n");
|
|
||||||
for (name, error) in &failures {
|
|
||||||
println!("{}", name);
|
|
||||||
println!("{}", error);
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("failures:\n");
|
if planned > reported {
|
||||||
for (name, _) in &failures {
|
has_error = true;
|
||||||
println!("\t{}", name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
if used_only {
|
||||||
println!(
|
println!(
|
||||||
"{} because the \"only\" option was used\n",
|
"{} because the \"only\" option was used\n",
|
||||||
|
|
Loading…
Add table
Reference in a new issue