mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-28 20:25:33 +08:00
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
This commit is contained in:
committed by
Facebook Github Bot 5
parent
0db22f184d
commit
35da174339
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1300627E1B59179B0043FE5A /* RCTGzipTests.m */; };
|
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1300627E1B59179B0043FE5A /* RCTGzipTests.m */; };
|
||||||
|
13129DD41C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */; };
|
||||||
1323F1891C04AB9F0091BED0 /* bunny.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1851C04AB9F0091BED0 /* bunny.png */; };
|
1323F1891C04AB9F0091BED0 /* bunny.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1851C04AB9F0091BED0 /* bunny.png */; };
|
||||||
1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1861C04AB9F0091BED0 /* flux@3x.png */; };
|
1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1861C04AB9F0091BED0 /* flux@3x.png */; };
|
||||||
1323F18B1C04AB9F0091BED0 /* hawk.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1871C04AB9F0091BED0 /* hawk.png */; };
|
1323F18B1C04AB9F0091BED0 /* hawk.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1871C04AB9F0091BED0 /* hawk.png */; };
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
|
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
|
||||||
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
|
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
|
||||||
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
|
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
|
||||||
|
134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */; };
|
||||||
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; };
|
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; };
|
||||||
138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; };
|
138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; };
|
||||||
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
|
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
|
||||||
@@ -178,6 +180,7 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
004D289E1AAF61C70097A701 /* UIExplorerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
004D289E1AAF61C70097A701 /* UIExplorerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
1300627E1B59179B0043FE5A /* RCTGzipTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGzipTests.m; sourceTree = "<group>"; };
|
1300627E1B59179B0043FE5A /* RCTGzipTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGzipTests.m; sourceTree = "<group>"; };
|
||||||
|
13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleInitNotificationRaceTests.m; sourceTree = "<group>"; };
|
||||||
1323F1851C04AB9F0091BED0 /* bunny.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bunny.png; sourceTree = "<group>"; };
|
1323F1851C04AB9F0091BED0 /* bunny.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bunny.png; sourceTree = "<group>"; };
|
||||||
1323F1861C04AB9F0091BED0 /* flux@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flux@3x.png"; sourceTree = "<group>"; };
|
1323F1861C04AB9F0091BED0 /* flux@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flux@3x.png"; sourceTree = "<group>"; };
|
||||||
1323F1871C04AB9F0091BED0 /* hawk.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = hawk.png; sourceTree = "<group>"; };
|
1323F1871C04AB9F0091BED0 /* hawk.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = hawk.png; sourceTree = "<group>"; };
|
||||||
@@ -188,6 +191,7 @@
|
|||||||
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
|
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
|
||||||
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
||||||
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||||
|
134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleInitTests.m; sourceTree = "<group>"; };
|
||||||
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = "<group>"; };
|
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = "<group>"; };
|
||||||
138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = ../../Libraries/CameraRoll/RCTCameraRoll.xcodeproj; sourceTree = "<group>"; };
|
138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = ../../Libraries/CameraRoll/RCTCameraRoll.xcodeproj; sourceTree = "<group>"; };
|
||||||
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = "<group>"; };
|
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = "<group>"; };
|
||||||
@@ -422,6 +426,8 @@
|
|||||||
144D21231B2204C5006DB32B /* RCTImageUtilTests.m */,
|
144D21231B2204C5006DB32B /* RCTImageUtilTests.m */,
|
||||||
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
|
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
|
||||||
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */,
|
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */,
|
||||||
|
134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */,
|
||||||
|
13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */,
|
||||||
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */,
|
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */,
|
||||||
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */,
|
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */,
|
||||||
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
|
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
|
||||||
@@ -882,7 +888,9 @@
|
|||||||
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */,
|
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */,
|
||||||
1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */,
|
1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */,
|
||||||
1497CFAE1B21F5E400C1F8F2 /* RCTJSCExecutorTests.m in Sources */,
|
1497CFAE1B21F5E400C1F8F2 /* RCTJSCExecutorTests.m in Sources */,
|
||||||
|
13129DD41C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m in Sources */,
|
||||||
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */,
|
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */,
|
||||||
|
134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */,
|
||||||
1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */,
|
1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */,
|
||||||
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */,
|
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */,
|
||||||
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */,
|
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */,
|
||||||
@@ -1276,4 +1284,4 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,8 @@
|
|||||||
|
|
||||||
@implementation TestExecutor
|
@implementation TestExecutor
|
||||||
|
|
||||||
|
@synthesize valid = _valid;
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (void)setUp {}
|
- (void)setUp {}
|
||||||
@@ -55,7 +57,7 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
- (BOOL)isValid
|
- (BOOL)isValid
|
||||||
{
|
{
|
||||||
return YES;
|
return _valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
|
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
|
||||||
@@ -98,7 +100,10 @@ RCT_EXPORT_MODULE()
|
|||||||
onComplete(nil);
|
onComplete(nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidate {}
|
- (void)invalidate
|
||||||
|
{
|
||||||
|
_valid = NO;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -132,7 +137,7 @@ RCT_EXPORT_MODULE(TestModule)
|
|||||||
[_bridge invalidate];
|
[_bridge invalidate];
|
||||||
[_bridge setUp];
|
[_bridge setUp];
|
||||||
|
|
||||||
_jsExecutor = [_bridge.batchedBridge valueForKey:@"javaScriptExecutor"];
|
_jsExecutor = _bridge.batchedBridge.javaScriptExecutor;
|
||||||
XCTAssertNotNil(_jsExecutor);
|
XCTAssertNotNil(_jsExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +148,7 @@ RCT_EXPORT_MODULE(TestModule)
|
|||||||
_testMethodCalled = NO;
|
_testMethodCalled = NO;
|
||||||
|
|
||||||
[_bridge invalidate];
|
[_bridge invalidate];
|
||||||
|
RUN_RUNLOOP_WHILE(_jsExecutor.isValid);
|
||||||
_bridge = nil;
|
_bridge = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* The examples provided by Facebook are for non-commercial testing and
|
||||||
|
* evaluation purposes only.
|
||||||
|
*
|
||||||
|
* Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* 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 NON INFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK 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 <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTBridge+Private.h"
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
#import "RCTUIManager.h"
|
||||||
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
|
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||||
|
{ \
|
||||||
|
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \
|
||||||
|
while ((CONDITION)) { \
|
||||||
|
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \
|
||||||
|
if ([timeout timeIntervalSinceNow] <= 0) { \
|
||||||
|
XCTFail(@"Runloop timed out before condition was met"); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be declared before RCTTestCustomSetBridgeModule in order to trigger the
|
||||||
|
// race condition that we are testing for - namely that the
|
||||||
|
// RCTDidInitializeModuleNotification for RCTTestViewManager gets sent before
|
||||||
|
// setBridge: is called on RCTTestCustomSetBridgeModule
|
||||||
|
@interface RCTTestViewManager : RCTViewManager
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTTestViewManager
|
||||||
|
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
@synthesize methodQueue = _methodQueue;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (void)setBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
_bridge = bridge;
|
||||||
|
(void)[_bridge uiManager]; // Needed to trigger a race condition
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)customDirectEventTypes
|
||||||
|
{
|
||||||
|
return @[@"foo"];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTNotificationObserverModule : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL didDetectViewManagerInit;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTNotificationObserverModule
|
||||||
|
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (void)setBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
_bridge = bridge;
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didInitViewManager:) name:RCTDidInitializeModuleNotification object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didInitViewManager:(NSNotification *)note
|
||||||
|
{
|
||||||
|
id<RCTBridgeModule> module = note.userInfo[@"module"];
|
||||||
|
if ([module isKindOfClass:[RCTTestViewManager class]]) {
|
||||||
|
_didDetectViewManagerInit = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTModuleInitNotificationRaceTests : XCTestCase <RCTBridgeDelegate>
|
||||||
|
{
|
||||||
|
RCTBridge *_bridge;
|
||||||
|
RCTNotificationObserverModule *_notificationObserver;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTModuleInitNotificationRaceTests
|
||||||
|
|
||||||
|
- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)extraModulesForBridge:(__unused RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
return @[_notificationObserver];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
|
||||||
|
_notificationObserver = [RCTNotificationObserverModule new];
|
||||||
|
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown
|
||||||
|
{
|
||||||
|
[super tearDown];
|
||||||
|
|
||||||
|
_notificationObserver = nil;
|
||||||
|
id<RCTJavaScriptExecutor> jsExecutor = _bridge.batchedBridge.javaScriptExecutor;
|
||||||
|
[_bridge invalidate];
|
||||||
|
RUN_RUNLOOP_WHILE(jsExecutor.isValid);
|
||||||
|
_bridge = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testViewManagerNotInitializedBeforeSetBridgeModule
|
||||||
|
{
|
||||||
|
RUN_RUNLOOP_WHILE(!_notificationObserver.didDetectViewManagerInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
254
Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m
Normal file
254
Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
/**
|
||||||
|
* The examples provided by Facebook are for non-commercial testing and
|
||||||
|
* evaluation purposes only.
|
||||||
|
*
|
||||||
|
* Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* 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 NON INFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK 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 <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTBridge+Private.h"
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
|
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||||
|
{ \
|
||||||
|
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \
|
||||||
|
while ((CONDITION)) { \
|
||||||
|
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \
|
||||||
|
if ([timeout timeIntervalSinceNow] <= 0) { \
|
||||||
|
XCTFail(@"Runloop timed out before condition was met"); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTTestInjectedModule : NSObject <RCTBridgeModule>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTTestInjectedModule
|
||||||
|
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
@synthesize methodQueue = _methodQueue;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTTestCustomInitModule : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL initializedOnMainThread;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTTestCustomInitModule
|
||||||
|
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
@synthesize methodQueue = _methodQueue;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (id)init
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_initializedOnMainThread = [NSThread isMainThread];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTTestCustomSetBridgeModule : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL setBridgeOnMainThread;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTTestCustomSetBridgeModule
|
||||||
|
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
@synthesize methodQueue = _methodQueue;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (void)setBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
_bridge = bridge;
|
||||||
|
_setBridgeOnMainThread = [NSThread isMainThread];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTTestExportConstantsModule : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL exportedConstants;
|
||||||
|
@property (nonatomic, assign) BOOL exportedConstantsOnMainThread;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTTestExportConstantsModule
|
||||||
|
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
@synthesize methodQueue = _methodQueue;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (NSDictionary<NSString *, id> *)constantsToExport
|
||||||
|
{
|
||||||
|
_exportedConstants = YES;
|
||||||
|
_exportedConstantsOnMainThread = [NSThread isMainThread];
|
||||||
|
return @{ @"foo": @"bar" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTLazyInitModule : NSObject <RCTBridgeModule>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTLazyInitModule
|
||||||
|
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
@synthesize methodQueue = _methodQueue;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCTModuleInitTests : XCTestCase <RCTBridgeDelegate>
|
||||||
|
{
|
||||||
|
RCTBridge *_bridge;
|
||||||
|
BOOL _injectedModuleInitNotificationSent;
|
||||||
|
BOOL _customInitModuleNotificationSent;
|
||||||
|
BOOL _customSetBridgeModuleNotificationSent;
|
||||||
|
BOOL _exportConstantsModuleNotificationSent;
|
||||||
|
BOOL _lazyInitModuleNotificationSent;
|
||||||
|
BOOL _lazyInitModuleNotificationSentOnMainThread;
|
||||||
|
BOOL _viewManagerModuleNotificationSent;
|
||||||
|
RCTTestInjectedModule *_injectedModule;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTModuleInitTests
|
||||||
|
|
||||||
|
- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)extraModulesForBridge:(__unused RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
return @[_injectedModule];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moduleDidInit:) name:RCTDidInitializeModuleNotification object:nil];
|
||||||
|
|
||||||
|
_injectedModuleInitNotificationSent = NO;
|
||||||
|
_customInitModuleNotificationSent = NO;
|
||||||
|
_customSetBridgeModuleNotificationSent = NO;
|
||||||
|
_exportConstantsModuleNotificationSent = NO;
|
||||||
|
_lazyInitModuleNotificationSent = NO;
|
||||||
|
_viewManagerModuleNotificationSent = NO;
|
||||||
|
_injectedModule = [RCTTestInjectedModule new];
|
||||||
|
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown
|
||||||
|
{
|
||||||
|
[super tearDown];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:RCTDidInitializeModuleNotification object:nil];
|
||||||
|
|
||||||
|
id<RCTJavaScriptExecutor> jsExecutor = _bridge.batchedBridge.javaScriptExecutor;
|
||||||
|
[_bridge invalidate];
|
||||||
|
RUN_RUNLOOP_WHILE(jsExecutor.isValid);
|
||||||
|
_bridge = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)moduleDidInit:(NSNotification *)note
|
||||||
|
{
|
||||||
|
id<RCTBridgeModule> module = note.userInfo[@"module"];
|
||||||
|
if ([module isKindOfClass:[RCTTestInjectedModule class]]) {
|
||||||
|
_injectedModuleInitNotificationSent = YES;
|
||||||
|
} else if ([module isKindOfClass:[RCTTestCustomInitModule class]]) {
|
||||||
|
_customInitModuleNotificationSent = YES;
|
||||||
|
} else if ([module isKindOfClass:[RCTTestCustomSetBridgeModule class]]) {
|
||||||
|
_customSetBridgeModuleNotificationSent = YES;
|
||||||
|
} else if ([module isKindOfClass:[RCTTestExportConstantsModule class]]) {
|
||||||
|
_exportConstantsModuleNotificationSent = YES;
|
||||||
|
} else if ([module isKindOfClass:[RCTLazyInitModule class]]) {
|
||||||
|
_lazyInitModuleNotificationSent = YES;
|
||||||
|
_lazyInitModuleNotificationSentOnMainThread = [NSThread isMainThread];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInjectedModulesInitializedDuringBridgeInit
|
||||||
|
{
|
||||||
|
XCTAssertTrue(_injectedModuleInitNotificationSent);
|
||||||
|
XCTAssertEqual(_injectedModule, [_bridge moduleForClass:[RCTTestInjectedModule class]]);
|
||||||
|
XCTAssertEqual(_injectedModule.bridge, _bridge.batchedBridge);
|
||||||
|
XCTAssertNotNil(_injectedModule.methodQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCustomInitModuleInitializedAtBridgeStartup
|
||||||
|
{
|
||||||
|
RUN_RUNLOOP_WHILE(!_customInitModuleNotificationSent);
|
||||||
|
XCTAssertTrue(_customInitModuleNotificationSent);
|
||||||
|
RCTTestCustomInitModule *module = [_bridge moduleForClass:[RCTTestCustomInitModule class]];
|
||||||
|
XCTAssertTrue(module.initializedOnMainThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCustomSetBridgeModuleInitializedAtBridgeStartup
|
||||||
|
{
|
||||||
|
RUN_RUNLOOP_WHILE(!_customSetBridgeModuleNotificationSent);
|
||||||
|
XCTAssertTrue(_customSetBridgeModuleNotificationSent);
|
||||||
|
RCTTestCustomSetBridgeModule *module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]];
|
||||||
|
XCTAssertTrue(module.setBridgeOnMainThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testExportConstantsModuleInitializedAtBridgeStartup
|
||||||
|
{
|
||||||
|
RUN_RUNLOOP_WHILE(!_exportConstantsModuleNotificationSent);
|
||||||
|
XCTAssertTrue(_exportConstantsModuleNotificationSent);
|
||||||
|
RCTTestExportConstantsModule *module = [_bridge moduleForClass:[RCTTestExportConstantsModule class]];
|
||||||
|
RUN_RUNLOOP_WHILE(!module.exportedConstants);
|
||||||
|
XCTAssertTrue(module.exportedConstants);
|
||||||
|
XCTAssertTrue(module.exportedConstantsOnMainThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testLazyInitModuleNotInitializedDuringBridgeInit
|
||||||
|
{
|
||||||
|
XCTAssertFalse(_lazyInitModuleNotificationSent);
|
||||||
|
|
||||||
|
__block RCTLazyInitModule *module;
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
module = [_bridge moduleForClass:[RCTLazyInitModule class]];
|
||||||
|
});
|
||||||
|
|
||||||
|
RUN_RUNLOOP_WHILE(!module);
|
||||||
|
XCTAssertTrue(_lazyInitModuleNotificationSent);
|
||||||
|
XCTAssertFalse(_lazyInitModuleNotificationSentOnMainThread);
|
||||||
|
XCTAssertNotNil(module);
|
||||||
|
XCTAssertEqual(module.bridge, _bridge.batchedBridge);
|
||||||
|
XCTAssertNotNil(module.methodQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -44,6 +44,7 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||||||
@interface RCTBatchedBridge : RCTBridge
|
@interface RCTBatchedBridge : RCTBridge
|
||||||
|
|
||||||
@property (nonatomic, weak) RCTBridge *parentBridge;
|
@property (nonatomic, weak) RCTBridge *parentBridge;
|
||||||
|
@property (nonatomic, weak) id<RCTJavaScriptExecutor> javaScriptExecutor;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -52,7 +53,6 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||||||
BOOL _loading;
|
BOOL _loading;
|
||||||
BOOL _valid;
|
BOOL _valid;
|
||||||
BOOL _wasBatchActive;
|
BOOL _wasBatchActive;
|
||||||
__weak id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
|
||||||
NSMutableArray<dispatch_block_t> *_pendingCalls;
|
NSMutableArray<dispatch_block_t> *_pendingCalls;
|
||||||
NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName;
|
NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName;
|
||||||
NSArray<RCTModuleData *> *_moduleDataByID;
|
NSArray<RCTModuleData *> *_moduleDataByID;
|
||||||
|
|||||||
@@ -54,6 +54,11 @@
|
|||||||
|
|
||||||
@interface RCTBridge (RCTBatchedBridge)
|
@interface RCTBridge (RCTBatchedBridge)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for unit testing, to detect when executor has been invalidated.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, weak, readonly) id<RCTJavaScriptExecutor> javaScriptExecutor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by RCTModuleData to register the module for frame updates after it is
|
* Used by RCTModuleData to register the module for frame updates after it is
|
||||||
* lazily initialized.
|
* lazily initialized.
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ void RCTRegisterModule(Class moduleClass)
|
|||||||
NSString *RCTBridgeModuleNameForClass(Class cls)
|
NSString *RCTBridgeModuleNameForClass(Class cls)
|
||||||
{
|
{
|
||||||
#if RCT_DEV
|
#if RCT_DEV
|
||||||
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)], @"Bridge module classes must conform to RCTBridgeModule");
|
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)],
|
||||||
|
@"Bridge module `%@` does not conform to RCTBridgeModule", cls);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NSString *name = [cls moduleName];
|
NSString *name = [cls moduleName];
|
||||||
@@ -104,34 +105,33 @@ dispatch_queue_t RCTJSThread;
|
|||||||
// Set up JS thread
|
// Set up JS thread
|
||||||
RCTJSThread = (id)kCFNull;
|
RCTJSThread = (id)kCFNull;
|
||||||
|
|
||||||
#if RCT_DEBUG
|
if (RCT_DEBUG && !RCTRunningInTestEnvironment()) {
|
||||||
|
|
||||||
// Set up module classes
|
// Set up module classes
|
||||||
static unsigned int classCount;
|
static unsigned int classCount;
|
||||||
Class *classes = objc_copyClassList(&classCount);
|
Class *classes = objc_copyClassList(&classCount);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < classCount; i++)
|
for (unsigned int i = 0; i < classCount; i++)
|
||||||
{
|
|
||||||
Class cls = classes[i];
|
|
||||||
Class superclass = cls;
|
|
||||||
while (superclass)
|
|
||||||
{
|
{
|
||||||
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
Class cls = classes[i];
|
||||||
|
Class superclass = cls;
|
||||||
|
while (superclass)
|
||||||
{
|
{
|
||||||
if (![RCTModuleClasses containsObject:cls]) {
|
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
||||||
RCTLogWarn(@"Class %@ was not exported. Did you forget to use "
|
{
|
||||||
"RCT_EXPORT_MODULE()?", cls);
|
if (![RCTModuleClasses containsObject:cls]) {
|
||||||
|
RCTLogWarn(@"Class %@ was not exported. Did you forget to use "
|
||||||
|
"RCT_EXPORT_MODULE()?", cls);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
superclass = class_getSuperclass(superclass);
|
||||||
}
|
}
|
||||||
superclass = class_getSuperclass(superclass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(classes);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(classes);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#import "RCTBridge+Private.h"
|
#import "RCTBridge+Private.h"
|
||||||
#import "RCTDefines.h"
|
#import "RCTDefines.h"
|
||||||
#import "RCTRedBox.h"
|
#import "RCTRedBox.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
static NSString *const RCTLogFunctionStack = @"RCTLogFunctionStack";
|
static NSString *const RCTLogFunctionStack = @"RCTLogFunctionStack";
|
||||||
|
|
||||||
@@ -226,8 +227,10 @@ void _RCTLogNativeInternal(RCTLogLevel level, const char *fileName, int lineNumb
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log to JS executor
|
if (!RCTRunningInTestEnvironment()) {
|
||||||
[[RCTBridge currentBridge] logMessage:message level:level ? @(RCTLogLevels[level]) : @"info"];
|
// Log to JS executor
|
||||||
|
[[RCTBridge currentBridge] logMessage:message level:level ? @(RCTLogLevels[level]) : @"info"];
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -238,4 +238,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
|||||||
_methodQueue = nil;
|
_methodQueue = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *)description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"<%@: %p; name=\"%@\">", [self class], self, self.name];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ RCT_EXPORT_MODULE()
|
|||||||
if (!sourceCodeModule.scriptURL) {
|
if (!sourceCodeModule.scriptURL) {
|
||||||
if (!sourceCodeModule) {
|
if (!sourceCodeModule) {
|
||||||
RCTLogWarn(@"RCTSourceCode module not found");
|
RCTLogWarn(@"RCTSourceCode module not found");
|
||||||
} else {
|
} else if (!RCTRunningInTestEnvironment()) {
|
||||||
RCTLogWarn(@"RCTSourceCode module scriptURL has not been set");
|
RCTLogWarn(@"RCTSourceCode module scriptURL has not been set");
|
||||||
}
|
}
|
||||||
} else if (!sourceCodeModule.scriptURL.fileURL) {
|
} else if (!sourceCodeModule.scriptURL.fileURL) {
|
||||||
|
|||||||
Reference in New Issue
Block a user