diff options
Diffstat (limited to 'cli/tools/test.rs')
-rw-r--r-- | cli/tools/test.rs | 342 |
1 files changed, 201 insertions, 141 deletions
diff --git a/cli/tools/test.rs b/cli/tools/test.rs index 964dbe0e8..1f3f4494f 100644 --- a/cli/tools/test.rs +++ b/cli/tools/test.rs @@ -377,6 +377,49 @@ impl TestSummary { } } +trait TestReporter { + fn report_register(&mut self, description: &TestDescription); + fn report_plan(&mut self, plan: &TestPlan); + fn report_wait(&mut self, description: &TestDescription); + fn report_output(&mut self, output: &[u8]); + fn report_result( + &mut self, + description: &TestDescription, + result: &TestResult, + elapsed: u64, + ); + fn report_uncaught_error(&mut self, origin: &str, error: Box<JsError>); + fn report_step_register(&mut self, description: &TestStepDescription); + fn report_step_wait(&mut self, description: &TestStepDescription); + fn report_step_result( + &mut self, + desc: &TestStepDescription, + result: &TestStepResult, + elapsed: u64, + tests: &IndexMap<usize, TestDescription>, + test_steps: &IndexMap<usize, TestStepDescription>, + ); + fn report_summary( + &mut self, + elapsed: &Duration, + tests: &IndexMap<usize, TestDescription>, + test_steps: &IndexMap<usize, TestStepDescription>, + ); + fn report_sigint( + &mut self, + tests_pending: &HashSet<usize>, + tests: &IndexMap<usize, TestDescription>, + test_steps: &IndexMap<usize, TestStepDescription>, + ); +} + +fn get_test_reporter(options: &TestSpecifiersOptions) -> Box<dyn TestReporter> { + Box::new(PrettyTestReporter::new( + options.concurrent_jobs.get() > 1, + options.log_level != Some(Level::Error), + )) +} + struct PrettyTestReporter { parallel: bool, echo_output: bool, @@ -387,6 +430,7 @@ struct PrettyTestReporter { started_tests: bool, child_results_buffer: HashMap<usize, IndexMap<usize, (TestStepDescription, TestStepResult, u64)>>, + summary: TestSummary, } impl PrettyTestReporter { @@ -400,6 +444,7 @@ impl PrettyTestReporter { did_have_user_output: false, started_tests: false, child_results_buffer: Default::default(), + summary: TestSummary::new(), } } @@ -511,9 +556,74 @@ impl PrettyTestReporter { } } - fn report_register(&mut self, _description: &TestDescription) {} + fn format_test_step_ancestry( + &self, + desc: &TestStepDescription, + tests: &IndexMap<usize, TestDescription>, + test_steps: &IndexMap<usize, TestStepDescription>, + ) -> String { + let root; + let mut ancestor_names = vec![]; + let mut current_desc = desc; + loop { + if let Some(step_desc) = test_steps.get(¤t_desc.parent_id) { + ancestor_names.push(&step_desc.name); + current_desc = step_desc; + } else { + root = tests.get(¤t_desc.parent_id).unwrap(); + break; + } + } + ancestor_names.reverse(); + let mut result = String::new(); + result.push_str(&root.name); + result.push_str(" ... "); + for name in ancestor_names { + result.push_str(name); + result.push_str(" ... "); + } + result.push_str(&desc.name); + result + } + + fn format_test_for_summary(&self, desc: &TestDescription) -> String { + format!( + "{} {}", + &desc.name, + colors::gray(format!( + "=> {}:{}:{}", + self.to_relative_path_or_remote_url(&desc.location.file_name), + desc.location.line_number, + desc.location.column_number + )) + ) + } + + fn format_test_step_for_summary( + &self, + desc: &TestStepDescription, + tests: &IndexMap<usize, TestDescription>, + test_steps: &IndexMap<usize, TestStepDescription>, + ) -> String { + let long_name = self.format_test_step_ancestry(desc, tests, test_steps); + format!( + "{} {}", + long_name, + colors::gray(format!( + "=> {}:{}:{}", + self.to_relative_path_or_remote_url(&desc.location.file_name), + desc.location.line_number, + desc.location.column_number + )) + ) + } +} +impl TestReporter for PrettyTestReporter { + fn report_register(&mut self, _description: &TestDescription) {} fn report_plan(&mut self, plan: &TestPlan) { + self.summary.total += plan.total; + self.summary.filtered_out += plan.filtered_out; if self.parallel { return; } @@ -562,6 +672,25 @@ impl PrettyTestReporter { result: &TestResult, elapsed: u64, ) { + match &result { + TestResult::Ok => { + self.summary.passed += 1; + } + TestResult::Ignored => { + self.summary.ignored += 1; + } + TestResult::Failed(failure) => { + self.summary.failed += 1; + self + .summary + .failures + .push((description.clone(), failure.clone())); + } + TestResult::Cancelled => { + self.summary.failed += 1; + } + } + if self.parallel { self.force_report_wait(description); } @@ -591,7 +720,13 @@ impl PrettyTestReporter { self.scope_test_id = None; } - fn report_uncaught_error(&mut self, origin: &str, _error: &JsError) { + fn report_uncaught_error(&mut self, origin: &str, error: Box<JsError>) { + self.summary.failed += 1; + self + .summary + .uncaught_errors + .push((origin.to_string(), error)); + if !self.in_new_line { println!(); } @@ -620,6 +755,29 @@ impl PrettyTestReporter { tests: &IndexMap<usize, TestDescription>, test_steps: &IndexMap<usize, TestStepDescription>, ) { + match &result { + TestStepResult::Ok => { + self.summary.passed_steps += 1; + } + TestStepResult::Ignored => { + self.summary.ignored_steps += 1; + } + TestStepResult::Failed(failure) => { + self.summary.failed_steps += 1; + self.summary.failures.push(( + TestDescription { + id: desc.id, + name: self.format_test_step_ancestry(desc, tests, test_steps), + ignore: false, + only: false, + origin: desc.origin.clone(), + location: desc.location.clone(), + }, + failure.clone(), + )) + } + } + if self.parallel { self.write_output_end(); print!( @@ -652,21 +810,29 @@ impl PrettyTestReporter { } } - fn report_summary(&mut self, summary: &TestSummary, elapsed: &Duration) { - if !summary.failures.is_empty() || !summary.uncaught_errors.is_empty() { + fn report_summary( + &mut self, + elapsed: &Duration, + _tests: &IndexMap<usize, TestDescription>, + _test_steps: &IndexMap<usize, TestStepDescription>, + ) { + if !self.summary.failures.is_empty() + || !self.summary.uncaught_errors.is_empty() + { #[allow(clippy::type_complexity)] // Type alias doesn't look better here let mut failures_by_origin: BTreeMap< String, (Vec<(&TestDescription, &TestFailure)>, Option<&JsError>), > = BTreeMap::default(); let mut failure_titles = vec![]; - for (description, failure) in &summary.failures { + for (description, failure) in &self.summary.failures { let (failures, _) = failures_by_origin .entry(description.origin.clone()) .or_default(); failures.push((description, failure)); } - for (origin, js_error) in &summary.uncaught_errors { + + for (origin, js_error) in &self.summary.uncaught_errors { let (_, uncaught_error) = failures_by_origin.entry(origin.clone()).or_default(); let _ = uncaught_error.insert(js_error.as_ref()); @@ -707,7 +873,7 @@ impl PrettyTestReporter { } } - let status = if summary.has_failed() { + let status = if self.summary.has_failed() { colors::red("FAILED").to_string() } else { colors::green("ok").to_string() @@ -728,30 +894,34 @@ impl PrettyTestReporter { write!( summary_result, "{} passed{} | {} failed{}", - summary.passed, - get_steps_text(summary.passed_steps), - summary.failed, - get_steps_text(summary.failed_steps), + self.summary.passed, + get_steps_text(self.summary.passed_steps), + self.summary.failed, + get_steps_text(self.summary.failed_steps), ) .unwrap(); - let ignored_steps = get_steps_text(summary.ignored_steps); - if summary.ignored > 0 || !ignored_steps.is_empty() { + let ignored_steps = get_steps_text(self.summary.ignored_steps); + if self.summary.ignored > 0 || !ignored_steps.is_empty() { write!( summary_result, " | {} ignored{}", - summary.ignored, ignored_steps + self.summary.ignored, ignored_steps ) .unwrap() } - if summary.measured > 0 { - write!(summary_result, " | {} measured", summary.measured,).unwrap(); + if self.summary.measured > 0 { + write!(summary_result, " | {} measured", self.summary.measured,).unwrap(); } - if summary.filtered_out > 0 { - write!(summary_result, " | {} filtered out", summary.filtered_out) - .unwrap() + if self.summary.filtered_out > 0 { + write!( + summary_result, + " | {} filtered out", + self.summary.filtered_out + ) + .unwrap() }; println!( @@ -795,68 +965,6 @@ impl PrettyTestReporter { println!(); self.in_new_line = true; } - - fn format_test_step_ancestry( - &self, - desc: &TestStepDescription, - tests: &IndexMap<usize, TestDescription>, - test_steps: &IndexMap<usize, TestStepDescription>, - ) -> String { - let root; - let mut ancestor_names = vec![]; - let mut current_desc = desc; - loop { - if let Some(step_desc) = test_steps.get(¤t_desc.parent_id) { - ancestor_names.push(&step_desc.name); - current_desc = step_desc; - } else { - root = tests.get(¤t_desc.parent_id).unwrap(); - break; - } - } - ancestor_names.reverse(); - let mut result = String::new(); - result.push_str(&root.name); - result.push_str(" ... "); - for name in ancestor_names { - result.push_str(name); - result.push_str(" ... "); - } - result.push_str(&desc.name); - result - } - - fn format_test_for_summary(&self, desc: &TestDescription) -> String { - format!( - "{} {}", - &desc.name, - colors::gray(format!( - "=> {}:{}:{}", - self.to_relative_path_or_remote_url(&desc.location.file_name), - desc.location.line_number, - desc.location.column_number - )) - ) - } - - fn format_test_step_for_summary( - &self, - desc: &TestStepDescription, - tests: &IndexMap<usize, TestDescription>, - test_steps: &IndexMap<usize, TestStepDescription>, - ) -> String { - let long_name = self.format_test_step_ancestry(desc, tests, test_steps); - format!( - "{} {}", - long_name, - colors::gray(format!( - "=> {}:{}:{}", - self.to_relative_path_or_remote_url(&desc.location.file_name), - desc.location.line_number, - desc.location.column_number - )) - ) - } } fn abbreviate_test_error(js_error: &JsError) -> JsError { @@ -1339,6 +1447,7 @@ async fn test_specifiers( sender_.upgrade().map(|s| s.send(TestEvent::Sigint).ok()); }); HAS_TEST_RUN_SIGINT_HANDLER.store(true, Ordering::Relaxed); + let mut reporter = get_test_reporter(&options); let join_handles = specifiers.into_iter().map(move |specifier| { let worker_factory = worker_factory.clone(); @@ -1362,11 +1471,6 @@ async fn test_specifiers( .buffer_unordered(concurrent_jobs.get()) .collect::<Vec<Result<Result<(), AnyError>, tokio::task::JoinError>>>(); - let mut reporter = Box::new(PrettyTestReporter::new( - concurrent_jobs.get() > 1, - options.log_level != Some(Level::Error), - )); - let handler = { spawn(async move { let earlier = Instant::now(); @@ -1374,8 +1478,8 @@ async fn test_specifiers( let mut test_steps = IndexMap::new(); let mut tests_started = HashSet::new(); let mut tests_with_result = HashSet::new(); - let mut summary = TestSummary::new(); let mut used_only = false; + let mut failed = false; while let Some(event) = receiver.recv().await { match event { @@ -1385,9 +1489,6 @@ async fn test_specifiers( } TestEvent::Plan(plan) => { - summary.total += plan.total; - summary.filtered_out += plan.filtered_out; - if plan.used_only { used_only = true; } @@ -1407,32 +1508,19 @@ async fn test_specifiers( TestEvent::Result(id, result, elapsed) => { if tests_with_result.insert(id) { - let description = tests.get(&id).unwrap(); - match &result { - TestResult::Ok => { - summary.passed += 1; - } - TestResult::Ignored => { - summary.ignored += 1; - } - TestResult::Failed(failure) => { - summary.failed += 1; - summary - .failures - .push((description.clone(), failure.clone())); - } - TestResult::Cancelled => { - summary.failed += 1; + match result { + TestResult::Failed(_) | TestResult::Cancelled => { + failed = true; } + _ => (), } - reporter.report_result(description, &result, elapsed); + reporter.report_result(tests.get(&id).unwrap(), &result, elapsed); } } TestEvent::UncaughtError(origin, error) => { - reporter.report_uncaught_error(&origin, &error); - summary.failed += 1; - summary.uncaught_errors.push((origin.clone(), error)); + failed = true; + reporter.report_uncaught_error(&origin, error); } TestEvent::StepRegister(description) => { @@ -1448,36 +1536,8 @@ async fn test_specifiers( TestEvent::StepResult(id, result, duration) => { if tests_with_result.insert(id) { - let description = test_steps.get(&id).unwrap(); - match &result { - TestStepResult::Ok => { - summary.passed_steps += 1; - } - TestStepResult::Ignored => { - summary.ignored_steps += 1; - } - TestStepResult::Failed(failure) => { - summary.failed_steps += 1; - summary.failures.push(( - TestDescription { - id: description.id, - name: reporter.format_test_step_ancestry( - description, - &tests, - &test_steps, - ), - ignore: false, - only: false, - origin: description.origin.clone(), - location: description.location.clone(), - }, - failure.clone(), - )) - } - } - reporter.report_step_result( - description, + test_steps.get(&id).unwrap(), &result, duration, &tests, @@ -1504,7 +1564,7 @@ async fn test_specifiers( HAS_TEST_RUN_SIGINT_HANDLER.store(false, Ordering::Relaxed); let elapsed = Instant::now().duration_since(earlier); - reporter.report_summary(&summary, &elapsed); + reporter.report_summary(&elapsed, &tests, &test_steps); if used_only { return Err(generic_error( @@ -1512,7 +1572,7 @@ async fn test_specifiers( )); } - if summary.failed > 0 { + if failed { return Err(generic_error("Test failed")); } |