/** * 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 #import #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 @end @implementation RCTTestInjectedModule @synthesize bridge = _bridge; @synthesize methodQueue = _methodQueue; RCT_EXPORT_MODULE() @end @interface RCTTestCustomInitModule : NSObject @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 @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 @property (nonatomic, assign) BOOL exportedConstants; @property (nonatomic, assign) BOOL exportedConstantsOnMainThread; @end @implementation RCTTestExportConstantsModule @synthesize bridge = _bridge; @synthesize methodQueue = _methodQueue; RCT_EXPORT_MODULE() - (NSDictionary *)constantsToExport { _exportedConstants = YES; _exportedConstantsOnMainThread = [NSThread isMainThread]; return @{ @"foo": @"bar" }; } @end @interface RCTLazyInitModule : NSObject @end @implementation RCTLazyInitModule @synthesize bridge = _bridge; @synthesize methodQueue = _methodQueue; RCT_EXPORT_MODULE() @end @interface RCTModuleInitTests : XCTestCase { 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 jsExecutor = _bridge.batchedBridge.javaScriptExecutor; [_bridge invalidate]; RUN_RUNLOOP_WHILE(jsExecutor.isValid); _bridge = nil; } - (void)moduleDidInit:(NSNotification *)note { id 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