mirror of
https://github.com/block/buzz.git
synced 2026-11-06 09:21:45 +00:00
`BuzzPushSnapshotEnrichmentTests` failed on two unrelated branches on 2026-11-01 in "Mobile Swift (Native)", step "Test native controls and Runner unit tests", and passed on re-run both times: - Run 41327410782 (PR #8896, job 124910378184): `testRestrictionCleanupFailurePreservesSnapshotAndRestoreReleasesAuthority` timed out its 10 s "allowed without restoring snapshot" wait (19.3 s total). The preceding `testNewBridgeRetriesRecordedCleanupWithoutRestrictingAccess` passed but took 4.3 s. - Run 41325606910 (PR #8897, job 124905175903): `testNewBridgeRetriesRecordedCleanupWithoutRestrictingAccess` timed out its "retry failure stays allowed" wait (nil instead of `interaction_cleanup_retry_failed`), then the restriction test timed out both waits (nil result, still restricted, 0 deletion calls). Cause: scheduling, not test logic. Every reply from `BuzzPushSnapshotBridge` hops through a private serial queue created at `.utility` QoS and then `DispatchQueue.main`. In both failing runs the test host (the full Flutter app, loaded as `TEST_HOST`) saturated the 3-vCPU `macos-latest` runner for roughly 40 s after launch: `BrowserAuthBridgeTests.testAllowsOnlyBuilderlabLoginPages`, a synchronous main-thread URL-parsing test, took 2.65 s instead of 0.013 s, and the since-deleted `testStrictAgeGateWriteFailsWhenAppGroupStoreIsUnavailable` took 4.9 s. In that window the utility queue was the lowest-priority runnable work in the process, so it got no CPU for 10-20 s while main-thread work kept moving slowly; in both passing re-runs every test in this class took under 0.3 s. The tests themselves have nothing to fix: the deletion mocks are synchronous, the deadline scheduler is a no-op, each test uses its own temp directory and bridge, and no state is shared across tests, so neither a state leak nor an expectation ordering race explains a callback that never arrives. Utility is also the wrong class in production: Dart awaits every one of these calls, and the age-gate restore runs during app launch, exactly when the process is busiest. Change: the bridge queue and the default interaction-deletion deadline timer run at `.userInitiated`. The restriction test now records `qos_class_self()` inside the injected deletion closure, which the bridge invokes synchronously on its queue, and asserts both calls ran at `QOS_CLASS_USER_INITIATED`. The 10 s waits are unchanged. Verification (Xcode 27.2, iOS 27.2 simulator, 16-core host; the CI starvation did not reproduce locally): - Class in isolation: 10/10 runs green, 0.1-0.4 s each. - Full RunnerTests bundle with CI's `-parallel-testing-enabled NO`: 2/2 runs green (101 tests, about 22 s). - Falsification: with the queue temporarily back at `.utility` the new assertion fails, observing `[17, 17]` (utility) instead of `[25, 25]`. - A throwaway probe confirmed that async blocks on a queue with an assigned QoS run at that QoS (17 for utility, 25 for user-initiated), so the change alters real scheduling. Saturating the host process's default-QoS pool with spinning blocks did not delay a utility hop (5 ms), so the CI stall is OS-level CPU priority starvation on a saturated 3-vCPU VM, which this machine cannot reproduce; the diagnosis rests on the CI timings above. - The repo has no swiftformat or swiftlint configuration; `git diff --check` is clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>