Skip to content

Commit 3719be2

Browse files
committed
fix(apple-runner): only the bounded tree stays behind a deferred plan, and names the budget
The query sweep's grind is what penalizes the channel, so it stays off a deferred plan; the occupancy test's no-sweep contract holds again. A capture the bounded tree recovered reports 'budget', not 'deferred'.
1 parent fc1a335 commit 3719be2

2 files changed

Lines changed: 54 additions & 8 deletions

File tree

‎apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift‎

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,20 @@ extension RunnerTests {
205205
}
206206
}
207207

208+
/// The reason a capture's verdict carries. An XCTest-backed tier that recovered a deferred plan
209+
/// ran on the bounded probe's short slice, so it reports that constraint ('budget') rather than
210+
/// the pre-selection ('deferred') the plan was seeded with.
211+
static func recoveredVerdictReason(
212+
backend: SnapshotBackendKind,
213+
state: SnapshotXCTestChannelPlanState,
214+
firstFailure: (reason: String, code: String)?
215+
) -> (reason: String, code: String)? {
216+
guard backend.usesXCTestAccessibilityChannel, state == .deferredToIndependentBackend else {
217+
return firstFailure
218+
}
219+
return xcTestChannelStateFirstFailure(.boundedXCTestProbe)
220+
}
221+
208222
/// Pure gate: a capture is planned as penalized when the channel penalty is
209223
/// active OR the daemon pinned the private-AX backend (same-backend evidence
210224
/// probe) — both mean "do not enter XCTest tree work first, and stamp the
@@ -250,13 +264,21 @@ extension RunnerTests {
250264
}
251265
let availablePlan = plan.filter { availableBackends.contains($0) }
252266
let recoveryPlan = availablePlan.filter { !$0.usesXCTestAccessibilityChannel }
253-
let boundedProbePlan = availablePlan.filter(\.usesXCTestAccessibilityChannel)
254-
// The independent backend reads only the app it can match as the active AX application, so
255-
// an out-of-process surface over the app (the Save Password sheet) leaves it empty for the
256-
// whole penalty. The bounded XCTest probe stays behind it as the last tier.
267+
if !recoveryPlan.isEmpty {
268+
// The independent backend reads only the app it can match as the active AX application, so
269+
// an out-of-process surface over the app (the Save Password sheet) leaves it empty for the
270+
// whole penalty. The tree, on the bounded probe's short slice, stays behind it; the query
271+
// sweep does not, since its grind is what the penalty keeps off the main thread.
272+
return EffectiveSnapshotCapturePlan(
273+
plan: recoveryPlan + availablePlan.filter { $0 == .recursiveTree },
274+
xCTestChannelState: .deferredToIndependentBackend,
275+
treeCaptureSliceBudgetOverride: Self.penalizedXCTestProbeTreeSliceBudget,
276+
preferredBackend: nil
277+
)
278+
}
257279
return EffectiveSnapshotCapturePlan(
258-
plan: recoveryPlan + boundedProbePlan,
259-
xCTestChannelState: recoveryPlan.isEmpty ? .boundedXCTestProbe : .deferredToIndependentBackend,
280+
plan: availablePlan.filter(\.usesXCTestAccessibilityChannel),
281+
xCTestChannelState: .boundedXCTestProbe,
260282
treeCaptureSliceBudgetOverride: Self.penalizedXCTestProbeTreeSliceBudget,
261283
preferredBackend: nil
262284
)
@@ -387,6 +409,11 @@ extension RunnerTests {
387409
}
388410

389411
let recovered = kind != effectivePlan.first || effective.xCTestChannelState != .normal
412+
let verdictReason = Self.recoveredVerdictReason(
413+
backend: kind,
414+
state: effective.xCTestChannelState,
415+
firstFailure: firstFailure
416+
)
390417
if recovered {
391418
NSLog(
392419
"AGENT_DEVICE_RUNNER_SNAPSHOT_RECOVERED backend=%@ reason=%@",
@@ -398,7 +425,7 @@ extension RunnerTests {
398425
capture,
399426
backend: kind,
400427
state: recovered ? .recovered : .healthy,
401-
reason: recovered || firstFailure?.code == "requested-backend" ? firstFailure : nil
428+
reason: recovered || firstFailure?.code == "requested-backend" ? verdictReason : nil
402429
)
403430
}
404431

‎apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/UnitTests/RunnerTests+SnapshotCapturePlanTests.swift‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ extension RunnerTests {
408408
Self.regularVisiblePlan,
409409
xCTestChannelPenalized: true
410410
)
411-
XCTAssertEqual(regular.plan, [.privateAX, .recursiveTree, .querySweep])
411+
XCTAssertEqual(regular.plan, [.privateAX, .recursiveTree])
412412
XCTAssertEqual(regular.xCTestChannelState, .deferredToIndependentBackend)
413413
XCTAssertEqual(regular.treeCaptureSliceBudgetOverride, Self.penalizedXCTestProbeTreeSliceBudget)
414414

@@ -430,6 +430,25 @@ extension RunnerTests {
430430
XCTAssertNil(raw.treeCaptureSliceBudgetOverride)
431431
}
432432

433+
func testRecoveredVerdictReasonNamesTheBoundedProbeForAnXCTestTierBehindPrivateAX() {
434+
let deferred = Self.xcTestChannelStateFirstFailure(.deferredToIndependentBackend)
435+
XCTAssertEqual(
436+
Self.recoveredVerdictReason(
437+
backend: .recursiveTree, state: .deferredToIndependentBackend, firstFailure: deferred
438+
)?.code,
439+
"budget"
440+
)
441+
XCTAssertEqual(
442+
Self.recoveredVerdictReason(
443+
backend: .privateAX, state: .deferredToIndependentBackend, firstFailure: deferred
444+
)?.code,
445+
"deferred"
446+
)
447+
XCTAssertNil(
448+
Self.recoveredVerdictReason(backend: .recursiveTree, state: .normal, firstFailure: nil)
449+
)
450+
}
451+
433452
func testEffectiveSnapshotCapturePlanUsesBoundedXCTestProbeWhenNoIndependentBackendRuns() {
434453
let physicalDevicePlan = Self.effectiveSnapshotCapturePlan(
435454
Self.regularVisiblePlan,

0 commit comments

Comments
 (0)