Skip to content

Commit 35da174

Browse files
nicklockwoodFacebook Github Bot 5
authored andcommitted
Added unit tests for module init
Summary: The module initialization process is complex and full of race conditions. This diff adds a set of unit tests that verify that modules setup happens in the correct order, and enforces all the various conditions for main/background init. Reviewed By: javache Differential Revision: D2994145 fb-gh-sync-id: 92ea84508cdeeb280ff0fb9e9b2dffa8dbc37e66 shipit-source-id: 92ea84508cdeeb280ff0fb9e9b2dffa8dbc37e66
1 parent 0db22f1 commit 35da174

10 files changed

Lines changed: 451 additions & 28 deletions

File tree

Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
/* Begin PBXBuildFile section */
1010
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1300627E1B59179B0043FE5A /* RCTGzipTests.m */; };
11+
13129DD41C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */; };
1112
1323F1891C04AB9F0091BED0 /* bunny.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1851C04AB9F0091BED0 /* bunny.png */; };
1213
1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1861C04AB9F0091BED0 /* flux@3x.png */; };
1314
1323F18B1C04AB9F0091BED0 /* hawk.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1871C04AB9F0091BED0 /* hawk.png */; };
@@ -18,6 +19,7 @@
1819
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
1920
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
2021
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
22+
134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */; };
2123
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; };
2224
138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; };
2325
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
@@ -178,6 +180,7 @@
178180
/* Begin PBXFileReference section */
179181
004D289E1AAF61C70097A701 /* UIExplorerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
180182
1300627E1B59179B0043FE5A /* RCTGzipTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGzipTests.m; sourceTree = "<group>"; };
183+
13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleInitNotificationRaceTests.m; sourceTree = "<group>"; };
181184
1323F1851C04AB9F0091BED0 /* bunny.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bunny.png; sourceTree = "<group>"; };
182185
1323F1861C04AB9F0091BED0 /* flux@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flux@3x.png"; sourceTree = "<group>"; };
183186
1323F1871C04AB9F0091BED0 /* hawk.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = hawk.png; sourceTree = "<group>"; };
@@ -188,6 +191,7 @@
188191
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
189192
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
190193
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
194+
134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleInitTests.m; sourceTree = "<group>"; };
191195
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = "<group>"; };
192196
138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = ../../Libraries/CameraRoll/RCTCameraRoll.xcodeproj; sourceTree = "<group>"; };
193197
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = "<group>"; };
@@ -422,6 +426,8 @@
422426
144D21231B2204C5006DB32B /* RCTImageUtilTests.m */,
423427
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
424428
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */,
429+
134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */,
430+
13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */,
425431
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */,
426432
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */,
427433
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
@@ -882,7 +888,9 @@
882888
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */,
883889
1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */,
884890
1497CFAE1B21F5E400C1F8F2 /* RCTJSCExecutorTests.m in Sources */,
891+
13129DD41C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m in Sources */,
885892
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */,
893+
134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */,
886894
1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */,
887895
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */,
888896
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */,
@@ -1276,4 +1284,4 @@
12761284
/* End XCConfigurationList section */
12771285
};
12781286
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
1279-
}
1287+
}

Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ @interface TestExecutor : NSObject <RCTJavaScriptExecutor>
4141

4242
@implementation TestExecutor
4343

44+
@synthesize valid = _valid;
45+
4446
RCT_EXPORT_MODULE()
4547

4648
- (void)setUp {}
@@ -55,7 +57,7 @@ - (instancetype)init
5557

5658
- (BOOL)isValid
5759
{
58-
return YES;
60+
return _valid;
5961
}
6062

6163
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
@@ -98,7 +100,10 @@ - (void)injectJSONText:(NSString *)script
98100
onComplete(nil);
99101
}
100102

101-
- (void)invalidate {}
103+
- (void)invalidate
104+
{
105+
_valid = NO;
106+
}
102107

103108
@end
104109

@@ -132,7 +137,7 @@ - (void)setUp
132137
[_bridge invalidate];
133138
[_bridge setUp];
134139

135-
_jsExecutor = [_bridge.batchedBridge valueForKey:@"javaScriptExecutor"];
140+
_jsExecutor = _bridge.batchedBridge.javaScriptExecutor;
136141
XCTAssertNotNil(_jsExecutor);
137142
}
138143

@@ -143,6 +148,7 @@ - (void)tearDown
143148
_testMethodCalled = NO;
144149

145150
[_bridge invalidate];
151+
RUN_RUNLOOP_WHILE(_jsExecutor.isValid);
146152
_bridge = nil;
147153
}
148154

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* The examples provided by Facebook are for non-commercial testing and
3+
* evaluation purposes only.
4+
*
5+
* Facebook reserves all rights not expressly granted.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
8+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
10+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
11+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
12+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
*/
14+
15+
#import <Foundation/Foundation.h>
16+
#import <XCTest/XCTest.h>
17+
18+
#import "RCTBridge.h"
19+
#import "RCTBridge+Private.h"
20+
#import "RCTBridgeModule.h"
21+
#import "RCTUtils.h"
22+
#import "RCTUIManager.h"
23+
#import "RCTViewManager.h"
24+
25+
#define RUN_RUNLOOP_WHILE(CONDITION) \
26+
{ \
27+
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \
28+
while ((CONDITION)) { \
29+
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \
30+
if ([timeout timeIntervalSinceNow] <= 0) { \
31+
XCTFail(@"Runloop timed out before condition was met"); \
32+
break; \
33+
} \
34+
} \
35+
}
36+
37+
// Must be declared before RCTTestCustomSetBridgeModule in order to trigger the
38+
// race condition that we are testing for - namely that the
39+
// RCTDidInitializeModuleNotification for RCTTestViewManager gets sent before
40+
// setBridge: is called on RCTTestCustomSetBridgeModule
41+
@interface RCTTestViewManager : RCTViewManager
42+
@end
43+
44+
@implementation RCTTestViewManager
45+
46+
@synthesize bridge = _bridge;
47+
@synthesize methodQueue = _methodQueue;
48+
49+
RCT_EXPORT_MODULE()
50+
51+
- (void)setBridge:(RCTBridge *)bridge
52+
{
53+
_bridge = bridge;
54+
(void)[_bridge uiManager]; // Needed to trigger a race condition
55+
}
56+
57+
- (NSArray<NSString *> *)customDirectEventTypes
58+
{
59+
return @[@"foo"];
60+
}
61+
62+
@end
63+
64+
65+
@interface RCTNotificationObserverModule : NSObject <RCTBridgeModule>
66+
67+
@property (nonatomic, assign) BOOL didDetectViewManagerInit;
68+
69+
@end
70+
71+
@implementation RCTNotificationObserverModule
72+
73+
@synthesize bridge = _bridge;
74+
75+
RCT_EXPORT_MODULE()
76+
77+
- (void)setBridge:(RCTBridge *)bridge
78+
{
79+
_bridge = bridge;
80+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didInitViewManager:) name:RCTDidInitializeModuleNotification object:nil];
81+
}
82+
83+
- (void)didInitViewManager:(NSNotification *)note
84+
{
85+
id<RCTBridgeModule> module = note.userInfo[@"module"];
86+
if ([module isKindOfClass:[RCTTestViewManager class]]) {
87+
_didDetectViewManagerInit = YES;
88+
}
89+
}
90+
91+
- (void)dealloc
92+
{
93+
[[NSNotificationCenter defaultCenter] removeObserver:self];
94+
}
95+
96+
@end
97+
98+
99+
@interface RCTModuleInitNotificationRaceTests : XCTestCase <RCTBridgeDelegate>
100+
{
101+
RCTBridge *_bridge;
102+
RCTNotificationObserverModule *_notificationObserver;
103+
}
104+
@end
105+
106+
@implementation RCTModuleInitNotificationRaceTests
107+
108+
- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge
109+
{
110+
return nil;
111+
}
112+
113+
- (NSArray *)extraModulesForBridge:(__unused RCTBridge *)bridge
114+
{
115+
return @[_notificationObserver];
116+
}
117+
118+
- (void)setUp
119+
{
120+
[super setUp];
121+
122+
_notificationObserver = [RCTNotificationObserverModule new];
123+
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];
124+
}
125+
126+
- (void)tearDown
127+
{
128+
[super tearDown];
129+
130+
_notificationObserver = nil;
131+
id<RCTJavaScriptExecutor> jsExecutor = _bridge.batchedBridge.javaScriptExecutor;
132+
[_bridge invalidate];
133+
RUN_RUNLOOP_WHILE(jsExecutor.isValid);
134+
_bridge = nil;
135+
}
136+
137+
- (void)testViewManagerNotInitializedBeforeSetBridgeModule
138+
{
139+
RUN_RUNLOOP_WHILE(!_notificationObserver.didDetectViewManagerInit);
140+
}
141+
142+
@end

0 commit comments

Comments
 (0)