agent-device 0.18.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.18.2.apk → agent-device-android-multitouch-helper-0.19.0.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.0.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.18.2.manifest.json → agent-device-android-multitouch-helper-0.19.0.manifest.json} +4 -4
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.18.2.apk → agent-device-android-snapshot-helper-0.19.0.apk} +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.0.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.18.2.manifest.json → agent-device-android-snapshot-helper-0.19.0.manifest.json} +6 -6
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +28 -0
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +9 -120
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +132 -66
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +155 -0
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +67 -0
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +20 -74
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +64 -0
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +0 -177
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +81 -48
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +102 -129
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +208 -46
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +82 -11
- package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +72 -0
- package/dist/src/agent-browser-tool.js +1 -0
- package/dist/src/android-adb.d.ts +64 -234
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android.js +1 -5
- package/dist/src/app-lifecycle.js +7 -0
- package/dist/src/apple-runner-options.js +1 -0
- package/dist/src/apps.js +5 -11
- package/dist/src/artifacts.d.ts +4 -3
- package/dist/src/artifacts.js +1 -1
- package/dist/src/audio-probe.js +1 -1
- package/dist/src/batch.d.ts +2 -177
- package/dist/src/batch.js +1 -1
- package/dist/src/cli-help.js +64 -38
- package/dist/src/cli-output.js +1 -1
- package/dist/src/cli-runner.js +2 -0
- package/dist/src/cli.js +12 -41
- package/dist/src/client-metro-companion.js +1 -0
- package/dist/src/client.js +3 -0
- package/dist/src/command-metadata.js +1 -0
- package/dist/src/command-schema.js +22 -0
- package/dist/src/command-surface.js +1 -1
- package/dist/src/contracts.d.ts +2 -213
- package/dist/src/contracts.js +1 -1
- package/dist/src/debug-symbols.js +5 -3
- package/dist/src/device-input-state.js +2 -0
- package/dist/src/device-rotation.js +14 -0
- package/dist/src/devices.js +1 -1
- package/dist/src/devices2.js +1 -0
- package/dist/src/devices3.js +1 -0
- package/dist/src/find.js +1 -1
- package/dist/src/finders.d.ts +2 -81
- package/dist/src/finders.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/handler-utils.js +1 -1
- package/dist/src/helper.js +2 -0
- package/dist/src/host-process.js +2 -0
- package/dist/src/index.d.ts +1265 -1768
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +3 -1
- package/dist/src/input-actions2.js +1 -0
- package/dist/src/install-source.d.ts +13 -14
- package/dist/src/install-source.js +1 -1
- package/dist/src/install-source2.js +1 -0
- package/dist/src/interaction-snapshot.js +1 -0
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +2 -1
- package/dist/src/internal/bin.d.ts +1 -1
- package/dist/src/internal/bin.js +2 -5
- package/dist/src/internal/companion-tunnel.d.ts +1 -1
- package/dist/src/internal/companion-tunnel.js +1 -1
- package/dist/src/internal/daemon.d.ts +1 -1
- package/dist/src/internal/daemon.js +16 -1
- package/dist/src/internal/png-worker.d.ts +29 -26
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/internal/update-check-entry.d.ts +1 -1
- package/dist/src/internal/update-check-entry.js +1 -1
- package/dist/src/io.d.ts +2 -85
- package/dist/src/io.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/logcat.js +6 -0
- package/dist/src/manifest.js +1 -0
- package/dist/src/metro.d.ts +70 -113
- package/dist/src/metro.js +1 -1
- package/dist/src/metro2.js +1 -0
- package/dist/src/notifications.js +1 -1
- package/dist/src/output-path.js +1 -0
- package/dist/src/path-resolution.js +1 -0
- package/dist/src/png-worker-contract.js +1 -0
- package/dist/src/points.js +4 -0
- package/dist/src/project-runtime.js +1 -0
- package/dist/src/provider.js +1 -1
- package/dist/src/providers.js +1 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace-recording.js +2 -5
- package/dist/src/record-trace.js +1 -1
- package/dist/src/recording-provider.js +1 -1
- package/dist/src/registry.js +37 -0
- package/dist/src/remote-config.d.ts +2 -83
- package/dist/src/reporting.js +9 -0
- package/dist/src/rolldown-runtime.js +1 -0
- package/dist/src/runner-client.js +1 -0
- package/dist/src/runner-provider.js +1 -0
- package/dist/src/runner-sequence.js +1 -1
- package/dist/src/runtime-transport.js +1 -0
- package/dist/src/runtime.js +3 -0
- package/dist/src/scoped-provider.js +1 -0
- package/dist/src/sdk-android-adb.d.ts +144 -0
- package/dist/src/sdk-android-adb.js +4 -0
- package/dist/src/sdk-app-inventory.d.ts +4 -0
- package/dist/src/sdk-app-inventory.js +1 -0
- package/dist/src/sdk-batch-runner.d.ts +40 -0
- package/dist/src/sdk-batch-runner.js +2 -0
- package/dist/src/sdk-batch.js +2 -0
- package/dist/src/sdk-contracts.d.ts +386 -0
- package/dist/src/sdk-contracts.js +2 -0
- package/dist/src/sdk-finders.d.ts +61 -0
- package/dist/src/sdk-finders.js +1 -0
- package/dist/src/sdk-io.d.ts +79 -0
- package/dist/src/sdk-io.js +1 -0
- package/dist/src/sdk-remote-config.d.ts +140 -0
- package/dist/src/sdk-remote-config.js +1 -0
- package/dist/src/sdk-selectors.js +1 -0
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selectors.d.ts +39 -137
- package/dist/src/selectors.js +1 -1
- package/dist/src/selectors2.js +1 -0
- package/dist/src/server.js +3 -2
- package/dist/src/session.js +10 -11
- package/dist/src/setting-state.js +1 -0
- package/dist/src/simctl.js +1 -0
- package/dist/src/simulator.js +1 -2
- package/dist/src/snapshot-diagnostics.js +3 -0
- package/dist/src/snapshot-helper.js +5 -0
- package/dist/src/snapshot-quality.js +1 -0
- package/dist/src/snapshot.js +1 -2
- package/dist/src/snapshot2.js +2 -0
- package/dist/src/{5560.js → timeouts.js} +1 -1
- package/dist/src/tool-provider.js +3 -0
- package/dist/src/tool-provider2.js +1 -0
- package/dist/src/ttl-memo.js +1 -0
- package/dist/src/update-check.js +1 -0
- package/dist/src/url.js +1 -0
- package/dist/src/version.js +1 -0
- package/dist/src/web.js +1 -1
- package/dist/src/xml.js +2 -0
- package/package.json +12 -19
- package/server.json +2 -2
- package/skills/agent-device/SKILL.md +2 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.18.2.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.18.2.apk.sha256 +0 -1
- package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +0 -443
- package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -169
- package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +0 -220
- package/dist/src/1404.js +0 -1
- package/dist/src/1986.js +0 -1
- package/dist/src/2748.js +0 -1
- package/dist/src/2948.js +0 -30
- package/dist/src/3006.js +0 -1
- package/dist/src/314.js +0 -1
- package/dist/src/3340.js +0 -8
- package/dist/src/4418.js +0 -1
- package/dist/src/4428.js +0 -3
- package/dist/src/485.js +0 -1
- package/dist/src/4917.js +0 -1
- package/dist/src/495.js +0 -5
- package/dist/src/5448.js +0 -3
- package/dist/src/5484.js +0 -1
- package/dist/src/5643.js +0 -1
- package/dist/src/587.js +0 -1
- package/dist/src/5913.js +0 -4
- package/dist/src/6849.js +0 -1
- package/dist/src/6918.js +0 -1
- package/dist/src/6967.js +0 -1
- package/dist/src/760.js +0 -1
- package/dist/src/7871.js +0 -1
- package/dist/src/8184.js +0 -1
- package/dist/src/8225.js +0 -1
- package/dist/src/8391.js +0 -1
- package/dist/src/8404.js +0 -2
- package/dist/src/8414.js +0 -2
- package/dist/src/8875.js +0 -4
- package/dist/src/893.js +0 -1
- package/dist/src/8936.js +0 -1
- package/dist/src/9414.js +0 -1
- package/dist/src/9581.js +0 -22
- package/dist/src/9612.js +0 -1
- package/dist/src/9624.js +0 -1
- package/dist/src/9722.js +0 -38
- package/dist/src/devices~1.js +0 -1
- package/dist/src/devices~2.js +0 -1
- package/dist/src/input-actions~1.js +0 -1
- package/dist/src/rslib-runtime.js +0 -1
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -0
- /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-trim.swift +0 -0
|
@@ -63,6 +63,46 @@ extension RunnerTests {
|
|
|
63
63
|
static let regularVisiblePlan: [SnapshotBackendKind] = [.recursiveTree, .querySweep, .privateAX]
|
|
64
64
|
static let rawDiagnosticPlan: [SnapshotBackendKind] = [.recursiveTree, .privateAX]
|
|
65
65
|
|
|
66
|
+
// MARK: Tree-backend penalty (cross-attempt memory, #1105)
|
|
67
|
+
//
|
|
68
|
+
// On some deep/dynamic screens the XCTest bulk snapshot no longer fails fast with
|
|
69
|
+
// kAXErrorIllegalArgument (the #758 signature) — it grinds for many seconds first. One slow
|
|
70
|
+
// grind is tolerable; re-grinding on every subsequent capture of the same screen buries the
|
|
71
|
+
// main thread past the execution watchdog. After a slow or abandoned tree capture, later
|
|
72
|
+
// plans for the same bundle lead with the private AX backend until the penalty expires.
|
|
73
|
+
|
|
74
|
+
func penalizeSnapshotTreeBackend(bundleId: String?, reason: String) {
|
|
75
|
+
snapshotTreePenaltyLock.lock()
|
|
76
|
+
snapshotTreePenaltyBundleId = bundleId
|
|
77
|
+
snapshotTreePenaltyUntil = Date().addingTimeInterval(snapshotTreePenaltyDuration)
|
|
78
|
+
snapshotTreePenaltyLock.unlock()
|
|
79
|
+
NSLog(
|
|
80
|
+
"AGENT_DEVICE_RUNNER_SNAPSHOT_TREE_PENALIZED bundle=%@ reason=%@",
|
|
81
|
+
bundleId ?? "",
|
|
82
|
+
reason
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func isSnapshotTreeBackendPenalized(bundleId: String?) -> Bool {
|
|
87
|
+
snapshotTreePenaltyLock.lock()
|
|
88
|
+
defer { snapshotTreePenaltyLock.unlock() }
|
|
89
|
+
guard Date() < snapshotTreePenaltyUntil else { return false }
|
|
90
|
+
// A penalty recorded without a bundle id applies to whatever target is current.
|
|
91
|
+
guard let penalized = snapshotTreePenaltyBundleId else { return true }
|
|
92
|
+
return penalized == bundleId
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Pure plan-reorder rule: a penalized tree backend defers to the private AX backend on the
|
|
96
|
+
/// regular plan only. The raw diagnostic plan keeps tree-first errors, and unknown plans are
|
|
97
|
+
/// left untouched.
|
|
98
|
+
static func effectiveSnapshotCapturePlan(
|
|
99
|
+
_ plan: [SnapshotBackendKind],
|
|
100
|
+
treePenalized: Bool
|
|
101
|
+
) -> (plan: [SnapshotBackendKind], treeDeferred: Bool) {
|
|
102
|
+
guard treePenalized, plan == Self.regularVisiblePlan else { return (plan, false) }
|
|
103
|
+
return ([.privateAX, .querySweep, .recursiveTree], true)
|
|
104
|
+
}
|
|
105
|
+
|
|
66
106
|
// MARK: Plan runner
|
|
67
107
|
|
|
68
108
|
func runSnapshotCapturePlan(
|
|
@@ -76,21 +116,56 @@ extension RunnerTests {
|
|
|
76
116
|
var axFailure: SnapshotCaptureFailure?
|
|
77
117
|
let deadline = Date().addingTimeInterval(Self.snapshotPlanBudget)
|
|
78
118
|
|
|
79
|
-
|
|
80
|
-
|
|
119
|
+
// Reorder is gated to iOS simulators — the only place the private AX backend exists, so
|
|
120
|
+
// the deferred plan cannot degrade platforms where the tree backend is the strongest tier.
|
|
121
|
+
var treePenalized = false
|
|
122
|
+
#if os(iOS) && targetEnvironment(simulator)
|
|
123
|
+
treePenalized = isSnapshotTreeBackendPenalized(bundleId: currentBundleId)
|
|
124
|
+
#endif
|
|
125
|
+
let (effectivePlan, treeDeferred) = Self.effectiveSnapshotCapturePlan(
|
|
126
|
+
plan,
|
|
127
|
+
treePenalized: treePenalized
|
|
128
|
+
)
|
|
129
|
+
if treeDeferred {
|
|
130
|
+
firstFailure = (
|
|
131
|
+
"the XCTest tree backend was deferred after a recent slow capture on this screen",
|
|
132
|
+
"budget"
|
|
133
|
+
)
|
|
134
|
+
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_TREE_DEFERRED bundle=%@", currentBundleId ?? "")
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for kind in effectivePlan {
|
|
138
|
+
if kind != effectivePlan.first && Date() >= deadline {
|
|
81
139
|
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_PLAN_BUDGET_EXHAUSTED skipped=%@", kind.rawValue)
|
|
82
140
|
if firstFailure == nil {
|
|
83
141
|
firstFailure = ("the capture plan ran out of its time budget", "budget")
|
|
84
142
|
}
|
|
85
143
|
break
|
|
86
144
|
}
|
|
145
|
+
// While an abandoned tree capture is still grinding inside testmanagerd, XCTest-backed
|
|
146
|
+
// tiers would block behind it; only the private AX backend stays responsive (#1105).
|
|
147
|
+
if kind != .privateAX, hasAbandonedTreeCapture() {
|
|
148
|
+
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_TIER_SKIPPED_XCTEST_OCCUPIED tier=%@", kind.rawValue)
|
|
149
|
+
if firstFailure == nil {
|
|
150
|
+
firstFailure = (
|
|
151
|
+
"the XCTest capture channel is occupied by an abandoned tree capture",
|
|
152
|
+
"budget"
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
87
157
|
let capture: SnapshotBackendCapture
|
|
158
|
+
let backendStartedAt = Date()
|
|
88
159
|
do {
|
|
89
|
-
guard let result = try captureWithBackend(kind, app: app, options: options
|
|
160
|
+
guard let result = try captureWithBackend(kind, app: app, options: options, deadline: deadline)
|
|
161
|
+
else {
|
|
162
|
+
recordSlowTreeBackendIfNeeded(kind, startedAt: backendStartedAt)
|
|
90
163
|
continue
|
|
91
164
|
}
|
|
92
165
|
capture = result
|
|
166
|
+
recordSlowTreeBackendIfNeeded(kind, startedAt: backendStartedAt)
|
|
93
167
|
} catch let failure as SnapshotCaptureFailure {
|
|
168
|
+
recordSlowTreeBackendIfNeeded(kind, startedAt: backendStartedAt)
|
|
94
169
|
if Self.isAxSnapshotFailure(failure) { axFailure = failure }
|
|
95
170
|
if firstFailure == nil {
|
|
96
171
|
firstFailure = (failure.message, Self.isAxSnapshotFailure(failure) ? "ax-rejected" : "capture-failed")
|
|
@@ -111,7 +186,7 @@ extension RunnerTests {
|
|
|
111
186
|
continue
|
|
112
187
|
}
|
|
113
188
|
|
|
114
|
-
let recovered = kind !=
|
|
189
|
+
let recovered = kind != effectivePlan.first || treeDeferred
|
|
115
190
|
if recovered {
|
|
116
191
|
NSLog(
|
|
117
192
|
"AGENT_DEVICE_RUNNER_SNAPSHOT_RECOVERED backend=%@ reason=%@",
|
|
@@ -157,14 +232,33 @@ extension RunnerTests {
|
|
|
157
232
|
return fallbackPayload
|
|
158
233
|
}
|
|
159
234
|
|
|
235
|
+
/// Marks the tree backend as penalized when a capture attempt ground past the slow-capture
|
|
236
|
+
/// threshold — even a successful one: the next capture of this screen must not re-grind.
|
|
237
|
+
private func recordSlowTreeBackendIfNeeded(_ kind: SnapshotBackendKind, startedAt: Date) {
|
|
238
|
+
guard kind == .recursiveTree else { return }
|
|
239
|
+
let elapsed = Date().timeIntervalSince(startedAt)
|
|
240
|
+
guard elapsed > snapshotTreeSlowCaptureThreshold else { return }
|
|
241
|
+
penalizeSnapshotTreeBackend(
|
|
242
|
+
bundleId: currentBundleId,
|
|
243
|
+
reason: "slow_tree_capture_\(Int(elapsed * 1000))ms"
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
160
247
|
private func captureWithBackend(
|
|
161
248
|
_ kind: SnapshotBackendKind,
|
|
162
249
|
app: XCUIApplication,
|
|
163
|
-
options: SnapshotOptions
|
|
250
|
+
options: SnapshotOptions,
|
|
251
|
+
deadline: Date
|
|
164
252
|
) throws -> SnapshotBackendCapture? {
|
|
165
253
|
switch kind {
|
|
166
254
|
case .recursiveTree:
|
|
167
|
-
guard
|
|
255
|
+
guard
|
|
256
|
+
let context = try makeSnapshotTraversalContext(
|
|
257
|
+
app: app,
|
|
258
|
+
options: options,
|
|
259
|
+
captureDeadline: deadline
|
|
260
|
+
)
|
|
261
|
+
else {
|
|
168
262
|
return nil
|
|
169
263
|
}
|
|
170
264
|
let payload = options.raw
|
|
@@ -173,11 +267,11 @@ extension RunnerTests {
|
|
|
173
267
|
return SnapshotBackendCapture(payload: payload, effectiveDepth: nil)
|
|
174
268
|
case .querySweep:
|
|
175
269
|
return SnapshotBackendCapture(
|
|
176
|
-
payload: snapshotFlatInteractive(app: app, options: options),
|
|
270
|
+
payload: snapshotFlatInteractive(app: app, options: options, planDeadline: deadline),
|
|
177
271
|
effectiveDepth: nil
|
|
178
272
|
)
|
|
179
273
|
case .privateAX:
|
|
180
|
-
return privateAXSnapshotCapture(app: app, options: options)
|
|
274
|
+
return privateAXSnapshotCapture(app: app, options: options, deadline: deadline)
|
|
181
275
|
}
|
|
182
276
|
}
|
|
183
277
|
|
|
@@ -304,124 +398,3 @@ extension RunnerTests {
|
|
|
304
398
|
}
|
|
305
399
|
}
|
|
306
400
|
|
|
307
|
-
#if AGENT_DEVICE_RUNNER_UNIT_TESTS
|
|
308
|
-
// MARK: - In-bundle unit tests
|
|
309
|
-
|
|
310
|
-
extension RunnerTests {
|
|
311
|
-
private func planTestNode(
|
|
312
|
-
index: Int,
|
|
313
|
-
type: String,
|
|
314
|
-
label: String? = nil,
|
|
315
|
-
identifier: String? = nil,
|
|
316
|
-
hittable: Bool = false,
|
|
317
|
-
parentIndex: Int? = nil
|
|
318
|
-
) -> SnapshotNode {
|
|
319
|
-
SnapshotNode(
|
|
320
|
-
index: index,
|
|
321
|
-
type: type,
|
|
322
|
-
label: label,
|
|
323
|
-
identifier: identifier,
|
|
324
|
-
value: nil,
|
|
325
|
-
rect: snapshotRect(from: .zero),
|
|
326
|
-
enabled: true,
|
|
327
|
-
focused: nil,
|
|
328
|
-
selected: nil,
|
|
329
|
-
hittable: hittable,
|
|
330
|
-
depth: parentIndex == nil ? 0 : 1,
|
|
331
|
-
parentIndex: parentIndex,
|
|
332
|
-
hiddenContentAbove: nil,
|
|
333
|
-
hiddenContentBelow: nil
|
|
334
|
-
)
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
func testSparsePayloadReasonMatrix() {
|
|
338
|
-
let root = planTestNode(index: 0, type: "Application", label: "Example App", hittable: true)
|
|
339
|
-
let window = planTestNode(index: 1, type: "Window", parentIndex: 0)
|
|
340
|
-
let button = planTestNode(index: 1, type: "Button", label: "Ok", hittable: true, parentIndex: 0)
|
|
341
|
-
|
|
342
|
-
// Labeled, hittable root over a bare window is still sparse.
|
|
343
|
-
XCTAssertNotNil(Self.sparsePayloadReason(DataPayload(nodes: [root, window], truncated: false)))
|
|
344
|
-
// Deadline-truncated near-empty sweep needs recovery even with one real control.
|
|
345
|
-
XCTAssertNotNil(Self.sparsePayloadReason(DataPayload(nodes: [root, button], truncated: true)))
|
|
346
|
-
// The same tiny tree from a completed sweep is a legitimately minimal screen.
|
|
347
|
-
XCTAssertNil(Self.sparsePayloadReason(DataPayload(nodes: [root, button], truncated: false)))
|
|
348
|
-
// Empty payloads are degraded.
|
|
349
|
-
XCTAssertNotNil(Self.sparsePayloadReason(DataPayload(nodes: [], truncated: false)))
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
func testCollapsedLeafIndexesFlagsMergedContainersOnly() {
|
|
353
|
-
let root = planTestNode(index: 0, type: "Application", label: "App")
|
|
354
|
-
let merged = planTestNode(
|
|
355
|
-
index: 1,
|
|
356
|
-
type: "Other",
|
|
357
|
-
label: (0...30).map { "Row \($0), Tap" }.joined(separator: ", "),
|
|
358
|
-
parentIndex: 0
|
|
359
|
-
)
|
|
360
|
-
let prose = planTestNode(
|
|
361
|
-
index: 2,
|
|
362
|
-
type: "StaticText",
|
|
363
|
-
label: (0...30).map { "clause \($0)" }.joined(separator: ", "),
|
|
364
|
-
parentIndex: 0
|
|
365
|
-
)
|
|
366
|
-
XCTAssertEqual(Self.collapsedLeafIndexes([root, merged, prose]), [1])
|
|
367
|
-
XCTAssertNil(Self.collapsedLeafIndexes([root, prose]))
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
func testLegacyQualityMessageStatesFallbackMeaning() {
|
|
371
|
-
let recovered = SnapshotQuality(
|
|
372
|
-
state: "recovered",
|
|
373
|
-
backend: "queries",
|
|
374
|
-
reason: "snapshot returned only structural application/window nodes",
|
|
375
|
-
reasonCode: "sparse-tree",
|
|
376
|
-
effectiveDepth: nil,
|
|
377
|
-
collapsedLeafIndexes: nil
|
|
378
|
-
)
|
|
379
|
-
let message = Self.legacyQualityMessage(recovered)
|
|
380
|
-
XCTAssertTrue(message?.contains("queries accessibility backend") == true)
|
|
381
|
-
XCTAssertTrue(message?.contains("fixing the app's accessibility") == true)
|
|
382
|
-
XCTAssertTrue(message?.contains("screenshot as visual truth") == true)
|
|
383
|
-
XCTAssertNil(
|
|
384
|
-
Self.legacyQualityMessage(
|
|
385
|
-
SnapshotQuality(
|
|
386
|
-
state: "healthy", backend: "tree", reason: nil, reasonCode: nil, effectiveDepth: nil,
|
|
387
|
-
collapsedLeafIndexes: nil)
|
|
388
|
-
)
|
|
389
|
-
)
|
|
390
|
-
}
|
|
391
|
-
func testTerminalFailsClosedOnInteractiveAxFailureRegardlessOfSparseBest() {
|
|
392
|
-
// Interactive AX failure must invalidate + fail closed; a later tier's sparse synthetic-root
|
|
393
|
-
// "best" must never downgrade this to a returned-sparse payload (regression: best == nil guard).
|
|
394
|
-
XCTAssertEqual(
|
|
395
|
-
Self.resolveSnapshotPlanTerminal(terminal: .sparseWithFatalOnAXFailure, interactiveOnly: true),
|
|
396
|
-
.failClosed
|
|
397
|
-
)
|
|
398
|
-
XCTAssertEqual(
|
|
399
|
-
Self.resolveSnapshotPlanTerminal(terminal: .sparseWithFatalOnAXFailure, interactiveOnly: false),
|
|
400
|
-
.sparseBest
|
|
401
|
-
)
|
|
402
|
-
XCTAssertEqual(
|
|
403
|
-
Self.resolveSnapshotPlanTerminal(terminal: .throwOnAXFailure, interactiveOnly: true),
|
|
404
|
-
.throwAxFailure
|
|
405
|
-
)
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
func testSnapshotAccessibilityUnavailableCarriesSparseVerdict() {
|
|
409
|
-
currentApp = app
|
|
410
|
-
currentBundleId = "com.example.app"
|
|
411
|
-
defer {
|
|
412
|
-
currentApp = nil
|
|
413
|
-
currentBundleId = nil
|
|
414
|
-
}
|
|
415
|
-
let payload = snapshotAccessibilityUnavailable(
|
|
416
|
-
failure: SnapshotCaptureFailure(
|
|
417
|
-
code: "IOS_AX_SNAPSHOT_FAILED",
|
|
418
|
-
message: "kAXErrorIllegalArgument",
|
|
419
|
-
hint: "use screenshot"
|
|
420
|
-
)
|
|
421
|
-
)
|
|
422
|
-
XCTAssertEqual(payload.runnerFatal, true)
|
|
423
|
-
XCTAssertEqual(payload.snapshotQuality?.state, "sparse")
|
|
424
|
-
XCTAssertEqual(payload.snapshotQuality?.reasonCode, "ax-rejected")
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
#endif
|