summaryrefslogtreecommitdiff
path: root/cli/tools/test.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tools/test.rs')
-rw-r--r--cli/tools/test.rs342
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(&current_desc.parent_id) {
+ ancestor_names.push(&step_desc.name);
+ current_desc = step_desc;
+ } else {
+ root = tests.get(&current_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(&current_desc.parent_id) {
- ancestor_names.push(&step_desc.name);
- current_desc = step_desc;
- } else {
- root = tests.get(&current_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"));
}