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
34 changes: 34 additions & 0 deletions WebDriverAgentLib/Categories/XCUIApplication+FBLaunch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* 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;

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

/**
* 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 error The actual error if present
* @return YES in case of success
*/
- (BOOL)fb_launchWithInterruptingAlertCheckInterval:(NSTimeInterval)interval
error:(NSError **)error;

@end

NS_ASSUME_NONNULL_END
107 changes: 107 additions & 0 deletions WebDriverAgentLib/Categories/XCUIApplication+FBLaunch.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* 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 "FBErrorBuilder.h"
#import "XCUIapplication.h"
#import "XCUIApplication+FBAlert.h"
#import "XCUIApplication+FBHelpers.h"

@implementation XCUIApplication (FBLaunch)

static char XCUIAPPLICATION_DID_START_WO_BLOCKING_ALERT;
static char XCUIAPPLICATION_BLOCKING_ALERT_TEXT;

@dynamic fb_didStartWithoutBlockingAlert;
@dynamic fb_blockingAlertText;

- (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)setFb_blockingAlertText:(NSString *)blockingAlertText
{
objc_setAssociatedObject(self, &XCUIAPPLICATION_BLOCKING_ALERT_TEXT,
blockingAlertText, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)fb_blockingAlertText
{
return objc_getAssociatedObject(self, &XCUIAPPLICATION_BLOCKING_ALERT_TEXT);
}

- (void)fb_scheduleNextDispatchWithInterval:(NSTimeInterval)interval
timeStarted:(uint64_t)timeStarted
timeout:(NSTimeInterval)timeout
{
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) {
self.fb_blockingAlertText = alert.text;
[self terminate];
return;
}
[self fb_scheduleNextDispatchWithInterval:interval
timeStarted:timeStarted
timeout:timeout];
});
}

- (BOOL)fb_launchWithInterruptingAlertCheckInterval:(NSTimeInterval)interval
error:(NSError **)error
{
self.fb_didStartWithoutBlockingAlert = @NO;
self.fb_blockingAlertText = nil;
[self fb_scheduleNextDispatchWithInterval:interval
timeStarted:clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)
timeout:65.];
@try {
[self launch];
self.fb_didStartWithoutBlockingAlert = @YES;
return YES;
} @catch (NSException *e) {
if (nil == self.fb_blockingAlertText) {
self.fb_didStartWithoutBlockingAlert = @YES;
return [[[FBErrorBuilder builder]
withDescriptionFormat:@"The application '%@' cannot be launched because of an unexpected error: %@", self.bundleID, e.reason]
buildError:error];;
}
}
return [[[FBErrorBuilder builder]
withDescriptionFormat:@"The application '%@' cannot be launched because it is blocked by an unexpected system alert: %@", self.bundleID, self.fb_blockingAlertText]
buildError:error];;
}
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved

@end
8 changes: 7 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,11 @@ + (NSArray *)routes
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg traceback:nil]);
}
} else {
[app launch];
NSError *launchError;
if (![app fb_launchWithInterruptingAlertCheckInterval:1. error:&launchError]) {
return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:launchError.description
traceback:nil]);
}
}
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