Skip to content

Commit

Permalink
Define the delay for '-[XCUIApplicationProcess setEventLoopHasIdled:]…
Browse files Browse the repository at this point in the history
…' with a desiredCapability (#140)
  • Loading branch information
dmissmann authored and mykola-mokhnach committed Feb 28, 2019
1 parent 1fb1117 commit 5896cce
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 32 deletions.
2 changes: 2 additions & 0 deletions WebDriverAgent.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@
1FC3B2E12121EC8C00B61EE0 /* FBApplicationProcessProxyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBApplicationProcessProxyTests.m; sourceTree = "<group>"; };
44757A831D42CE8300ECF35E /* XCUIDeviceRotationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XCUIDeviceRotationTests.m; sourceTree = "<group>"; };
631B523421F6174300625362 /* FBImageIOScalerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageIOScalerTests.m; sourceTree = "<group>"; };
633E904A220DEE7F007CADF9 /* XCUIApplicationProcessDelay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCUIApplicationProcessDelay.h; sourceTree = "<group>"; };
6385F4A5220A40760095BBDB /* XCUIApplicationProcessDelay.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XCUIApplicationProcessDelay.m; sourceTree = "<group>"; };
63CCF91021ECE4C700E94ABD /* FBImageIOScaler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBImageIOScaler.h; sourceTree = "<group>"; };
63CCF91121ECE4C700E94ABD /* FBImageIOScaler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageIOScaler.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1172,6 +1173,7 @@
EE6B64FC1D0F86EF00E85F5D /* XCTestPrivateSymbols.m */,
63CCF91021ECE4C700E94ABD /* FBImageIOScaler.h */,
63CCF91121ECE4C700E94ABD /* FBImageIOScaler.m */,
633E904A220DEE7F007CADF9 /* XCUIApplicationProcessDelay.h */,
6385F4A5220A40760095BBDB /* XCUIApplicationProcessDelay.m */,
);
name = Utilities;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,6 @@
value = "$(WDA_PRODUCT_BUNDLE_IDENTIFIER)"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "EVENTLOOP_IDLE_DELAY_SEC"
value = "0"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
Expand Down
7 changes: 7 additions & 0 deletions WebDriverAgentLib/Commands/FBSessionCommands.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#import "XCUIDevice.h"
#import "XCUIDevice+FBHealthCheck.h"
#import "XCUIDevice+FBHelpers.h"
#import "XCUIApplicationProcessDelay.h"

static NSString* const USE_COMPACT_RESPONSES = @"shouldUseCompactResponses";
static NSString* const ELEMENT_RESPONSE_ATTRIBUTES = @"elementResponseAttributes";
Expand Down Expand Up @@ -92,6 +93,12 @@ + (NSArray *)routes
if (requirements[@"shouldUseSingletonTestManager"]) {
[FBConfiguration setShouldUseSingletonTestManager:[requirements[@"shouldUseSingletonTestManager"] boolValue]];
}
NSNumber *delay = requirements[@"eventloopIdleDelaySec"];
if ([delay doubleValue] > 0.0) {
[XCUIApplicationProcessDelay setEventLoopHasIdledDelay:[delay doubleValue]];
} else {
[XCUIApplicationProcessDelay disableEventLoopDelay];
}

[FBConfiguration setShouldWaitForQuiescence:[requirements[@"shouldWaitForQuiescence"] boolValue]];

Expand Down
38 changes: 38 additions & 0 deletions WebDriverAgentLib/Utilities/XCUIApplicationProcessDelay.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* 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 <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/**
In certain cases WebDriverAgent fails to create a session because -[XCUIApplication launch] doesn't return
since it waits for the target app to be quiescenced.
The reason for this seems to be that 'testmanagerd' doesn't send the events WebDriverAgent is waiting for.
The expected events would trigger calls to '-[XCUIApplicationProcess setEventLoopHasIdled:]' and
'-[XCUIApplicationProcess setAnimationsHaveFinished:]', which are the properties that are checked to
determine whether an app has quiescenced or not.
Delaying the call to on of the setters can fix this issue.
*/
@interface XCUIApplicationProcessDelay : NSObject

/**
Delays the invocation of '-[XCUIApplicationProcess setEventLoopHasIdled:]' by the timer interval passed
@param delay The duration of the sleep before the original method is called
*/
+ (void)setEventLoopHasIdledDelay:(NSTimeInterval)delay;

/**
Disables the delayed invocation of '-[XCUIApplicationProcess setEventLoopHasIdled:]'.
*/
+ (void)disableEventLoopDelay;

@end

NS_ASSUME_NONNULL_END
65 changes: 38 additions & 27 deletions WebDriverAgentLib/Utilities/XCUIApplicationProcessDelay.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,49 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "XCUIApplicationProcessDelay.h"
#import <objc/runtime.h>
#import "XCUIApplicationProcess.h"
#import "FBLogger.h"

/**
In certain cases WebDriverAgent fails to create a session because -[XCUIApplication launch] doesn't return
since it waits for the target app to be quiescenced.
The reason for this seems to be that 'testmanagerd' doesn't send the events WebDriverAgent is waiting for.
The expected events would trigger calls to '-[XCUIApplicationProcess setEventLoopHasIdled:]' and
'-[XCUIApplicationProcess setAnimationsHaveFinished:]', which are the properties that are checked to
determine whether an app has quiescenced or not.
Delaying the call to on of the setters can fix this issue. Setting the environment variable
'EVENTLOOP_IDLE_DELAY_SEC' will swizzle the method '-[XCUIApplicationProcess setEventLoopHasIdled:]'
and add a thread sleep of the value specified in the environment variable in seconds.
*/
@interface XCUIApplicationProcessDelay : NSObject

@end

static NSString *const EVENTLOOP_IDLE_DELAY_SEC = @"EVENTLOOP_IDLE_DELAY_SEC";
static void (*orig_set_event_loop_has_idled)(id, SEL, BOOL);
static NSTimeInterval delay = 0;
static NSTimeInterval eventloopIdleDelay = 0;
static BOOL isSwizzled = NO;
// '-[XCUIApplicationProcess setEventLoopHasIdled:]' can be called from different queues.
// Lets lock the setup and access to the 'eventloopIdleDelay' variable
static NSLock * lock = nil;

@implementation XCUIApplicationProcessDelay

+ (void)load {
NSDictionary *env = [[NSProcessInfo processInfo] environment];
NSString *setEventLoopIdleDelay = [env objectForKey:EVENTLOOP_IDLE_DELAY_SEC];
if (!setEventLoopIdleDelay || [setEventLoopIdleDelay length] == 0) {
[FBLogger verboseLog:@"don't delay -[XCUIApplicationProcess setEventLoopHasIdled:]"];
lock = [[NSLock alloc] init];
}

+ (void)setEventLoopHasIdledDelay:(NSTimeInterval)delay
{
[lock lock];
if (!isSwizzled && delay < DBL_EPSILON) {
// don't swizzle methods until we need to
[lock unlock];
return;
}
delay = [setEventLoopIdleDelay doubleValue];
if (delay < DBL_EPSILON) {
[FBLogger log:[NSString stringWithFormat:@"Value of '%@' has to be greater than zero to delay -[XCUIApplicationProcess setEventLoopHasIdled:]",
EVENTLOOP_IDLE_DELAY_SEC]];
eventloopIdleDelay = delay;
if (isSwizzled) {
[lock unlock];
return;
}
[self swizzleSetEventLoopHasIdled];
[lock unlock];
}

+ (void)disableEventLoopDelay
{
// Once the methods were swizzled they stay like that since the only change in the implementation
// is the thread sleep, which is skipped on setting it to zero.
[self setEventLoopHasIdledDelay:0];
}

+ (void)swizzleSetEventLoopHasIdled {
Method original = class_getInstanceMethod([XCUIApplicationProcess class], @selector(setEventLoopHasIdled:));
if (original == nil) {
[FBLogger log:@"Could not find method -[XCUIApplicationProcess setEventLoopHasIdled:]"];
Expand All @@ -53,11 +58,17 @@ + (void)load {
orig_set_event_loop_has_idled = (void(*)(id, SEL, BOOL)) method_getImplementation(original);
Method replace = class_getClassMethod([XCUIApplicationProcessDelay class], @selector(setEventLoopHasIdled:));
method_setImplementation(original, method_getImplementation(replace));
isSwizzled = YES;
}

+ (void)setEventLoopHasIdled:(BOOL)idled {
[FBLogger verboseLog:[NSString stringWithFormat:@"Delaying -[XCUIApplicationProcess setEventLoopHasIdled:] by %.2f seconds", delay]];
[NSThread sleepForTimeInterval:delay];
[lock lock];
NSTimeInterval delay = eventloopIdleDelay;
[lock unlock];
if (delay > 0.0) {
[FBLogger verboseLog:[NSString stringWithFormat:@"Delaying -[XCUIApplicationProcess setEventLoopHasIdled:] by %.2f seconds", delay]];
[NSThread sleepForTimeInterval:delay];
}
orig_set_event_loop_has_idled(self, _cmd, idled);
}

Expand Down

0 comments on commit 5896cce

Please sign in to comment.