From c1c502257322ae2c3b1208e5915f5d2884503785 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 20 Oct 2026 23:47:20 -0700 Subject: [PATCH 1/8] quest(m1): claim audit-test-cost Co-Authored-By: Claude Opus 5.5 From abab8bb71dcc7a9462eb061f4c7de2922811d76d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 00:11:54 -0700 Subject: [PATCH 2/8] test(auth): run the stalled re-check close on the paused clock The test waited out the stalled re-check's 10 s bound on the wall clock against a wiremock server. The in-process Script already drives the lease on Tokio's paused clock, so the same case now takes milliseconds. The refusal and invalid-grant re-check tests nudge a re-check instead of waiting out the one-second cadence, which the cadence test still covers. Co-Authored-By: Claude Opus 5.5 --- rs/moq-auth/src/client.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/rs/moq-auth/src/client.rs b/rs/moq-auth/src/client.rs index ff2c5232cdc..bf683e07909 100644 --- a/rs/moq-auth/src/client.rs +++ b/rs/moq-auth/src/client.rs @@ -989,16 +989,17 @@ mod tests { let answer = answer.clone(); move |request| match request.event { Event::Connect => ResponseTemplate::new(200) - .set_body_json(grant(Some(Duration::from_secs(3600)), Some(Duration::from_secs(1)))), + .set_body_json(grant(Some(Duration::from_secs(3600)), Some(Duration::from_secs(60)))), _ => answer.clone(), } }) .await; let consumer = client(&server).connect(request()).await.unwrap(); + consumer.revalidate(); let reason = tokio::time::timeout(Duration::from_secs(3), consumer.closed()) .await - .expect("revoked within the cadence"); + .expect("revoked by the nudged re-check"); assert_eq!(reason, Reason::Refused); } } @@ -1008,15 +1009,16 @@ mod tests { let unbounded = grant(None, Some(Duration::from_secs(1))); let server = server(Log::default(), move |request| match request.event { Event::Connect => ResponseTemplate::new(200) - .set_body_json(grant(Some(Duration::from_secs(3600)), Some(Duration::from_secs(1)))), + .set_body_json(grant(Some(Duration::from_secs(3600)), Some(Duration::from_secs(60)))), _ => ResponseTemplate::new(200).set_body_json(unbounded.clone()), }) .await; let consumer = client(&server).connect(request()).await.unwrap(); + consumer.revalidate(); let reason = tokio::time::timeout(Duration::from_secs(3), consumer.closed()) .await - .expect("revoked within the cadence"); + .expect("revoked by the nudged re-check"); assert_eq!(reason, Reason::Invalid); } @@ -1120,20 +1122,15 @@ mod tests { assert_eq!(reason, Reason::Expired); } - #[tokio::test] + #[tokio::test(start_paused = true)] async fn a_close_is_reported_while_a_recheck_is_stalled() { let log = Log::default(); - let server = server(log.clone(), |request| match request.event { - Event::Connect => ResponseTemplate::new(200) - .set_body_json(grant(Some(Duration::from_secs(3600)), Some(Duration::from_secs(1)))), - Event::Revalidate => ResponseTemplate::new(200) - .set_body_json(grant(Some(Duration::from_secs(3600)), Some(Duration::from_secs(60)))) - .set_delay(Duration::from_secs(30)), - Event::End { .. } => ResponseTemplate::new(200), - }) - .await; + let script = Script::new(log.clone(), |request| match request.event { + Event::Connect => Some(Ok(grant(Some(Duration::from_secs(3600)), Some(Duration::from_secs(1))))), + _ => None, + }); - let consumer = client(&server).connect(request()).await.unwrap(); + let consumer = script.connect().await.unwrap(); tokio::time::sleep(Duration::from_millis(1500)).await; assert!(log.events().contains(&Event::Revalidate), "the re-check is in flight"); consumer.close("disconnected", Bytes::default()); From 0f1e1ae0df3804222dbca6e5d5452b60aee33118 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 00:11:54 -0700 Subject: [PATCH 3/8] test(mux): drop the ten-minute TS export past-limit failure It simulated 900 s of media to see the export fail once a 400 ppm source used half the delay. The jitter buffer's own test times that failure at 40 and 400 ppm, and the far-off source case covers the export surfacing the same error within seconds of media. Co-Authored-By: Claude Opus 5.5 --- rs/moq-mux/src/container/ts/export_timing_test.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/rs/moq-mux/src/container/ts/export_timing_test.rs b/rs/moq-mux/src/container/ts/export_timing_test.rs index 26d0326f5c6..bac0a3e0d52 100644 --- a/rs/moq-mux/src/container/ts/export_timing_test.rs +++ b/rs/moq-mux/src/container/ts/export_timing_test.rs @@ -736,15 +736,9 @@ async fn a_source_past_the_limit_is_counted() { assert!(stats.out_of_tolerance > 0, "not counted"); } -/// The same source fails the export once it has used half the delay, about ten minutes in, -/// rather than drop nearly every frame from then on. -#[tokio::test(start_paused = true)] -#[should_panic(expected = "past the 30 ppm the output's may follow")] -async fn a_source_past_the_limit_fails_once_half_the_delay_is_gone() { - on_a_scaled_clock(1.0004, 900_000_000 / VIDEO_US).await; -} - -/// A source 22% slow, as an importer starved of CPU, fails the export within seconds. +/// A source 22% slow, as an importer starved of CPU, fails the export within seconds. Past the +/// limit, a source fails once it has used half the delay however far off it runs; the jitter +/// buffer's own tests time that at 40 and 400 ppm. #[tokio::test(start_paused = true)] #[should_panic(expected = "the source's clock runs -220")] async fn a_source_far_off_real_time_fails_within_seconds() { From e267e1069d2338ab0bba633137d47373f2d4ce8f Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 00:11:54 -0700 Subject: [PATCH 4/8] test(tokio): dial the close-code server at 127.0.0.1 The server binds IPv4 only. On a host whose /etc/hosts lists ::1 first for localhost, as the NixOS queue runner does, each of the ten QUIC dials first waited out the 250 ms Happy Eyeballs stagger on a dead ::1, so the three tests took 3 s each instead of 0.5 s. Co-Authored-By: Claude Opus 5.5 --- rs/moq-tokio/tests/integration/close_code.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rs/moq-tokio/tests/integration/close_code.rs b/rs/moq-tokio/tests/integration/close_code.rs index 1a45c9cf6d4..dcb62c99c92 100644 --- a/rs/moq-tokio/tests/integration/close_code.rs +++ b/rs/moq-tokio/tests/integration/close_code.rs @@ -93,9 +93,11 @@ async fn client_error(url: &str, websocket: bool) -> moq_tokio::Error { async fn check(case: Case) { let server = serve(case).await; + // The server binds IPv4 only, so `localhost` would race a dead `::1` on hosts that list it + // first, paying the Happy Eyeballs stagger on every dial. let urls = [ - (format!("https://localhost:{}/", server.quic), false), - (format!("moqt://localhost:{}/", server.quic), false), + (format!("https://127.0.0.1:{}/", server.quic), false), + (format!("moqt://127.0.0.1:{}/", server.quic), false), (format!("ws://127.0.0.1:{}/", server.websocket), true), ]; From 175372165f126e37e920d6ec2311bea15da65222 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 00:15:03 -0700 Subject: [PATCH 5/8] test(tokio): give the blocked-handshake pair a loopback initial RTT A connection closed before its handshake drains for three PTOs, and the default 333 ms initial RTT made that three seconds in each split-accept test that waits for the endpoints to go idle. A 10 ms initial RTT, as the handshake timeout test already uses, keeps the same states and takes about 0.2 s. Co-Authored-By: Claude Opus 5.5 --- rs/moq-tokio/src/quic/driver/tests.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/rs/moq-tokio/src/quic/driver/tests.rs b/rs/moq-tokio/src/quic/driver/tests.rs index 96aac54e64b..6ffa3b8228f 100755 --- a/rs/moq-tokio/src/quic/driver/tests.rs +++ b/rs/moq-tokio/src/quic/driver/tests.rs @@ -476,6 +476,14 @@ fn blocking_server_pair( let mut server_config = crate::quic::driver::ServerConfig::with_crypto(Arc::new( QuicServerConfig::try_from(server_crypto).unwrap(), )); + // Loopback answers in microseconds. The default 333 ms initial RTT would hold every + // connection closed before its handshake in draining for three seconds. + let transport = Arc::new({ + let mut transport = TransportConfig::default(); + transport.initial_rtt(Duration::from_millis(10)); + transport + }); + server_config.transport_config(transport.clone()); let blocker = Arc::new(HandshakeBlocker::default()); server_config.crypto = Arc::new(BlockingServerConfig { inner: server_config.crypto.clone(), @@ -505,9 +513,9 @@ fn blocking_server_pair( .with_root_certificates(roots) .with_no_client_auth(); client_crypto.alpn_protocols = vec![client_alpn.to_vec()]; - client.set_default_client_config(ClientConfig::new(Arc::new( - QuicClientConfig::try_from(client_crypto).unwrap(), - ))); + let mut client_config = ClientConfig::new(Arc::new(QuicClientConfig::try_from(client_crypto).unwrap())); + client_config.transport_config(transport); + client.set_default_client_config(client_config); (client, server, blocker) } From 50f83e45f548f0dfb538afc4d39ee642c0b05a1f Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 00:20:16 -0700 Subject: [PATCH 6/8] ci: a benchmark-only edit selects only its own crate or workspace A Rust crate's benches/ and a JS workspace's bench/ are no dependency of anything else, and the gate's test build never compiles Rust benches. Yet selection walked their owners' dependents: a moq-stats bench edit ran the moq-relay and moq-cli suites, about 40% of the workspace's test time, plus the play build, and a js/net bench edit checked and tested 22 workspaces and built the WASM. Each now selects only its owner, whose lint and typecheck still cover the file. Co-Authored-By: Claude Opus 5.5 --- sh/js/workspace.py | 6 +++++- sh/js/workspace.test.py | 5 +++++ sh/rs/select.sh | 11 ++++++++--- sh/rs/select.test.py | 10 ++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/sh/js/workspace.py b/sh/js/workspace.py index 78c3314e33a..261e716883d 100644 --- a/sh/js/workspace.py +++ b/sh/js/workspace.py @@ -65,6 +65,8 @@ def selection(root, paths): "inputs": inputs(root, directory)} selected = set() + # A workspace's `bench/` is no export, so no dependent can import it: only its owner checks it. + benched = set() fallback = paths is None for path in paths or []: owners = [(name, p["directory"]) for name, p in packages.items() @@ -72,7 +74,8 @@ def selection(root, paths): if not owners: fallback = True break - selected.add(max(owners, key=lambda owner: len(owner[1]))[0]) + name, directory = max(owners, key=lambda owner: len(owner[1])) + (benched if path.startswith(directory + "/bench/") else selected).add(name) # Manifests miss a package reading another tree's files, so add those readers too. selected |= {name for name, package in packages.items() if any(reads(path, i) for i in package["inputs"])} @@ -88,6 +91,7 @@ def selection(root, paths): if expanded == selected: break selected = expanded + selected |= benched # A selected consumer can need an unchanged dependency's generated bindings. dependencies = set(selected) diff --git a/sh/js/workspace.test.py b/sh/js/workspace.test.py index 040a564e2db..beb77837941 100644 --- a/sh/js/workspace.test.py +++ b/sh/js/workspace.test.py @@ -89,6 +89,11 @@ def test_transitive_dependents_from_manifests(self): self.assertFalse(plan["all"]) self.assertFalse(plan["wasm"]) + def test_a_bench_selects_only_its_workspace(self): + self.assertEqual(list(self.plan(["js/core/bench/frames.ts"])["packages"]), ["@example/core"]) + self.assertEqual(set(self.plan(["js/core/bench/frames.ts", "js/core/src/index.ts"])["packages"]), + {"@example/core", "@example/view", "docs"}) + def test_leaf_does_not_select_its_dependencies(self): self.assertEqual(list(self.plan(["doc/index.md"])["packages"]), ["docs"]) diff --git a/sh/rs/select.sh b/sh/rs/select.sh index 203ed9748c0..c70ab573e70 100755 --- a/sh/rs/select.sh +++ b/sh/rs/select.sh @@ -48,9 +48,13 @@ select_packages() { return fi - local seeds edges selected + local seeds benches edges selected + # A crate's `benches/` builds only its own bench targets, which no other crate can + # depend on and the test build never compiles, so it selects that crate alone + # (for its lint) rather than every dependent's tests. + benches=$(sed -n 's|^rs/\([^/]*\)/benches/.*|\1|p' "$list") seeds=$({ - sed -n 's|^rs/\([^/]*\)/.*|\1|p' "$list" + sed -n '\|^rs/[^/]*/benches/|d; s|^rs/\([^/]*\)/.*|\1|p' "$list" jq -r --rawfile list "$list" '($list | split("\n")) as $paths | .packages[] | (.manifest_path | split("/")) as $p | select($p[-4] == "demo") | select(any($paths[]; startswith("demo/\($p[-3])/\($p[-2])/"))) | .name' <<<"$metadata" @@ -74,7 +78,8 @@ select_packages() { } while (grew) for (name in want) if (want[name]) print name } - ' <<<"$edges" | sort -u) + ' <<<"$edges") + selected=$(printf '%s\n%s\n' "$selected" "$benches" | sed '/^$/d' | sort -u) [[ -n "$selected" ]] || return 0 diff --git a/sh/rs/select.test.py b/sh/rs/select.test.py index 1cf3b15358c..7d7f9bee0cf 100644 --- a/sh/rs/select.test.py +++ b/sh/rs/select.test.py @@ -54,6 +54,16 @@ def test_demo_crate_follows_its_dependencies(self): self.assertTrue(any("/demo/carla/bridge#moq-carla-bridge" in call for call in calls), calls) self.assertEqual(self.selected("demo/carla/web/src/main.ts"), []) + def test_a_bench_selects_only_its_crate(self): + bench = "rs/moq-stats/benches/producer_tick.rs" + for action in ("lint", "test"): + with self.subTest(action=action): + calls = self.selected(bench, action=action) + self.assertTrue(any("#moq-stats@" in call or "/rs/moq-stats#" in call for call in calls), calls) + self.assertFalse(any("moq-relay" in call or "moq-cli" in call for call in calls), calls) + calls = self.selected(bench, "rs/moq-stats/src/lib.rs", action="test") + self.assertTrue(any("/rs/moq-relay#" in call for call in calls), calls) + class RecipeTest(unittest.TestCase): From cb336ba0888afdcdc3fda0a541e4287b504f4e3e Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 00:34:14 -0700 Subject: [PATCH 7/8] quest: finish the m1 test cost audit and plan the m2 run Deletes the m1 run and adds its successor at the end of m2's Required, behind the release cut, with this run's baselines and leads. Co-Authored-By: Claude Opus 5.5 --- quest/m0/ci/audit.md | 2 +- quest/m1/README.md | 1 - quest/m1/audit-ci.md | 2 +- quest/m1/audit-test-cost.md | 51 ----------------------- quest/m2/README.md | 1 + quest/m2/audit-test-cost.md | 83 +++++++++++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 54 deletions(-) delete mode 100644 quest/m1/audit-test-cost.md create mode 100644 quest/m2/audit-test-cost.md diff --git a/quest/m0/ci/audit.md b/quest/m0/ci/audit.md index 711e0efa078..793fc3ef111 100644 --- a/quest/m0/ci/audit.md +++ b/quest/m0/ci/audit.md @@ -6,7 +6,7 @@ Every merge queue and nightly job is timed, and every expensive step either earns its minutes or is cut, moved or planned. The Rust cache reuses unchanged crates, and CI compiles nothing it doesn't need. Fixes land in this quest's PR, one commit each. Ranking individual tests stays with the -recurring [test cost audit](/quest/m1/audit-test-cost.md). +recurring [test cost audit](/quest/m2/audit-test-cost.md). ## Plan diff --git a/quest/m1/README.md b/quest/m1/README.md index 0cec85655e8..d8034a3c656 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -123,7 +123,6 @@ lands before the cut. - [Dead code and API surface audit](/quest/m1/audit-dead-code.md) - repeat the removal audit before the next major release - [Dependency and alternatives audit](/quest/m1/audit-deps.md) - dependencies are current, deduplicated, needed, and the best maintained option - [Test coverage audit](/quest/m1/audit-test-coverage.md) - repeat the coverage audit against the m0 baselines before the next major release -- [Test cost audit](/quest/m1/audit-test-cost.md) - the slowest, flakiest, and least valuable tests are fixed, deleted, or planned - [Interop report](/quest/m1/interop-report.md) - nightly Interop results publish as a color-coded matrix on the Pages site instead of only in logs - [CI workflow audit](/quest/m1/audit-ci.md) - every workflow and job gives a signal someone sees, at a cost that signal justifies, or is fixed, moved, or deleted - [Documentation audit](/quest/m1/audit-docs.md) - stale pages, examples, links, and undocumented exports are fixed or planned diff --git a/quest/m1/audit-ci.md b/quest/m1/audit-ci.md index 59b2ced6414..a402aa7f7be 100644 --- a/quest/m1/audit-ci.md +++ b/quest/m1/audit-ci.md @@ -5,7 +5,7 @@ Every CI workflow and job earns its place: its results reach someone (a gate, a Discord alert, or a published report) at a cost proportional to that signal. Each one that doesn't is fixed, moved, or deleted. Individual tests stay with -the [test cost audit](/quest/m1/audit-test-cost.md), and runner and cache +the [test cost audit](/quest/m2/audit-test-cost.md), and runner and cache infrastructure with [CI cost and latency](/quest/m0/ci/README.md). ## Plan diff --git a/quest/m1/audit-test-cost.md b/quest/m1/audit-test-cost.md deleted file mode 100644 index f9db6a07e1c..00000000000 --- a/quest/m1/audit-test-cost.md +++ /dev/null @@ -1,51 +0,0 @@ -# [M] Test cost audit - -## Goal - -The slowest, flakiest, and least valuable tests are sped up, fixed, deleted, -or planned, so the suite costs what it earns. Workflows and jobs belong to the -[CI workflow audit](/quest/m1/audit-ci.md). - -## Plan - -Recurs once per major release (decided 2026-10-19). The PR that finishes -this run also adds the next one with the same file name at the end of the next -milestone's `Required` (m0 to m1, m1 to m2). The successor keeps the Goal -and this paragraph; the rest of its Plan is rewritten with what this run -learned: baselines, results, and what to check first. A successor placed in m2 -also lists the [release cut](/quest/m2/release/cut.md) under `Required`, so it -waits for the release in flight to ship. Only `/quest-delete` ends the -recurrence. Fixes land in the run's PR, one commit each, rather than as follow-ups. -Only a change that needs a maintainer decision (public API, scope) or is -independently landable and too large to review alongside the rest becomes a -follow-up through `/quest-plan`. - -Use nextest's normal CI log to rank the slowest tests (`slow-timeout` is 30s). -Flakes are tracked by hand in [test flakes](/quest/m1/test-flakes-2/README.md), -and `retries = 0` is deliberate. A test that sleeps on the wall clock should -mock time (AGENTS.md). Delete tests whose coverage another test already -provides. Failed Nightly, Interop, and Platform jobs, and what each job costs, moved to -the [CI workflow audit](/quest/m1/audit-ci.md) (2026-10-20). - -What the m0 run (2026-10-19) fixed: the ACME rate-limit test's released port, -the stats session graph's recursive Send proof (send-depth and fuzz), -bench/ffi-gen's stale wasm-bindgen lock, moq-uring's fixed 1 ms calibration -slack, the Nightly JS benchmark stall, the CI selection test's feature count, -and the stats producer benchmark lint. It planned -[redundant failover drift](/quest/m1/test-flakes-2/redundant-failover-drift.md) -and [macOS QUIC connects](https://github.com/moq-dev/moq/pull/6110). - -Baselines from queue run -[39371990135](https://github.com/moq-dev/moq/actions/runs/39371990135): -Test took 17 min and Check 15 min; nextest ran 7,657 tests in 220 s. The -slowest were the three new redundant-importer exports (37, 29 and 15 s, -waiting out a killed importer's 10 s QUIC idle timeout), the CLI -restart/stitch case (18.6 s), same-instance linger (17.5 s), TS source-limit -timing (16.5 s), the impaired flapping-peer drill (16.4 s), and the one-day -jitter drift simulation (14.7 s). Apart from the redundant exports, the -leaders match the earlier sample and cover different behaviors. - -Still to measure: a JS net benchmark-only edit selects WASM and the net -dependents, and a stats benchmark-only edit selects relay and CLI tests, docs, -and CLI's optional play build. Compare that cost with the regression signal -before changing selection. diff --git a/quest/m2/README.md b/quest/m2/README.md index 502885431fa..74dfde121e9 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -176,3 +176,4 @@ ranked just below deterministic TS legs, which it requires. - [Relay metrics through prometheus-client](/quest/m2/relay-prometheus-client.md) - `/metrics` renders through the maintained client with the same series - [Dead routes on IETF name their cause](/quest/m2/js-ietf-dead-route-error.md) - a JS subscription on a dead moq-transport session ends with the session error, not `unauthorized` - [Error cleanups](/quest/m2/error-cleanups.md) - delete the unused `Error::SessionClosed`; raw transport failures already map to `Unroutable` +- [Test cost audit](/quest/m2/audit-test-cost.md) - the slowest, flakiest, and least valuable tests are fixed, deleted, or planned once the release ships diff --git a/quest/m2/audit-test-cost.md b/quest/m2/audit-test-cost.md new file mode 100644 index 00000000000..8224aaa9da8 --- /dev/null +++ b/quest/m2/audit-test-cost.md @@ -0,0 +1,83 @@ +# [M] Test cost audit + +## Goal + +The slowest, flakiest, and least valuable tests are sped up, fixed, deleted, +or planned, so the suite costs what it earns. Workflows and jobs belong to the +[CI workflow audit](/quest/m1/audit-ci.md). + +## Plan + +Recurs once per major release (decided 2026-10-19). The PR that finishes +this run also adds the next one with the same file name at the end of the next +milestone's `Required` (m0 to m1, m1 to m2). The successor keeps the Goal +and this paragraph; the rest of its Plan is rewritten with what this run +learned: baselines, results, and what to check first. A successor placed in m2 +also lists the [release cut](/quest/m2/release/cut.md) under `Required`, so it +waits for the release in flight to ship. Only `/quest-delete` ends the +recurrence. Fixes land in the run's PR, one commit each, rather than as follow-ups. +Only a change that needs a maintainer decision (public API, scope) or is +independently landable and too large to review alongside the rest becomes a +follow-up through `/quest-plan`. + +Rank tests from nextest's lines in a full-selection queue log (`slow-timeout` +is 60 s under the `ci` profile). Flakes are tracked by hand in their own +quests, and `retries = 0` is deliberate. A test that sleeps on the wall clock +should mock time (AGENTS.md). Delete tests whose coverage another test already +provides. Repeat each local measurement: parallel agents load the host, so a +single run can be off by 2x. Pin to the queue VM's 8 CPUs with `taskset -c +0-7` when timing the whole suite. A test whose time differs between runs by +about 3 s is waiting out a QUIC draining period, not computing. + +What the m1 run (2026-10-21) fixed: moq-auth's stalled re-check close on the +paused clock (11 s to 0.03 s) and its refusal re-checks by a nudge instead +of the cadence (3 s and 1 s), the 900 s TS export past-limit failure the +jitter buffer's tests already cover (16.6 s), the close-code tests dialing +`localhost` against an IPv4-only server (3 s to 0.5 s each on the NixOS +runner, which lists `::1` first), and the split-accept driver tests' 333 ms +initial RTT (3 s to 0.2 s each). A benchmark-only edit now selects only its +own Rust crate or JS workspace; a moq-stats bench edit had run the relay and +CLI suites. To catch the `localhost` race on a host without it, run the +suite under an `/etc/hosts` that lists `::1` first (`unshare -rm`, then a +bind mount over `/etc/hosts`); only the close-code tests regressed. + +Baselines from queue run +[39546261474](https://github.com/moq-dev/moq/actions/runs/39546261474), a full +selection on the 8-core runner VM: nextest ran 9,312 tests in 127 s. Their +summed time was 995 s, so the run is packed by slot time; the 48 tests over +5 s were 480 s of it. By crate: moq-cli 225 s, moq-relay 202 s, moq-mux 130 s, +moq-net 73 s, moq-tokio 59 s, moq-gst 56 s, moq-uring 41 s. + +Check first: + +- Test threads. Pinned to 8 CPUs on a loaded host, the whole suite took + 105 to 113 s at 8 threads (about 185% CPU), 60 s at 16 (315%), 47 s at 24 + (420%), and 41 s at 32 (500%): the suite needs about 200 CPU-seconds, and + most slots wait on sockets, subprocesses, and wall-clock timeouts. + Oversubscribing the queue job is a maintainer decision, since it loads + every timing-sensitive test and a flake ejects a queue entry. Only the + known redundant-importer flake failed, once, at 16 threads. +- moq-gst: about ten tests per run each pay 3 s, at random. `stop` closes the + client, which waits out the draining period (three PTOs at the 333 ms + initial RTT) of a dial to the tests' unreachable relay that never got an + answer. A user stopping a pipeline whose relay is down waits the same. Ask + whether moq-quic may skip draining for a connection that never heard from + its peer. +- moq-uring: three endpoint tests wait out moq-quic's fixed 10 s handshake + idle timeout (30 s together). Neither moq-uring's `Transport` nor + `--quic-idle-timeout` sets it; ask whether the configured idle timeout + should bound the handshake too (public API). +- CPU-bound simulations in a debug build: five TS export timing cases (3 to + 11 s each) and the jitter buffer's day of drift (6 to 13 s), whose `Steer` + rebuilds the hull and median over its whole window on every step. +- moq-cli's export tests pace a real clip through separate processes; the + redundant-importer cases wait out a killed importer's idle timeout + ([moq-dev/moq#6113](https://github.com/moq-dev/moq/pull/6113) shortens it). + moq-relay's `cluster_idle` and flapping-peer drill wait the default 10 s + idle timeout on purpose, and moq-gst's `relay_loss` test the retry budget. +- moq-c tests took 0.8 to 1.7 s in the queue against 0.02 s locally; not yet + explained. + +## Required + +- [Cut and publish the prepared breaking release](/quest/m2/release/cut.md) - the release in flight ships before this run starts From 4e8a0c217910a20f22a31661fd40b7a319acc37b Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 00:35:06 -0700 Subject: [PATCH 8/8] style(tokio): rustfmt the blocked-handshake client config Co-Authored-By: Claude Opus 5.5 --- rs/moq-tokio/src/quic/driver/tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rs/moq-tokio/src/quic/driver/tests.rs b/rs/moq-tokio/src/quic/driver/tests.rs index 6ffa3b8228f..2e14b88f67a 100755 --- a/rs/moq-tokio/src/quic/driver/tests.rs +++ b/rs/moq-tokio/src/quic/driver/tests.rs @@ -513,7 +513,8 @@ fn blocking_server_pair( .with_root_certificates(roots) .with_no_client_auth(); client_crypto.alpn_protocols = vec![client_alpn.to_vec()]; - let mut client_config = ClientConfig::new(Arc::new(QuicClientConfig::try_from(client_crypto).unwrap())); + let mut client_config = + ClientConfig::new(Arc::new(QuicClientConfig::try_from(client_crypto).unwrap())); client_config.transport_config(transport); client.set_default_client_config(client_config);