Merge branch 'sk-chrome-xcode-tests'

* sk-chrome-xcode-tests:
  Actually report test failures when running in Chrome
  Change RealmReactTests to run with both executors
  Fix RealmReactTests to work through Chrome
  Use a default to override Chrome debugging setting
  Change ReactTests project to 4 space indentation
This commit is contained in:
Scott Kyle 2015-10-23 16:38:17 -07:00
commit 4d73c13466
4 changed files with 181 additions and 85 deletions

View File

@ -14,7 +14,7 @@
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* RealmReactTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* RealmReactTests.m */; }; 00E356F31AD99517003FC87E /* RealmReactTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* RealmReactTests.m */; };
02409E1E1BCF1F2E005F3B3E /* RealmJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 02409E1B1BCF1F2E005F3B3E /* RealmJSTests.mm */; settings = {ASSET_TAGS = (); }; }; 02409E1E1BCF1F2E005F3B3E /* RealmJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 02409E1B1BCF1F2E005F3B3E /* RealmJSTests.mm */; };
0277991C1BBF3BC600C96559 /* RealmReact.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0277991B1BBF3BB700C96559 /* RealmReact.framework */; }; 0277991C1BBF3BC600C96559 /* RealmReact.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0277991B1BBF3BB700C96559 /* RealmReact.framework */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
@ -167,7 +167,7 @@
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; }; 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
00E356EE1AD99517003FC87E /* RealmReactTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmReactTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356EE1AD99517003FC87E /* RealmReactTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmReactTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* RealmReactTests.m */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = RealmReactTests.m; sourceTree = "<group>"; tabWidth = 4; }; 00E356F21AD99517003FC87E /* RealmReactTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RealmReactTests.m; sourceTree = "<group>"; };
02409E1A1BCF1F2E005F3B3E /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ../../../RealmJSTests.h; sourceTree = "<group>"; }; 02409E1A1BCF1F2E005F3B3E /* RealmJSTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RealmJSTests.h; path = ../../../RealmJSTests.h; sourceTree = "<group>"; };
02409E1B1BCF1F2E005F3B3E /* RealmJSTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmJSTests.mm; path = ../../../RealmJSTests.mm; sourceTree = "<group>"; }; 02409E1B1BCF1F2E005F3B3E /* RealmJSTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = RealmJSTests.mm; path = ../../../RealmJSTests.mm; sourceTree = "<group>"; };
027799061BBF3BB700C96559 /* RealmJS.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RealmJS.xcodeproj; path = ../../../RealmJS.xcodeproj; sourceTree = "<group>"; }; 027799061BBF3BB700C96559 /* RealmJS.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RealmJS.xcodeproj; path = ../../../RealmJS.xcodeproj; sourceTree = "<group>"; };
@ -367,9 +367,7 @@
00E356EF1AD99517003FC87E /* RealmReactTests */, 00E356EF1AD99517003FC87E /* RealmReactTests */,
83CBBA001A601CBA00E9B192 /* Products */, 83CBBA001A601CBA00E9B192 /* Products */,
); );
indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
}; };
83CBBA001A601CBA00E9B192 /* Products */ = { 83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@ -1,11 +1,20 @@
/** ////////////////////////////////////////////////////////////////////////////
* Copyright (c) 2015-present, Facebook, Inc. //
* All rights reserved. // Copyright 2015 Realm Inc.
* //
* This source code is licensed under the BSD-style license found in the // Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. An additional grant // you may not use this file except in compliance with the License.
* of patent rights can be found in the PATENTS file in the same directory. // You may obtain a copy of the License at
*/ //
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>

View File

@ -1,61 +1,68 @@
/** ////////////////////////////////////////////////////////////////////////////
* Copyright (c) 2015-present, Facebook, Inc. //
* All rights reserved. // Copyright 2015 Realm Inc.
* //
* This source code is licensed under the BSD-style license found in the // Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. An additional grant // you may not use this file except in compliance with the License.
* of patent rights can be found in the PATENTS file in the same directory. // You may obtain a copy of the License at
*/ //
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "AppDelegate.h" #import "AppDelegate.h"
#import "RCTRootView.h" #import "RCTRootView.h"
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
{ NSURL *jsCodeLocation;
NSURL *jsCodeLocation;
/** /**
* Loading JavaScript code - uncomment the one you want. * Loading JavaScript code - uncomment the one you want.
* *
* OPTION 1 * OPTION 1
* Load from development server. Start the server from the repository root: * Load from development server. Start the server from the repository root:
* *
* $ npm start * $ npm start
* *
* To run on device, change `localhost` to the IP address of your computer * To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the * (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are * `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network. * on the same Wi-Fi network.
*/ */
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"]; jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
/** /**
* OPTION 2 * OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle * Load from pre-bundled file on disk. To re-generate the static bundle
* from the root of your project directory, run * from the root of your project directory, run
* *
* $ react-native bundle --minify * $ react-native bundle --minify
* *
* see http://facebook.github.io/react-native/docs/runningondevice.html * see http://facebook.github.io/react-native/docs/runningondevice.html
*/ */
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"ReactTests" moduleName:@"ReactTests"
initialProperties:nil initialProperties:nil
launchOptions:launchOptions]; launchOptions:launchOptions];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init]; UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view = rootView; rootViewController.view = rootView;
self.window.rootViewController = rootViewController; self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible]; [self.window makeKeyAndVisible];
return YES; return YES;
} }
@end @end

View File

@ -19,32 +19,71 @@
#import "RealmJSTests.h" #import "RealmJSTests.h"
#import "Base/RCTJavaScriptExecutor.h" #import "Base/RCTJavaScriptExecutor.h"
#import "Base/RCTBridge.h" #import "Base/RCTBridge.h"
#import "Modules/RCTDevMenu.h"
@import ObjectiveC;
@import RealmReact; @import RealmReact;
extern void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx, bool includesNativeCallStack); extern void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx, bool includesNativeCallStack);
static id<RCTJavaScriptExecutor> s_currentJavaScriptExecutor;
@interface RealmReactTests : RealmJSTests @interface RealmReactTests : RealmJSTests
@end @end
@interface RealmReactChromeTests : RealmReactTests
@end
@implementation RealmReactTests @implementation RealmReactTests
+ (XCTestSuite *)defaultTestSuite { + (void)load {
NSNotification *notification = [self waitForNotification:RCTJavaScriptDidLoadNotification]; // Swap the [RCTDevMenu init] method with [NSObject init] in order to disable RCTDevMenu completely.
RCTBridge *bridge = notification.userInfo[@"bridge"]; IMP init = class_getMethodImplementation([NSObject class], @selector(init));
class_replaceMethod([RCTDevMenu class], @selector(init), init, NULL);
}
if (!bridge) { + (Class)executorClass {
NSLog(@"No RCTBridge provided by RCTJavaScriptDidLoadNotification"); return NSClassFromString(@"RCTContextExecutor");
}
+ (NSString *)classNameSuffix {
return @"";
}
+ (id<RCTJavaScriptExecutor>)currentExecutor {
Class executorClass = [self executorClass];
if (!executorClass) {
NSLog(@"%@: Executor class not found", self);
exit(1); exit(1);
} }
s_currentJavaScriptExecutor = [bridge valueForKey:@"javaScriptExecutor"]; static RCTBridge *s_bridge;
assert(s_currentJavaScriptExecutor); if (!s_bridge.valid) {
NSNotification *notification = [self waitForNotification:RCTJavaScriptDidLoadNotification];;
s_bridge = notification.userInfo[@"bridge"];
if (!s_bridge) {
NSLog(@"No RCTBridge provided by RCTJavaScriptDidLoadNotification");
exit(1);
}
}
if (s_bridge.executorClass != executorClass) {
s_bridge.executorClass = executorClass;
[s_bridge reload];
// The [RCTBridge reload] method does a dispatch_async that we must run before trying again.
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
return [self currentExecutor];
}
return [s_bridge valueForKey:@"javaScriptExecutor"];
}
+ (XCTestSuite *)defaultTestSuite {
id<RCTJavaScriptExecutor> executor = [self currentExecutor];
// FIXME: Remove this nonsense once the crashes go away when a test fails! // FIXME: Remove this nonsense once the crashes go away when a test fails!
JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(s_currentJavaScriptExecutor, false); JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(executor, false);
if (ctx) { if (ctx) {
JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(ctx, false); JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(ctx, false);
} }
@ -57,8 +96,16 @@ static id<RCTJavaScriptExecutor> s_currentJavaScriptExecutor;
exit(1); exit(1);
} }
XCTestSuite *suite = [super defaultTestSuite]; NSString *nameSuffix = [self classNameSuffix];
if (nameSuffix.length) {
NSMutableDictionary *renamedTestCaseNames = [[NSMutableDictionary alloc] init];
for (NSString *name in testCaseNames) {
renamedTestCaseNames[[name stringByAppendingString:nameSuffix]] = testCaseNames[name];
}
testCaseNames = renamedTestCaseNames;
}
XCTestSuite *suite = [super defaultTestSuite];
for (XCTestSuite *testSuite in [self testSuitesFromDictionary:testCaseNames]) { for (XCTestSuite *testSuite in [self testSuitesFromDictionary:testCaseNames]) {
[suite addTest:testSuite]; [suite addTest:testSuite];
} }
@ -67,50 +114,73 @@ static id<RCTJavaScriptExecutor> s_currentJavaScriptExecutor;
} }
+ (NSNotification *)waitForNotification:(NSString *)notificationName { + (NSNotification *)waitForNotification:(NSString *)notificationName {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
__block BOOL condition = NO;
__block NSNotification *notification; __block NSNotification *notification;
id token = [nc addObserverForName:notificationName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { id token = [nc addObserverForName:notificationName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
condition = YES;
notification = note; notification = note;
}]; }];
while (!notification) { [self waitForCondition:&condition];
[nc removeObserver:token];
return notification;
}
+ (void)waitForCondition:(BOOL *)condition {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (!*condition) {
@autoreleasepool { @autoreleasepool {
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} }
} }
[nc removeObserver:token];
return notification;
} }
+ (id)invokeMethod:(NSString *)method inModule:(NSString *)module error:(NSError * __strong *)outError { + (id)invokeMethod:(NSString *)method inModule:(NSString *)module error:(NSError * __strong *)outError {
id<RCTJavaScriptExecutor> executor = [self currentExecutor];
module = [NSString stringWithFormat:@"realm-tests/%@.js", module]; module = [NSString stringWithFormat:@"realm-tests/%@.js", module];
dispatch_group_t group = dispatch_group_create(); __block BOOL condition = NO;
__block id result; __block id result;
dispatch_group_enter(group); [executor executeJSCall:module method:method arguments:@[] callback:^(id json, NSError *error) {
// The React Native debuggerWorker.js very bizarrely returns an array five empty arrays to signify an error.
if ([json isKindOfClass:[NSArray class]] && [json isEqualToArray:@[@[], @[], @[], @[], @[]]]) {
json = nil;
[s_currentJavaScriptExecutor executeJSCall:module method:method arguments:@[] callback:^(id json, NSError *error) { if (!error) {
result = json; error = [NSError errorWithDomain:@"JS" code:1 userInfo:@{NSLocalizedDescriptionKey: @"unknown JS error"}];
}
if (error && outError) {
*outError = error;
} }
dispatch_group_leave(group); dispatch_async(dispatch_get_main_queue(), ^{
condition = YES;
result = json;
if (error && outError) {
*outError = error;
}
});
}]; }];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER); [self waitForCondition:&condition];
return result; return result;
} }
- (void)invokeMethod:(NSString *)method { - (void)invokeMethod:(NSString *)method {
NSString *module = NSStringFromClass(self.class);
NSString *suffix = [self.class classNameSuffix];
if (suffix.length && [module hasSuffix:suffix]) {
module = [module substringToIndex:(module.length - suffix.length)];
}
NSError *error; NSError *error;
[self.class invokeMethod:method inModule:NSStringFromClass(self.class) error:&error]; [self.class invokeMethod:method inModule:module error:&error];
if (error) { if (error) {
// TODO: Parse and use localizedFailureReason info once we can source map the failure location in JS. // TODO: Parse and use localizedFailureReason info once we can source map the failure location in JS.
@ -119,3 +189,15 @@ static id<RCTJavaScriptExecutor> s_currentJavaScriptExecutor;
} }
@end @end
@implementation RealmReactChromeTests
+ (Class)executorClass {
return NSClassFromString(@"RCTWebSocketExecutor");
}
+ (NSString *)classNameSuffix {
return @"_Chrome";
}
@end