1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat(cli): Add slow test warning (#23874)

By default, uses a 60 second timeout, backing off 2x each time (can be
overridden using the hidden `DENO_SLOW_TEST_TIMEOUT` which we implement
only really for spec testing.

```
Deno.test(async function test() {
  await new Promise(r => setTimeout(r, 130_000));
});
```

```
$ target/debug/deno test /tmp/test_slow.ts 
Check file:///tmp/test_slow.ts
running 1 test from ../../../../../../tmp/test_slow.ts
test ...'test' is running very slowly (1m0s)
'test' is running very slowly (2m0s)
 ok (2m10s)

ok | 1 passed | 0 failed (2m10s)
```

---------

Signed-off-by: Matt Mastracci <matthew@mastracci.com>
Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
Matt Mastracci 2024-05-22 08:08:27 -06:00 committed by GitHub
parent 7ab7a14db7
commit 596a2996cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 83 additions and 3 deletions

View file

@ -353,6 +353,9 @@ impl TestRun {
test::TestEvent::Output(_, output) => { test::TestEvent::Output(_, output) => {
reporter.report_output(&output); reporter.report_output(&output);
} }
test::TestEvent::Slow(id, elapsed) => {
reporter.report_slow(tests.read().get(&id).unwrap(), elapsed);
}
test::TestEvent::Result(id, result, elapsed) => { test::TestEvent::Result(id, result, elapsed) => {
if tests_with_result.insert(id) { if tests_with_result.insert(id) {
let description = tests.read().get(&id).unwrap().clone(); let description = tests.read().get(&id).unwrap().clone();
@ -610,6 +613,8 @@ impl LspTestReporter {
self.progress(lsp_custom::TestRunProgressMessage::Started { test }); self.progress(lsp_custom::TestRunProgressMessage::Started { test });
} }
fn report_slow(&mut self, _desc: &test::TestDescription, _elapsed: u64) {}
fn report_output(&mut self, output: &[u8]) { fn report_output(&mut self, output: &[u8]) {
let test = self let test = self
.current_test .current_test

View file

@ -74,6 +74,7 @@ use std::collections::BTreeMap;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::env;
use std::fmt::Write as _; use std::fmt::Write as _;
use std::future::poll_fn; use std::future::poll_fn;
use std::io::Write; use std::io::Write;
@ -454,6 +455,7 @@ pub enum TestEvent {
Plan(TestPlan), Plan(TestPlan),
Wait(usize), Wait(usize),
Output(TestStdioStream, Vec<u8>), Output(TestStdioStream, Vec<u8>),
Slow(usize, u64),
Result(usize, TestResult, u64), Result(usize, TestResult, u64),
UncaughtError(String, Box<JsError>), UncaughtError(String, Box<JsError>),
StepRegister(TestStepDescription), StepRegister(TestStepDescription),
@ -912,11 +914,44 @@ async fn run_tests_for_worker_inner(
let earlier = Instant::now(); let earlier = Instant::now();
let call = worker.js_runtime.call(&function); let call = worker.js_runtime.call(&function);
let result = match worker
let slow_state_rc = state_rc.clone();
let slow_test_id = desc.id;
let slow_test_warning = spawn(async move {
// The slow test warning should pop up every DENO_SLOW_TEST_TIMEOUT*(2**n) seconds,
// with a duration that is doubling each time. So for a warning time of 60s,
// we should get a warning at 60s, 120s, 240s, etc.
let base_timeout = env::var("DENO_SLOW_TEST_TIMEOUT").unwrap_or_default();
let base_timeout = base_timeout.parse().unwrap_or(60).max(1);
let mut multiplier = 1;
let mut elapsed = 0;
loop {
tokio::time::sleep(Duration::from_secs(
base_timeout * (multiplier - elapsed),
))
.await;
if send_test_event(
&slow_state_rc,
TestEvent::Slow(
slow_test_id,
Duration::from_secs(base_timeout * multiplier).as_millis() as _,
),
)
.is_err()
{
break;
}
multiplier *= 2;
elapsed += 1;
}
});
let result = worker
.js_runtime .js_runtime
.with_event_loop_promise(call, PollEventLoopOptions::default()) .with_event_loop_promise(call, PollEventLoopOptions::default())
.await .await;
{ slow_test_warning.abort();
let result = match result {
Ok(r) => r, Ok(r) => r,
Err(error) => { Err(error) => {
if error.is::<JsError>() { if error.is::<JsError>() {
@ -1458,6 +1493,9 @@ pub async fn report_tests(
TestEvent::Output(_, output) => { TestEvent::Output(_, output) => {
reporter.report_output(&output); reporter.report_output(&output);
} }
TestEvent::Slow(id, elapsed) => {
reporter.report_slow(tests.get(&id).unwrap(), elapsed);
}
TestEvent::Result(id, result, elapsed) => { TestEvent::Result(id, result, elapsed) => {
if tests_with_result.insert(id) { if tests_with_result.insert(id) {
match result { match result {

View file

@ -31,6 +31,12 @@ impl TestReporter for CompoundTestReporter {
} }
} }
fn report_slow(&mut self, description: &TestDescription, elapsed: u64) {
for reporter in &mut self.test_reporters {
reporter.report_slow(description, elapsed);
}
}
fn report_output(&mut self, output: &[u8]) { fn report_output(&mut self, output: &[u8]) {
for reporter in &mut self.test_reporters { for reporter in &mut self.test_reporters {
reporter.report_output(output); reporter.report_output(output);

View file

@ -95,6 +95,7 @@ impl TestReporter for DotTestReporter {
std::io::stdout().flush().unwrap(); std::io::stdout().flush().unwrap();
} }
fn report_slow(&mut self, _description: &TestDescription, _elapsed: u64) {}
fn report_output(&mut self, _output: &[u8]) {} fn report_output(&mut self, _output: &[u8]) {}
fn report_result( fn report_result(

View file

@ -92,6 +92,7 @@ impl TestReporter for JunitTestReporter {
fn report_plan(&mut self, _plan: &TestPlan) {} fn report_plan(&mut self, _plan: &TestPlan) {}
fn report_slow(&mut self, _description: &TestDescription, _elapsed: u64) {}
fn report_wait(&mut self, _description: &TestDescription) {} fn report_wait(&mut self, _description: &TestDescription) {}
fn report_output(&mut self, _output: &[u8]) { fn report_output(&mut self, _output: &[u8]) {

View file

@ -19,6 +19,7 @@ pub trait TestReporter {
fn report_register(&mut self, description: &TestDescription); fn report_register(&mut self, description: &TestDescription);
fn report_plan(&mut self, plan: &TestPlan); fn report_plan(&mut self, plan: &TestPlan);
fn report_wait(&mut self, description: &TestDescription); fn report_wait(&mut self, description: &TestDescription);
fn report_slow(&mut self, description: &TestDescription, elapsed: u64);
fn report_output(&mut self, output: &[u8]); fn report_output(&mut self, output: &[u8]);
fn report_result( fn report_result(
&mut self, &mut self,

View file

@ -197,6 +197,18 @@ impl TestReporter for PrettyTestReporter {
self.started_tests = true; self.started_tests = true;
} }
fn report_slow(&mut self, description: &TestDescription, elapsed: u64) {
writeln!(
&mut self.writer,
"{}",
colors::yellow_bold(format!(
"'{}' has been running for over {}",
description.name,
colors::gray(format!("({})", display::human_elapsed(elapsed.into()))),
))
)
.unwrap();
}
fn report_output(&mut self, output: &[u8]) { fn report_output(&mut self, output: &[u8]) {
if !self.echo_output { if !self.echo_output {
return; return;

View file

@ -140,6 +140,7 @@ impl TestReporter for TapTestReporter {
std::io::stdout().flush().unwrap(); std::io::stdout().flush().unwrap();
} }
fn report_slow(&mut self, _description: &TestDescription, _elapsed: u64) {}
fn report_output(&mut self, _output: &[u8]) {} fn report_output(&mut self, _output: &[u8]) {}
fn report_result( fn report_result(

View file

@ -0,0 +1,8 @@
{
"args": "test main.js",
"envs": {
"DENO_SLOW_TEST_TIMEOUT": "1"
},
"output": "main.out",
"exitCode": 0
}

View file

@ -0,0 +1,4 @@
Deno.test(async function test() {
// We want to get at least one slow test warning
await new Promise((r) => setTimeout(r, 3_000));
});

View file

@ -0,0 +1,3 @@
running 1 test from [WILDCARD]
test ...'test' has been running for over (1s)
[WILDCARD]