refactor(move): split RunnerTests+CommandExecution.swift along command families - #2855
Conversation
…d families The 1,985-line file becomes six, each under 1,000 lines, by moving whole declarations: - CommandDispatch: executeAccepted through executeOnMain, the dispatched recovery loop, target preparation, activation preflight, and recorded-failure conversion. - CommandExecution: the prepared-command switch (executeOnMainPrepared). - GestureExecution: gesture timing and gesture response building. - ScrollDragExecution: scroll and drag execution and their refusals. - TypeExecution: executeTypeCommand. - SnapshotExecution: snapshot dispatch, preparation, and post-capture marks. Its tests split the same way, and the dispatch recovery test follows executeDispatchedWithRecovery into CommandDispatchTests. The shared test helpers move beside what they drive: execute(command:) to TransportTests and runnerCommandFixture to ModelsTests. Declarations now read across files widen from private to internal. Closes #2792 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Size Report
Startup median (7 runs, lower is better):
|
|
This is a clean move: |
main (#2854, #2855) moved the runner's inline unit tests into UnitTests/ and split RunnerTests+CommandExecution.swift along command families. Rebase conflicts dropped this PR's edits from the inline blocks the rebase kept on main; relocate them into the file main now owns for each family: - RunnerTests+SnapshotTests.swift: the query-sweep slice-deadline and fail-closed-invalidation tests, and the bounded-modal-probe helper now takes a SnapshotCaptureTarget. - RunnerTests+SnapshotCapturePlanTests.swift: the tier-timeout rejection pair and the private-AX depth helper now builds a SnapshotCaptureTarget. - RunnerTests+SnapshotTimingTests.swift: the deadline-exhausted tier penalty test.
…e query sweep by its slice (#2836) * fix(ios): take snapshot target identity on main and hop plan state writes The capture plan runs on the command queue but read currentBundleId, currentAppProcessIdentifier and the penalty warm-up exemption, and wrote runnerAccessibilityHealth and invalidated the cached target directly. Snapshot preparation now takes a SnapshotCaptureTarget on main (app, bundle id, pid, consumed warm-up exemption) and the plan, the modal probe penalty, the XCTest penalty recorder and the private AX tier read only that copy. Health writes and the fail-closed invalidation go through applyMainOwnedSnapshotState, the abandoned-work-guarded main hop that invalidateCachedTargetAfterSnapshotFailure now also uses. Fixes #2781 * fix(ios): bound the query sweep by its tier slice deadline A non-interactive query sweep used Date.distantFuture as its own deadline, so only the up-to-20 s plan deadline bounded it while the caller abandoned the tier after its 1 s slice. XCTest queries cannot be cancelled, so the sweep kept the main thread busy and later commands got RUNNER_BUSY. The caller now derives one slice deadline (querySweepSliceDeadline) for both its wait and the sweep, for interactive and non-interactive requests alike. Neither the element queries nor the per-element reads start with less than flatInteractiveQueryBudget left before that deadline (querySweepCanStartQuery). Fixes #2783 * fix(ios): spend the penalty warm-up exemption only in a capture plan that runs Taking the snapshot target on main consumed the exemption during command preparation, so a snapshot answered by the blocking system-modal probe, or an abandoned preparation that ran late, spent it without running a plan. The exemption now lives in a lock-owned type that lifecycle code arms on main and runSnapshotCapturePlan consumes at its start. * test(ios): pin the non-interactive query sweep to its tier slice through the plan Swizzles XCUIElementQuery.allElementsBoundByIndex with a slow query and runs a non-interactive [.querySweep] plan with a 20 s plan deadline. The sweep must not be abandoned, and no query may start after the plan answers, so passing the plan deadline to the sweep in either loop turns it red. * test(ios): pin the query-sweep tier timeout and the tap routing identity to main Two regressions #2781 still leaves open, both red on this head: A query sweep that stops at its own slice deadline is a tier timeout, but the plan classified its payload by node count, so a partial sweep that beat the sparse threshold armed no XCTest-channel penalty and no later capture of the same screen was deferred. The coordinate tap's system-modal routing probe armed its penalty with currentBundleId read on the command queue, while main owned that state and could still be clearing or rebinding it. * fix(ios): count a query sweep that ends on its slice deadline as a tier timeout The sweep tier stopped starting queries at its slice deadline, but the plan classified only what it collected. sparsePayloadReason accepts anything above the sparse node threshold, so a partial flat sweep became a `recovered` capture: no XCTest-channel penalty was armed, private AX never ran, and every later capture of that screen paid for the full sweep again. `runFlatInteractiveQueries` now returns a typed `SnapshotTierOutcome`, the sweep tier carries it into the attempt, and the plan rejects a `.deadlineExhausted` tier through `snapshotTierRejectionReason` — keeping its payload only as the fallback, arming the penalty through the existing tier timeout reason, and letting the next backend answer. * fix(ios): take the system-modal probe's target identity on main The coordinate tap resolved its system-modal routing on the command queue and armed an abandoned probe's penalty with `currentBundleId` read there, while `applyMainOwnedSnapshotState` and the lifecycle code write that identity through `DispatchQueue.main.async`. A tap could penalize a target main was still clearing or rebinding. The probe now takes a `SnapshotProbePenaltyTarget`: a capture hands over the identity it already took on main, and the tap route hands over nothing and lets the probe's own main-side block capture the identity main holds once its work starts. No off-main path reads target identity any more. * test(ios): place this PR's runner tests in main's split UnitTests files main (#2854, #2855) moved the runner's inline unit tests into UnitTests/ and split RunnerTests+CommandExecution.swift along command families. Rebase conflicts dropped this PR's edits from the inline blocks the rebase kept on main; relocate them into the file main now owns for each family: - RunnerTests+SnapshotTests.swift: the query-sweep slice-deadline and fail-closed-invalidation tests, and the bounded-modal-probe helper now takes a SnapshotCaptureTarget. - RunnerTests+SnapshotCapturePlanTests.swift: the tier-timeout rejection pair and the private-AX depth helper now builds a SnapshotCaptureTarget. - RunnerTests+SnapshotTimingTests.swift: the deadline-exhausted tier penalty test.
|
Follow-up on the name-collision check. I ran it only on the 12 names this split widened from
So there are no internal overloads or shadowing among the widened names. The 🤖 Generated with Claude Code |
Summary
Closes #2792. Stacked on #2854.
Pure move:
RunnerTests+CommandExecution.swift(1,985 lines after #2854) is split by moving whole declarations along command families:CommandDispatch(620): dispatch entry, recovery loop, target preparation, recorded-failure conversionCommandExecution(638): the prepared-command switchGestureExecution(165)ScrollDragExecution(287)TypeExecution(142)SnapshotExecution(151)The tests split the same way. The dispatch recovery test follows
executeDispatchedWithRecoveryintoCommandDispatchTests. The shared helpers move beside what they drive:execute(command:)goes toTransportTestsandrunnerCommandFixturetoModelsTests. The split widens 12 declarations fromprivatetointernal, each read from another file. Every #if guard is kept. 15 files.git diff -M90% --stat refactor/2792-move-runner-inline-tests...HEAD: 15 files, +2041/−1986. A removed-vs-added line multiset differs only inprivate→internal, the per-fileimport/extension RunnerTests {headers, and the iOS stub block split between two test files under the same guard.Validation
Tested at
e9a7193a7(tree-identical to the originally testedfe30c8f65):60e533284) and 233, 0 failures after.pnpm check:xctest-selection: host 233, iOS PR list 103, nightly 280, green.pnpm check:packaged-runner-swift: green.pnpm check:affected --run: all runnable checks passed.build-for-testing, with and without the unit-test flag: succeeded.func testnames: 285 before and after.An adversarial fresh-context review found no functional issues: bodies, guards, lane reach, and widenings were confirmed, and every file is under 1,000 lines. The three stale comments it found are fixed in #2854.
🤖 Generated with Claude Code