realm-js/tests/RealmJSTests.mm

259 lines
8.1 KiB
Plaintext

////////////////////////////////////////////////////////////////////////////
//
// Copyright 2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// 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 "RealmJSTests.h"
#import "RJSUtil.hpp"
#import "RJSRealm.hpp"
NSString *RealmPathForFile(NSString *fileName) {
#if TARGET_OS_IPHONE
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
#else
NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
path = [path stringByAppendingPathComponent:[[[NSBundle mainBundle] executablePath] lastPathComponent]];
#endif
return [path stringByAppendingPathComponent:fileName];
}
static NSString *s_testPrefix;
NSString *TestRealmPath() {
return RealmPathForFile([s_testPrefix stringByAppendingPathComponent:@"test.realm"]);
}
static void DeleteOrThrow(NSString *path) {
NSError *error;
if (![[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
if (error.code != NSFileNoSuchFileError) {
@throw [NSException exceptionWithName:@"RLMTestException"
reason:[@"Unable to delete realm: " stringByAppendingString:error.description]
userInfo:nil];
}
}
}
static void DeleteRealmFilesAtPath(NSString *path) {
DeleteOrThrow(path);
DeleteOrThrow([path stringByAppendingString:@".lock"]);
DeleteOrThrow([path stringByAppendingString:@".note"]);
}
static JSClassRef s_globalClass;
@interface RealmJSTests ()
@property (nonatomic, strong) JSContext *context;
@end
@implementation RealmJSTests
+ (void)initialize {
JSClassDefinition globalDefinition = kJSClassDefinitionEmpty;
globalDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
s_globalClass = JSClassCreate(&globalDefinition);
}
- (void)setUp {
[super setUp];
s_testPrefix = [[NSUUID UUID] UUIDString];
NSString *defaultDir = RealmPathForFile(s_testPrefix);
[[NSFileManager defaultManager] createDirectoryAtPath:defaultDir withIntermediateDirectories:YES attributes:nil error:nil];
RJSSetDefaultPath([defaultDir stringByAppendingPathComponent:@"default.realm"].UTF8String);
JSGlobalContextRef ctx = JSGlobalContextCreateInGroup(NULL, s_globalClass);
self.context = [JSContext contextWithJSGlobalContextRef:ctx];
[RealmJS initializeContext:ctx];
[self evaluateScript:@"var exports = {};" fromURL:nil];
[self evaluateScriptWithName:@"TestCase"];
[self evaluateScriptWithName:@"TestObjects"];
[self evaluateScriptWithName:self.class.jsSuiteName];
}
- (void)tearDown {
self.context = nil;
DeleteRealmFilesAtPath(TestRealmPath());
DeleteRealmFilesAtPath(@(RJSDefaultPath().c_str()));
[super tearDown];
}
- (void)invokeTest {
@autoreleasepool {
[super invokeTest];
}
}
- (void)evaluateScriptWithName:(NSString *)name {
NSURL *url = [self.class scriptURLWithName:name];
NSString *script = [self.class loadScriptWithURL:url];
[self evaluateScript:script fromURL:url];
}
- (void)evaluateScript:(NSString *)script fromURL:(NSURL *)url {
JSValue *exception;
[self.class evaluateScript:script fromURL:url inContext:self.context exception:&exception];
if (exception) {
JSValue *message = [exception hasProperty:@"message"] ? exception[@"message"] : exception;
NSString *source = [exception hasProperty:@"sourceURL"] ? [exception[@"sourceURL"] toString] : nil;
NSUInteger line = [exception hasProperty:@"line"] ? [exception[@"line"] toUInt32] : 0;
NSURL *sourceURL = source ? [NSURL URLWithString:source.lastPathComponent relativeToURL:[NSURL URLWithString:@(__FILE__)]] : nil;
const char *sourcePath = sourceURL.absoluteString.UTF8String;
_XCTFailureHandler(self, YES, sourcePath ?: __FILE__, sourcePath ? line : __LINE__, @"JS", @"%@", message);
}
}
+ (JSValue *)evaluateScript:(NSString *)script fromURL:(NSURL *)url inContext:(JSContext *)context exception:(JSValue **)exception {
JSStringRef jsScript = JSStringCreateWithUTF8CString(script.UTF8String);
JSStringRef jsURL = url ? JSStringCreateWithUTF8CString(url.absoluteString.UTF8String) : NULL;
JSValueRef jsException = NULL;
JSValueRef jsResult = JSEvaluateScript(context.JSGlobalContextRef, jsScript, NULL, jsURL, 1, &jsException);
JSStringRelease(jsScript);
if (jsURL) {
JSStringRelease(jsURL);
}
if (jsException) {
*exception = [JSValue valueWithJSValueRef:jsException inContext:context];
return NULL;
}
return [JSValue valueWithJSValueRef:jsResult inContext:context];
}
+ (NSURL *)scriptURLWithName:(NSString *)name {
NSURL *url = [[NSBundle bundleForClass:self] URLForResource:name withExtension:@"js"];
if (!url) {
NSLog(@"JS file does not exist: %@", url);
exit(1);
}
return url;
}
+ (NSString *)loadScriptWithURL:(NSURL *)url {
NSError *error;
NSString *script = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (!script) {
NSLog(@"Error reading JS file (%@): %@", url, error);
exit(1);
}
return script;
}
+ (NSString *)jsSuiteName {
return nil;
}
+ (NSString *)jsSuiteScript {
NSString *name = [self jsSuiteName];
return name ? [self loadScriptWithURL:[self scriptURLWithName:name]] : nil;
}
+ (XCTestSuite *)defaultTestSuite {
XCTestSuite *suite = [super defaultTestSuite];
NSString *suiteName = [self jsSuiteName];
NSURL *scriptURL = suiteName ? [self scriptURLWithName:suiteName] : nil;
NSString *script = scriptURL ? [self loadScriptWithURL:scriptURL] : nil;
if (!script) {
return suite;
}
JSContext *context = [[JSContext alloc] init];
JSValue *exception;
[self evaluateScript:@"var exports = {};" fromURL:nil inContext:context exception:&exception];
[self evaluateScript:script fromURL:scriptURL inContext:context exception:&exception];
if (exception) {
NSLog(@"%@.js - %@", suiteName, exception);
exit(1);
}
JSValue *suiteObject = [self evaluateScript:suiteName fromURL:nil inContext:context exception:&exception];
if (exception) {
NSLog(@"%@.js - %@", suiteName, exception);
exit(1);
}
if (![suiteObject isObject]) {
NSLog(@"%@.js - JS test suite is not an object: %@", suiteName, suiteObject);
exit(1);
}
for (NSString *testName in [suiteObject toDictionary]) {
[suite addTest:[self testCaseWithSelector:NSSelectorFromString(testName)]];
}
return suite;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig = [super methodSignatureForSelector:aSelector];
if (sig) {
return sig;
}
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSString *script = [NSString stringWithFormat:@"%@.%@();", [self.class jsSuiteName], NSStringFromSelector(anInvocation.selector)];
[self evaluateScript:script fromURL:nil];
}
@end
@interface RJSResultsTests : RealmJSTests
@end
@implementation RJSResultsTests
+ (NSString *)jsSuiteName {
return @"ResultsTests";
}
@end
@interface RJSObjectTests : RealmJSTests
@end
@implementation RJSObjectTests
+ (NSString *)jsSuiteName {
return @"ObjectTests";
}
@end
@interface RJSArrayTests : RealmJSTests
@end
@implementation RJSArrayTests
+ (NSString *)jsSuiteName {
return @"ArrayTests";
}
@end
@interface RJSRealmTests : RealmJSTests
@end
@implementation RJSRealmTests
+ (NSString *)jsSuiteName {
return @"RealmTests";
}
@end