agent-device 0.21.3 → 0.21.5
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/ime-helper/dist/{agent-device-android-ime-helper-0.21.3.apk → agent-device-android-ime-helper-0.21.5.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.5.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.3.manifest.json → agent-device-android-ime-helper-0.21.5.manifest.json} +4 -4
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.3.apk → agent-device-android-snapshot-helper-0.21.5.apk} +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.5.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.5.manifest.json +16 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +39 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +9 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +2 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +10 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +26 -124
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +46 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +36 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +229 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +15 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +4 -40
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +6 -40
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +30 -9
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +22 -6
- package/dist/src/adapter.js +1 -1
- package/dist/src/adb-executor.js +2 -2
- package/dist/src/adb-failure.js +1 -1
- package/dist/src/agent-browser-process-record.js +1 -1
- package/dist/src/agent-device-client.js +2 -2
- package/dist/src/api.js +3 -11
- package/dist/src/app-launch.js +1 -1
- package/dist/src/app-lifecycle.js +1 -1
- package/dist/src/app-log-reconnect.js +1 -1
- package/dist/src/app-log-runtime2.js +1 -1
- package/dist/src/archive.js +2 -2
- package/dist/src/cli-help.js +2 -2
- package/dist/src/cli.js +4 -4
- package/dist/src/client-metro-companion.js +1 -1
- package/dist/src/client-metro.js +1 -1
- package/dist/src/client-types.d.ts +69 -4
- package/dist/src/code-signature-cache.js +1 -1
- package/dist/src/command-tools.js +2 -2
- package/dist/src/command.js +1 -1
- package/dist/src/connection-runtime.js +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/daemon-client-lifecycle.js +1 -1
- package/dist/src/daemon-shutdown-report.js +1 -1
- package/dist/src/device-claim-rule.js +1 -1
- package/dist/src/device-input-state.d.ts +10 -8
- package/dist/src/device-inventory-context.js +1 -1
- package/dist/src/device-session.js +1 -1
- package/dist/src/diagnostics.js +1 -1
- package/dist/src/error.js +2 -2
- package/dist/src/exec.js +1 -1
- package/dist/src/file.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/gesture-runtime.js +1 -1
- package/dist/src/helper.js +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/install-source-network-transport.js +1 -1
- package/dist/src/interaction2.js +1 -1
- package/dist/src/interactor.js +4 -4
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +2 -2
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/ios-client.js +121 -7
- package/dist/src/ios-snapshot-adapter.js +1 -1
- package/dist/src/ios-system-surface.js +1 -1
- package/dist/src/limrun-delta-sync-omitted.js +1 -0
- package/dist/src/limrun.d.ts +0 -1598
- package/dist/src/location-coordinates.js +1 -1
- package/dist/src/mechanics.js +16 -16
- package/dist/src/output-common.js +2 -0
- package/dist/src/owner-identity.js +1 -1
- package/dist/src/perf-runtime-operation-builder.js +1 -1
- package/dist/src/perf-runtime-plan.js +1 -1
- package/dist/src/platform-runtime-android-adb-host.js +1 -1
- package/dist/src/platform-runtime-app-log-android-transport.js +1 -1
- package/dist/src/platform-runtime-app-log-process.js +1 -1
- package/dist/src/platform-runtime-host.js +1 -1
- package/dist/src/platform-runtime-operation-host.js +2 -2
- package/dist/src/platform-runtime-screen-recording-android-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-output-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
- package/dist/src/process.js +1 -1
- package/dist/src/proxy.js +1 -1
- package/dist/src/quality-warnings.js +1 -1
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-runtime.js +1 -1
- package/dist/src/recording-stop-observation.js +1 -0
- package/dist/src/registry.js +35 -36
- package/dist/src/replay-divergence.js +1 -1
- package/dist/src/reporting.js +11 -9
- package/dist/src/result-serialization.js +1 -1
- package/dist/src/runner-cache-metadata.js +1 -1
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-disposal.js +1 -1
- package/dist/src/runner-operations-facade.js +1 -1
- package/dist/src/runner.js +1 -1
- package/dist/src/runtime.js +3 -3
- package/dist/src/runtime10.js +1 -1
- package/dist/src/runtime2.js +4 -4
- package/dist/src/runtime3.js +1 -1
- package/dist/src/runtime4.js +4 -4
- package/dist/src/runtime5.js +1 -1
- package/dist/src/runtime6.js +1 -1
- package/dist/src/runtime9.js +1 -1
- package/dist/src/screen-recording-completion.js +1 -1
- package/dist/src/screen-recording-resource-recovery.js +1 -1
- package/dist/src/screenshot-result.js +1 -1
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-selectors.d.ts +36 -24
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/semantic-index.js +1 -1
- package/dist/src/session-observation.js +1 -1
- package/dist/src/session-snapshot.js +1 -1
- package/dist/src/session-store.js +2 -2
- package/dist/src/session2.js +5 -5
- package/dist/src/snapshot-process.js +1 -1
- package/dist/src/snapshot-runtime.js +1 -1
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot4.js +1 -1
- package/dist/src/stop-sequence.js +1 -0
- package/dist/src/success-text.js +1 -1
- package/dist/src/text-entry-focus.js +1 -0
- package/dist/src/tool-provider.js +1 -1
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/tool-provider3.js +1 -1
- package/dist/src/type-text-runtime.js +1 -1
- package/dist/src/video.js +2 -2
- package/dist/src/yauzl.js +1 -0
- package/package.json +2 -2
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.3.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.3.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.3.manifest.json +0 -17
- package/dist/src/audio-probe-support.js +0 -1
- package/dist/src/screen-recording-live-handle.js +0 -1
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2b8fe19f9cf38877dbc4e0bd9400e4d1aebc7b8c2f6063e04f5f198d1564c08d agent-device-android-ime-helper-0.21.5.apk
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "android-ime-helper",
|
|
3
|
-
"version": "0.21.
|
|
4
|
-
"assetName": "agent-device-android-ime-helper-0.21.
|
|
5
|
-
"sha256": "
|
|
3
|
+
"version": "0.21.5",
|
|
4
|
+
"assetName": "agent-device-android-ime-helper-0.21.5.apk",
|
|
5
|
+
"sha256": "2b8fe19f9cf38877dbc4e0bd9400e4d1aebc7b8c2f6063e04f5f198d1564c08d",
|
|
6
6
|
"packageName": "com.callstack.agentdevice.imehelper",
|
|
7
|
-
"versionCode":
|
|
7
|
+
"versionCode": 21005,
|
|
8
8
|
"serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
|
|
9
9
|
"broadcastProtocol": "android-ime-helper-v1"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e77b65f51664e95680379cd1a5c8331926b63cf506261df56151854673787514 agent-device-android-snapshot-helper-0.21.5.apk
|
package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.5.manifest.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "android-snapshot-helper",
|
|
3
|
+
"version": "0.21.5",
|
|
4
|
+
"releaseTag": "v0.21.5",
|
|
5
|
+
"assetName": "agent-device-android-snapshot-helper-0.21.5.apk",
|
|
6
|
+
"apkUrl": null,
|
|
7
|
+
"sha256": "e77b65f51664e95680379cd1a5c8331926b63cf506261df56151854673787514",
|
|
8
|
+
"checksumName": "agent-device-android-snapshot-helper-0.21.5.apk.sha256",
|
|
9
|
+
"packageName": "com.callstack.agentdevice.snapshothelper",
|
|
10
|
+
"versionCode": 21005,
|
|
11
|
+
"instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
|
|
12
|
+
"minSdk": 23,
|
|
13
|
+
"targetSdk": 36,
|
|
14
|
+
"outputFormat": "uiautomator-xml",
|
|
15
|
+
"statusProtocol": "android-snapshot-helper-v1"
|
|
16
|
+
}
|
|
@@ -62,11 +62,46 @@ int main(int argc, const char *argv[]) {
|
|
|
62
62
|
@property(nonatomic, assign) NSUInteger firstAlertActions;
|
|
63
63
|
@property(nonatomic, assign) NSUInteger replacementAlertActions;
|
|
64
64
|
@property(nonatomic, assign) BOOL alertFixtureStarted;
|
|
65
|
+
@property(nonatomic, strong) NSTimer *alertActivationBusyBackstop;
|
|
65
66
|
@end
|
|
66
67
|
|
|
67
68
|
@implementation AgentDeviceRunnerViewController
|
|
68
69
|
|
|
69
70
|
#if TARGET_OS_IOS
|
|
71
|
+
// An animation that never ends is what "busy" looks like to XCTest while it decides whether the app
|
|
72
|
+
// may receive an event: the app keeps reporting work in flight, which is the state that cost an alert
|
|
73
|
+
// command its whole deadline in #2546. It stops the moment an alert button is answered, since that
|
|
74
|
+
// answer is the event the runner is trying to land, and the backstop stops it even when no answer
|
|
75
|
+
// arrives so a regressed run finishes rather than waiting out XCTest's own timeout. A layer
|
|
76
|
+
// animation on its own is not enough; only a UIView animation counts as in-flight work here.
|
|
77
|
+
static NSTimeInterval const kAgentDeviceAlertActivationBusyWindow = 20.0;
|
|
78
|
+
|
|
79
|
+
- (void)startAlertActivationBusy {
|
|
80
|
+
if (self.alertActivationBusyBackstop != nil) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
self.alertActivationBusyBackstop = [NSTimer scheduledTimerWithTimeInterval:kAgentDeviceAlertActivationBusyWindow
|
|
84
|
+
target:self
|
|
85
|
+
selector:@selector(stopAlertActivationBusy)
|
|
86
|
+
userInfo:nil
|
|
87
|
+
repeats:NO];
|
|
88
|
+
[UIView animateWithDuration:0.4
|
|
89
|
+
delay:0
|
|
90
|
+
options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
|
|
91
|
+
animations:^{
|
|
92
|
+
self.alertActionStatus.transform = CGAffineTransformMakeTranslation(0, 8);
|
|
93
|
+
}
|
|
94
|
+
completion:nil];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
- (void)stopAlertActivationBusy {
|
|
98
|
+
[self.alertActionStatus.layer removeAllAnimations];
|
|
99
|
+
self.alertActionStatus.transform = CGAffineTransformIdentity;
|
|
100
|
+
[self.alertActivationBusyBackstop invalidate];
|
|
101
|
+
self.alertActivationBusyBackstop = nil;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
70
105
|
- (void)updateAlertActionStatus {
|
|
71
106
|
self.alertActionStatus.text = [NSString stringWithFormat:@"First actions: %lu; replacement actions: %lu",
|
|
72
107
|
(unsigned long)self.firstAlertActions,
|
|
@@ -88,6 +123,7 @@ int main(int argc, const char *argv[]) {
|
|
|
88
123
|
? UIAlertActionStyleCancel : UIAlertActionStyleDefault;
|
|
89
124
|
[alert addAction:[UIAlertAction actionWithTitle:buttonTitle style:style handler:^(UIAlertAction *action) {
|
|
90
125
|
(void)action;
|
|
126
|
+
[self stopAlertActivationBusy];
|
|
91
127
|
if (replacement) {
|
|
92
128
|
self.replacementAlertActions += 1;
|
|
93
129
|
} else {
|
|
@@ -110,6 +146,9 @@ int main(int argc, const char *argv[]) {
|
|
|
110
146
|
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-alert-replacement-regression"]) {
|
|
111
147
|
self.alertFixtureStarted = YES;
|
|
112
148
|
[self presentAlertFixtureReplacement:NO];
|
|
149
|
+
if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-alert-activation-busy"]) {
|
|
150
|
+
[self startAlertActivationBusy];
|
|
151
|
+
}
|
|
113
152
|
}
|
|
114
153
|
}
|
|
115
154
|
#endif
|
|
@@ -4,8 +4,10 @@ import Foundation
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
enum SystemSurfaceHostKind: String {
|
|
8
9
|
case webAuth = "web-auth"
|
|
10
|
+
case payment = "payment"
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
struct SystemSurfaceHost: Equatable {
|
|
@@ -15,7 +17,8 @@ struct SystemSurfaceHost: Equatable {
|
|
|
15
17
|
|
|
16
18
|
enum SystemSurfaceHostRegistry {
|
|
17
19
|
static let hosts: [SystemSurfaceHost] = [
|
|
18
|
-
SystemSurfaceHost(bundleId: "com.apple.SafariViewService", kind: .webAuth)
|
|
20
|
+
SystemSurfaceHost(bundleId: "com.apple.SafariViewService", kind: .webAuth),
|
|
21
|
+
SystemSurfaceHost(bundleId: "com.apple.PassbookUIService", kind: .payment),
|
|
19
22
|
]
|
|
20
23
|
|
|
21
24
|
static func host(forBundleId bundleId: String?) -> SystemSurfaceHost? {
|
|
@@ -64,6 +67,11 @@ enum SystemSurfaceHostRegistry {
|
|
|
64
67
|
|
|
65
68
|
|
|
66
69
|
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
67
75
|
|
|
68
76
|
|
|
69
77
|
|
|
@@ -266,7 +266,7 @@ extension RunnerTests {
|
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
func shouldReadPrivateAXViewportViaXCTest() -> Bool {
|
|
269
|
-
!
|
|
269
|
+
!hasAbandonedMainThreadWork() && !isSnapshotXCTestChannelPenalized(bundleId: currentBundleId)
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
private func privateAXSnapshotViewport(app: XCUIApplication, rootFrame: CGRect) -> CGRect {
|
|
@@ -276,6 +276,7 @@ extension RunnerTests {
|
|
|
276
276
|
}
|
|
277
277
|
do {
|
|
278
278
|
let viewport = try runMainThreadWork(
|
|
279
|
+
"private_ax_viewport",
|
|
279
280
|
timeout: 1,
|
|
280
281
|
timeoutError: snapshotMainThreadTimeoutError("reading private AX viewport")
|
|
281
282
|
) {
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift
CHANGED
|
@@ -68,7 +68,16 @@ extension RunnerTests {
|
|
|
68
68
|
guard waitUntilAlertButtonHittable(button, deadline: deadline) else {
|
|
69
69
|
return alertVerificationResponse(.timedOut, action: action, activated: false)
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
78
|
+
withBoundedInteractionIdleTimeoutIfSupported(alert.ownerApp, waits: .preEventSkipped) {
|
|
79
|
+
outcome = activateElement(app: alert.ownerApp, element: button, action: "alert \(action)")
|
|
80
|
+
}
|
|
72
81
|
if let response = unsupportedResponse(for: outcome) {
|
|
73
82
|
return response
|
|
74
83
|
}
|
|
@@ -97,7 +97,9 @@ extension RunnerTests {
|
|
|
97
97
|
var outcome = RunnerInteractionOutcome.performed
|
|
98
98
|
let timing = measureGesture {
|
|
99
99
|
if idleTimeout {
|
|
100
|
-
|
|
100
|
+
withBoundedInteractionIdleTimeoutIfSupported(app, waits: .bothSkipped) {
|
|
101
|
+
outcome = action()
|
|
102
|
+
}
|
|
101
103
|
} else {
|
|
102
104
|
outcome = action()
|
|
103
105
|
}
|
|
@@ -896,29 +898,6 @@ extension RunnerTests {
|
|
|
896
898
|
|
|
897
899
|
|
|
898
900
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
901
|
|
|
923
902
|
|
|
924
903
|
|
|
@@ -977,13 +956,6 @@ extension RunnerTests {
|
|
|
977
956
|
)
|
|
978
957
|
}
|
|
979
958
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
private final class MainThreadWorkState {
|
|
983
|
-
var finished = false
|
|
984
|
-
var abandoned = false
|
|
985
|
-
}
|
|
986
|
-
|
|
987
959
|
struct ActiveCommandContext {
|
|
988
960
|
let app: XCUIApplication
|
|
989
961
|
|
|
@@ -995,23 +967,6 @@ extension RunnerTests {
|
|
|
995
967
|
case context(ActiveCommandContext)
|
|
996
968
|
}
|
|
997
969
|
|
|
998
|
-
enum MainThreadBusyState {
|
|
999
|
-
case idle
|
|
1000
|
-
case busy(abandonedForSeconds: TimeInterval)
|
|
1001
|
-
case wedged(abandonedForSeconds: TimeInterval)
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
func currentMainThreadBusyState() -> MainThreadBusyState {
|
|
1005
|
-
mainThreadWorkLock.lock()
|
|
1006
|
-
defer { mainThreadWorkLock.unlock() }
|
|
1007
|
-
guard abandonedMainThreadWorkCount > 0 else { return .idle }
|
|
1008
|
-
let abandonedFor = abandonedMainThreadWorkSince.map { Date().timeIntervalSince($0) } ?? 0
|
|
1009
|
-
if abandonedFor > mainThreadWedgeThreshold {
|
|
1010
|
-
return .wedged(abandonedForSeconds: abandonedFor)
|
|
1011
|
-
}
|
|
1012
|
-
return .busy(abandonedForSeconds: abandonedFor)
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
970
|
private func runnerBusyResponse(command: Command, abandonedForSeconds: TimeInterval) -> Response {
|
|
1016
971
|
NSLog(
|
|
1017
972
|
"AGENT_DEVICE_RUNNER_BUSY command=%@ commandId=%@ abandonedForSeconds=%.1f",
|
|
@@ -1092,6 +1047,7 @@ extension RunnerTests {
|
|
|
1092
1047
|
}
|
|
1093
1048
|
if command.command == .alert, let deadline = alertDeadline {
|
|
1094
1049
|
return try runMainThreadWork(
|
|
1050
|
+
"command_execution",
|
|
1095
1051
|
timeout: max(0.001, deadline.timeIntervalSinceNow),
|
|
1096
1052
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1097
1053
|
) {
|
|
@@ -1103,6 +1059,7 @@ extension RunnerTests {
|
|
|
1103
1059
|
}
|
|
1104
1060
|
}
|
|
1105
1061
|
return try runMainThreadWork(
|
|
1062
|
+
"command_execution",
|
|
1106
1063
|
timeout: mainThreadExecutionTimeout,
|
|
1107
1064
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1108
1065
|
) {
|
|
@@ -1110,77 +1067,6 @@ extension RunnerTests {
|
|
|
1110
1067
|
}
|
|
1111
1068
|
}
|
|
1112
1069
|
|
|
1113
|
-
func runMainThreadWork<T>(
|
|
1114
|
-
timeout: TimeInterval,
|
|
1115
|
-
timeoutError: @escaping () -> Error,
|
|
1116
|
-
onAbandoned: (() -> Void)? = nil,
|
|
1117
|
-
onDrained: (() -> Void)? = nil,
|
|
1118
|
-
_ work: @escaping () throws -> T
|
|
1119
|
-
) throws -> T {
|
|
1120
|
-
if Thread.isMainThread {
|
|
1121
|
-
return try work()
|
|
1122
|
-
}
|
|
1123
|
-
var result: Result<T, Error>?
|
|
1124
|
-
let semaphore = DispatchSemaphore(value: 0)
|
|
1125
|
-
let workState = MainThreadWorkState()
|
|
1126
|
-
DispatchQueue.main.async {
|
|
1127
|
-
do {
|
|
1128
|
-
result = .success(try work())
|
|
1129
|
-
} catch {
|
|
1130
|
-
result = .failure(error)
|
|
1131
|
-
}
|
|
1132
|
-
self.mainThreadWorkLock.lock()
|
|
1133
|
-
if workState.abandoned {
|
|
1134
|
-
self.abandonedMainThreadWorkCount -= 1
|
|
1135
|
-
if self.abandonedMainThreadWorkCount == 0 {
|
|
1136
|
-
self.abandonedMainThreadWorkSince = nil
|
|
1137
|
-
NSLog("AGENT_DEVICE_RUNNER_ABANDONED_WORK_DRAINED")
|
|
1138
|
-
}
|
|
1139
|
-
self.mainThreadWorkLock.unlock()
|
|
1140
|
-
onDrained?()
|
|
1141
|
-
} else {
|
|
1142
|
-
workState.finished = true
|
|
1143
|
-
self.mainThreadWorkLock.unlock()
|
|
1144
|
-
}
|
|
1145
|
-
semaphore.signal()
|
|
1146
|
-
}
|
|
1147
|
-
let waitResult = semaphore.wait(timeout: .now() + timeout)
|
|
1148
|
-
if waitResult == .timedOut {
|
|
1149
|
-
mainThreadWorkLock.lock()
|
|
1150
|
-
let stillRunning = !workState.finished
|
|
1151
|
-
if stillRunning {
|
|
1152
|
-
workState.abandoned = true
|
|
1153
|
-
abandonedMainThreadWorkCount += 1
|
|
1154
|
-
if abandonedMainThreadWorkSince == nil {
|
|
1155
|
-
abandonedMainThreadWorkSince = Date()
|
|
1156
|
-
}
|
|
1157
|
-
onAbandoned?()
|
|
1158
|
-
}
|
|
1159
|
-
mainThreadWorkLock.unlock()
|
|
1160
|
-
throw timeoutError()
|
|
1161
|
-
}
|
|
1162
|
-
switch result {
|
|
1163
|
-
case .success(let value):
|
|
1164
|
-
return value
|
|
1165
|
-
case .failure(let error):
|
|
1166
|
-
throw error
|
|
1167
|
-
case .none:
|
|
1168
|
-
throw NSError(
|
|
1169
|
-
domain: RunnerErrorDomain.general,
|
|
1170
|
-
code: RunnerErrorCode.noResponseFromMainThread,
|
|
1171
|
-
userInfo: [NSLocalizedDescriptionKey: "no response from main thread"]
|
|
1172
|
-
)
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
private func mainThreadExecutionTimeoutError() -> Error {
|
|
1177
|
-
NSError(
|
|
1178
|
-
domain: RunnerErrorDomain.general,
|
|
1179
|
-
code: RunnerErrorCode.mainThreadExecutionTimedOut,
|
|
1180
|
-
userInfo: [NSLocalizedDescriptionKey: "main thread execution timed out"]
|
|
1181
|
-
)
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
1070
|
|
|
1185
1071
|
|
|
1186
1072
|
private func executeOnMainSafely(
|
|
@@ -1282,6 +1168,7 @@ extension RunnerTests {
|
|
|
1282
1168
|
var hasRetried = false
|
|
1283
1169
|
while true {
|
|
1284
1170
|
let failureCountBefore = try runMainThreadWork(
|
|
1171
|
+
"recorded_failure_count",
|
|
1285
1172
|
timeout: mainThreadExecutionTimeout,
|
|
1286
1173
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1287
1174
|
) {
|
|
@@ -1290,7 +1177,7 @@ extension RunnerTests {
|
|
|
1290
1177
|
let response = try perform()
|
|
1291
1178
|
|
|
1292
1179
|
|
|
1293
|
-
if
|
|
1180
|
+
if hasAbandonedMainThreadWork() {
|
|
1294
1181
|
NSLog(
|
|
1295
1182
|
"AGENT_DEVICE_RUNNER_DISPATCH_RECOVERY_SKIPPED_XCTEST_OCCUPIED command=%@",
|
|
1296
1183
|
command.command.rawValue
|
|
@@ -1298,6 +1185,7 @@ extension RunnerTests {
|
|
|
1298
1185
|
return response
|
|
1299
1186
|
}
|
|
1300
1187
|
let recordedFailureResponse = try runMainThreadWork(
|
|
1188
|
+
"recorded_failure_count",
|
|
1301
1189
|
timeout: mainThreadExecutionTimeout,
|
|
1302
1190
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1303
1191
|
) {
|
|
@@ -1307,6 +1195,7 @@ extension RunnerTests {
|
|
|
1307
1195
|
}
|
|
1308
1196
|
if let recordedFailureResponse {
|
|
1309
1197
|
try runMainThreadWork(
|
|
1198
|
+
"target_invalidation",
|
|
1310
1199
|
timeout: mainThreadExecutionTimeout,
|
|
1311
1200
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1312
1201
|
) {
|
|
@@ -1321,6 +1210,7 @@ extension RunnerTests {
|
|
|
1321
1210
|
)
|
|
1322
1211
|
hasRetried = true
|
|
1323
1212
|
try runMainThreadWork(
|
|
1213
|
+
"target_invalidation",
|
|
1324
1214
|
timeout: mainThreadExecutionTimeout,
|
|
1325
1215
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1326
1216
|
) {
|
|
@@ -1335,6 +1225,7 @@ extension RunnerTests {
|
|
|
1335
1225
|
|
|
1336
1226
|
private func executeSnapshotDispatchedOnce(command: Command) throws -> Response {
|
|
1337
1227
|
let preparation = try runMainThreadWork(
|
|
1228
|
+
"command_preparation",
|
|
1338
1229
|
timeout: mainThreadExecutionTimeout,
|
|
1339
1230
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1340
1231
|
) {
|
|
@@ -1409,12 +1300,13 @@ extension RunnerTests {
|
|
|
1409
1300
|
needsPostSnapshotInteractionDelay = true
|
|
1410
1301
|
return
|
|
1411
1302
|
}
|
|
1412
|
-
guard !
|
|
1303
|
+
guard !hasAbandonedMainThreadWork() else {
|
|
1413
1304
|
NSLog("AGENT_DEVICE_RUNNER_POST_SNAPSHOT_DELAY_MARK_SKIPPED_XCTEST_OCCUPIED")
|
|
1414
1305
|
return
|
|
1415
1306
|
}
|
|
1416
1307
|
do {
|
|
1417
1308
|
try runMainThreadWork(
|
|
1309
|
+
"post_snapshot_delay_mark",
|
|
1418
1310
|
timeout: 1,
|
|
1419
1311
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1420
1312
|
) {
|
|
@@ -1430,8 +1322,18 @@ extension RunnerTests {
|
|
|
1430
1322
|
invalidateCachedTarget(reason: "ax_snapshot_failure")
|
|
1431
1323
|
return
|
|
1432
1324
|
}
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
guard !hasAbandonedMainThreadWork() else {
|
|
1328
|
+
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_INVALIDATION_DEFERRED_XCTEST_OCCUPIED")
|
|
1329
|
+
DispatchQueue.main.async {
|
|
1330
|
+
self.invalidateCachedTarget(reason: "ax_snapshot_failure")
|
|
1331
|
+
}
|
|
1332
|
+
return
|
|
1333
|
+
}
|
|
1433
1334
|
do {
|
|
1434
1335
|
try runMainThreadWork(
|
|
1336
|
+
"target_invalidation",
|
|
1435
1337
|
timeout: 1,
|
|
1436
1338
|
timeoutError: mainThreadExecutionTimeoutError
|
|
1437
1339
|
) {
|
|
@@ -2075,7 +1977,7 @@ extension RunnerTests {
|
|
|
2075
1977
|
return Response(ok: true, data: DataPayload(message: "remote pressed"))
|
|
2076
1978
|
case .type:
|
|
2077
1979
|
var response: Response?
|
|
2078
|
-
|
|
1980
|
+
withBoundedInteractionIdleTimeoutIfSupported(activeApp, waits: .bothSkipped) {
|
|
2079
1981
|
response = executeTypeCommand(activeApp: activeApp, command: command)
|
|
2080
1982
|
}
|
|
2081
1983
|
return response ?? Response(ok: false, error: ErrorPayload(message: "type produced no response"))
|
|
@@ -2087,7 +1989,7 @@ extension RunnerTests {
|
|
|
2087
1989
|
|
|
2088
1990
|
var executedFrame: DragVisualizationFrame?
|
|
2089
1991
|
let timing = measureGesture {
|
|
2090
|
-
|
|
1992
|
+
withBoundedInteractionIdleTimeoutIfSupported(activeApp, waits: .bothSkipped) {
|
|
2091
1993
|
executedFrame = swipe(app: activeApp, direction: direction)
|
|
2092
1994
|
}
|
|
2093
1995
|
}
|
|
@@ -2187,7 +2089,7 @@ extension RunnerTests {
|
|
|
2187
2089
|
code: "UNSUPPORTED_OPERATION",
|
|
2188
2090
|
message: "Unable to dismiss the iOS keyboard: the keyboard exposes no dismiss key, and background taps are never attempted (no tap outside the keyboard can be proven side-effect-free)",
|
|
2189
2091
|
hint:
|
|
2190
|
-
"
|
|
2092
|
+
"An element whose center sits behind the on-screen keyboard is refused with tap_keyboard_occludes_target; one whose center stays above the keys presses normally. To end editing, tap the app's own Done/Cancel control, or use keyboard enter to press the return key when submission is wanted."
|
|
2191
2093
|
)
|
|
2192
2094
|
)
|
|
2193
2095
|
}
|
|
@@ -415,6 +415,52 @@ final class RunnerCommandJournal {
|
|
|
415
415
|
|
|
416
416
|
|
|
417
417
|
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
418
464
|
|
|
419
465
|
|
|
420
466
|
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift
CHANGED
|
@@ -21,6 +21,24 @@ func runnerCGImage(from image: RunnerImage) -> CGImage? {
|
|
|
21
21
|
#endif
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
enum RunnerInteractionIdleWaits {
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
case bothSkipped
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
case preEventSkipped
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
extension RunnerTests {
|
|
25
43
|
|
|
26
44
|
|
|
@@ -209,8 +227,14 @@ extension RunnerTests {
|
|
|
209
227
|
return target
|
|
210
228
|
}
|
|
211
229
|
|
|
212
|
-
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
func withBoundedInteractionIdleTimeoutIfSupported(
|
|
213
236
|
_ target: XCUIApplication,
|
|
237
|
+
waits: RunnerInteractionIdleWaits,
|
|
214
238
|
operation: () -> Void
|
|
215
239
|
) {
|
|
216
240
|
let setter = NSSelectorFromString("setWaitForIdleTimeout:")
|
|
@@ -219,19 +243,20 @@ extension RunnerTests {
|
|
|
219
243
|
? (target.value(forKey: "waitForIdleTimeout") as? NSNumber)
|
|
220
244
|
: nil
|
|
221
245
|
if supportsWaitForIdleTimeout {
|
|
222
|
-
target.setValue(
|
|
246
|
+
target.setValue(interactionIdleTimeoutDefault, forKey: "waitForIdleTimeout")
|
|
223
247
|
}
|
|
224
248
|
defer {
|
|
225
249
|
if let previous {
|
|
226
250
|
target.setValue(previous.doubleValue, forKey: "waitForIdleTimeout")
|
|
227
251
|
}
|
|
228
252
|
}
|
|
229
|
-
performWithQuiescenceSkippedIfSupported(target, operation: operation)
|
|
253
|
+
performWithQuiescenceSkippedIfSupported(target, waits: waits, operation: operation)
|
|
230
254
|
}
|
|
231
255
|
|
|
232
256
|
|
|
233
257
|
private func performWithQuiescenceSkippedIfSupported(
|
|
234
258
|
_ target: XCUIApplication,
|
|
259
|
+
waits: RunnerInteractionIdleWaits,
|
|
235
260
|
operation: () -> Void
|
|
236
261
|
) {
|
|
237
262
|
let selector = NSSelectorFromString("_performWithInteractionOptions:block:")
|
|
@@ -252,12 +277,19 @@ extension RunnerTests {
|
|
|
252
277
|
)
|
|
253
278
|
let skipPreEventQuiescence = UInt(1)
|
|
254
279
|
let skipPostEventQuiescence = UInt(2)
|
|
280
|
+
let options: UInt
|
|
281
|
+
switch waits {
|
|
282
|
+
case .bothSkipped:
|
|
283
|
+
options = skipPreEventQuiescence | skipPostEventQuiescence
|
|
284
|
+
case .preEventSkipped:
|
|
285
|
+
options = skipPreEventQuiescence
|
|
286
|
+
}
|
|
255
287
|
withoutActuallyEscaping(operation) { escapableOperation in
|
|
256
288
|
let block: @convention(block) () -> Void = escapableOperation
|
|
257
289
|
performWithOptions(
|
|
258
290
|
target,
|
|
259
291
|
selector,
|
|
260
|
-
|
|
292
|
+
options,
|
|
261
293
|
block
|
|
262
294
|
)
|
|
263
295
|
}
|