diff --git a/cli/flags.rs b/cli/flags.rs index 400798cbd1..2d71762ceb 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -100,6 +100,7 @@ pub enum DenoSubcommand { no_run: bool, fail_fast: bool, quiet: bool, + terse: bool, allow_none: bool, include: Option>, filter: Option, @@ -1010,6 +1011,11 @@ fn test_subcommand<'a, 'b>() -> App<'a, 'b> { .help("Don't return error code if no test files are found") .takes_value(false), ) + .arg( + Arg::with_name("terse") + .long("terse") + .help("UNSTABLE: Display one character per test instead of one line"), + ) .arg( Arg::with_name("filter") .set(ArgSettings::AllowLeadingHyphen) @@ -1699,6 +1705,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) { let fail_fast = matches.is_present("fail-fast"); let allow_none = matches.is_present("allow-none"); let quiet = matches.is_present("quiet"); + let terse = matches.is_present("terse"); let filter = matches.value_of("filter").map(String::from); let shuffle = if matches.is_present("shuffle") { @@ -1753,6 +1760,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) { doc, fail_fast, quiet, + terse, include, filter, shuffle, @@ -3391,6 +3399,7 @@ mod tests { filter: Some("- foo".to_string()), allow_none: true, quiet: false, + terse: false, include: Some(svec!["dir1/", "dir2/"]), shuffle: None, concurrent_jobs: 1, diff --git a/cli/main.rs b/cli/main.rs index 93bae02206..df66293911 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -988,6 +988,7 @@ async fn test_command( doc: bool, fail_fast: bool, quiet: bool, + terse: bool, allow_none: bool, filter: Option, shuffle: Option, @@ -1205,6 +1206,7 @@ async fn test_command( no_run, fail_fast, quiet, + terse, true, filter.clone(), shuffle, @@ -1243,6 +1245,7 @@ async fn test_command( no_run, fail_fast, quiet, + terse, allow_none, filter, shuffle, @@ -1350,6 +1353,7 @@ fn get_subcommand( doc, fail_fast, quiet, + terse, include, allow_none, filter, @@ -1362,6 +1366,7 @@ fn get_subcommand( doc, fail_fast, quiet, + terse, allow_none, filter, shuffle, diff --git a/cli/tests/integration/test_tests.rs b/cli/tests/integration/test_tests.rs index c029b154d0..134a2d47b6 100644 --- a/cli/tests/integration/test_tests.rs +++ b/cli/tests/integration/test_tests.rs @@ -49,6 +49,12 @@ itest!(doc { output: "test/doc.out", }); +itest!(terse { + args: "test --terse test/terse.ts", + exit_code: 1, + output: "test/terse.out", +}); + itest!(quiet { args: "test --quiet test/quiet.ts", exit_code: 0, diff --git a/cli/tests/test/terse.out b/cli/tests/test/terse.out new file mode 100644 index 0000000000..3d5deaf395 --- /dev/null +++ b/cli/tests/test/terse.out @@ -0,0 +1,11 @@ +Check [WILDCARD]/test/terse.ts +running 3 tests from [WILDCARD] +.Fi +failures: + +fail +Error: fail + [WILDCARD] + +test result: FAILED. 1 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out [WILDCARD] + diff --git a/cli/tests/test/terse.ts b/cli/tests/test/terse.ts new file mode 100644 index 0000000000..b2168a0d9e --- /dev/null +++ b/cli/tests/test/terse.ts @@ -0,0 +1,11 @@ +Deno.test("ok", function () {}); + +Deno.test("fail", function () { + throw new Error("fail"); +}); + +Deno.test({ + name: "ignore", + fn() {}, + ignore: true, +}); diff --git a/cli/tools/test_runner.rs b/cli/tools/test_runner.rs index d1a0c6d656..4a8d42e82e 100644 --- a/cli/tools/test_runner.rs +++ b/cli/tools/test_runner.rs @@ -202,8 +202,128 @@ impl TestReporter for PrettyTestReporter { } } -fn create_reporter(concurrent: bool) -> Box { - Box::new(PrettyTestReporter::new(concurrent)) +struct TerseTestReporter { + time: Instant, + failed: usize, + filtered_out: usize, + ignored: usize, + passed: usize, + measured: usize, + pending: usize, + failures: Vec<(String, String)>, +} + +impl TerseTestReporter { + fn new() -> TerseTestReporter { + TerseTestReporter { + time: Instant::now(), + failed: 0, + filtered_out: 0, + ignored: 0, + passed: 0, + measured: 0, + pending: 0, + failures: Vec::new(), + } + } +} + +impl TestReporter for TerseTestReporter { + fn visit_event(&mut self, event: TestEvent) { + match &event.message { + TestMessage::Plan { + pending, + filtered, + only: _, + } => { + if *pending == 1 { + println!("running {} test from {}", pending, event.origin); + } else { + println!("running {} tests from {}", pending, event.origin); + } + + self.pending += pending; + self.filtered_out += filtered; + } + + TestMessage::Result { + name, + duration: _, + result, + } => { + self.pending -= 1; + + match result { + TestResult::Ok => { + print!("{}", colors::green("."),); + + self.passed += 1; + } + TestResult::Ignored => { + print!("{}", colors::yellow("i"),); + + self.ignored += 1; + } + TestResult::Failed(error) => { + print!("{}", colors::red("F"),); + + 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())), + ); + } +} + +enum TestReporterKind { + Pretty, + Terse, +} + +fn create_reporter( + kind: TestReporterKind, + concurrent: bool, +) -> Box { + match kind { + TestReporterKind::Pretty => Box::new(PrettyTestReporter::new(concurrent)), + TestReporterKind::Terse => Box::new(TerseTestReporter::new()), + } } pub(crate) fn is_supported(p: &Path) -> bool { @@ -344,6 +464,7 @@ pub async fn run_tests( no_run: bool, fail_fast: bool, quiet: bool, + terse: bool, allow_none: bool, filter: Option, shuffle: Option, @@ -510,7 +631,13 @@ pub async fn run_tests( .buffer_unordered(concurrent_jobs) .collect::, tokio::task::JoinError>>>(); - let mut reporter = create_reporter(concurrent_jobs > 1); + let reporter_kind = if terse { + TestReporterKind::Terse + } else { + TestReporterKind::Pretty + }; + + let mut reporter = create_reporter(reporter_kind, concurrent_jobs > 1); let handler = { tokio::task::spawn_blocking(move || { let mut used_only = false;