Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Handle app startup errors as session creation exceptions #855

Merged
merged 8 commits into from
Mar 3, 2024
12 changes: 12 additions & 0 deletions WebDriverAgent.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,10 @@
71A7EAF91E224648001DA4F2 /* FBClassChainQueryParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 71A7EAF71E224648001DA4F2 /* FBClassChainQueryParser.h */; };
71A7EAFA1E224648001DA4F2 /* FBClassChainQueryParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 71A7EAF81E224648001DA4F2 /* FBClassChainQueryParser.m */; };
71A7EAFC1E229302001DA4F2 /* FBClassChainTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 71A7EAFB1E229302001DA4F2 /* FBClassChainTests.m */; };
71AB65B32B8F156200187057 /* XCUIApplication+FBLaunch.h in Headers */ = {isa = PBXBuildFile; fileRef = 71AB65B12B8F156200187057 /* XCUIApplication+FBLaunch.h */; };
71AB65B42B8F156200187057 /* XCUIApplication+FBLaunch.h in Headers */ = {isa = PBXBuildFile; fileRef = 71AB65B12B8F156200187057 /* XCUIApplication+FBLaunch.h */; };
71AB65B52B8F156200187057 /* XCUIApplication+FBLaunch.m in Sources */ = {isa = PBXBuildFile; fileRef = 71AB65B22B8F156200187057 /* XCUIApplication+FBLaunch.m */; };
71AB65B62B8F156200187057 /* XCUIApplication+FBLaunch.m in Sources */ = {isa = PBXBuildFile; fileRef = 71AB65B22B8F156200187057 /* XCUIApplication+FBLaunch.m */; };
71ACF5B8242F2FDC00F0AAD4 /* FBSafariAlertTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 71ACF5B7242F2FDC00F0AAD4 /* FBSafariAlertTests.m */; };
71B155DA23070ECF00646AFB /* FBHTTPStatusCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B155D923070ECF00646AFB /* FBHTTPStatusCodes.h */; settings = {ATTRIBUTES = (Public, ); }; };
71B155DC230711E900646AFB /* FBCommandStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 71B155DB230711E900646AFB /* FBCommandStatus.m */; };
Expand Down Expand Up @@ -1028,6 +1032,8 @@
71A7EAF71E224648001DA4F2 /* FBClassChainQueryParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBClassChainQueryParser.h; sourceTree = "<group>"; };
71A7EAF81E224648001DA4F2 /* FBClassChainQueryParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBClassChainQueryParser.m; sourceTree = "<group>"; };
71A7EAFB1E229302001DA4F2 /* FBClassChainTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBClassChainTests.m; sourceTree = "<group>"; };
71AB65B12B8F156200187057 /* XCUIApplication+FBLaunch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCUIApplication+FBLaunch.h"; sourceTree = "<group>"; };
71AB65B22B8F156200187057 /* XCUIApplication+FBLaunch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "XCUIApplication+FBLaunch.m"; sourceTree = "<group>"; };
71ACF5B7242F2FDC00F0AAD4 /* FBSafariAlertTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSafariAlertTests.m; sourceTree = "<group>"; };
71B155D923070ECF00646AFB /* FBHTTPStatusCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBHTTPStatusCodes.h; sourceTree = "<group>"; };
71B155DB230711E900646AFB /* FBCommandStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBCommandStatus.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1709,6 +1715,8 @@
71A5C67229A4F39600421C37 /* XCTIssue+FBPatcher.m */,
AD6C269A1CF2494200F8B5FF /* XCUIApplication+FBHelpers.h */,
AD6C269B1CF2494200F8B5FF /* XCUIApplication+FBHelpers.m */,
71AB65B12B8F156200187057 /* XCUIApplication+FBLaunch.h */,
71AB65B22B8F156200187057 /* XCUIApplication+FBLaunch.m */,
71C8E54F25399A6B008572C1 /* XCUIApplication+FBQuiescence.h */,
71C8E55025399A6B008572C1 /* XCUIApplication+FBQuiescence.m */,
716C9DFE27315EFF005AD475 /* XCUIApplication+FBUIInterruptions.h */,
Expand Down Expand Up @@ -2331,6 +2339,7 @@
641EE6882240C5CA00173FCB /* FBElement.h in Headers */,
641EE6892240C5CA00173FCB /* XCTAXClient-Protocol.h in Headers */,
641EE68B2240C5CA00173FCB /* FBExceptionHandler.h in Headers */,
71AB65B42B8F156200187057 /* XCUIApplication+FBLaunch.h in Headers */,
71822726258744AE00661B83 /* RoutingConnection.h in Headers */,
641EE68C2240C5CA00173FCB /* FBRoute.h in Headers */,
641EE68D2240C5CA00173FCB /* XCTestDriver.h in Headers */,
Expand Down Expand Up @@ -2662,6 +2671,7 @@
71930C4220662E1F00D3AFEC /* FBPasteboard.h in Headers */,
EE7E271C1D06C69F001BEC7B /* FBDebugLogDelegateDecorator.h in Headers */,
EEDFE1211D9C06F800E6FFE5 /* XCUIDevice+FBHealthCheck.h in Headers */,
71AB65B32B8F156200187057 /* XCUIApplication+FBLaunch.h in Headers */,
7155D703211DCEF400166C20 /* FBMjpegServer.h in Headers */,
EE35AD761E3B77D600A02D78 /* XCUIRecorderNodeFinderMatch.h in Headers */,
EE35AD6C1E3B77D600A02D78 /* XCUIApplicationProcess.h in Headers */,
Expand Down Expand Up @@ -3071,6 +3081,7 @@
641EE5EB2240C5CA00173FCB /* XCUIElement+FBFind.m in Sources */,
641EE5EC2240C5CA00173FCB /* FBResponsePayload.m in Sources */,
C845206322D5E79700EA68CB /* FBUnattachedAppLauncher.m in Sources */,
71AB65B62B8F156200187057 /* XCUIApplication+FBLaunch.m in Sources */,
641EE5ED2240C5CA00173FCB /* FBRoute.m in Sources */,
641EE5EE2240C5CA00173FCB /* NSString+FBVisualLength.m in Sources */,
641EE5EF2240C5CA00173FCB /* FBRunLoopSpinner.m in Sources */,
Expand Down Expand Up @@ -3216,6 +3227,7 @@
EE158AC51CBD456F00A3E3F0 /* FBOrientationCommands.m in Sources */,
716F0DA32A16CA1000CDD977 /* NSDictionary+FBUtf8SafeDictionary.m in Sources */,
71D475C42538F5A8008D9401 /* XCUIApplicationProcess+FBQuiescence.m in Sources */,
71AB65B52B8F156200187057 /* XCUIApplication+FBLaunch.m in Sources */,
641EE7082240CDEB00173FCB /* XCUIElement+FBTVFocuse.m in Sources */,
71E75E6F254824230099FC87 /* XCUIElementQuery+FBHelpers.m in Sources */,
71D04DCA25356C43008A052C /* XCUIElement+FBCaching.m in Sources */,
Expand Down
30 changes: 30 additions & 0 deletions WebDriverAgentLib/Categories/XCUIApplication+FBLaunch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <XCTest/XCTest.h>

NS_ASSUME_NONNULL_BEGIN

@interface XCUIApplication (FBLaunch)

/**This property must only be used internally */
@property (nonatomic, nullable) NSNumber *fb_didStartWithoutBlockingAlert;

/**
* Launches the app with background blocking alert validation.
* This allows to avoid deadlocks or long timeouts on app startup.
* @param interval The amount of float ssconds between blocking alert presence checks
* @param exceptionName The name of the exception to be thrown in case an alert is detected
*/
- (void)fb_launchWithInterruptingAlertCheckInterval:(NSTimeInterval)interval
exceptionName:(NSString *)exceptionName;

@end

NS_ASSUME_NONNULL_END
81 changes: 81 additions & 0 deletions WebDriverAgentLib/Categories/XCUIApplication+FBLaunch.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "XCUIApplication+FBLaunch.h"

#import <objc/runtime.h>

#import "FBAlert.h"
#import "FBLogger.h"
#import "XCUIapplication.h"
#import "XCUIApplication+FBAlert.h"
#import "XCUIApplication+FBHelpers.h"

@implementation XCUIApplication (FBLaunch)

static char XCUIAPPLICATION_DID_START_WO_BLOCKING_ALERT;

@dynamic fb_didStartWithoutBlockingAlert;

- (void)setFb_didStartWithoutBlockingAlert:(NSNumber *)didStartWithoutBlockingAlert
{
objc_setAssociatedObject(self, &XCUIAPPLICATION_DID_START_WO_BLOCKING_ALERT,
didStartWithoutBlockingAlert, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSNumber *)fb_didStartWithoutBlockingAlert
{
return objc_getAssociatedObject(self, &XCUIAPPLICATION_DID_START_WO_BLOCKING_ALERT);
}

- (void)fb_scheduleNextDispatchWithInterval:(NSTimeInterval)interval
timeStarted:(uint64_t)timeStarted
timeout:(NSTimeInterval)timeout
exceptionName:(NSString *)exceptionName
{
dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (interval * NSEC_PER_SEC));
dispatch_after(dispatchTime, dispatch_get_main_queue(), ^{
NSTimeInterval duration = (clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) - timeStarted) / NSEC_PER_SEC;
if ([self.fb_didStartWithoutBlockingAlert boolValue] || duration > timeout) {
return;
}

FBAlert *alert = nil;
@try {
XCUIElement *alertElement = [self.class.fb_systemApplication fb_alertElement];
if (nil != alertElement) {
alert = [FBAlert alertWithElement:alertElement];
}
} @catch (NSException *e) {
[FBLogger logFmt:@"Got an unexpected exception while checking for system alerts: %@\n%@", e.reason, e.callStackSymbols];
}
if (nil != alert) {
NSString *reason = [NSString stringWithFormat:@"The application '%@' cannot be launched because it is blocked by an unexpected system alert: %@", self.bundleID, alert.text];
@throw [NSException exceptionWithName:exceptionName reason:reason userInfo:nil];
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
}
[self fb_scheduleNextDispatchWithInterval:interval
timeStarted:timeStarted
timeout:timeout
exceptionName:exceptionName];
});
}

- (void)fb_launchWithInterruptingAlertCheckInterval:(NSTimeInterval)interval
exceptionName:(NSString *)exceptionName
{
self.fb_didStartWithoutBlockingAlert = @NO;
[self fb_scheduleNextDispatchWithInterval:interval
timeStarted:clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)
timeout:65.
exceptionName:exceptionName];
[self launch];
self.fb_didStartWithoutBlockingAlert = @YES;
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
}

@end
4 changes: 3 additions & 1 deletion WebDriverAgentLib/Commands/FBSessionCommands.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#import "FBCapabilities.h"
#import "FBConfiguration.h"
#import "FBExceptions.h"
#import "FBLogger.h"
#import "FBProtocolHelpers.h"
#import "FBRouteRequest.h"
Expand All @@ -20,6 +21,7 @@
#import "FBActiveAppDetectionPoint.h"
#import "FBXCodeCompatibility.h"
#import "XCUIApplication+FBHelpers.h"
#import "XCUIApplication+FBLaunch.h"
#import "XCUIApplication+FBQuiescence.h"
#import "XCUIDevice.h"
#import "XCUIDevice+FBHealthCheck.h"
Expand Down Expand Up @@ -164,7 +166,7 @@ + (NSArray *)routes
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg traceback:nil]);
}
} else {
[app launch];
[app fb_launchWithInterruptingAlertCheckInterval:1. exceptionName:FBSessionCreationException];
}
if (!app.running) {
NSString *errorMsg = [NSString stringWithFormat:@"Cannot launch %@ application. Make sure the correct bundle identifier has been provided in capabilities and check the device log for possible crash report occurrences", bundleID];
Expand Down
3 changes: 3 additions & 0 deletions WebDriverAgentLib/Routing/FBExceptionHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ - (void)handleException:(NSException *)exception forResponse:(RouteResponse *)re
} else if ([exception.name isEqualToString:FBTimeoutException]) {
commandStatus = [FBCommandStatus timeoutErrorWithMessage:exception.reason
traceback:traceback];
} else if ([exception.name isEqualToString:FBSessionCreationException]) {
commandStatus = [FBCommandStatus sessionNotCreatedError:exception.reason
traceback:traceback];
} else {
commandStatus = [FBCommandStatus unknownErrorWithMessage:exception.reason
traceback:traceback];
Expand Down
3 changes: 3 additions & 0 deletions WebDriverAgentLib/Routing/FBExceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ NS_ASSUME_NONNULL_BEGIN
/*! Exception used to notify about missing session */
extern NSString *const FBSessionDoesNotExistException;

/*! Exception used to notify about session creation issues */
extern NSString *const FBSessionCreationException;

/*! Exception used to notify about application deadlock */
extern NSString *const FBApplicationDeadlockDetectedException;

Expand Down
1 change: 1 addition & 0 deletions WebDriverAgentLib/Routing/FBExceptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "FBExceptions.h"

NSString *const FBInvalidArgumentException = @"FBInvalidArgumentException";
NSString *const FBSessionCreationException = @"FBSessionCreationException";
NSString *const FBSessionDoesNotExistException = @"FBSessionDoesNotExistException";
NSString *const FBApplicationDeadlockDetectedException = @"FBApplicationDeadlockDetectedException";
NSString *const FBElementAttributeUnknownException = @"FBElementAttributeUnknownException";
Expand Down
Loading