Skip to content

ci(tui): run the TUI tests with cargo nextest - #31

Merged
zy1233 merged 3 commits into
fix/tui-protoc-absolute-pathfrom
ci/tui-nextest
Oct 20, 2026
Merged

zy1233 merged 3 commits into
fix/tui-protoc-absolute-pathfrom
ci/tui-nextest

Conversation

@zy1233

@zy1233 zy1233 commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

Plan item nextest_ci, chosen by the owner on 2026-10-20: TUI CI and the baseline runner run the Rust tests with cargo nextest (one process for each test), not with cargo test (all tests of a suite as threads of one process, 4 threads, fixed order).

Why: the vendored tests were written for nextest. Their comments say so (pi-shell/src/config/tests.rs:2515, pi-pager-render/src/terminal/xtversion.rs:210, pi-workspace/src/lib.rs:78, pi-telemetry/src/debug_log.rs:528). Under cargo test they share the environment variables and every static, and #28 had to find the resulting races one by one. Under nextest, this class of failure cannot occur, also for tests that someone writes later.

 scripts/tui_baseline.py
   suite_command(runner, suite)
-    cargo test --locked --no-fail-fast -p PKG <targets> [-- --skip X]
+    "cargo":   cargo test --locked --no-fail-fast -p PKG <targets> [-- --skip X]   (--runner cargo)
+    "nextest": cargo nextest run --locked --no-fail-fast -p PKG <targets> [-- --skip X]
   parse the result
+    parse_nextest_output(text)   the "Summary [..] N tests run: P passed, F failed, S skipped" line,
+                                 and the recap after it for the names of failed tests
   stream()  (a hung suite)
-    SIGKILL to the process group
+    SIGTERM to the process group, SIGKILL after 20 s
 scripts/tui_baseline.toml           [test] runner = "nextest"
 .github/workflows/tui-ci.yml        job "cargo nextest (Linux x86_64)"; step: install [email protected]
 scripts/tests/test_tui_baseline.py  21 new tests
 AGENTS.md, scripts/tui_envspy/README.md   say that TUI CI runs nextest

Base: fix/tui-protoc-absolute-path (#30), the same chain as #27 to #29. Not merged. #27, #28, #29 and #30 are not merged either.

Three things to know:

  • Skipped, not ignored. nextest counts the #[ignore] tests and the tests that a --skip filter removes together as skipped. The expected number is 22 (21 ignored + 1 --skip filter of pi-acp-lib in the manifest), where cargo test printed 21 ignored. The runner and the report say "skipped" for nextest.
  • No doctests. nextest runs none. These suites never ran them either: the runner selects explicit --lib, --test and --bin targets.
  • Version pin. The runner reads the human output of nextest, so the CI installs [email protected] (taiki-e/install-action, which checks the checksums of the download). Change the pin together with the parser and its tests.

Evidence

  • Before: cargo test; pi-pager lib alone: 29.7 s (WSL2, 4 threads).
    After: cargo nextest; the same suite: 79.5 s (nextest starts one process for each test). The CI time comes below with the other numbers.
  • Unit tests of the runner (scripts/tests/test_tui_baseline.py): Windows 50 passed / 3 skipped (the 3 POSIX-only stream tests), Linux (WSL2) 53 passed. The fixture of the parser is real output of nextest 0.9.148 for a run with failures.
  • End to end on a small crate made for this check (not on the TUI tree): a run that passes, a run with a failing test, a known failure from known_failures, a machine without nextest (stops with the install hint), and a test that hangs (see the next point).
  • A hung suite. nextest runs each test in its own process group. The runner killed only its own group with SIGKILL, and so it left the hanging test alive. Now the runner sends SIGTERM first: nextest stops its tests and exits (exit code 100), and SIGKILL follows after 20 s. I measured both: SIGKILL alone leaves the test process; SIGTERM does not. Three unit tests keep this (POSIX only): SIGTERM goes first, SIGKILL follows when the child ignores it, a normal end returns the output.
  • The first run of the whole tree under nextest (TUI CI run 39359879537, 1a49eef): 8,490 passed, 0 failed, 22 skipped, as expected. Every suite equals its reference_passed: pi-shell 838, pi-pager 5,651, pi-pager-render 1,092, pi-pager-minimal 79, pi-pager-bin 21, pi-acp-lib 21, pi-http 13, pi-telemetry 239, pi-file-utils 216, pi-sampling-types 320. The log has 8,490 PASS lines and no SLOW, LEAK, FLAKY or TIMEOUT line. The 14 tests of python_agent_e2e ran and passed (PI_E2E_REQUIRED=1). The gates did not change: dependency graph 970, 0 warnings, leader 243, x.ai 297, 0 errors in cargo check --workspace --tests. CI run 39359879347 passed too (Ruff 9 s; pytest 3.11, 3.12 and 3.13 took about 2 min 18 s each).
  • Time. The tests themselves take about 130 s with nextest (the Summary times of the suites, added up; pi-pager 85.2 s) and about 64.5 s with cargo test (same code: run 39359526868 of fix(tui): give cargo the absolute path of protoc, so it stops rebuilding pi-tools-api #30, e2de4c4). That is +66 s, of which 48 s are in pi-pager. The whole job took 36 min 17 s, against 33 min 56 s for the cargo test job that started 4 minutes earlier. The total time of the job changes much from run to run (17 min 34 s to 33 min 56 s in the last 7 runs of cargo test: compile and cache differ), so compare the 66 s of test time, not the total.
  • A second run of the same code, and the cache. TUI CI run 39364431757 (the docs commit 464d8c9; the code is the same) passed with the same counts (8,490 passed / 0 failed / 22 skipped), tests of about 125 s (pi-pager 82.5 s) and a job time of 23 min 12 s. The difference to the first run is the cache of the build (Swatinem/rust-cache; only runs on main save it): the first run restored nothing (the cache step took 1 s; the Test suites step took 35 min 22 s), the second restored a cache (74 s; Test suites 21 min 07 s). The cargo test runs show the same: without cache e2de4c4 (0 s; Test suites 33 min 06 s), with cache 7a4ce9d (65 s; Test suites 20 min 16 s). So with a cache, the step takes 21 min 07 s with nextest and 20 min 16 s with cargo test: about 1 min more. The older runs of this tree with a cache (59 s to 108 s) took 12 to 23 min.
  • Docs. The docs commit 464d8c9 (docs only; the code is the same as in the run above) also corrects two claims of r17 in the plan and the execution log: the 7 / 4 / 3 compiles of pi-tools-api in the cargo test job were not caused by the protoc path (see fix(tui): give cargo the absolute path of protoc, so it stops rebuilding pi-tools-api #30), and the runner never ran doctests. It removes a duplicated heading from the r17 text of test(tui): fix the tests that share process-wide state and fail at random (plan decision 10) #28, and it records the dry run of release.yml for ci(release): pass repo-token to setup-protoc in the release workflow #29 (it passed). A docs-only push starts TUI CI again; the code did not change.

Merge Danger

Door: two-way. Revert the commit, or set runner = "cargo" in the manifest.

Blast Radius: the test job of TUI CI and the local baseline runner. The job is now named cargo nextest (Linux x86_64). The ruleset on main has no required status check (only deletion and non_fast_forward), so the new name blocks nothing; older docs keep the old name as history. A new third-party action (taiki-e/install-action@v2) runs in the job, as arduino/setup-protoc and Swatinem/rust-cache do already. If one of these tests needs the shared process, it fails in this PR's run, not after the merge. The vendored comments name none; only pi-fast-worktree says that its tests must pass under both runners, and the baseline does not run that package (its tests do not compile).

The vendored tests were written for cargo nextest (one process per test), and some say in a
comment that they are reliable only under it. TUI CI ran cargo test, where all tests of a
binary share one process.

- scripts/tui_baseline.toml: `[test] runner = "nextest"`; `--runner {cargo,nextest}` on the
  command line takes the other one. The libtest path stays as it was.
- scripts/tui_baseline.py: `parse_nextest_output` reads the Summary line (nextest counts the
  ignored tests and the tests removed by --skip together as "skipped") and the recap of the
  failed tests (the live part has the captured output of a test, so it is not read);
  `suite_command` builds the command line; the report names the runner; a missing
  cargo-nextest stops the run at once, with a hint.
- stream(): after the timeout, SIGTERM to the process group, and SIGKILL 20 s later. nextest
  puts each test in a process group of its own, so a SIGKILL to nextest left a hanging test
  alive (measured on a demo crate); a nextest that gets a SIGTERM stops its tests.
- tui-ci.yml: install cargo-nextest 0.9.148 (taiki-e/install-action) in the test job; the job
  is now called "cargo nextest (Linux x86_64)".
- 21 new unit tests. The fixtures are real output of nextest 0.9.148.
- Plan (status, section 10 index, revision log), execution log (new section
  10.21), CHANGELOG, baselines and baseline history: the numbers of the first
  nextest run on the whole tree (TUI CI run 39359879537: 8,490 passed, 0 failed,
  22 skipped), the test time (about 130 s with nextest, about 65 s with
  cargo test, same code), and the dry run of release.yml for PR #29.
- Correct the r17 notes: the 7 / 4 / 3 compiles of pi-tools-api in the cargo
  test job were not caused by the protoc path (PR #30 shows it), and the runner
  never ran doctests, so nextest loses nothing there.
- Remove a duplicated "verification" heading from the r17 text.
@zy1233
zy1233 added this pull request to stack #32 October 20, 2026 06:16
@zy1233
zy1233 merged commit b33017a into main Oct 20, 2026
7 checks passed
@zy1233
zy1233 deleted the ci/tui-nextest branch October 20, 2026 06:19
zy1233 added a commit that referenced this pull request Oct 25, 2026
…to #31 (plan r19) (#33)

The owner asked for three things: merge the five open PRs (#27 to #31) into main with
`gh stack`, set the V0.6 milestone from the present state, and pause development for
1 to 2 days.

The merge is done (stack #32, `gh stack merge 32 --yes --squash`). main goes from
7d82c27 to b33017a with five squash commits. The tree of each commit is the tree of its
PR head. CI and TUI CI pass on b33017a (8,490 passed, 0 failed, 22 skipped).

The plan gets a new section "发布里程碑 V0.6": the scope (the 10 commits on main after
v0.5.0), six exit conditions with their state, the known issues (two are new), and what
is not in V0.6. It also gets the r19 status, the pause, and the fixed status of the
places that still said "waiting for merge" for #25 to #28. The execution log gets
section 10.22: the install of `gh stack` (the first download was cut short), the merge,
the check of the trees, CI on main, the milestone, the pause, and what is not verified.

The GitHub milestone v0.6.0 (#2) is created. It has no due date and no PRs, as v0.5.0.
No condition is met yet. Only documents change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant