123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // 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 "RLMMultiProcessTestCase.h"
- @interface RLMMultiProcessTestCase ()
- @property (nonatomic) bool isParent;
- @property (nonatomic, strong) NSString *testName;
- @property (nonatomic, strong) NSString *xctestPath;
- @property (nonatomic, strong) NSString *testsPath;
- @end
- @implementation RLMMultiProcessTestCase
- // Override all of the methods for creating a XCTestCase object to capture the current test name
- + (id)testCaseWithInvocation:(NSInvocation *)invocation {
- RLMMultiProcessTestCase *testCase = [super testCaseWithInvocation:invocation];
- testCase.testName = NSStringFromSelector(invocation.selector);
- return testCase;
- }
- - (id)initWithInvocation:(NSInvocation *)invocation {
- self = [super initWithInvocation:invocation];
- if (self) {
- self.testName = NSStringFromSelector(invocation.selector);
- }
- return self;
- }
- + (id)testCaseWithSelector:(SEL)selector {
- RLMMultiProcessTestCase *testCase = [super testCaseWithSelector:selector];
- testCase.testName = NSStringFromSelector(selector);
- return testCase;
- }
- - (id)initWithSelector:(SEL)selector {
- self = [super initWithSelector:selector];
- if (self) {
- self.testName = NSStringFromSelector(selector);
- }
- return self;
- }
- - (void)setUp {
- self.isParent = !getenv("RLMProcessIsChild");
- self.xctestPath = [self locateXCTest];
- self.testsPath = [NSBundle bundleForClass:[self class]].bundlePath;
- if (!self.isParent) {
- // For multi-process tests, the child's concept of a default path needs to match the parent.
- // RLMRealmConfiguration isn't aware of this, but our test's RLMDefaultRealmURL helper does.
- // Use it to reset the default configuration's path so it matches the parent.
- RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
- configuration.fileURL = RLMDefaultRealmURL();
- [RLMRealmConfiguration setDefaultConfiguration:configuration];
- }
- [super setUp];
- }
- - (void)invokeTest {
- CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
- [super invokeTest];
- CFRunLoopStop(CFRunLoopGetCurrent());
- });
- CFRunLoopRun();
- }
- - (void)deleteFiles {
- // Only the parent should delete files in setUp/tearDown
- if (self.isParent) {
- [super deleteFiles];
- }
- }
- + (void)preintializeSchema {
- // Do nothing so that we can test global schema init in child processes
- }
- - (NSString *)locateXCTest {
- NSString *pathString = [NSProcessInfo processInfo].environment[@"PATH"];
- NSFileManager *fileManager = [NSFileManager defaultManager];
- for (NSString *directory in [pathString componentsSeparatedByString:@":"]) {
- NSString *candidatePath = [directory stringByAppendingPathComponent:@"xctest"];
- if ([fileManager isExecutableFileAtPath:candidatePath])
- return candidatePath;
- }
- return nil;
- }
- #if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
- - (NSTask *)childTask {
- NSString *testName = [NSString stringWithFormat:@"%@/%@", self.className, self.testName];
- NSMutableDictionary *env = [NSProcessInfo.processInfo.environment mutableCopy];
- env[@"RLMProcessIsChild"] = @"true";
- env[@"RLMParentProcessBundleID"] = [NSBundle mainBundle].bundleIdentifier;
- // Don't inherit the config file in the subprocess, as multiple XCTest
- // processes talking to a single Xcode instance doesn't work at all
- [env removeObjectForKey:@"XCTestConfigurationFilePath"];
- NSTask *task = [[NSTask alloc] init];
- task.launchPath = self.xctestPath;
- task.arguments = @[@"-XCTest", testName, self.testsPath];
- task.environment = env;
- task.standardError = nil;
- return task;
- }
- - (int)runChildAndWait {
- NSPipe *pipe = [NSPipe pipe];
- NSMutableData *buffer = [[NSMutableData alloc] init];
- // Filter the output from the child process to reduce xctest noise
- pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *file) {
- [buffer appendData:[file availableData]];
- const char *newline;
- const char *start = buffer.bytes;
- const char *end = start + buffer.length;
- while ((newline = memchr(start, '\n', end - start))) {
- if (newline < start + 17 ||
- (memcmp(start, "Test Suite", 10) && memcmp(start, "Test Case", 9) && memcmp(start, " Executed 1 test", 17))) {
- fwrite(start, newline - start + 1, 1, stderr);
- }
- start = newline + 1;
- }
- // Remove everything up to the last newline, leaving any data not newline-terminated in the buffer
- [buffer replaceBytesInRange:NSMakeRange(0, start - (char *)buffer.bytes) withBytes:0 length:0];
- };
- NSTask *task = [self childTask];
- task.standardError = pipe;
- [task launch];
- [task waitUntilExit];
- return task.terminationStatus;
- }
- #else
- - (NSTask *)childTask {
- return nil;
- }
- - (int)runChildAndWait {
- return 1;
- }
- #endif
- @end
|