Skip to content

Commit 240e3ca

Browse files
feat: update timed out test counting logic
Since timed out tests can now be treated as either successes or failures, update the logic for handling them. This commit also adds tests for exercising edge cases between `on-timeout="pass"` and retries/flaky tests
1 parent 2153e5c commit 240e3ca

File tree

11 files changed

+377
-14
lines changed

11 files changed

+377
-14
lines changed

fixture-data/src/nextest_tests.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ pub static EXPECTED_TEST_SUITES: LazyLock<IdOrdMap<TestSuiteFixture>> = LazyLock
4747
"test_slow_timeout_subprocess",
4848
TestCaseFixtureStatus::IgnoredPass,
4949
),
50+
TestCaseFixture::new(
51+
"test_flaky_slow_timeout_mod_3",
52+
TestCaseFixtureStatus::IgnoredFail
53+
),
5054
TestCaseFixture::new("test_stdin_closed", TestCaseFixtureStatus::Pass),
5155
TestCaseFixture::new("test_subprocess_doesnt_exit", TestCaseFixtureStatus::Leak),
5256
TestCaseFixture::new("test_subprocess_doesnt_exit_fail", TestCaseFixtureStatus::FailLeak),

fixtures/nextest-tests/.config/nextest.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ test-group = '@global'
9191
[profile.with-timeout-success]
9292
slow-timeout = { period = "10ms", terminate-after = 2, on-timeout = "pass" }
9393

94+
[[profile.with-timeout-success.overrides]]
95+
filter = 'test(=test_slow_timeout_2)'
96+
slow-timeout = { period = "500ms", terminate-after = 2, on-timeout = "fail" }
97+
test-group = '@global'
98+
99+
[[profile.with-timeout-success.overrides]]
100+
filter = 'test(=test_slow_timeout_subprocess)'
101+
slow-timeout = { period = "10ms", terminate-after = 2, on-timeout = "fail" }
102+
103+
[profile.with-timeout-retries-success]
104+
slow-timeout = { period = "10ms", terminate-after = 2, on-timeout = "pass" }
105+
retries = 2
106+
94107
[profile.with-junit]
95108
retries = 2
96109

fixtures/nextest-tests/tests/basic.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,17 @@ fn test_slow_timeout_subprocess() {
319319
cmd.output().unwrap();
320320
}
321321

322+
#[test]
323+
#[ignore]
324+
fn test_flaky_slow_timeout_mod_3() {
325+
let nextest_attempt = nextest_attempt();
326+
if nextest_attempt % 3 != 0 {
327+
panic!("Failed because attempt {} % 3 != 0", nextest_attempt)
328+
}
329+
// The timeout for the with-timeout-success profile is set to 2 seconds.
330+
std::thread::sleep(std::time::Duration::from_secs(4));
331+
}
332+
322333
#[test]
323334
fn test_result_failure() -> Result<(), std::io::Error> {
324335
Err(std::io::Error::new(

nextest-runner/src/config/elements/slow_timeout.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ where
8383
/// The result of controlling slow timeout behavior.
8484
///
8585
/// In most situations a timed out test should be marked failing. However, there are certain
86-
/// classes of tests which are expected to run indefinetely long, like fuzzing, which explores a
86+
/// classes of tests which are expected to run indefinitely long, like fuzzing, which explores a
8787
/// huge state space. For these tests it's nice to be able to treat a timeout as a success since
8888
/// they generally check for invariants and other properties of the code under test during their
8989
/// execution. A timeout in this context doesn't mean that there are no failing inputs, it just
90-
/// means that they weren't foudn up until that moment, which is still valuable information.
90+
/// means that they weren't found up until that moment, which is still valuable information.
9191
#[derive(Clone, Copy, Debug, Deserialize, Default, PartialEq, Eq)]
9292
#[serde(rename_all = "kebab-case")]
9393
pub enum SlowTimeoutResult {
@@ -204,6 +204,19 @@ mod tests {
204204
205205
; "timeout result failure"
206206
)]
207+
#[test_case(
208+
indoc! {r#"
209+
[profile.default]
210+
slow-timeout = { period = "60s", on-timeout = "pass" }
211+
212+
[profile.ci]
213+
slow-timeout = { period = "30s", on-timeout = "fail" }
214+
"#},
215+
Ok(SlowTimeout { period: Duration::from_secs(60), terminate_after: None, grace_period: Duration::from_secs(10), on_timeout: SlowTimeoutResult::Pass }),
216+
Some(SlowTimeout { period: Duration::from_secs(30), terminate_after: None, grace_period: Duration::from_secs(10), on_timeout: SlowTimeoutResult::Fail })
217+
218+
; "override on-timeout option"
219+
)]
207220
#[test_case(
208221
indoc! {r#"
209222
[profile.default]

nextest-runner/src/reporter/displayer/imp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2298,7 +2298,7 @@ fn short_status_str(result: ExecutionResult) -> Cow<'static, str> {
22982298
} => "LKFAIL".into(),
22992299
ExecutionResult::Timeout {
23002300
result: SlowTimeoutResult::Pass,
2301-
} => "TMTPAS".into(),
2301+
} => "TMPASS".into(),
23022302
ExecutionResult::Timeout {
23032303
result: SlowTimeoutResult::Fail,
23042304
} => "TMT".into(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: nextest-runner/src/reporter/displayer/progress.rs
3+
expression: s
4+
---
5+
 Cancelling [ 34:17:36] 10/20: 10 running, 0 passed, 1 timed out, 0 skipped
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: nextest-runner/src/reporter/displayer/progress.rs
3+
expression: s
4+
---
5+
 Running [ 34:17:36] 10/20: 10 running, 0 passed, 1 timed out, 0 skipped

nextest-runner/src/reporter/events.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ pub struct RunStats {
534534
/// The number of tests that passed on retry.
535535
pub flaky: usize,
536536

537-
/// The number of tests that failed.
537+
/// The number of tests that failed. Includes `leaky_failed`.
538538
pub failed: usize,
539539

540540
/// The number of failed tests that were slow.
@@ -701,7 +701,20 @@ impl RunStats {
701701
self.failed_slow += 1;
702702
}
703703
}
704-
ExecutionResult::Timeout { .. } => self.failed_timed_out += 1,
704+
ExecutionResult::Timeout {
705+
result: SlowTimeoutResult::Pass,
706+
} => {
707+
self.passed += 1;
708+
self.passed_timed_out += 1;
709+
if run_statuses.len() > 1 {
710+
self.flaky += 1;
711+
}
712+
}
713+
ExecutionResult::Timeout {
714+
result: SlowTimeoutResult::Fail,
715+
} => {
716+
self.failed_timed_out += 1;
717+
}
705718
ExecutionResult::ExecFail => self.exec_failed += 1,
706719
}
707720
}
@@ -1597,7 +1610,18 @@ mod tests {
15971610
initial_run_count: 42,
15981611
not_run: 0
15991612
}),
1600-
"timed out => failure"
1613+
"timed out => failure {:?} {:?}",
1614+
RunStats {
1615+
initial_run_count: 42,
1616+
finished_count: 42,
1617+
failed_timed_out: 1,
1618+
..RunStats::default()
1619+
}
1620+
.summarize_final(),
1621+
FinalRunStats::Failed(RunStatsFailureKind::Test {
1622+
initial_run_count: 42,
1623+
not_run: 0
1624+
}),
16011625
);
16021626
assert_eq!(
16031627
RunStats {

0 commit comments

Comments
 (0)