Final review fixes for the per-worker test servers (#274):
- A failed test gets the stderr its server wrote during that test
attached as `server-stderr`, so an aiohttp traceback behind a 500
reaches the CI report. The launcher keeps the newest 256 KB with a
stream position per test (stderrMark/stderrSince), and the fixture
attaches it only when the test did not end as expected.
- The port announcement counts only as a complete line; a chunk ending
mid-number was read as a shorter port.
- server.py --exit-on-stdin-eof shuts down gracefully (database closed,
temp root removed) when stdin closes. The launcher holds that pipe,
so a SIGKILLed worker leaves no server behind, on every OS.
- settled() gives up after 600 frames with the observation stats
instead of hanging to the test timeout.
- The Classic text-sync test waits for the page's 50 ms timer to
install serializeValue instead of sleeping 60 ms. With that timer run
100 ms late (a scratch copy), the old wait failed 20/20 and the poll
passed 20/20; the spec passed 240/240 at 8 workers x10 and 240/240
at 1 CPU with 4 workers.
- The workflow test checks each npm cache key names its job's pinned
image tag.
Every new unit test failed before its change (missing export, the old
regex timing out on the split announcement, an unknown server flag, a
hung settle, a mismatched cache key).
tests/browser/server.py accepts --port 0: it binds 127.0.0.1:0 before
building the app, holds that socket for its whole life and prints
PM_TEST_SERVER_PORT=<n> to stdout. startBrowserServer defaults to port 0
and reads the announced port, so no port is reserved and released before
the server binds and two launchers can no longer race for one. The
"address already in use" retry and free-port.mjs are gone; --port N still
works and is checked against the announcement.
The page workflows started one global webServer and ran with one worker.
Each worker now owns its own tests/browser/server.py on an OS-chosen port,
with its own temp root and database, through a shared `test` in
tests/browser/fixtures.mjs. baseURL, and so page, request and
browser.newContext, point at that worker's server. A server that dies
fails the running test with its stderr tail and the next test gets a
fresh one. The worker count comes from PM_TEST_WORKERS, else 1 under CI,
else a quarter of the CPUs up to 8.
Chromium workflows in the pinned image: 510 tests in 606 s with one
worker on main, 511 (one new isolation spec) in 119 s with eight.