Backfill Unit Tests EventManager

Summary: Backfills some unit tests for event manager and introduces a protocol `FBSDKSwizzling` to be able to examine what is being swizzled by the type without actually doing any swizzling.

Reviewed By: KylinChang

Differential Revision: D26855106

fbshipit-source-id: f57a7dc8a4a00a0a0a16ae4355644f4e9d492583
This commit is contained in:
Joe Susnick
2021-03-09 09:53:27 -08:00
committed by Facebook GitHub Bot
parent 9bf746529b
commit 99786670d6
16 changed files with 883 additions and 229 deletions

View File

@@ -703,7 +703,7 @@
C4FC99F1202CD56D0038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = C4FC99D7202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.h */; };
C4FC99F2202CD56D0038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C4FC99D8202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m */; };
C51121CB20A27EF50041DC94 /* FBSDKEventBindingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C51121C920A27EF50041DC94 /* FBSDKEventBindingTests.m */; };
C51121CC20A27EF50041DC94 /* FBSDKSampleEventBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.swift */; };
C51121CC20A27EF50041DC94 /* SampleRawRemoteEventBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C51121CA20A27EF50041DC94 /* SampleRawRemoteEventBindings.swift */; };
C51122A020A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C511229F20A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m */; };
C5188DF9222F388400F4D8BC /* FBSDKApplicationObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = C5188DF8222F388400F4D8BC /* FBSDKApplicationObserving.h */; };
C5188DFA222F388400F4D8BC /* FBSDKApplicationObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = C5188DF8222F388400F4D8BC /* FBSDKApplicationObserving.h */; };
@@ -1047,6 +1047,9 @@
F4A033C525F13E1300D19DE9 /* UrlSessionTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A033C425F13E1300D19DE9 /* UrlSessionTaskTests.swift */; };
F4A033D425F13FD700D19DE9 /* SampleUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A033D325F13FD700D19DE9 /* SampleUrl.swift */; };
F4A033E325F1545B00D19DE9 /* GraphRequestPiggyBackManagerProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A033E225F1545B00D19DE9 /* GraphRequestPiggyBackManagerProviderTests.swift */; };
F4A0341925F15D9500D19DE9 /* EventBindingManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A0341825F15D9500D19DE9 /* EventBindingManagerTests.swift */; };
F4A0346E25F1985300D19DE9 /* TestSwizzler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A0346D25F1985200D19DE9 /* TestSwizzler.swift */; };
F4A035C225F2C6F800D19DE9 /* SampleEventBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A035C125F2C6F800D19DE9 /* SampleEventBindings.swift */; };
F4A2B1A525E56BF400DE13D9 /* FBSDKDeviceButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9E16AA1CB46C7D00C8B68F /* FBSDKDeviceButton.h */; settings = {ATTRIBUTES = (Public, ); }; };
F4A2B1BA25E56BF500DE13D9 /* FBSDKDeviceButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9E16AA1CB46C7D00C8B68F /* FBSDKDeviceButton.h */; settings = {ATTRIBUTES = (Public, ); }; };
F4A2B1D525E56C3200DE13D9 /* FBSDKDeviceViewControllerBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D10A68B1CB38D5D00F42AC1 /* FBSDKDeviceViewControllerBase.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1295,6 +1298,13 @@
remoteGlobalIDString = 81B71CFC1D19C87400933E93;
remoteInfo = "FBSDKCoreKit-Dynamic";
};
F46873E425F7D0D500ABD912 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F4A4069C25F4522500F0E578 /* TestTools.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = F4E5F09025F4354900D67852;
remoteInfo = TestTools;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@@ -1581,7 +1591,7 @@
C4FC99D7202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKHybridAppEventsScriptMessageHandler.h; sourceTree = "<group>"; };
C4FC99D8202CD5590038C5ED /* FBSDKHybridAppEventsScriptMessageHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKHybridAppEventsScriptMessageHandler.m; sourceTree = "<group>"; };
C51121C920A27EF50041DC94 /* FBSDKEventBindingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKEventBindingTests.m; sourceTree = "<group>"; };
C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBSDKSampleEventBinding.swift; sourceTree = "<group>"; };
C51121CA20A27EF50041DC94 /* SampleRawRemoteEventBindings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleRawRemoteEventBindings.swift; sourceTree = "<group>"; };
C511229F20A4BCEB0041DC94 /* FBSDKApplicationDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKApplicationDelegateTests.m; sourceTree = "<group>"; };
C5188DF8222F388400F4D8BC /* FBSDKApplicationObserving.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKApplicationObserving.h; sourceTree = "<group>"; };
C5188E312231C4B500F4D8BC /* FBSDKBridgeAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSDKBridgeAPI.h; sourceTree = "<group>"; };
@@ -1708,6 +1718,11 @@
F4A033C425F13E1300D19DE9 /* UrlSessionTaskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlSessionTaskTests.swift; sourceTree = "<group>"; };
F4A033D325F13FD700D19DE9 /* SampleUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleUrl.swift; sourceTree = "<group>"; };
F4A033E225F1545B00D19DE9 /* GraphRequestPiggyBackManagerProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphRequestPiggyBackManagerProviderTests.swift; sourceTree = "<group>"; };
F4A0341825F15D9500D19DE9 /* EventBindingManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBindingManagerTests.swift; sourceTree = "<group>"; };
F4A0342725F164EF00D19DE9 /* FBSDKSwizzling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSwizzling.h; sourceTree = "<group>"; };
F4A0342825F1650900D19DE9 /* FBSDKSwizzler+Swizzling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FBSDKSwizzler+Swizzling.h"; sourceTree = "<group>"; };
F4A0346D25F1985200D19DE9 /* TestSwizzler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSwizzler.swift; sourceTree = "<group>"; };
F4A035C125F2C6F800D19DE9 /* SampleEventBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleEventBindings.swift; sourceTree = "<group>"; };
F4A11B4824E36E5200D4C010 /* FBSDKTypeUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKTypeUtility.h; sourceTree = "<group>"; };
F4A11B4924E36E5200D4C010 /* FBSDKURLSessionTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKURLSessionTask.h; sourceTree = "<group>"; };
F4A11B4A24E36E5200D4C010 /* FBSDKSafeCast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKSafeCast.h; sourceTree = "<group>"; };
@@ -2111,6 +2126,7 @@
9D195CC61B9FE2E000BD6BEC /* FBSDKContainerViewController.h */,
9D195CC71B9FE2E000BD6BEC /* FBSDKContainerViewController.m */,
890414731A647D2E00617215 /* FBSDKCoreKit+Internal.h */,
F4A02F8C25EEAF6600D19DE9 /* FBSDKDataPersisting.h */,
520223F51D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.h */,
520223F61D83C8D200CE0AB5 /* FBSDKDeviceRequestsHelper.m */,
81C969301C114723002FC037 /* FBSDKDynamicFrameworkLoader.h */,
@@ -2141,15 +2157,16 @@
F4EC338525E6A86B0003C0CF /* FBSDKSettingsProtocol.h */,
C5696FA1209CCEB3009C931F /* FBSDKSwizzler.h */,
C5696FA2209CCEB4009C931F /* FBSDKSwizzler.m */,
F4A0342825F1650900D19DE9 /* FBSDKSwizzler+Swizzling.h */,
F4A0342725F164EF00D19DE9 /* FBSDKSwizzling.h */,
52963A9D215993C100C7B252 /* FBSDKURL_Internal.h */,
5DB7ADCA22EA59E60012E8CB /* Instrument */,
89FB8C3D1A84237A003CAE60 /* Network */,
F4A02F9425EEB09500D19DE9 /* NSUserDefaults+FBSDKDataPersisting.h */,
89830F251A7805A900226ABB /* ServerConfiguration */,
9D32A8381A69941A000A936D /* TokenCaching */,
89FB8C3E1A842393003CAE60 /* UI */,
89FB8C431A842644003CAE60 /* WebDialog */,
F4A02F9425EEB09500D19DE9 /* NSUserDefaults+FBSDKDataPersisting.h */,
F4A02F8C25EEAF6600D19DE9 /* FBSDKDataPersisting.h */,
);
path = Internal;
sourceTree = "<group>";
@@ -2633,7 +2650,7 @@
C51121C720A27EF50041DC94 /* Codeless */ = {
isa = PBXGroup;
children = (
C51121CA20A27EF50041DC94 /* FBSDKSampleEventBinding.swift */,
F4A0341825F15D9500D19DE9 /* EventBindingManagerTests.swift */,
C51121C920A27EF50041DC94 /* FBSDKEventBindingTests.m */,
);
path = Codeless;
@@ -2762,6 +2779,14 @@
path = ML;
sourceTree = "<group>";
};
F46873DA25F7D0D500ABD912 /* Products */ = {
isa = PBXGroup;
children = (
F46873E525F7D0D500ABD912 /* TestTools.framework */,
);
name = Products;
sourceTree = "<group>";
};
F487DBCA231EBD8B008416A9 /* Swift */ = {
isa = PBXGroup;
children = (
@@ -2830,8 +2855,10 @@
F496E58E24E49DBC006231A2 /* SampleAppEvents.swift */,
F48AD347257700B80052C056 /* SampleAuthenticationToken.swift */,
F4A0308E25EEE42300D19DE9 /* SampleError.swift */,
F4A035C125F2C6F800D19DE9 /* SampleEventBindings.swift */,
F4EB31BA2540A1B800736B67 /* SampleGraphRequestConnection.swift */,
F4EB31D22540A35800736B67 /* SampleGraphRequests.swift */,
C51121CA20A27EF50041DC94 /* SampleRawRemoteEventBindings.swift */,
F4A02FF625EEC1CB00D19DE9 /* SampleRawRemoteGateKeeper.swift */,
F4A0304825EECC6800D19DE9 /* SampleRawRemoteGateKeeperList.swift */,
F4EB33CD2542323500736B67 /* SampleRawRemotePermissions.swift */,
@@ -2852,6 +2879,7 @@
F40D25F225E41C71004681FC /* TestOperationQueue.swift */,
F494285C25829C72008FE009 /* TestSessionProvider.swift */,
F4EC339425E6ABE60003C0CF /* TestSettings.swift */,
F4A0346D25F1985200D19DE9 /* TestSwizzler.swift */,
F496E5FD24E5D0D5006231A2 /* TestTokenCache.swift */,
F43F397C25D48C4B0046027A /* TestURLSessionProxy.swift */,
F4F6036F25D5D3A30053D3E7 /* TestURLSessionProxyFactory.swift */,
@@ -3779,6 +3807,10 @@
ProductGroup = 9D9947101A9531B5003375EC /* Products */;
ProjectRef = 9D99470F1A9531B5003375EC /* OCMock.xcodeproj */;
},
{
ProductGroup = F46873DA25F7D0D500ABD912 /* Products */;
ProjectRef = F4A4069C25F4522500F0E578 /* TestTools.xcodeproj */;
},
);
projectRoot = "";
targets = (
@@ -3849,6 +3881,13 @@
remoteRef = 9D9947281A9531B5003375EC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
F46873E525F7D0D500ABD912 /* TestTools.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = TestTools.framework;
remoteRef = F46873E425F7D0D500ABD912 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@@ -4403,6 +4442,7 @@
C77C07A62486BAC600345460 /* FBSDKRestrictiveDataFilterTests.m in Sources */,
F4E50155243648A100C99262 /* FBSDKServerConfigurationFixtures.m in Sources */,
894C0B4A1A70524F009137EF /* FBSDKBase64Tests.swift in Sources */,
F4A0346E25F1985300D19DE9 /* TestSwizzler.swift in Sources */,
894C0B3A1A702EBE009137EF /* FBSDKBridgeAPIProtocolNativeV1Tests.m in Sources */,
F4E50156243648A100C99262 /* FBSDKServerConfigurationTests.m in Sources */,
F48AD3252576FEB20052C056 /* FBSDKAuthenticationTokenTests.m in Sources */,
@@ -4436,7 +4476,7 @@
89C8B19C1A8D7A27009B07F5 /* FBSDKUtilityTests.swift in Sources */,
F441DDE725DDDB670092C8F6 /* GraphRequestConnectionFactoryTests.swift in Sources */,
F41979282475A20E003007CC /* FBSDKTypeUtilityTests.m in Sources */,
C51121CC20A27EF50041DC94 /* FBSDKSampleEventBinding.swift in Sources */,
C51121CC20A27EF50041DC94 /* SampleRawRemoteEventBindings.swift in Sources */,
F916581524F6BAB200BB759A /* FBSDKSKAdNetworkConversionConfigurationTests.m in Sources */,
F4A826B724EC6FAD00EB2CD4 /* FBSDKProfileTests.m in Sources */,
F4CCDC8825E958320057FE5F /* GateKeeperManagerTests.swift in Sources */,
@@ -4449,8 +4489,10 @@
C6E3622625E03FD0007D555D /* FBSDKAuthenticationTokenHeaderTests.m in Sources */,
C6C5CBDC2581875600AA3BB3 /* FBSDKAuthenticationStatusUtilityTests.m in Sources */,
F4EB31D32540A35800736B67 /* SampleGraphRequests.swift in Sources */,
F4A0341925F15D9500D19DE9 /* EventBindingManagerTests.swift in Sources */,
F402B062255C645600473083 /* FakeLoginManager.m in Sources */,
F441DC3E25DC97E60092C8F6 /* TestGraphRequestConnection.swift in Sources */,
F4A035C225F2C6F800D19DE9 /* SampleEventBindings.swift in Sources */,
F4D80B582565D6B3008CCAF0 /* FBSDKRestrictiveDataTests.m in Sources */,
F4EB33CE2542323500736B67 /* SampleRawRemotePermissions.swift in Sources */,
5DBB0447227FEF700009E0A6 /* FBSDKBasicUtilityTests.m in Sources */,

View File

@@ -22,13 +22,17 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(EventBindingManager)
@interface FBSDKEventBindingManager : NSObject
- (FBSDKEventBindingManager*)initWithJSON:(NSDictionary*)dict;
- (instancetype)initWithJSON:(NSDictionary*)dict;
- (void)updateBindings:(NSArray *)bindings;
+ (NSArray *)parseArray:(NSArray *)array;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@@ -29,7 +29,7 @@
#import "FBSDKCodelessPathComponent.h"
#import "FBSDKEventBinding.h"
#import "FBSDKInternalUtility.h"
#import "FBSDKSwizzler.h"
#import "FBSDKSwizzler+Swizzling.h"
#import "FBSDKViewHierarchy.h"
#import "FBSDKViewHierarchyMacros.h"
@@ -37,29 +37,36 @@
#define ReactNativeTouchEndEventName @"touchEnd"
#define ReactNativeClassRCTTextView "RCTTextView"
#define ReactNativeClassRCTImageView "RCTImageVIew"
#define ReactNativeClassRCTImageView "RCTImageView"
#define ReactNativeClassRCTTouchEvent "RCTTouchEvent"
#define ReactNativeClassRCTTouchHandler "RCTTouchHandler"
@interface FBSDKEventBindingManager ()
{
BOOL isStarted;
NSMutableDictionary *reactBindings;
NSSet *validClasses;
BOOL hasReactNative;
NSArray *eventBindings;
BOOL _isStarted;
NSMutableDictionary *_reactBindings;
NSSet *_validClasses;
BOOL _hasReactNative;
NSArray *_eventBindings;
Class<FBSDKSwizzling> _swizzler;
}
@end
@implementation FBSDKEventBindingManager
- (id)init
- (instancetype)init
{
return [self initWithSwizzler:FBSDKSwizzler.class];
}
- (instancetype)initWithSwizzler:(Class<FBSDKSwizzling>)swizzling;
{
self = [super init];
if (self) {
isStarted = NO;
hasReactNative = NO;
reactBindings = [NSMutableDictionary dictionary];
_swizzler = swizzling;
_isStarted = NO;
_hasReactNative = NO;
_reactBindings = [NSMutableDictionary dictionary];
NSMutableSet *classes = [NSMutableSet set];
[classes addObject:[UIControl class]];
@@ -68,7 +75,7 @@
// ReactNative
Class classRCTRootView = objc_lookUpClass(ReactNativeClassRCTRootView);
if (classRCTRootView != nil) {
hasReactNative = YES;
_hasReactNative = YES;
Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView);
Class classRCTTextView = objc_lookUpClass(ReactNativeClassRCTTextView);
Class classRCTImageView = objc_lookUpClass(ReactNativeClassRCTImageView);
@@ -82,7 +89,28 @@
[classes addObject:classRCTImageView];
}
}
validClasses = [NSSet setWithSet:classes];
_validClasses = [NSSet setWithSet:classes];
}
return self;
}
- (instancetype)initWithJSON:(NSDictionary *)dict
{
return [self initWithSwizzler:FBSDKSwizzler.class json:dict];
}
- (instancetype)initWithSwizzler:(Class<FBSDKSwizzling>)swizzling
json:(NSDictionary *)dict
{
if ((self = [super init])) {
_swizzler = swizzling;
NSArray *eventBindingsDict = [FBSDKTypeUtility arrayValue:dict[@"event_bindings"]];
NSMutableArray *bindings = [NSMutableArray array];
for (NSDictionary *d in eventBindingsDict) {
FBSDKEventBinding *e = [[FBSDKEventBinding alloc] initWithJSON:d];
[FBSDKTypeUtility array:bindings addObject:e];
}
_eventBindings = [bindings copy];
}
return self;
}
@@ -99,65 +127,52 @@
return [result copy];
}
- (FBSDKEventBindingManager *)initWithJSON:(NSDictionary *)dict
{
if ((self = [super init])) {
NSArray *eventBindingsDict = [FBSDKTypeUtility arrayValue:dict[@"event_bindings"]];
NSMutableArray *bindings = [NSMutableArray array];
for (NSDictionary *d in eventBindingsDict) {
FBSDKEventBinding *e = [[FBSDKEventBinding alloc] initWithJSON:d];
[FBSDKTypeUtility array:bindings addObject:e];
}
eventBindings = [bindings copy];
}
return self;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
- (void)start
{
if (isStarted) {
if (self.isStarted) {
return;
}
if (0 == eventBindings.count) {
if (0 == self.eventBindings.count) {
return;
}
isStarted = YES;
self.isStarted = YES;
void (^blockToWindow)(id view) = ^(id view) {
[self matchView:view delegate:nil];
};
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:[UIControl class]
withBlock:blockToWindow named:@"map_control"];
withBlock:blockToWindow
named:@"map_control"];
// ReactNative
if (hasReactNative) { // If app is built via ReactNative
if (self.hasReactNative) { // If app is built via ReactNative
Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView);
Class classRCTTextView = objc_lookUpClass(ReactNativeClassRCTTextView);
Class classRCTImageView = objc_lookUpClass(ReactNativeClassRCTImageView);
Class classRCTTouchHandler = objc_lookUpClass(ReactNativeClassRCTTouchHandler);
// All react-native views would be added tp RCTRootView, so no need to check didMoveToWindow
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:classRCTView
withBlock:blockToWindow
named:@"match_react_native"];
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:classRCTTextView
withBlock:blockToWindow
named:@"match_react_native"];
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow)
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:classRCTImageView
withBlock:blockToWindow
named:@"match_react_native"];
// RCTTouchHandler handles with touch events, like touchEnd and uses RCTEventDispather to dispatch events, so we can check _updateAndDispatchTouches to fire events
[FBSDKSwizzler swizzleSelector:@selector(_updateAndDispatchTouches:eventName:) onClass:classRCTTouchHandler withBlock:^(id touchHandler, SEL command, id touches, id eventName) {
[self.swizzler swizzleSelector:@selector(_updateAndDispatchTouches:eventName:) onClass:classRCTTouchHandler withBlock:^(id touchHandler, SEL command, id touches, id eventName) {
if ([touches isKindOfClass:[NSSet class]] && [eventName isKindOfClass:[NSString class]]) {
@try {
NSString *reactEventName = (NSString *)eventName;
@@ -174,7 +189,7 @@
}
targetView = targetView.superview;
}
FBSDKEventBinding *eventBinding = self->reactBindings[reactTag];
FBSDKEventBinding *eventBinding = self->_reactBindings[reactTag];
if (reactTag != nil && eventBinding != nil) {
[eventBinding trackEvent:nil];
}
@@ -198,7 +213,7 @@
[self matchView:tableView delegate:delegate];
};
[FBSDKSwizzler swizzleSelector:@selector(setDelegate:)
[self.swizzler swizzleSelector:@selector(setDelegate:)
onClass:[UITableView class]
withBlock:tableViewBlock
named:@"match_table_view"];
@@ -213,7 +228,7 @@
[self matchView:collectionView delegate:delegate];
};
[FBSDKSwizzler swizzleSelector:@selector(setDelegate:)
[self.swizzler swizzleSelector:@selector(setDelegate:)
onClass:[UICollectionView class]
withBlock:collectionViewBlock
named:@"handle_collection_view"];
@@ -221,7 +236,7 @@
- (void)rematchBindings
{
if (0 == eventBindings.count) {
if (0 == self.eventBindings.count) {
return;
}
@@ -239,7 +254,7 @@
for (UIView *subview in view.subviews) {
BOOL isValidClass = NO;
for (Class cls in validClasses) {
for (Class cls in self.validClasses) {
if ([subview isKindOfClass:cls]) {
isValidClass = YES;
break;
@@ -271,10 +286,12 @@
// check if the view is matched to any event
- (void)matchView:(UIView *)view delegate:(id)delegate
{
if (0 == eventBindings.count) {
if (0 == self.eventBindings.count) {
return;
}
__weak Class<FBSDKSwizzling> weakSwizzler = self.swizzler;
__block BOOL hasReactNative = self.hasReactNative;
fb_dispatch_on_main_thread(^{
if (![view window]) {
return;
@@ -285,7 +302,7 @@
fb_dispatch_on_default_thread(^{
if ([view isKindOfClass:[UIControl class]]) {
UIControl *control = (UIControl *)view;
for (FBSDKEventBinding *binding in self->eventBindings) {
for (FBSDKEventBinding *binding in self->_eventBindings) {
if ([FBSDKEventBinding isPath:binding.path matchViewPath:path]) {
fb_dispatch_on_main_thread(^{
[control addTarget:binding
@@ -295,15 +312,15 @@
break;
}
}
} else if (self->hasReactNative
} else if (hasReactNative
&& [view respondsToSelector:@selector(reactTag)]) {
for (FBSDKEventBinding *binding in self->eventBindings) {
for (FBSDKEventBinding *binding in self->_eventBindings) {
if ([FBSDKEventBinding isPath:binding.path matchViewPath:path]) {
fb_dispatch_on_main_thread(^{
if (view) {
NSNumber *reactTag = [FBSDKViewHierarchy getViewReactTag:view];
if (reactTag != nil) {
[FBSDKTypeUtility dictionary:self->reactBindings setObject:binding forKey:reactTag];
[FBSDKTypeUtility dictionary:self->_reactBindings setObject:binding forKey:reactTag];
}
}
});
@@ -314,7 +331,7 @@
&& [delegate conformsToProtocol:@protocol(UITableViewDelegate)]) {
fb_dispatch_on_default_thread(^{
NSMutableSet *matchedBindings = [NSMutableSet set];
for (FBSDKEventBinding *binding in self->eventBindings) {
for (FBSDKEventBinding *binding in self->_eventBindings) {
if (binding.path.count > 1) {
NSArray *shortPath = [binding.path
subarrayWithRange:NSMakeRange(0, binding.path.count - 1)];
@@ -338,17 +355,17 @@
}
});
};
[FBSDKSwizzler swizzleSelector:@selector(tableView:didSelectRowAtIndexPath:)
onClass:[delegate class]
withBlock:block
named:@"handle_table_view"];
[weakSwizzler swizzleSelector:@selector(tableView:didSelectRowAtIndexPath:)
onClass:[delegate class]
withBlock:block
named:@"handle_table_view"];
}
});
} else if ([view isKindOfClass:[UICollectionView class]]
&& [delegate conformsToProtocol:@protocol(UICollectionViewDelegate)]) {
fb_dispatch_on_default_thread(^{
NSMutableSet *matchedBindings = [NSMutableSet set];
for (FBSDKEventBinding *binding in self->eventBindings) {
for (FBSDKEventBinding *binding in self->_eventBindings) {
if (binding.path.count > 1) {
NSArray *shortPath = [binding.path
subarrayWithRange:NSMakeRange(0, binding.path.count - 1)];
@@ -372,10 +389,10 @@
}
});
};
[FBSDKSwizzler swizzleSelector:@selector(collectionView:didSelectItemAtIndexPath:)
onClass:[delegate class]
withBlock:block
named:@"handle_collection_view"];
[weakSwizzler swizzleSelector:@selector(collectionView:didSelectItemAtIndexPath:)
onClass:[delegate class]
withBlock:block
named:@"handle_collection_view"];
}
});
}
@@ -386,11 +403,11 @@
#pragma clang diagnostic pop
- (void)updateBindings:(NSArray *)bindings
{
if (eventBindings.count > 0 && eventBindings.count == bindings.count) {
if (self.eventBindings.count > 0 && self.eventBindings.count == bindings.count) {
// Check whether event bindings are the same
BOOL isSame = YES;
for (int i = 0; i < eventBindings.count; i++) {
if (![[FBSDKTypeUtility array:eventBindings objectAtIndex:i] isEqualToBinding:[FBSDKTypeUtility array:bindings objectAtIndex:i]]) {
for (int i = 0; i < self.eventBindings.count; i++) {
if (![[FBSDKTypeUtility array:self.eventBindings objectAtIndex:i] isEqualToBinding:[FBSDKTypeUtility array:bindings objectAtIndex:i]]) {
isSame = NO;
break;
}
@@ -401,9 +418,9 @@
}
}
eventBindings = bindings;
[reactBindings removeAllObjects];
if (!isStarted) {
self.eventBindings = bindings;
[self.reactBindings removeAllObjects];
if (!self.isStarted) {
[self start];
}
@@ -412,6 +429,57 @@
});
}
- (BOOL)isStarted
{
return _isStarted;
}
- (void)setIsStarted:(BOOL)isStarted
{
_isStarted = isStarted;
}
- (Class<FBSDKSwizzling>)swizzler
{
return _swizzler;
}
- (BOOL)hasReactNative
{
return _hasReactNative;
}
- (NSSet *)validClasses
{
return _validClasses;
}
- (NSArray<FBSDKEventBinding *> *)eventBindings
{
return _eventBindings;
}
- (NSMutableDictionary *)reactBindings
{
return _reactBindings;
}
- (void)setEventBindings:(NSArray<FBSDKEventBinding *> *)bindings
{
_eventBindings = bindings;
}
#if DEBUG
#if FBSDKTEST
- (void)setReactBindings:(NSMutableDictionary *)bindings
{
_reactBindings = bindings;
}
#endif
#endif
@end
#endif

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKSwizzling.h"
NS_ASSUME_NONNULL_BEGIN
// Default conformance to the Swizzling interface
@interface FBSDKSwizzler (Swizzling) <FBSDKSwizzling>
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Swizzling)
@protocol FBSDKSwizzling
+ (void)swizzleSelector:(SEL)aSelector onClass:(Class)aClass withBlock:(swizzleBlock)block named:(NSString *)aName;
@end
NS_ASSUME_NONNULL_END

View File

@@ -22,6 +22,8 @@
#import "FBSDKBridgeAPI+Testing.h"
#import "FBSDKCloseIcon.h"
#import "FBSDKEventDeactivationManager.h"
#import "FBSDKEventBinding.h"
#import "FBSDKEventBindingManager.h"
#import "FBSDKMath.h"
#import "FBSDKSKAdNetworkEvent.h"
#import "FBSDKSKAdNetworkRule.h"
@@ -52,6 +54,8 @@
// Data Persistance
#import "FBSDKDataPersisting.h"
#import "NSUserDefaults+FBSDKDataPersisting.h"
// Swizzling
#import "FBSDKSwizzling.h"
NS_ASSUME_NONNULL_BEGIN
@@ -142,4 +146,20 @@ NS_SWIFT_NAME(parse(result:error:));
@end
@interface FBSDKEventBindingManager (Testing)
@property (nonatomic) BOOL isStarted;
@property (nonatomic, nullable) NSMutableDictionary *reactBindings;
@property (nonatomic, readonly) NSSet *validClasses;
@property (nonatomic) BOOL hasReactNative;
@property (nonatomic, nullable) NSArray<FBSDKEventBinding *> *eventBindings;
@property (nonatomic, nullable, readonly) Class<FBSDKSwizzling> swizzler;
- (instancetype)initWithSwizzler:(Class<FBSDKSwizzling>)swizzling;
- (instancetype)initWithSwizzler:(Class<FBSDKSwizzling>)swizzling
json:(NSDictionary *)dict;
- (void)start;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,300 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import XCTest
@objc
protocol WindowMoving {
func didMoveToWindow()
}
class EventBindingManagerTests: XCTestCase {
var manager: EventBindingManager! // swiftlint:disable:this implicitly_unwrapped_optional
var bindings = SampleEventBindingList.valid
let expectedEvidenceWithoutReactNative = [
SwizzleEvidence(selector: #selector(UIControl.didMoveToWindow), class: UIControl.self),
SwizzleEvidence(selector: #selector(setter: UITableView.delegate), class: UITableView.self),
SwizzleEvidence(selector: #selector(setter: UICollectionView.delegate), class: UICollectionView.self)
]
override func setUp() {
super.setUp()
registerReactNativeClasses()
TestSwizzler.reset()
manager = EventBindingManager(
swizzler: TestSwizzler.self,
json: SampleRawRemoteEventBindings.sampleDictionary
)
}
// MARK: - Dependencies
func testCreatingDefault() {
XCTAssertTrue(
EventBindingManager().swizzler is Swizzler.Type,
"Should be created with the expected concrete swizzling type by default"
)
}
func testCreatingDefaultWithJson() {
manager = EventBindingManager(json: ["some": "stuff"])
XCTAssertTrue(
manager.swizzler is Swizzler.Type,
"Should be created with the expected concrete swizzling type by default"
)
}
func testCreatingCustom() {
manager = EventBindingManager(swizzler: TestSwizzler.self)
XCTAssertTrue(
manager.swizzler is TestSwizzler.Type,
"Should be created with the provided swizzling type"
)
}
func testCreatingCustomWithJson() {
manager = EventBindingManager(
swizzler: TestSwizzler.self,
json: ["some": "stuff"]
)
XCTAssertTrue(
manager.swizzler is TestSwizzler.Type,
"Should be created with the provided swizzling type"
)
}
func testCreatingWithReactNativeUnavailable() {
deregisterReactNativeClasses()
manager = EventBindingManager(swizzler: TestSwizzler.self)
XCTAssertFalse(
manager.hasReactNative,
"Should detect if react native is in the runtime"
)
manager.validClasses.forEach { item in
switch item.base {
case is UIControl.Type: break
case is UICollectionView.Type: break
case is UITableView.Type: break
default: XCTFail("\(item.description) should not be considered valid")
}
}
}
func testCreatingWithReactNativeAvailable() {
manager = EventBindingManager(swizzler: TestSwizzler.self)
XCTAssertTrue(
manager.hasReactNative,
"Should detect if react native is in the runtime"
)
let classNames = Set(manager.validClasses.map { $0.description })
let expected = Set([
"RCTTextView",
"RCTImageView",
"UITableView",
"UIControl",
"UICollectionView",
"RCTView"
])
XCTAssertEqual(
classNames,
expected,
"Should have a known set of valid classes"
)
}
// MARK: - Starting
func testStartingWithEventsWhenStarted() {
manager.isStarted = true
manager.start()
XCTAssertTrue(TestSwizzler.evidence.isEmpty)
}
func testStartingWithEventsWhenNotStarted() {
manager.isStarted = false
manager.start()
XCTAssertEqual(
TestSwizzler.evidence,
expectedEvidenceWithoutReactNative
)
}
func testStartingWithoutEventsWhenStarted() {
manager.eventBindings = []
manager.isStarted = true
manager.start()
XCTAssertTrue(TestSwizzler.evidence.isEmpty)
}
func testStartingWithoutEventsWhenNotStarted() {
manager.eventBindings = []
manager.isStarted = false
manager.start()
XCTAssertTrue(TestSwizzler.evidence.isEmpty)
}
func testStartingWithReactNativeClasses() {
manager = EventBindingManager(swizzler: TestSwizzler.self)
// Updating bindings will actually call start if it is not in a started state
manager.isStarted = true
manager.updateBindings(bindings)
manager.isStarted = false
manager.start()
// This is ugly but there is no good way to do this in Swift.
// swiftlint:disable line_length
let expected = "["
.appending(
[
"FBSDKCoreKitTests.SwizzleEvidence(selector: didMoveToWindow, class: UIControl, block: Optional((Function)))",
"FBSDKCoreKitTests.SwizzleEvidence(selector: didMoveToWindow, class: RCTView, block: Optional((Function)))",
"FBSDKCoreKitTests.SwizzleEvidence(selector: didMoveToWindow, class: RCTTextView, block: Optional((Function)))",
"FBSDKCoreKitTests.SwizzleEvidence(selector: didMoveToWindow, class: RCTImageView, block: Optional((Function)))"
]
.joined(separator: ", ")
)
.appending("]")
// swiftlint:enable line_length
XCTAssertEqual(
TestSwizzler.evidence.description,
expected
)
}
// MARK: - Updating Bindings
func testUpdatingEventBindings() {
manager = EventBindingManager(swizzler: TestSwizzler.self)
manager.reactBindings = ["foo": EventBinding()]
manager.updateBindings(bindings)
XCTAssertEqual(
manager.eventBindings,
bindings,
"Should persist updated event bindings"
)
XCTAssertEqual(
manager.reactBindings?.count,
0,
"Should clear react bindings when updating bindings"
)
}
func testUpdatingEventBindingsWithIdenticalBindings() {
manager = EventBindingManager(swizzler: TestSwizzler.self)
manager.updateBindings(bindings)
manager.updateBindings(bindings)
XCTAssertEqual(
manager.eventBindings,
bindings,
"Should not add duplicate event bindings"
)
}
func testUpdatingEventBindingsWithRecreatedBindings() {
manager.updateBindings(bindings)
guard let eventBindings = manager.eventBindings else {
return XCTFail("There should be event bindings on the manager")
}
eventBindings.enumerated().forEach { pair in
let (index, element) = pair
XCTAssertTrue(
element.isEqual(to: bindings[index]),
"Bindings with the same information should be considered equal"
)
}
}
func testUpdatingEventBindingsWithDifferentBindingsDifferentNumberOfBindings() {
bindings.append(EventBinding())
manager.updateBindings(bindings)
XCTAssertEqual(
manager.eventBindings,
bindings,
"Setting a different number of bindings from the number of stored bindings should overwrite the stored bindings"
)
}
func testUpdatingEventBindingsWithDifferentBindingsSameNumberOfBindings() {
let binding = SampleEventBinding.valid(withName: "foo")
let binding2 = SampleEventBinding.valid(withName: "bar")
let binding3 = SampleEventBinding.valid(withName: "baz")
manager.updateBindings([binding, binding2])
manager.updateBindings([binding2, binding3])
XCTAssertEqual(
manager.eventBindings,
[binding2, binding3],
"Setting different bindings from the stored bindings should overwrite the stored bindings"
)
}
func testUpdatingBindingsStarts() {
manager.isStarted = false
manager = EventBindingManager(swizzler: TestSwizzler.self)
manager.updateBindings(bindings)
XCTAssertTrue(
manager.isStarted,
"Updating bindings should start the manager if it is not started"
)
}
// MARK: - Helpers
func registerReactNativeClasses() {
if objc_lookUpClass("RCTRootView") == nil,
let reactRootView: AnyClass = objc_allocateClassPair(NSObject.self, "RCTRootView", 0),
let imageView: AnyClass = objc_allocateClassPair(NSObject.self, "RCTImageView", 0),
let textView: AnyClass = objc_allocateClassPair(NSObject.self, "RCTTextView", 0),
let view: AnyClass = objc_allocateClassPair(NSObject.self, "RCTView", 0) {
objc_registerClassPair(reactRootView)
objc_registerClassPair(imageView)
objc_registerClassPair(textView)
objc_registerClassPair(view)
}
}
func deregisterReactNativeClasses() {
if let rootViewClass = objc_lookUpClass("RCTRootView"),
let imageView = objc_lookUpClass("RCTImageView"),
let textView = objc_lookUpClass("RCTTextView"),
let view = objc_lookUpClass("RCTView") {
objc_disposeClassPair(rootViewClass)
objc_disposeClassPair(imageView)
objc_disposeClassPair(textView)
objc_disposeClassPair(view)
}
}
}

View File

@@ -50,7 +50,7 @@
[super setUp];
eventBindingManager = [[FBSDKEventBindingManager alloc]
initWithJSON:[FBSDKSampleEventBinding getSampleDictionary]];
initWithJSON:[SampleRawRemoteEventBindings sampleDictionary]];
window = [[UIWindow alloc] init];
UIViewController *vc = [[UIViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
@@ -94,7 +94,7 @@
- (void)testMatching
{
NSArray *bindings = [FBSDKEventBindingManager parseArray:[FBSDKSampleEventBinding getSampleDictionary][@"event_bindings"]];
NSArray *bindings = [FBSDKEventBindingManager parseArray:[SampleRawRemoteEventBindings sampleDictionary][@"event_bindings"]];
FBSDKEventBinding *binding = bindings[0];
XCTAssertTrue([FBSDKEventBinding isViewMatchPath:stepper path:binding.path]);
@@ -116,7 +116,7 @@
- (void)testEventBindingEquation
{
NSArray *bindings = [FBSDKEventBindingManager parseArray:[FBSDKSampleEventBinding getSampleDictionary][@"event_bindings"]];
NSArray *bindings = [FBSDKEventBindingManager parseArray:[SampleRawRemoteEventBindings sampleDictionary][@"event_bindings"]];
XCTAssertTrue([bindings[0] isEqualToBinding:bindings[0]]);
XCTAssertFalse([bindings[0] isEqualToBinding:bindings[1]]);
}
@@ -124,7 +124,7 @@
- (void)testParsing
{
for (int i = 0; i < 100; i++) {
NSDictionary *sampleData = [FBSDKSampleEventBinding getSampleDictionary];
NSDictionary *sampleData = [SampleRawRemoteEventBindings sampleDictionary];
[FBSDKEventBindingManager parseArray:@[[Fuzzer randomizeWithJson:sampleData]]];
}
}

View File

@@ -1,140 +0,0 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@objcMembers
class FBSDKSampleEventBinding: NSObject {
class func getSampleDictionary() -> [String: Any] { // swiftlint:disable:this function_body_length
return [
"event_bindings": [
[
"event_name": "Quantity Changed",
"event_type": "click",
"app_version": "1.2",
"path": [
[
"class_name": "UIWindow"
],
[
"class_name": "UITabBarController"
],
[
"class_name": "UINavigationController"
],
[
"class_name": "UIViewController"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIStepper"
],
],
],
[
"event_name": "Add To Cart",
"event_type": "click",
"app_version": "1.2",
"path": [
[
"class_name": "UIViewController"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIButton",
"text": "Buy",
],
],
"parameters": [
[
"parameter_name": "price",
"path_type": "relative",
"path": [
[
"class_name": ".."
],
[
"class_name": "UILabel",
"index": 2,
]
]
]
]
],
[
"event_name": "Purchase",
"event_type": "click",
"app_version": "1.2",
"path": [
[
"class_name": "UIWindow"
],
[
"class_name": "UITabBarController"
],
[
"class_name": "UINavigationController"
],
[
"class_name": "UIViewController"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIButton",
"text": "Confirm",
],
],
"parameters": [
[
"parameter_name": "price",
"path_type": "relative",
"path": [
[
"class_name": ".."
],
[
"class_name": "UIStackView"
],
[
"class_name": "UILabel",
"index": 0,
],
],
],
[
"parameter_name": "action",
"path_type": "relative",
"path": [
[
"class_name": "."
]
]
]
]
]
]
]
}
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@objcMembers
class SampleEventBinding: NSObject {
static func valid(withName name: String) -> EventBinding {
// swiftlint:disable:next force_unwrapping
return EventBinding(json: SampleRawRemoteEventBindings.rawBinding(name: name))!
}
}
@objcMembers
class SampleEventBindingList: NSObject {
static var valid: [EventBinding] {
return SampleRawRemoteEventBindings.bindings.compactMap { EventBinding(json: $0) }
}
}

View File

@@ -0,0 +1,158 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@objcMembers
class SampleRawRemoteEventBindings: NSObject {
static var sampleDictionary: [String: Any] {
return [
"event_bindings": bindings
]
}
static var bindings: [[String: Any]] {
return [
[
"event_name": "Quantity Changed",
"event_type": "click",
"app_version": "1.2",
"path": [
[
"class_name": "UIWindow"
],
[
"class_name": "UITabBarController"
],
[
"class_name": "UINavigationController"
],
[
"class_name": "UIViewController"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIStepper"
],
],
],
[
"event_name": "Add To Cart",
"event_type": "click",
"app_version": "1.2",
"path": [
[
"class_name": "UIViewController"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIButton",
"text": "Buy",
],
],
"parameters": [
[
"parameter_name": "price",
"path_type": "relative",
"path": [
[
"class_name": ".."
],
[
"class_name": "UILabel",
"index": 2,
]
]
]
]
],
[
"event_name": "Purchase",
"event_type": "click",
"app_version": "1.2",
"path": [
[
"class_name": "UIWindow"
],
[
"class_name": "UITabBarController"
],
[
"class_name": "UINavigationController"
],
[
"class_name": "UIViewController"
],
[
"class_name": "UIStackView"
],
[
"class_name": "UIButton",
"text": "Confirm",
],
],
"parameters": [
[
"parameter_name": "price",
"path_type": "relative",
"path": [
[
"class_name": ".."
],
[
"class_name": "UIStackView"
],
[
"class_name": "UILabel",
"index": 0,
],
],
],
[
"parameter_name": "action",
"path_type": "relative",
"path": [
[
"class_name": "."
]
]
]
]
]
]
}
static func rawBinding(name: String) -> [String: Any] {
return [
"event_name": name,
"event_type": "click",
"app_version": "1.2",
"path": [
[
"class_name": "UIWindow"
]
]
]
}
}

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
struct SwizzleEvidence: Equatable {
let selector: Selector
let `class`: AnyClass
let block: swizzleBlock?
init(
selector: Selector,
`class`: AnyClass,
block: swizzleBlock? = nil
) {
self.selector = selector
self.`class` = `class`
self.block = block
}
static func == (lhs: SwizzleEvidence, rhs: SwizzleEvidence) -> Bool {
return lhs.selector == rhs.selector && lhs.class == rhs.class
}
}
class TestSwizzler: Swizzling {
static var evidence = [SwizzleEvidence]()
static func swizzleSelector(
_ aSelector: Selector,
on aClass: AnyClass,
with block: @escaping swizzleBlock,
named aName: String
) {
evidence.append(.init(selector: aSelector, class: aClass, block: block))
}
static func reset() {
evidence = []
}
}

View File

@@ -16,8 +16,8 @@
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import TestTools
import FBSDKCoreKit
import TestTools
import XCTest
class ProfilePictureViewTests: XCTestCase {

View File

@@ -153,7 +153,6 @@
C6E1332C259173B000B611C6 /* FBSDKPermission.h in Headers */ = {isa = PBXBuildFile; fileRef = C6E1332B259173AF00B611C6 /* FBSDKPermission.h */; };
C6E1333F259174A500B611C6 /* FBSDKPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = C6E1333E259174A500B611C6 /* FBSDKPermission.m */; };
C6E63D2D24F822620015FC7C /* FBSDKReferralManagerLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = C6E63D2C24F822620015FC7C /* FBSDKReferralManagerLogger.m */; };
F401866525F45BD100C57ADE /* TestTools.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F401866425F45BD100C57ADE /* TestTools.framework */; };
F40BFD56257852E5007B85AC /* LoginButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40BFD55257852E5007B85AC /* LoginButtonTests.swift */; };
F462DC0E23B958E000FFCECA /* FBSDKLoginKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9DB8DE1A114E500086167B /* FBSDKLoginKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
F462DC0F23B958E100FFCECA /* FBSDKLoginKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9DB8DE1A114E500086167B /* FBSDKLoginKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -309,6 +308,20 @@
remoteGlobalIDString = 818EB4211D1A283100252851;
remoteInfo = "FBSDKLoginKit-Dynamic";
};
F468741F25F7D39A00ABD912 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F468741B25F7D39A00ABD912 /* TestTools.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = F4E5F09025F4354900D67852;
remoteInfo = TestTools;
};
F468742825F7D3AF00ABD912 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F468741B25F7D39A00ABD912 /* TestTools.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = F4E5F08F25F4354900D67852;
remoteInfo = TestTools;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@@ -410,7 +423,6 @@
C6E63D2224F8219A0015FC7C /* FBSDKReferralManagerLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSDKReferralManagerLogger.h; sourceTree = "<group>"; };
C6E63D2C24F822620015FC7C /* FBSDKReferralManagerLogger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSDKReferralManagerLogger.m; sourceTree = "<group>"; };
F401864D25F45B8100C57ADE /* TestTools.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TestTools.xcodeproj; path = ../../../TestTools/TestTools.xcodeproj; sourceTree = "<group>"; };
F401866425F45BD100C57ADE /* TestTools.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TestTools.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F40BFD55257852E5007B85AC /* LoginButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginButtonTests.swift; sourceTree = "<group>"; };
F4647451256AD09000502449 /* FBSDKLoginKitTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FBSDKLoginKitTests-Bridging-Header.h"; sourceTree = "<group>"; };
F4647478256AEF8E00502449 /* NonceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceTests.swift; sourceTree = "<group>"; };
@@ -419,6 +431,7 @@
F464749B256B048500502449 /* LoginConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginConfigurationTests.swift; sourceTree = "<group>"; };
F4665CC0258D684300981FDE /* Fuzzer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fuzzer.swift; sourceTree = "<group>"; };
F4665CD2258D687500981FDE /* FakeGraphRequestConnectionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeGraphRequestConnectionProvider.swift; sourceTree = "<group>"; };
F468741B25F7D39A00ABD912 /* TestTools.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TestTools.xcodeproj; path = ../TestTools/TestTools.xcodeproj; sourceTree = "<group>"; };
F46FA659245347570060C902 /* FBLoginButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBLoginButton.swift; sourceTree = "<group>"; };
F46FA65C245347820060C902 /* LoginManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginManager.swift; sourceTree = "<group>"; };
F47FB44E25805E7900922543 /* FBSDKLoginManagerLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSDKLoginManagerLoggerTests.m; sourceTree = "<group>"; };
@@ -467,7 +480,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F401866525F45BD100C57ADE /* TestTools.framework in Frameworks */,
6B4453551A8AF2B9004C4437 /* Accounts.framework in Frameworks */,
9DCDEC9E1A82ECA1002A2A66 /* libOCMock.a in Frameworks */,
9DEE5C941A671B5500D750E1 /* XCTest.framework in Frameworks */,
@@ -566,7 +578,7 @@
893774AF1A3B815D00BE2807 /* Frameworks */ = {
isa = PBXGroup;
children = (
F401866425F45BD100C57ADE /* TestTools.framework */,
F468741B25F7D39A00ABD912 /* TestTools.xcodeproj */,
0BD1F3512093A1A3005109EC /* UIKit.framework */,
0BD1F34C2093A1A0005109EC /* CoreGraphics.framework */,
8118B6781D0A5B9400962084 /* FBSDKCoreKit.xcodeproj */,
@@ -729,6 +741,14 @@
path = Helpers;
sourceTree = "<group>";
};
F468741C25F7D39A00ABD912 /* Products */ = {
isa = PBXGroup;
children = (
F468742025F7D39A00ABD912 /* TestTools.framework */,
);
name = Products;
sourceTree = "<group>";
};
F46FA66E24535E3F0060C902 /* Swift */ = {
isa = PBXGroup;
children = (
@@ -932,7 +952,7 @@
buildRules = (
);
dependencies = (
F45045AE25F425FF0032CC7E /* PBXTargetDependency */,
F468742925F7D3AF00ABD912 /* PBXTargetDependency */,
F410D0552370CD0F005B9318 /* PBXTargetDependency */,
F410D0532370CD0B005B9318 /* PBXTargetDependency */,
);
@@ -985,6 +1005,10 @@
ProductGroup = 8118B6791D0A5B9400962084 /* Products */;
ProjectRef = 8118B6781D0A5B9400962084 /* FBSDKCoreKit.xcodeproj */;
},
{
ProductGroup = F468741C25F7D39A00ABD912 /* Products */;
ProjectRef = F468741B25F7D39A00ABD912 /* TestTools.xcodeproj */;
},
);
projectRoot = "";
targets = (
@@ -1063,6 +1087,13 @@
remoteRef = 817490FE1D1C6C11006E09DF /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
F468742025F7D39A00ABD912 /* TestTools.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = TestTools.framework;
remoteRef = F468741F25F7D39A00ABD912 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@@ -1299,9 +1330,10 @@
target = 818EB4211D1A283100252851 /* FBSDKLoginKit-Dynamic */;
targetProxy = F410D0542370CD0F005B9318 /* PBXContainerItemProxy */;
};
F45045AE25F425FF0032CC7E /* PBXTargetDependency */ = {
F468742925F7D3AF00ABD912 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = F45045AD25F425FF0032CC7E /* TestTools */;
name = TestTools;
targetProxy = F468742825F7D3AF00ABD912 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@@ -1521,13 +1553,6 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
F45045AD25F425FF0032CC7E /* TestTools */ = {
isa = XCSwiftPackageProductDependency;
productName = TestTools;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 9D9DB8D01A114E500086167B /* Project object */;
}

View File

@@ -434,6 +434,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/TestTools/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -457,6 +458,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/TestTools/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = (

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>