Skip to content

Commit eb67f38

Browse files
committed
Testing: cap every Rust test at 8s, document the two exceptions
Nextest's default `slow-timeout` was `60s × terminate-after=2` (= 120 s) — the same cap that let a stuck `accent_color_linux::tests::read_accent_color_returns_valid_hex` burn 4 days of red main CI. Matches the Playwright E2E suite's 8 s convention. Policy lives at the top of `.config/nextest.toml`: - Default: `period = 8s, terminate-after = 1` (hard kill after 8 s of wall-clock). - Exceptions need an `[[profile.default.overrides]]` block with a per-test filter and a comment explaining WHY the test legitimately needs more. Two exceptions justified inline: - `smb_integration_*` (Docker SMB integration tests): 30 s. Each test pays a real SMB session-setup + auth + network round-trip; pathological-case tests intentionally probe slow behavior. - `error_reporter::tests::cap_bundle_keeps_newest_lines_and_manifest` + `cap_bundle_prefers_newer_files`: 30 s. They build a 5+ MB compressed zip from 200 000 synthetic log lines to pin the headline regression. Sub-2 s on a quiet machine, but contended runs (50 parallel checks) push past 8 s.
1 parent 91afacb commit eb67f38

1 file changed

Lines changed: 50 additions & 3 deletions

File tree

.config/nextest.toml

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,64 @@
11
# cargo-nextest configuration
22
# See https://nexte.st/docs/configuration/ for full documentation
3+
#
4+
# Default test timeout policy
5+
# ---------------------------
6+
# Every Rust test must complete in ≤ 8 s, matching the Playwright E2E suite's
7+
# default. Anything slower is a bug: a real-time-dependent test, an unguarded
8+
# network/IPC call, or an environment race. The cap is intentionally tight so a
9+
# hung test fails the suite in seconds instead of blocking CI for the legacy
10+
# 120 s nextest default. Background:
11+
#
12+
# 2026-05-19/20: `accent_color_linux::tests::read_accent_color_returns_valid_hex`
13+
# hit nextest's prior 120 s cap because `zbus::blocking::Connection::session()`
14+
# had no timeout and the GitHub Actions runner had a half-configured D-Bus
15+
# (orphan socket, no daemon). Cost: 4 days of red main CI before anyone
16+
# noticed. Fix combined a 500 ms probe timeout in the production code with
17+
# this tightened nextest cap so the suite shouts immediately next time.
18+
#
19+
# Exceptions
20+
# ----------
21+
# To grant a higher cap, add a `[[profile.default.overrides]]` block matching
22+
# the specific test(s) by name and explain WHY in a comment alongside it. The
23+
# bar is high: the test must exercise something that can't be made faster
24+
# without losing coverage (a real network call, a build artifact materialisation,
25+
# etc.). "It just takes that long" is not a reason — refactor the test instead.
326

27+
[test-groups]
428
# Tests in the `virtual-mtp` group share a single backing-dir fixture root at
529
# `/tmp/cmdr-mtp-e2e-fixtures` (see `mtp::virtual_device::setup_virtual_mtp_device`),
630
# so running them concurrently races on a shared filesystem resource and one
731
# wins with `DirectoryNotEmpty`. Cap concurrency at 1 to serialize them across
832
# nextest's per-process forks.
9-
[test-groups]
1033
virtual-mtp = { max-threads = 1 }
1134

1235
[[profile.default.overrides]]
1336
filter = 'test(mtp_scan) + test(listing_is_watched_flips_with_connection)'
1437
test-group = 'virtual-mtp'
1538

39+
[[profile.default.overrides]]
40+
# SMB integration tests (`#[ignore]`-gated, only run by the
41+
# `desktop-rust-integration-tests` check, which boots a real Docker Samba stack).
42+
# Each test pays the SMB session setup + auth + network round-trip cost; the
43+
# happy paths land in 1–3 s, but pathological-case tests (timeouts, slow
44+
# servers, big-listing pagination) intentionally probe slow behavior and need
45+
# headroom. 30 s is generous enough for those without losing the "hung forever"
46+
# safety net.
47+
filter = 'test(smb_integration_)'
48+
slow-timeout = { period = "30s", terminate-after = 1 }
49+
50+
[[profile.default.overrides]]
51+
# `error_reporter::tests::cap_bundle_*` builds a 5+ MB compressed zip from
52+
# 200 000 synthetic log lines, then trims it. The point of the input size is to
53+
# mirror a realistic full-rotation log dir; running the test on a smaller input
54+
# would stop covering the headline regression it pins (1-MB cap producing a
55+
# 542-byte zip with only the manifest — see the test doc-comment). Generation +
56+
# deflate + cap takes ~1–2 s on a quiet warm machine; under contention (50 other
57+
# checks running in parallel under `./scripts/check.sh`), it crosses 8 s.
58+
# 30 s headroom keeps the test honest without removing the "hung forever" net.
59+
filter = 'test(=error_reporter::tests::cap_bundle_keeps_newest_lines_and_manifest) + test(=error_reporter::tests::cap_bundle_prefers_newer_files)'
60+
slow-timeout = { period = "30s", terminate-after = 1 }
61+
1662
[profile.default]
1763
# Show test output for failing tests only
1864
failure-output = "immediate"
@@ -24,8 +70,9 @@ retries = 0
2470
test-threads = "num-cpus"
2571
# Fail fast - stop on first failure
2672
fail-fast = false
27-
# Show the slowest tests
28-
slow-timeout = { period = "60s", terminate-after = 2 }
73+
# Hard cap on every test (see policy at top of file). period × terminate-after
74+
# = 8 s × 1 = kill after 8 s of wall-clock.
75+
slow-timeout = { period = "8s", terminate-after = 1 }
2976

3077
[profile.ci]
3178
# Inherit from default profile

0 commit comments

Comments
 (0)