Skip to content
This repository has been archived by the owner on Dec 26, 2019. It is now read-only.

Use NSProxy as base class for FBApplicationProcessProxy #979

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions WebDriverAgent.xcodeproj/project.pbxproj
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
18033EFF208761FC00FED81D /* RoutingHTTPServer.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = AD42DD2B1CF1238500806E5D /* RoutingHTTPServer.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1FC3B2E32121ECF600B61EE0 /* FBApplicationProcessProxyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FC3B2E12121EC8C00B61EE0 /* FBApplicationProcessProxyTests.m */; };
711084441DA3AA7500F913D6 /* FBXPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 711084421DA3AA7500F913D6 /* FBXPath.h */; settings = {ATTRIBUTES = (Public, ); }; };
711084451DA3AA7500F913D6 /* FBXPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 711084431DA3AA7500F913D6 /* FBXPath.m */; };
7119E1EC1E891F8600D0B125 /* FBPickerWheelSelectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7119E1EB1E891F8600D0B125 /* FBPickerWheelSelectTests.m */; };
Expand Down Expand Up @@ -412,6 +413,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
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>"; };
711084421DA3AA7500F913D6 /* FBXPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBXPath.h; sourceTree = "<group>"; };
711084431DA3AA7500F913D6 /* FBXPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBXPath.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1126,6 +1128,7 @@
isa = PBXGroup;
children = (
ADBC39951D07840300327304 /* Doubles */,
1FC3B2E12121EC8C00B61EE0 /* FBApplicationProcessProxyTests.m */,
71A7EAFB1E229302001DA4F2 /* FBClassChainTests.m */,
EEE16E961D33A25500172525 /* FBConfigurationTests.m */,
ADBC39931D0782CD00327304 /* FBElementCacheTests.m */,
Expand Down Expand Up @@ -1882,6 +1885,7 @@
EE3F8CFE1D08AA17006F02CE /* FBRunLoopSpinnerTests.m in Sources */,
714801D11FA9D9FA00DC5997 /* FBSDKVersionTests.m in Sources */,
EE3F8D001D08B05F006F02CE /* FBElementTypeTransformerTests.m in Sources */,
1FC3B2E32121ECF600B61EE0 /* FBApplicationProcessProxyTests.m in Sources */,
EEE16E971D33A25500172525 /* FBConfigurationTests.m in Sources */,
ADBC39941D0782CD00327304 /* FBElementCacheTests.m in Sources */,
EE8DDD7920C565FB004D4925 /* XCUIApplicationFBHelpersTests.m in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion WebDriverAgentLib/FBApplication.m
Expand Up @@ -122,7 +122,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
return;
}
XCUIApplicationProcess *applicationProcess = change[NSKeyValueChangeNewKey];
if (!applicationProcess || ![applicationProcess isMemberOfClass:XCUIApplicationProcess.class]) {
if (!applicationProcess || [applicationProcess isProxy] || ![applicationProcess isMemberOfClass:XCUIApplicationProcess.class]) {

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

return;
}
[object setValue:[FBApplicationProcessProxy proxyWithApplicationProcess:applicationProcess] forKey:keyPath];
Expand Down
2 changes: 1 addition & 1 deletion WebDriverAgentLib/FBApplicationProcessProxy.h
Expand Up @@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
Proxy that would forward all calls to it's applicationProcess.
However it will block call to waitForQuiescence if shouldWaitForQuiescence is set to NO
*/
@interface FBApplicationProcessProxy : NSObject
@interface FBApplicationProcessProxy : NSProxy

/**
Convenience initializer
Expand Down
20 changes: 17 additions & 3 deletions WebDriverAgentLib/FBApplicationProcessProxy.m
Expand Up @@ -20,11 +20,18 @@ @implementation FBApplicationProcessProxy
+ (instancetype)proxyWithApplicationProcess:(XCUIApplicationProcess *)applicationProcess
{
NSParameterAssert(applicationProcess);
FBApplicationProcessProxy *proxy = [self.class new];
NSParameterAssert([[applicationProcess class] isEqual:XCUIApplicationProcess.class]);
FBApplicationProcessProxy *proxy = [[self.class alloc] init];
proxy.applicationProcess = applicationProcess;
return proxy;
}

- (instancetype)init {
return self;

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

}

#pragma mark - Override XCUIApplicationProcess methods

- (void)waitForQuiescence
{
if (!self.shouldWaitForQuiescence) {
Expand All @@ -45,9 +52,16 @@ - (void)waitForQuiescenceIncludingAnimationsIdle:(BOOL)includeAnimations
[self.applicationProcess waitForQuiescenceIncludingAnimationsIdle:includeAnimations];
}

- (id)forwardingTargetForSelector:(SEL)aSelector
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why kill forwardingTargetForSelector? it is faster then invocation right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't done any benchmarking, and implemented the only two required methods. if this is faster, then let's use it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From https://developer.apple.com/documentation/objectivec/nsobject/1418855-forwardingtargetforselector

This method gives an object a chance to redirect an unknown message sent to it before the much more expensive forwardInvocation: machinery takes over.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Simple benchmark with 10 million invocations answers the question "How much more expensive?" :-) 0.3 seconds vs 26 seconds

#pragma mark - Forward not implemented methods to applicationProcess

- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.applicationProcess];
}

- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return self.applicationProcess;
return [self.applicationProcess methodSignatureForSelector:sel];
}

@end
82 changes: 82 additions & 0 deletions WebDriverAgentTests/UnitTests/FBApplicationProcessProxyTests.m
@@ -0,0 +1,82 @@
/**
* 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>
#import "FBApplicationProcessProxy.h"
#import "XCUIApplicationProcess.h"

@interface FBApplicationProcessProxy (NonProxiedMethod)
- (void)objectsMethod;
@end

@implementation FBApplicationProcessProxy (NonProxiedMethod)

- (void)objectsMethod
{
// intentionally empty
}

@end

@interface FBApplicationProcessProxy (ProxiedMethod)
- (NSInteger)proxiedMethod;
@end

@interface XCUIApplicationProcess (TestableMethods)
- (void)objectsMethod;
- (int)proxiedMethod;
@end

@implementation XCUIApplicationProcess (TestableMethods)

- (int)proxiedMethod;
{
return 1;
}

- (void)objectsMethod
{
NSString *errorMessage = [NSString stringWithFormat:@"Method %@ must NOT be proxied", NSStringFromSelector(_cmd)];
NSException * exception = [[NSException alloc] initWithName:@"Test failed" reason:errorMessage userInfo:nil];
[exception raise];
}

@end

@interface FBApplicationProcessProxyTest : XCTestCase

@end

@implementation FBApplicationProcessProxyTest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!


- (void)testMethodCallIsProxied {
XCUIApplicationProcess *applicationProcess = [[XCUIApplicationProcess alloc] init];
FBApplicationProcessProxy *proxy = [FBApplicationProcessProxy proxyWithApplicationProcess:applicationProcess];
XCTAssertEqual([proxy proxiedMethod], 1);
}

- (void)testMethodCallIsNotProxied {
XCUIApplicationProcess *applicationProcess = [[XCUIApplicationProcess alloc] init];
FBApplicationProcessProxy *proxy = [FBApplicationProcessProxy proxyWithApplicationProcess:applicationProcess];
XCTAssertNoThrow([proxy objectsMethod]);
}

- (void)testProxyIsProxy {
XCUIApplicationProcess *applicationProcess = [[XCUIApplicationProcess alloc] init];
FBApplicationProcessProxy *proxy = [FBApplicationProcessProxy proxyWithApplicationProcess:applicationProcess];
XCTAssertTrue([proxy isProxy]);
}

- (void)testAssertProxyObjectParameter {
XCUIApplicationProcess *applicationProcess = [[XCUIApplicationProcess alloc] init];
id proxy = (id)[FBApplicationProcessProxy proxyWithApplicationProcess:applicationProcess];
XCTAssertThrows([FBApplicationProcessProxy proxyWithApplicationProcess:proxy]);
}

@end