Files
Monkey-Test/WebDriverAgent/WebDriverAgentLib/FBApplication.m
T
2019-01-25 17:01:52 +08:00

158 lines
4.7 KiB
Objective-C
Executable File

/**
* 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 "FBApplication.h"
#import "FBApplicationProcessProxy.h"
#import "FBRunLoopSpinner.h"
#import "FBMacros.h"
#import "FBXCodeCompatibility.h"
#import "XCAccessibilityElement.h"
#import "XCAXClient_iOS.h"
#import "XCUIApplication.h"
#import "XCUIApplicationImpl.h"
#import "XCUIApplicationProcess.h"
#import "XCUIElement.h"
#import "XCUIElementQuery.h"
@interface FBApplication ()
@property (nonatomic, assign) BOOL fb_isObservingAppImplCurrentProcess;
@end
@implementation FBApplication
+ (instancetype)fb_activeApplication
{
[[[FBRunLoopSpinner new]
timeout:5]
spinUntilTrue:^BOOL{
return [[XCAXClient_iOS sharedClient] activeApplications].count == 1;
}];
XCAccessibilityElement *activeApplicationElement = [[[XCAXClient_iOS sharedClient] activeApplications] firstObject];
if (!activeApplicationElement) {
return nil;
}
FBApplication *application = [FBApplication fb_applicationWithPID:activeApplicationElement.processIdentifier];
NSAssert(nil != application, @"Active application instance is not expected to be equal to nil", nil);
return application;
}
+ (instancetype)appWithPID:(pid_t)processID
{
if ([NSProcessInfo processInfo].processIdentifier == processID) {
return nil;
}
FBApplication *application = [self fb_registeredApplicationWithProcessID:processID];
if (application) {
return application;
}
application = [super appWithPID:processID];
[FBApplication fb_registerApplication:application withProcessID:processID];
return application;
}
+ (instancetype)applicationWithPID:(pid_t)processID
{
if ([NSProcessInfo processInfo].processIdentifier == processID) {
return nil;
}
FBApplication *application = [self fb_registeredApplicationWithProcessID:processID];
if (application) {
return application;
}
application = [super applicationWithPID:processID];
[FBApplication fb_registerApplication:application withProcessID:processID];
return application;
}
- (void)launch
{
if (!self.fb_shouldWaitForQuiescence) {
[self.fb_appImpl addObserver:self forKeyPath:FBStringify(XCUIApplicationImpl, currentProcess) options:(NSKeyValueObservingOptions)(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:nil];
self.fb_isObservingAppImplCurrentProcess = YES;
}
[super launch];
[FBApplication fb_registerApplication:self withProcessID:self.processID];
}
- (void)terminate
{
if (self.fb_isObservingAppImplCurrentProcess) {
[self.fb_appImpl removeObserver:self forKeyPath:FBStringify(XCUIApplicationImpl, currentProcess)];
}
[super terminate];
}
#pragma mark - Quiescence
- (void)_waitForQuiescence
{
if (!self.fb_shouldWaitForQuiescence) {
return;
}
[super _waitForQuiescence];
}
- (XCUIApplicationImpl *)fb_appImpl
{
if (![self respondsToSelector:@selector(applicationImpl)]) {
return nil;
}
XCUIApplicationImpl *appImpl = [self applicationImpl];
if (![appImpl respondsToSelector:@selector(currentProcess)]) {
return nil;
}
return appImpl;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context
{
if (object != self.fb_appImpl) {
return;
}
if (![keyPath isEqualToString:FBStringify(XCUIApplicationImpl, currentProcess)]) {
return;
}
if ([change[NSKeyValueChangeKindKey] unsignedIntegerValue] != NSKeyValueChangeSetting) {
return;
}
XCUIApplicationProcess *applicationProcess = change[NSKeyValueChangeNewKey];
if (!applicationProcess || [applicationProcess isProxy] || ![applicationProcess isMemberOfClass:XCUIApplicationProcess.class]) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// Dispatching this call out of KVO call stack will trigger additional KVO call,
// which is required in case there are already registered observers.
self.fb_appImpl.currentProcess = (XCUIApplicationProcess *)[FBApplicationProcessProxy proxyWithApplicationProcess:applicationProcess];
});
}
#pragma mark - Process registration
static NSMutableDictionary *FBPidToApplicationMapping;
+ (instancetype)fb_registeredApplicationWithProcessID:(pid_t)processID
{
return FBPidToApplicationMapping[@(processID)];
}
+ (void)fb_registerApplication:(FBApplication *)application withProcessID:(pid_t)processID
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
FBPidToApplicationMapping = [NSMutableDictionary dictionary];
});
FBPidToApplicationMapping[@(application.processID)] = application;
}
@end