@devicelab/maestro-runner-darwin-arm64 1.1.28 → 1.1.30
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/README.md +30 -3
- package/bin/maestro-runner +0 -0
- package/drivers/android/devicelab-android-driver-test.apk +0 -0
- package/drivers/android/devicelab-android-driver.apk +0 -0
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/ATTRIBUTION.md +16 -0
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Lifecycle.swift +33 -0
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Snapshot.swift +195 -88
- package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests.swift +3 -0
- package/drivers/ios/WebDriverAgent/CHANGELOG.md +202 -0
- package/drivers/ios/WebDriverAgent/README.md +18 -9
- package/drivers/ios/WebDriverAgent/Scripts/ci/build-real.sh +6 -10
- package/drivers/ios/WebDriverAgent/Scripts/fetch-prebuilt-wda.mjs +3 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.pbxproj +119 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.m +8 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +3 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +30 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.h +53 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.m +124 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +6 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +100 -23
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.h +41 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.m +215 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +8 -3
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +20 -7
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +110 -50
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.h +7 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +20 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +14 -11
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.m +86 -10
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBElementCommands.m +57 -7
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBSessionCommands.m +3 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Info.plist +2 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBHTTPServer.m +39 -16
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingRequest.m +7 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.h +22 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.m +78 -12
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.h +5 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.m +10 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.m +4 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.h +2 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +20 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.h +8 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.m +2 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.h +41 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.m +244 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageProcessor.m +3 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.h +11 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.m +38 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMjpegServer.m +5 -3
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.h +17 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.m +76 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.h +2 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.m +6 -2
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.h +2 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.m +2 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettingsHandler.m +48 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +121 -13
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath.m +181 -128
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +8 -5
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +1 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentLib/WebDriverAgentLib.h +1 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/COORDINATE_PROBE.md +3 -1
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBCoordinateProbeViewController.m +18 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/SceneDelegate.m +33 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/ViewController.m +26 -7
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBConfigurationTests.m +52 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBDisplayGestureTests.m +541 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementSwipingTests.m +95 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBImageProcessorTests.m +34 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +392 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +97 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTypingTest.m +33 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +126 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +111 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +97 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceRotationTests.m +53 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +113 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_tvOS/FBTVFocusIntegrationTests.m +21 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +12 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBCustomCommandsTests.m +61 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementUtilitiesTests.m +40 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerSessionTests.m +22 -4
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerTests.m +199 -5
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBLRUCacheTests.m +34 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +45 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBSessionTests.m +201 -0
- package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBXPathTests.m +101 -0
- package/drivers/ios/WebDriverAgent/docs/hinge-angle.md +54 -0
- package/drivers/ios/WebDriverAgent/docs/multiple-displays.md +168 -0
- package/drivers/ios/WebDriverAgent/lib/webdriveragent.ts +1 -2
- package/drivers/ios/WebDriverAgent/lib/xcodebuild.ts +58 -10
- package/drivers/ios/WebDriverAgent/package.json +8 -15
- package/drivers/ios/WebDriverAgent/test/functional/helpers/display-settings.ts +49 -0
- package/drivers/ios/WebDriverAgent/test/functional/helpers/simulator.ts +7 -7
- package/drivers/ios/WebDriverAgent/test/functional/webdriveragent-e2e.spec.ts +4 -0
- package/drivers/ios/WebDriverAgent/test/unit/xcodebuild.spec.ts +230 -0
- package/drivers/ios/WebDriverAgent/tsconfig.json +2 -1
- package/drivers/ios/devicelab-ios-agent/README.md +12 -0
- package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
- package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
- package/drivers/ios/devicelab-ios-agent/device/manifest.json +3 -3
- package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
- package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
- package/drivers/ios/devicelab-ios-agent/simulator/manifest.json +3 -3
- package/package.json +1 -1
- package/drivers/ios/WebDriverAgent/.editorconfig +0 -11
- package/drivers/ios/WebDriverAgent/.github/ISSUE_TEMPLATE.md +0 -4
- package/drivers/ios/WebDriverAgent/.github/actions/xcode-setup/action.yml +0 -32
- package/drivers/ios/WebDriverAgent/.github/dependabot.yml +0 -22
- package/drivers/ios/WebDriverAgent/.github/workflows/functional-test.yml +0 -103
- package/drivers/ios/WebDriverAgent/.github/workflows/pr-title.yml +0 -10
- package/drivers/ios/WebDriverAgent/.github/workflows/publish.js.yml +0 -112
- package/drivers/ios/WebDriverAgent/.github/workflows/unit-test.yml +0 -32
- package/drivers/ios/WebDriverAgent/.github/workflows/wda-package.yml +0 -114
- package/drivers/ios/WebDriverAgent/.github/workflows/wda-tests.yml +0 -246
- package/drivers/ios/WebDriverAgent/oxfmt.config.mjs +0 -6
- package/drivers/ios/WebDriverAgent/oxlint.config.mjs +0 -6
- package/drivers/ios/WebDriverAgent/release.config.mjs +0 -14
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
#import "FBElementCommands.h"
|
|
10
10
|
|
|
11
11
|
#import "FBConfiguration.h"
|
|
12
|
-
#import "
|
|
12
|
+
#import "FBScreen.h"
|
|
13
|
+
#import "XCUIScreen.h"
|
|
13
14
|
#import "FBRoute.h"
|
|
14
15
|
#import "FBRouteRequest.h"
|
|
15
16
|
#import "FBRunLoopSpinner.h"
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
#import "FBRuntimeUtils.h"
|
|
23
24
|
#import "NSPredicate+FBFormat.h"
|
|
24
25
|
#import "XCTestPrivateSymbols.h"
|
|
26
|
+
#import "XCUIApplication+FBTouchAction.h"
|
|
25
27
|
#import "XCUICoordinate.h"
|
|
26
28
|
#import "XCUIDevice.h"
|
|
27
29
|
#import "XCUIElement+FBIsVisible.h"
|
|
@@ -370,10 +372,14 @@
|
|
|
370
372
|
if (nil == endCoordinate) {
|
|
371
373
|
return FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description traceback:nil]);
|
|
372
374
|
}
|
|
373
|
-
[
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
375
|
+
if (![application fb_pressAtCoordinate:startCoordinate
|
|
376
|
+
forDuration:[request.arguments[@"pressDuration"] doubleValue]
|
|
377
|
+
thenDragToCoordinate:endCoordinate
|
|
378
|
+
withVelocity:[request.arguments[@"velocity"] doubleValue]
|
|
379
|
+
thenHoldForDuration:[request.arguments[@"holdDuration"] doubleValue]
|
|
380
|
+
error:&error]) {
|
|
381
|
+
return FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description traceback:nil]);
|
|
382
|
+
}
|
|
377
383
|
return FBResponseWithOK();
|
|
378
384
|
}
|
|
379
385
|
|
|
@@ -456,7 +462,17 @@
|
|
|
456
462
|
return FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description traceback:nil]);
|
|
457
463
|
}
|
|
458
464
|
NSTimeInterval duration = [request.arguments[@"duration"] doubleValue];
|
|
459
|
-
[
|
|
465
|
+
XCUIApplication *application = [target isKindOfClass:XCUIApplication.class]
|
|
466
|
+
? (XCUIApplication *)target
|
|
467
|
+
: target.application;
|
|
468
|
+
if (![application fb_pressAtCoordinate:startCoordinate
|
|
469
|
+
forDuration:duration
|
|
470
|
+
thenDragToCoordinate:endCoordinate
|
|
471
|
+
withVelocity:XCUIGestureVelocityDefault
|
|
472
|
+
thenHoldForDuration:0
|
|
473
|
+
error:&error]) {
|
|
474
|
+
return FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description traceback:nil]);
|
|
475
|
+
}
|
|
460
476
|
return FBResponseWithOK();
|
|
461
477
|
}
|
|
462
478
|
|
|
@@ -539,6 +555,13 @@
|
|
|
539
555
|
NSString *textToType = [request.arguments[@"value"] componentsJoinedByString:@""];
|
|
540
556
|
NSUInteger frequency = [request.arguments[@"frequency"] unsignedIntegerValue] ?: FBConfiguration.sharedInstance.maxTypingFrequency;
|
|
541
557
|
NSError *error;
|
|
558
|
+
#if TARGET_OS_IOS
|
|
559
|
+
NSTimeInterval keyboardWait = FBConfiguration.sharedInstance.keyboardWaitTimeout;
|
|
560
|
+
if (keyboardWait > 0) {
|
|
561
|
+
XCUIApplication *application = request.session.activeApplication ?: XCUIApplication.fb_activeApplication;
|
|
562
|
+
FBWaitForHittableKey(application, keyboardWait);
|
|
563
|
+
}
|
|
564
|
+
#endif
|
|
542
565
|
if (!FBTypeText(textToType, frequency, &error)) {
|
|
543
566
|
return FBResponseWithStatus([FBCommandStatus invalidElementStateErrorWithMessage:error.description
|
|
544
567
|
traceback:nil]);
|
|
@@ -683,7 +706,34 @@ static const NSInteger DEFAULT_MAX_PICKER_ATTEMPTS = 25;
|
|
|
683
706
|
element:(XCUIElement *)element
|
|
684
707
|
error:(NSError **)error
|
|
685
708
|
{
|
|
686
|
-
|
|
709
|
+
if (element.elementType != XCUIElementTypeApplication) {
|
|
710
|
+
return FBCoordinateWithAnchorOffset(element, CGVectorMake(0, 0), offset, error);
|
|
711
|
+
}
|
|
712
|
+
XCUIScreen *screen = [FBScreen currentScreenWithError:error];
|
|
713
|
+
if (nil == screen) {
|
|
714
|
+
return nil;
|
|
715
|
+
}
|
|
716
|
+
if (screen.displayID == XCUIScreen.mainScreen.displayID) {
|
|
717
|
+
return FBCoordinateWithAnchorOffset(element, CGVectorMake(0, 0), offset, error);
|
|
718
|
+
}
|
|
719
|
+
// Application-root coordinates are rotated using the main display on Duo.
|
|
720
|
+
// A window coordinate keeps XCTest's display affinity and rotation intact.
|
|
721
|
+
// It is only a coordinate anchor, not a hit-test boundary. Preserve edge and
|
|
722
|
+
// outside offsets just as on the main display; XCTest handles their delivery.
|
|
723
|
+
CGPoint point = CGPointMake(offset.dx, offset.dy);
|
|
724
|
+
for (XCUIElement *window in [element childrenMatchingType:XCUIElementTypeWindow].allElementsBoundByIndex) {
|
|
725
|
+
// Resolve the window before reading its display affinity: an unresolved
|
|
726
|
+
// element may report displayID == 0.
|
|
727
|
+
(void)window.frame;
|
|
728
|
+
CGRect frame = window.wdFrame;
|
|
729
|
+
if (window.screen.displayID == screen.displayID && !CGRectIsEmpty(frame)) {
|
|
730
|
+
return FBCoordinateWithAnchorOffset(window, CGVectorMake(0, 0),
|
|
731
|
+
CGVectorMake(point.x - frame.origin.x, point.y - frame.origin.y), error);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
[[[FBErrorBuilder builder] withDescriptionFormat:@"No non-empty application window on display %lld is available as a coordinate anchor", screen.displayID]
|
|
735
|
+
buildError:error];
|
|
736
|
+
return nil;
|
|
687
737
|
}
|
|
688
738
|
|
|
689
739
|
/**
|
|
@@ -131,7 +131,8 @@
|
|
|
131
131
|
[request.session launchApplicationWithBundleId:(id)request.arguments[@"bundleId"]
|
|
132
132
|
shouldWaitForQuiescence:request.arguments[@"shouldWaitForQuiescence"]
|
|
133
133
|
arguments:request.arguments[@"arguments"]
|
|
134
|
-
environment:request.arguments[@"environment"]
|
|
134
|
+
environment:request.arguments[@"environment"]
|
|
135
|
+
asApplicationUnderTest:[request.arguments[@"asAppUnderTest"] boolValue]];
|
|
135
136
|
return FBResponseWithOK();
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -410,7 +411,7 @@
|
|
|
410
411
|
if (nil != initialUrl) {
|
|
411
412
|
return [self openDeepLink:initialUrl withApplication:bundleID timeout:nil];
|
|
412
413
|
}
|
|
413
|
-
[app
|
|
414
|
+
[app fb_activate];
|
|
414
415
|
return nil;
|
|
415
416
|
}
|
|
416
417
|
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
<key>CFBundlePackageType</key>
|
|
16
16
|
<string>FMWK</string>
|
|
17
17
|
<key>CFBundleShortVersionString</key>
|
|
18
|
-
<string>16.
|
|
18
|
+
<string>16.17.5</string>
|
|
19
19
|
<key>CFBundleSignature</key>
|
|
20
20
|
<string>????</string>
|
|
21
21
|
<key>CFBundleVersion</key>
|
|
22
|
-
<string>16.
|
|
22
|
+
<string>16.17.5</string>
|
|
23
23
|
<key>NSPrincipalClass</key>
|
|
24
24
|
<string/>
|
|
25
25
|
</dict>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#import "FBLogger.h"
|
|
14
14
|
#import "FBResponsePayload.h"
|
|
15
15
|
#import "FBTCPSocket.h"
|
|
16
|
+
#import "FBSession.h"
|
|
16
17
|
|
|
17
18
|
static NSData *FBCRLFCRLFData(void)
|
|
18
19
|
{
|
|
@@ -131,10 +132,6 @@ static BOOL FBParseContentLength(NSString *value, NSUInteger *outLength)
|
|
|
131
132
|
// standalone or not (except DELETE /session itself - see -dispatchMethod:). See
|
|
132
133
|
// -abandonPendingRequestsForSessionID:. Guarded by @synchronized(self.pendingSessionRequests).
|
|
133
134
|
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSMutableSet<FBPendingRequest *> *> *pendingSessionRequests;
|
|
134
|
-
// Already-abandoned sessions mapped to the response they were abandoned with, so a request parsed
|
|
135
|
-
// after that point is answered at once instead of queueing for a session that is gone. Kept for
|
|
136
|
-
// the server's lifetime; ids are UUIDs. Guarded by @synchronized(self.pendingSessionRequests).
|
|
137
|
-
@property (nonatomic, strong) NSMutableDictionary<NSString *, RouteResponse *> *abandonedSessionResponses;
|
|
138
135
|
// When each connection started waiting for its current request. The reaper closes connections
|
|
139
136
|
// whose entry outlives FBIncompleteRequestTimeout; idle keep-alive connections have no entry and
|
|
140
137
|
// are exempt. Guarded by @synchronized(self.connectionBuffers).
|
|
@@ -163,7 +160,6 @@ static const int64_t FBStaleConnectionSweepIntervalSec = 10;
|
|
|
163
160
|
_connectionsAwaitingResponse = [NSMutableSet set];
|
|
164
161
|
_standaloneWaiters = [NSMutableDictionary dictionary];
|
|
165
162
|
_pendingSessionRequests = [NSMutableDictionary dictionary];
|
|
166
|
-
_abandonedSessionResponses = [NSMutableDictionary dictionary];
|
|
167
163
|
_incompleteRequestStarts = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsOptions)(NSMapTableObjectPointerPersonality | NSMapTableStrongMemory)
|
|
168
164
|
valueOptions:(NSPointerFunctionsOptions)NSMapTableStrongMemory];
|
|
169
165
|
}
|
|
@@ -340,12 +336,28 @@ static const int64_t FBStaleConnectionSweepIntervalSec = 10;
|
|
|
340
336
|
|
|
341
337
|
- (void)didClientDisconnect:(nw_connection_t)client
|
|
342
338
|
{
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
339
|
+
// EOF must follow every receive already queued for this client. Otherwise it can
|
|
340
|
+
// remove the buffer before those bytes have even been appended and parsed.
|
|
341
|
+
__weak typeof(self) weakSelf = self;
|
|
342
|
+
dispatch_async(self.bufferProcessingQueue, ^{
|
|
343
|
+
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
344
|
+
if (nil == strongSelf) {
|
|
345
|
+
nw_connection_cancel(client);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
BOOL isAwaitingResponse;
|
|
349
|
+
@synchronized (strongSelf.connectionBuffers) {
|
|
350
|
+
isAwaitingResponse = [strongSelf.connectionsAwaitingResponse containsObject:client];
|
|
351
|
+
[strongSelf.connectionBuffers removeObjectForKey:client];
|
|
352
|
+
[strongSelf.pendingRequestHeaders removeObjectForKey:client];
|
|
353
|
+
[strongSelf.incompleteRequestStarts removeObjectForKey:client];
|
|
354
|
+
}
|
|
355
|
+
// A client that only stopped sending may still read the response to its in-flight request, so
|
|
356
|
+
// that response's send completion closes the connection instead (it finds no buffer left).
|
|
357
|
+
if (!isAwaitingResponse) {
|
|
358
|
+
nw_connection_cancel(client);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
349
361
|
}
|
|
350
362
|
|
|
351
363
|
- (void)client:(nw_connection_t)client didReceiveData:(NSData *)data
|
|
@@ -723,9 +735,15 @@ static const int64_t FBStaleConnectionSweepIntervalSec = 10;
|
|
|
723
735
|
- (nullable RouteResponse *)trackPendingRequest:(FBPendingRequest *)pendingRequest forSessionID:(NSString *)sessionID
|
|
724
736
|
{
|
|
725
737
|
@synchronized (self.pendingSessionRequests) {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
738
|
+
// Checking the current session under this lock closes the admission/teardown
|
|
739
|
+
// race without retaining a tombstone and a full response for every past UUID.
|
|
740
|
+
// -kill clears the active session before posting its abandonment notification.
|
|
741
|
+
if (nil == [FBSession sessionWithIdentifier:sessionID]) {
|
|
742
|
+
RouteResponse *response = [RouteResponse new];
|
|
743
|
+
[FBResponseWithStatus([FBCommandStatus noSuchDriverErrorWithMessage:@"Session does not exist"
|
|
744
|
+
traceback:nil]) dispatchWithResponse:response];
|
|
745
|
+
[self applyDefaultHeadersToResponse:response];
|
|
746
|
+
return response;
|
|
729
747
|
}
|
|
730
748
|
NSMutableSet<FBPendingRequest *> *pendingRequests = self.pendingSessionRequests[sessionID];
|
|
731
749
|
if (nil == pendingRequests) {
|
|
@@ -760,8 +778,6 @@ static const int64_t FBStaleConnectionSweepIntervalSec = 10;
|
|
|
760
778
|
@synchronized (self.pendingSessionRequests) {
|
|
761
779
|
pendingRequests = [self.pendingSessionRequests[sessionID] copy];
|
|
762
780
|
[self.pendingSessionRequests removeObjectForKey:sessionID];
|
|
763
|
-
// Recorded before the lock is dropped, so requests admitted from here on are rejected.
|
|
764
|
-
self.abandonedSessionResponses[sessionID] = response;
|
|
765
781
|
}
|
|
766
782
|
for (FBPendingRequest *pendingRequest in pendingRequests) {
|
|
767
783
|
[self writeResponse:response toClient:pendingRequest.client];
|
|
@@ -881,14 +897,21 @@ static const int64_t FBStaleConnectionSweepIntervalSec = 10;
|
|
|
881
897
|
if (nil == queuedSelf) {
|
|
882
898
|
return;
|
|
883
899
|
}
|
|
900
|
+
BOOL isDisconnected;
|
|
884
901
|
@synchronized (queuedSelf.connectionBuffers) {
|
|
885
902
|
[queuedSelf.connectionsAwaitingResponse removeObject:client];
|
|
903
|
+
isDisconnected = nil == [queuedSelf.connectionBuffers objectForKey:client];
|
|
886
904
|
// Mid-request connections get their window from when parsing could resume, not from the
|
|
887
905
|
// previous request. Absent entries stay absent, so idle keep-alives remain exempt.
|
|
888
906
|
if (nil != [queuedSelf.incompleteRequestStarts objectForKey:client]) {
|
|
889
907
|
[queuedSelf.incompleteRequestStarts setObject:[NSDate date] forKey:client];
|
|
890
908
|
}
|
|
891
909
|
}
|
|
910
|
+
if (isDisconnected) {
|
|
911
|
+
// The peer stopped sending while this request was in flight - see -didClientDisconnect:.
|
|
912
|
+
[queuedSelf closeClient:client];
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
892
915
|
[queuedSelf processBufferForClient:client];
|
|
893
916
|
});
|
|
894
917
|
}];
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#import "FBScreenRecordingRequest.h"
|
|
10
10
|
|
|
11
11
|
#import "FBErrorBuilder.h"
|
|
12
|
+
#import "FBScreen.h"
|
|
12
13
|
#import "XCUIScreen.h"
|
|
13
14
|
|
|
14
15
|
@implementation FBScreenRecordingRequest
|
|
@@ -56,6 +57,10 @@
|
|
|
56
57
|
|
|
57
58
|
- (id)toNativeRequestWithError:(NSError **)error
|
|
58
59
|
{
|
|
60
|
+
XCUIScreen *screen = [FBScreen currentScreenWithError:error];
|
|
61
|
+
if (nil == screen) {
|
|
62
|
+
return nil;
|
|
63
|
+
}
|
|
59
64
|
Class screenRecordingRequestClass = NSClassFromString(@"XCTScreenRecordingRequest");
|
|
60
65
|
if (nil == screenRecordingRequestClass) {
|
|
61
66
|
[[[FBErrorBuilder builder]
|
|
@@ -80,8 +85,8 @@
|
|
|
80
85
|
NSMethodSignature *screenRecordingRequestContructorSignature = [screenRecordingRequestAllocated methodSignatureForSelector:screenRecordingRequestConstructorSelector];
|
|
81
86
|
NSInvocation *screenRecordingRequestInitInvocation = [NSInvocation invocationWithMethodSignature:screenRecordingRequestContructorSignature];
|
|
82
87
|
[screenRecordingRequestInitInvocation setSelector:screenRecordingRequestConstructorSelector];
|
|
83
|
-
long long
|
|
84
|
-
[screenRecordingRequestInitInvocation setArgument:&
|
|
88
|
+
long long screenId = screen.displayID;
|
|
89
|
+
[screenRecordingRequestInitInvocation setArgument:&screenId atIndex:2];
|
|
85
90
|
CGRect fullScreenRect = CGRectNull;
|
|
86
91
|
[screenRecordingRequestInitInvocation setArgument:&fullScreenRect atIndex:3];
|
|
87
92
|
[screenRecordingRequestInitInvocation setArgument:&videoEncoding atIndex:4];
|
|
@@ -107,6 +107,28 @@ extern NSString* const FBSessionWasKilledNotification;
|
|
|
107
107
|
arguments:(nullable NSArray<NSString *> *)arguments
|
|
108
108
|
environment:(nullable NSDictionary <NSString *, NSString *> *)environment;
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
Launch an application with given bundle identifier in scope of current session
|
|
112
|
+
and optionally make it the application under test of this session.
|
|
113
|
+
|
|
114
|
+
@param bundleIdentifier Valid bundle identifier of the application to be launched
|
|
115
|
+
@param shouldWaitForQuiescence whether to wait for quiescence on application startup.
|
|
116
|
+
If not provided then the setting of the current application under test is inherited.
|
|
117
|
+
If there is no application under test yet then quiescence is only awaited if asApplicationUnderTest is YES
|
|
118
|
+
@param arguments The optional array of application command line arguments. The arguments are going to be applied if the application was not running before.
|
|
119
|
+
@param environment The optional dictionary of environment variables for the application, which is going to be executed. The environment variables are going to be applied if the application was not running before.
|
|
120
|
+
@param asApplicationUnderTest If YES then the launched application replaces the current application under test
|
|
121
|
+
of the session (if any), the same way as if it was launched on session startup: other applications launched later
|
|
122
|
+
inherit its quiescence setting, system alerts over it are respected, its unexpected termination is reported as a crash
|
|
123
|
+
and it gets terminated on session deletion if shouldTerminateApp is enabled
|
|
124
|
+
@return The application instance
|
|
125
|
+
*/
|
|
126
|
+
- (XCUIApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
|
|
127
|
+
shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
|
|
128
|
+
arguments:(nullable NSArray<NSString *> *)arguments
|
|
129
|
+
environment:(nullable NSDictionary <NSString *, NSString *> *)environment
|
|
130
|
+
asApplicationUnderTest:(BOOL)asApplicationUnderTest;
|
|
131
|
+
|
|
110
132
|
/**
|
|
111
133
|
Activate an application with given bundle identifier in scope of current session.
|
|
112
134
|
!This method is only available since Xcode9 SDK
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
#import "FBXCTestDaemonsProxy.h"
|
|
25
25
|
#import "XCUIApplication+FBQuiescence.h"
|
|
26
26
|
#import "XCUIElement.h"
|
|
27
|
+
#import "XCUIElement+FBUtilities.h"
|
|
27
28
|
|
|
28
29
|
/*!
|
|
29
30
|
The intial value for the default application property.
|
|
@@ -43,13 +44,14 @@ static const NSTimeInterval FB_KILL_WAIT_TIMEOUT_SEC = 35.;
|
|
|
43
44
|
NSString *const FBSessionWasKilledNotification = @"FBSessionWasKilledNotification";
|
|
44
45
|
|
|
45
46
|
@interface FBSession ()
|
|
46
|
-
|
|
47
|
+
// Atomic, since /wda/apps/launch may replace it while a concurrent DELETE /session reads it in -kill
|
|
48
|
+
@property (nullable, atomic) XCUIApplication *testedApplication;
|
|
47
49
|
@property (nonatomic) BOOL isTestedApplicationExpectedToRun;
|
|
48
50
|
@property (nonatomic) BOOL shouldAppsWaitForQuiescence;
|
|
49
51
|
@property (nonatomic, nullable) FBAlertsMonitor *alertsMonitor;
|
|
50
52
|
@property (nonatomic, readwrite) NSMutableDictionary<NSNumber *, NSMutableDictionary<NSString *, NSNumber *> *> *elementsVisibilityCache;
|
|
51
53
|
|
|
52
|
-
- (BOOL)
|
|
54
|
+
- (BOOL)fb_isApplicationSameAsSystemApp:(XCUIApplication *)application timeout:(NSTimeInterval)timeout;
|
|
53
55
|
- (void)fb_terminateTestedApplicationWithTimeout:(NSTimeInterval)timeout generation:(NSUInteger)generation;
|
|
54
56
|
@end
|
|
55
57
|
|
|
@@ -332,7 +334,7 @@ static NSUInteger _committedTerminationCount = 0;
|
|
|
332
334
|
if (nil != self.testedApplication
|
|
333
335
|
&& FBConfiguration.sharedInstance.shouldTerminateApp
|
|
334
336
|
&& self.testedApplication.running
|
|
335
|
-
&& ![self
|
|
337
|
+
&& ![self fb_isApplicationSameAsSystemApp:self.testedApplication timeout:FB_IS_SYSTEM_APP_CHECK_TIMEOUT_SEC]) {
|
|
336
338
|
// Blocks until the app is either actually terminated or durably given up on (never left
|
|
337
339
|
// pending) - see -fb_terminateTestedApplicationWithTimeout:generation: - so it's safe to
|
|
338
340
|
// report this teardown as finished as soon as this returns.
|
|
@@ -387,10 +389,42 @@ static NSUInteger _committedTerminationCount = 0;
|
|
|
387
389
|
arguments:(nullable NSArray<NSString *> *)arguments
|
|
388
390
|
environment:(nullable NSDictionary <NSString *, NSString *> *)environment
|
|
389
391
|
{
|
|
392
|
+
return [self launchApplicationWithBundleId:bundleIdentifier
|
|
393
|
+
shouldWaitForQuiescence:shouldWaitForQuiescence
|
|
394
|
+
arguments:arguments
|
|
395
|
+
environment:environment
|
|
396
|
+
asApplicationUnderTest:NO];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
- (XCUIApplication *)launchApplicationWithBundleId:(NSString *)bundleIdentifier
|
|
400
|
+
shouldWaitForQuiescence:(nullable NSNumber *)shouldWaitForQuiescence
|
|
401
|
+
arguments:(nullable NSArray<NSString *> *)arguments
|
|
402
|
+
environment:(nullable NSDictionary <NSString *, NSString *> *)environment
|
|
403
|
+
asApplicationUnderTest:(BOOL)asApplicationUnderTest
|
|
404
|
+
{
|
|
405
|
+
NSCondition *condition = self.class.teardownCondition;
|
|
406
|
+
NSUInteger generation = 0;
|
|
407
|
+
BOOL shouldTerminateApp = NO;
|
|
408
|
+
if (asApplicationUnderTest) {
|
|
409
|
+
[condition lock];
|
|
410
|
+
BOOL isActive = self == _activeSession;
|
|
411
|
+
generation = _sessionGeneration;
|
|
412
|
+
shouldTerminateApp = FBConfiguration.sharedInstance.shouldTerminateApp;
|
|
413
|
+
[condition unlock];
|
|
414
|
+
if (!isActive) {
|
|
415
|
+
@throw [NSException exceptionWithName:FBSessionDoesNotExistException
|
|
416
|
+
reason:@"Session does not exist"
|
|
417
|
+
userInfo:nil];
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
390
421
|
XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
|
|
391
422
|
if (nil == shouldWaitForQuiescence) {
|
|
392
|
-
// Iherit the quiescence check setting from the main app under test by default
|
|
393
|
-
app
|
|
423
|
+
// Iherit the quiescence check setting from the main app under test by default.
|
|
424
|
+
// The first app under test of the session waits for quiescence, as it would on session startup
|
|
425
|
+
app.fb_shouldWaitForQuiescence = nil == self.testedApplication
|
|
426
|
+
? asApplicationUnderTest
|
|
427
|
+
: self.shouldAppsWaitForQuiescence;
|
|
394
428
|
} else {
|
|
395
429
|
app.fb_shouldWaitForQuiescence = [shouldWaitForQuiescence boolValue];
|
|
396
430
|
}
|
|
@@ -399,7 +433,39 @@ static NSUInteger _committedTerminationCount = 0;
|
|
|
399
433
|
app.launchEnvironment = environment ?: @{};
|
|
400
434
|
[app launch];
|
|
401
435
|
} else {
|
|
402
|
-
[app
|
|
436
|
+
[app fb_activate];
|
|
437
|
+
}
|
|
438
|
+
if (asApplicationUnderTest) {
|
|
439
|
+
BOOL waitForQuiescence = app.fb_shouldWaitForQuiescence;
|
|
440
|
+
// A DELETE can finish while launch/activate is still waiting for XCTest.
|
|
441
|
+
// Publish the AUT under the same lock that -kill uses to clear the session.
|
|
442
|
+
[condition lock];
|
|
443
|
+
BOOL isActive = self == _activeSession && generation == _sessionGeneration;
|
|
444
|
+
if (isActive) {
|
|
445
|
+
self.testedApplication = app;
|
|
446
|
+
self.shouldAppsWaitForQuiescence = waitForQuiescence;
|
|
447
|
+
}
|
|
448
|
+
[condition unlock];
|
|
449
|
+
if (!isActive) {
|
|
450
|
+
// The HTTP request may already have been abandoned, but its handler keeps
|
|
451
|
+
// running. Clean up only while this launch still owns the generation;
|
|
452
|
+
// a replacement session may already be using the same bundle identifier.
|
|
453
|
+
if (shouldTerminateApp
|
|
454
|
+
&& ![self fb_isApplicationSameAsSystemApp:app timeout:FB_IS_SYSTEM_APP_CHECK_TIMEOUT_SEC]
|
|
455
|
+
&& [self.class beginTerminationForGeneration:generation]) {
|
|
456
|
+
@try {
|
|
457
|
+
[app terminate];
|
|
458
|
+
} @catch (NSException *e) {
|
|
459
|
+
[FBLogger logFmt:@"%@", e.description];
|
|
460
|
+
} @finally {
|
|
461
|
+
[self.class endTermination];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
@throw [NSException exceptionWithName:FBSessionDoesNotExistException
|
|
465
|
+
reason:@"Session was deleted during application launch"
|
|
466
|
+
userInfo:nil];
|
|
467
|
+
}
|
|
468
|
+
[FBLogger logFmt:@"The application '%@' is set as the application under test", bundleIdentifier];
|
|
403
469
|
}
|
|
404
470
|
if ([app fb_isSameAppAs:self.testedApplication]) {
|
|
405
471
|
self.isTestedApplicationExpectedToRun = YES;
|
|
@@ -410,7 +476,7 @@ static NSUInteger _committedTerminationCount = 0;
|
|
|
410
476
|
- (XCUIApplication *)activateApplicationWithBundleId:(NSString *)bundleIdentifier
|
|
411
477
|
{
|
|
412
478
|
XCUIApplication *app = [self makeApplicationWithBundleId:bundleIdentifier];
|
|
413
|
-
[app
|
|
479
|
+
[app fb_activate];
|
|
414
480
|
return app;
|
|
415
481
|
}
|
|
416
482
|
|
|
@@ -440,9 +506,9 @@ static NSUInteger _committedTerminationCount = 0;
|
|
|
440
506
|
}
|
|
441
507
|
|
|
442
508
|
// Has no async variant and can block on the shared accessibility channel. Run off-thread and give
|
|
443
|
-
// up after `timeout`, assuming the
|
|
509
|
+
// up after `timeout`, assuming the supplied app IS the system app - safer, since it means skipping
|
|
444
510
|
// termination rather than risking terminating springboard.
|
|
445
|
-
- (BOOL)
|
|
511
|
+
- (BOOL)fb_isApplicationSameAsSystemApp:(XCUIApplication *)application timeout:(NSTimeInterval)timeout
|
|
446
512
|
{
|
|
447
513
|
__block XCUIApplication *systemApp = nil;
|
|
448
514
|
__block NSException *caughtException = nil;
|
|
@@ -451,7 +517,7 @@ static NSUInteger _committedTerminationCount = 0;
|
|
|
451
517
|
// Undocumented private API; guard in case it hard-asserts off-main like -terminate does on
|
|
452
518
|
// some Xcode/iOS version - uncaught, that would crash the whole process.
|
|
453
519
|
@try {
|
|
454
|
-
systemApp =
|
|
520
|
+
systemApp = [[application class] fb_systemApplication];
|
|
455
521
|
} @catch (NSException *e) {
|
|
456
522
|
caughtException = e;
|
|
457
523
|
}
|
|
@@ -459,10 +525,10 @@ static NSUInteger _committedTerminationCount = 0;
|
|
|
459
525
|
});
|
|
460
526
|
int64_t timeoutNs = (int64_t)(timeout * NSEC_PER_SEC);
|
|
461
527
|
if (0 != dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, timeoutNs)) || nil != caughtException) {
|
|
462
|
-
[FBLogger logFmt:@"Could not determine the system application within %@ seconds%@; assuming '%@' might be it and skipping its termination", @(timeout), nil == caughtException ? @"" : [NSString stringWithFormat:@" (%@)", caughtException.description],
|
|
528
|
+
[FBLogger logFmt:@"Could not determine the system application within %@ seconds%@; assuming '%@' might be it and skipping its termination", @(timeout), nil == caughtException ? @"" : [NSString stringWithFormat:@" (%@)", caughtException.description], application.bundleID];
|
|
463
529
|
return YES;
|
|
464
530
|
}
|
|
465
|
-
return [
|
|
531
|
+
return [application fb_isSameAppAs:systemApp];
|
|
466
532
|
}
|
|
467
533
|
|
|
468
534
|
// -terminate hard-asserts off-main, but -kill can now run on a background queue. Dispatching to
|
|
@@ -36,7 +36,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
36
36
|
- (void)client:(nw_connection_t)client didReceiveData:(NSData *)data;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
The callback which is fired when TCP client disconnects
|
|
39
|
+
The callback which is fired when TCP client disconnects or finishes sending
|
|
40
|
+
|
|
41
|
+
A connection that failed is cancelled by the socket. One whose peer only finished sending stays
|
|
42
|
+
writable (the peer may be awaiting a response), and the delegate must cancel it once it has
|
|
43
|
+
nothing left to send - an uncancelled connection keeps its socket open.
|
|
40
44
|
|
|
41
45
|
@param client The actual disconnected client
|
|
42
46
|
*/
|
|
@@ -137,6 +137,8 @@
|
|
|
137
137
|
} else if (nw_connection_state_failed == state || nw_connection_state_cancelled == state) {
|
|
138
138
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
139
139
|
[strongSelf handleDisconnectForConnection:connection];
|
|
140
|
+
// A failed connection keeps its socket until cancelled (a no-op once cancelled).
|
|
141
|
+
nw_connection_cancel(connection);
|
|
140
142
|
}
|
|
141
143
|
});
|
|
142
144
|
nw_connection_start(connection);
|
|
@@ -167,7 +169,14 @@
|
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
|
-
if (nil != receiveError
|
|
172
|
+
if (nil != receiveError) {
|
|
173
|
+
[strongSelf handleDisconnectForConnection:connection];
|
|
174
|
+
nw_connection_cancel(connection);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (isComplete && nil == content) {
|
|
178
|
+
// The peer is done sending but may still be reading - a half-closed client awaits its
|
|
179
|
+
// response - so the delegate cancels the connection once it has nothing left to send.
|
|
171
180
|
[strongSelf handleDisconnectForConnection:connection];
|
|
172
181
|
return;
|
|
173
182
|
}
|
|
@@ -133,6 +133,9 @@ static NSString *const FBServerURLEndMarker = @"<-ServerURLHere";
|
|
|
133
133
|
self.mjpegServer = [[FBMjpegServer alloc] init];
|
|
134
134
|
self.screenshotsBroadcaster = [[FBTCPSocket alloc]
|
|
135
135
|
initWithPort:(uint16_t)FBConfiguration.sharedInstance.mjpegServerPort];
|
|
136
|
+
// Bind to the same address as the HTTP server when USE_IP is set.
|
|
137
|
+
// nil (the default) keeps listening on all interfaces.
|
|
138
|
+
self.screenshotsBroadcaster.interface = FBConfiguration.sharedInstance.bindingIPAddress;
|
|
136
139
|
self.mjpegServer.socket = self.screenshotsBroadcaster;
|
|
137
140
|
self.screenshotsBroadcaster.delegate = self.mjpegServer;
|
|
138
141
|
NSError *error;
|
|
@@ -265,7 +268,7 @@ static NSString *const FBServerURLEndMarker = @"<-ServerURLHere";
|
|
|
265
268
|
|
|
266
269
|
- (void)registerServerKeyRouteHandlers
|
|
267
270
|
{
|
|
268
|
-
[self.server
|
|
271
|
+
[self.server handleMethod:@"GET" withPath:@"/health" standalone:YES block:^(RouteRequest *request, RouteResponse *response) {
|
|
269
272
|
[response respondWithString:@"<!DOCTYPE html><html><title>Health Check</title><body><p>I-AM-ALIVE</p></body></html>"];
|
|
270
273
|
}];
|
|
271
274
|
|
|
@@ -24,14 +24,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
24
24
|
* Calculates the accessbility element which is located at the given screen coordinates
|
|
25
25
|
*
|
|
26
26
|
* @param point The screen coordinates
|
|
27
|
-
* @returns The retrieved accessbility element or nil if it cannot be detected
|
|
27
|
+
* @returns The retrieved accessbility element or nil if it cannot be detected or a secondary display is selected
|
|
28
28
|
*/
|
|
29
29
|
+ (nullable id<FBXCAccessibilityElement>)axElementWithPoint:(CGPoint)point;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Retrieves the accessbility element for the current screen point
|
|
33
33
|
*
|
|
34
|
-
* @returns The retrieved accessbility element or nil if it cannot be detected
|
|
34
|
+
* @returns The retrieved accessbility element or nil if it cannot be detected or a secondary display is selected
|
|
35
35
|
*/
|
|
36
36
|
- (nullable id<FBXCAccessibilityElement>)axElement;
|
|
37
37
|
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
#endif
|
|
14
14
|
|
|
15
15
|
#import "FBErrorBuilder.h"
|
|
16
|
+
#import "FBScreen.h"
|
|
17
|
+
#import "FBConfiguration.h"
|
|
18
|
+
#import "FBExceptions.h"
|
|
19
|
+
#import "XCUIScreen.h"
|
|
16
20
|
#import "FBLogger.h"
|
|
17
21
|
#import "FBXCTestDaemonsProxy.h"
|
|
18
22
|
#import "XCTMessagingChannel_RunnerToDaemon-Protocol.h"
|
|
@@ -45,6 +49,22 @@
|
|
|
45
49
|
|
|
46
50
|
+ (id<FBXCAccessibilityElement>)axElementWithPoint:(CGPoint)point
|
|
47
51
|
{
|
|
52
|
+
if (nil != FBConfiguration.sharedInstance.currentDisplayId) {
|
|
53
|
+
NSError *error;
|
|
54
|
+
XCUIScreen *screen = [FBScreen currentScreenWithError:&error];
|
|
55
|
+
if (nil == screen) {
|
|
56
|
+
@throw [NSException exceptionWithName:FBInvalidArgumentException
|
|
57
|
+
reason:error.localizedDescription
|
|
58
|
+
userInfo:nil];
|
|
59
|
+
}
|
|
60
|
+
// XCTest's point lookup has no display parameter and always hits the main
|
|
61
|
+
// display. On Duo this can select the outer SpringBoard while the app is
|
|
62
|
+
// foreground on the inner display. Let callers use their active-app fallback
|
|
63
|
+
// instead of treating an element from a different display as authoritative.
|
|
64
|
+
if (screen.displayID != XCUIScreen.mainScreen.displayID) {
|
|
65
|
+
return nil;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
48
68
|
__block id<FBXCAccessibilityElement> onScreenElement = nil;
|
|
49
69
|
id<XCTMessagingChannel_RunnerToDaemon> proxy = [FBXCTestDaemonsProxy testRunnerProxy];
|
|
50
70
|
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
|
|
@@ -60,6 +60,8 @@ typedef NS_ENUM(NSInteger, FBConfigurationKeyboardPreference) {
|
|
|
60
60
|
|
|
61
61
|
/*! The maximum typing frequency for all typing activities */
|
|
62
62
|
@property (atomic, assign) NSUInteger maxTypingFrequency;
|
|
63
|
+
/*! Seconds to wait for a hittable key before typing on iOS. 0 disables the wait. */
|
|
64
|
+
@property (atomic, assign) NSTimeInterval keyboardWaitTimeout;
|
|
63
65
|
@property (atomic, readonly) NSUInteger defaultTypingFrequency;
|
|
64
66
|
|
|
65
67
|
/*! Use singleton test manager proxy */
|
|
@@ -368,6 +370,12 @@ typedef NS_ENUM(NSInteger, FBConfigurationKeyboardPreference) {
|
|
|
368
370
|
*/
|
|
369
371
|
@property (atomic, assign) BOOL enforceCustomSnapshots;
|
|
370
372
|
|
|
373
|
+
/**
|
|
374
|
+
* The identifier of the display targeted by screenshots, MJPEG, and new screen recordings.
|
|
375
|
+
* nil means the main display. Reset to nil for each new session.
|
|
376
|
+
*/
|
|
377
|
+
@property (atomic, copy, nullable) NSNumber *currentDisplayId;
|
|
378
|
+
|
|
371
379
|
@end
|
|
372
380
|
|
|
373
381
|
NS_ASSUME_NONNULL_END
|
|
@@ -343,6 +343,7 @@ static NSString *const axSettingsClassName = @"AXSettings";
|
|
|
343
343
|
self.shouldUseCompactResponses = YES;
|
|
344
344
|
self.elementResponseAttributes = @"type,label";
|
|
345
345
|
self.maxTypingFrequencyOverride = @(self.defaultTypingFrequency);
|
|
346
|
+
self.keyboardWaitTimeout = 0;
|
|
346
347
|
self.screenshotQuality = 3;
|
|
347
348
|
self.useFirstMatch = NO;
|
|
348
349
|
self.boundElementsByIndex = NO;
|
|
@@ -357,6 +358,7 @@ static NSString *const axSettingsClassName = @"AXSettings";
|
|
|
357
358
|
FBSetCustomParameterForElementSnapshot(FBSnapshotMaxChildrenKey, @INT_MAX);
|
|
358
359
|
self.useClearTextShortcut = YES;
|
|
359
360
|
self.limitXpathContextScope = YES;
|
|
361
|
+
self.currentDisplayId = nil;
|
|
360
362
|
#if !TARGET_OS_TV && !TARGET_OS_WATCH
|
|
361
363
|
self.screenshotOrientationStorage = UIInterfaceOrientationUnknown;
|
|
362
364
|
#endif
|