feat(test): Represent uncaught errors (#14513)

This commit adds better reporting of uncaught errors
in top level scope of testing files. This change affects
both console runner as well as LSP runner.
This commit is contained in:
Nayeem Rahman 2022-05-09 10:44:50 +01:00 committed by GitHub
parent ab728e9ccf
commit 23efc4fcab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 316 additions and 53 deletions

View file

@ -20,6 +20,7 @@ use crate::tools::test::TestEventSender;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::error::JsError;
use deno_core::futures::future;
use deno_core::futures::stream;
use deno_core::futures::StreamExt;
@ -181,7 +182,7 @@ async fn test_specifier(
permissions: Permissions,
specifier: ModuleSpecifier,
mode: test::TestMode,
sender: TestEventSender,
sender: &TestEventSender,
token: CancellationToken,
options: Option<Value>,
) -> Result<(), AnyError> {
@ -337,22 +338,32 @@ impl TestRun {
let specifier = specifier.clone();
let ps = ps.clone();
let permissions = permissions.clone();
let sender = sender.clone();
let mut sender = sender.clone();
let options = self.filters.get(&specifier).map(|f| f.as_test_options());
let token = self.token.clone();
tokio::task::spawn_blocking(move || {
let future = test_specifier(
let origin = specifier.to_string();
let file_result = run_basic(test_specifier(
ps,
permissions,
specifier,
test::TestMode::Executable,
sender,
&sender,
token,
options,
);
run_basic(future)
));
if let Err(error) = file_result {
if error.is::<JsError>() {
sender.send(test::TestEvent::UncaughtError(
origin,
Box::new(error.downcast::<JsError>().unwrap()),
))?;
} else {
return Err(error);
}
}
Ok(())
})
});
@ -404,6 +415,11 @@ impl TestRun {
reporter.report_result(&description, &result, elapsed);
}
test::TestEvent::UncaughtError(origin, error) => {
reporter.report_uncaught_error(&origin, &error);
summary.failed += 1;
summary.uncaught_errors.push((origin, error));
}
test::TestEvent::StepWait(description) => {
reporter.report_step_wait(&description);
}
@ -805,6 +821,37 @@ impl test::TestReporter for LspTestReporter {
}
}
fn report_uncaught_error(&mut self, origin: &str, js_error: &JsError) {
if self.current_origin == Some(origin.to_string()) {
self.current_origin = None;
}
let stack = self.stack.remove(origin).unwrap_or_default();
let err_string = format!(
"Uncaught error from {}: {}\nThis error was not caught from a test and caused the test runner to fail on the referenced module.\nIt most likely originated from a dangling promise, event/timeout handler or top-level code.",
origin,
test::format_test_error(js_error)
);
let messages = as_test_messages(err_string, false);
for t in stack.iter().rev() {
match t {
TestOrTestStepDescription::TestDescription(desc) => {
self.progress(lsp_custom::TestRunProgressMessage::Failed {
test: desc.into(),
messages: messages.clone(),
duration: None,
});
}
TestOrTestStepDescription::TestStepDescription(desc) => {
self.progress(lsp_custom::TestRunProgressMessage::Failed {
test: desc.into(),
messages: messages.clone(),
duration: None,
});
}
}
}
}
fn report_step_wait(&mut self, desc: &test::TestStepDescription) {
if !self.includes_step(desc) {
self.add_step(desc);