@@ -21,6 +21,8 @@ extension RunnerTests {
2121 struct SnapshotTraversalContext {
2222 let queryRoot : XCUIElement
2323 let rootSnapshot : XCUIElementSnapshot
24+ /// How long reading `rootSnapshot` took: what one more query over the whole app is expected to cost.
25+ let rootReadDuration : TimeInterval
2426 /** Carries which way the app's interface is turned from the device's native space (#2612). */
2527 let viewport : SnapshotViewport
2628 /**
@@ -144,9 +146,11 @@ extension RunnerTests {
144146 min ( startedAt. addingTimeInterval ( flatInteractiveFallbackBudget) , planDeadline)
145147 }
146148
147- /// Whether a sweep query started at `now` can still finish before the slice `deadline`.
148- static func querySweepCanStartQuery( deadline: Date , now: Date ) -> Bool {
149- deadline. timeIntervalSince ( now) >= flatInteractiveQueryBudget
149+ /// Whether a query started at `now` and expected to take `expectedCost` can still finish before
150+ /// `deadline`. XCTest cannot cancel a query once it starts; the least admitted cost is a sweep
151+ /// query's.
152+ static func xctestQueryCanStart( deadline: Date , now: Date , expectedCost: TimeInterval = 0 ) -> Bool {
153+ deadline. timeIntervalSince ( now) >= max ( flatInteractiveQueryBudget, expectedCost)
150154 }
151155
152156 /// What one capture may spend reading the keyboard band before it gives up on the fact and lets the
@@ -181,18 +185,19 @@ extension RunnerTests {
181185 @MainActor
182186 func recursiveTreeSnapshotAcquisition(
183187 context: SnapshotTraversalContext ,
184- hint: CaptureHint
185- ) throws -> SnapshotAcquisition {
186- var cachedDescendantElements : [ XCUIElement ] ?
187- func collapsedTabDescendants( ) -> [ XCUIElement ] {
188- if let cachedDescendantElements {
189- return cachedDescendantElements
190- }
191- let result = snapshotElementsQuery {
192- context. queryRoot. descendants ( matching: . any) . allElementsBoundByIndex
188+ hint: CaptureHint ,
189+ processingDeadline: Date
190+ ) throws -> ( acquisition: SnapshotAcquisition , outcome: SnapshotTierOutcome ) {
191+ var outcome = SnapshotTierOutcome . completed
192+ var cachedCollapsedTabCandidates : [ CollapsedTabCandidate ] ?
193+ func collapsedTabCandidatesOnce( ) -> [ CollapsedTabCandidate ] {
194+ if let cachedCollapsedTabCandidates {
195+ return cachedCollapsedTabCandidates
193196 }
194- cachedDescendantElements = result. elements
195- return result. elements
197+ let candidates = collapsedTabCandidates ( context: context, processingDeadline: processingDeadline)
198+ if candidates == nil { outcome = . deadlineExhausted }
199+ cachedCollapsedTabCandidates = candidates ?? [ ]
200+ return candidates ?? [ ]
196201 }
197202
198203 // Acquisition serializes reported frames only; no coordinate space or fold decision is carried
@@ -212,7 +217,7 @@ extension RunnerTests {
212217 appendCollapsedTabFallbackNodes (
213218 to: & nodes,
214219 containerSnapshot: context. rootSnapshot,
215- resolveElements : collapsedTabDescendants ,
220+ resolveCandidates : collapsedTabCandidatesOnce ,
216221 depth: 1 ,
217222 parentIndex: 0
218223 )
@@ -269,19 +274,22 @@ extension RunnerTests {
269274 appendCollapsedTabFallbackNodes (
270275 to: & nodes,
271276 containerSnapshot: snapshot,
272- resolveElements : collapsedTabDescendants ,
277+ resolveCandidates : collapsedTabCandidatesOnce ,
273278 depth: depth + 1 ,
274279 parentIndex: node. index
275280 )
276281 }
277282 }
278283
279- return SnapshotAcquisition (
280- hint: hint,
281- nodes: nodes,
282- truncated: false ,
283- effectiveDepth: nil ,
284- viewport: context. viewport
284+ return (
285+ SnapshotAcquisition (
286+ hint: hint,
287+ nodes: nodes,
288+ truncated: outcome == . deadlineExhausted,
289+ effectiveDepth: nil ,
290+ viewport: context. viewport
291+ ) ,
292+ outcome
285293 )
286294 }
287295
@@ -469,7 +477,7 @@ extension RunnerTests {
469477 let flatElements = flatInteractiveElements ( app: app, deadline: deadline)
470478 var outcome = flatElements. outcome
471479 for element in flatElements. elements {
472- if !Self. querySweepCanStartQuery ( deadline: deadline, now: Date ( ) ) {
480+ if !Self. xctestQueryCanStart ( deadline: deadline, now: Date ( ) ) {
473481 NSLog ( " AGENT_DEVICE_RUNNER_SNAPSHOT_FLAT_FALLBACK_DEADLINE " )
474482 outcome = . deadlineExhausted
475483 break
0 commit comments