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:
parent
0db22f184d
commit
35da174339
|
@ -8,6 +8,7 @@
|
|||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1861C04AB9F0091BED0 /* flux@3x.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 */; };
|
||||
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.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 */; };
|
||||
138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; };
|
||||
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
|
||||
|
@ -178,6 +180,7 @@
|
|||
/* Begin PBXFileReference section */
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -188,6 +191,7 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -422,6 +426,8 @@
|
|||
144D21231B2204C5006DB32B /* RCTImageUtilTests.m */,
|
||||
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
|
||||
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */,
|
||||
134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */,
|
||||
13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */,
|
||||
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */,
|
||||
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */,
|
||||
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
|
||||
|
@ -882,7 +888,9 @@
|
|||
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */,
|
||||
1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */,
|
||||
1497CFAE1B21F5E400C1F8F2 /* RCTJSCExecutorTests.m in Sources */,
|
||||
13129DD41C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m in Sources */,
|
||||
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */,
|
||||
134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */,
|
||||
1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */,
|
||||
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */,
|
||||
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */,
|
||||
|
@ -1276,4 +1284,4 @@
|
|||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
|
||||
@implementation TestExecutor
|
||||
|
||||
@synthesize valid = _valid;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)setUp {}
|
||||
|
@ -55,7 +57,7 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return YES;
|
||||
return _valid;
|
||||
}
|
||||
|
||||
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
|
||||
|
@ -98,7 +100,10 @@ RCT_EXPORT_MODULE()
|
|||
onComplete(nil);
|
||||
}
|
||||
|
||||
- (void)invalidate {}
|
||||
- (void)invalidate
|
||||
{
|
||||
_valid = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
@ -132,7 +137,7 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
[_bridge invalidate];
|
||||
[_bridge setUp];
|
||||
|
||||
_jsExecutor = [_bridge.batchedBridge valueForKey:@"javaScriptExecutor"];
|
||||
_jsExecutor = _bridge.batchedBridge.javaScriptExecutor;
|
||||
XCTAssertNotNil(_jsExecutor);
|
||||
}
|
||||
|
||||
|
@ -143,6 +148,7 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
_testMethodCalled = NO;
|
||||
|
||||
[_bridge invalidate];
|
||||
RUN_RUNLOOP_WHILE(_jsExecutor.isValid);
|
||||
_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
|
|
@ -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
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *parentBridge;
|
||||
@property (nonatomic, weak) id<RCTJavaScriptExecutor> javaScriptExecutor;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -52,7 +53,6 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||
BOOL _loading;
|
||||
BOOL _valid;
|
||||
BOOL _wasBatchActive;
|
||||
__weak id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||
NSMutableArray<dispatch_block_t> *_pendingCalls;
|
||||
NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName;
|
||||
NSArray<RCTModuleData *> *_moduleDataByID;
|
||||
|
|
|
@ -54,6 +54,11 @@
|
|||
|
||||
@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
|
||||
* lazily initialized.
|
||||
|
|
|
@ -68,7 +68,8 @@ void RCTRegisterModule(Class moduleClass)
|
|||
NSString *RCTBridgeModuleNameForClass(Class cls)
|
||||
{
|
||||
#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
|
||||
|
||||
NSString *name = [cls moduleName];
|
||||
|
@ -104,34 +105,33 @@ dispatch_queue_t RCTJSThread;
|
|||
// Set up JS thread
|
||||
RCTJSThread = (id)kCFNull;
|
||||
|
||||
#if RCT_DEBUG
|
||||
if (RCT_DEBUG && !RCTRunningInTestEnvironment()) {
|
||||
|
||||
// Set up module classes
|
||||
static unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
// Set up module classes
|
||||
static unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
|
||||
for (unsigned int i = 0; i < classCount; i++)
|
||||
{
|
||||
Class cls = classes[i];
|
||||
Class superclass = cls;
|
||||
while (superclass)
|
||||
for (unsigned int i = 0; i < classCount; i++)
|
||||
{
|
||||
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
||||
Class cls = classes[i];
|
||||
Class superclass = cls;
|
||||
while (superclass)
|
||||
{
|
||||
if (![RCTModuleClasses containsObject:cls]) {
|
||||
RCTLogWarn(@"Class %@ was not exported. Did you forget to use "
|
||||
"RCT_EXPORT_MODULE()?", cls);
|
||||
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
||||
{
|
||||
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 "RCTDefines.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
static NSString *const RCTLogFunctionStack = @"RCTLogFunctionStack";
|
||||
|
||||
|
@ -226,8 +227,10 @@ void _RCTLogNativeInternal(RCTLogLevel level, const char *fileName, int lineNumb
|
|||
});
|
||||
}
|
||||
|
||||
// Log to JS executor
|
||||
[[RCTBridge currentBridge] logMessage:message level:level ? @(RCTLogLevels[level]) : @"info"];
|
||||
if (!RCTRunningInTestEnvironment()) {
|
||||
// Log to JS executor
|
||||
[[RCTBridge currentBridge] logMessage:message level:level ? @(RCTLogLevels[level]) : @"info"];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -238,4 +238,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
|||
_methodQueue = nil;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: %p; name=\"%@\">", [self class], self, self.name];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -369,7 +369,7 @@ RCT_EXPORT_MODULE()
|
|||
if (!sourceCodeModule.scriptURL) {
|
||||
if (!sourceCodeModule) {
|
||||
RCTLogWarn(@"RCTSourceCode module not found");
|
||||
} else {
|
||||
} else if (!RCTRunningInTestEnvironment()) {
|
||||
RCTLogWarn(@"RCTSourceCode module scriptURL has not been set");
|
||||
}
|
||||
} else if (!sourceCodeModule.scriptURL.fileURL) {
|
||||
|
|
Loading…
Reference in New Issue