Skip to content

Commit

Permalink
Merge pull request #384 from pivotal/swizzle-for-xcode73
Browse files Browse the repository at this point in the history
New attempt at fixing test output under Xcode 7.3 while not breaking …
  • Loading branch information
idoru committed Apr 1, 2016
2 parents c044494 + 46d3b6d commit 1bbc910
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 25 deletions.
8 changes: 6 additions & 2 deletions Source/Headers/Project/XCTest/CDRXCTestSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
- (id)CDR_original_allTests;
- (id)initWithName:(NSString *)aName;

// XCTestObservationCenter
@end

@interface XCTestObservationCenter: NSObject
+ (instancetype)sharedTestObservationCenter;
- (void)addTestObserver:(id<XCTestObservation>)observer;

- (void)_addLegacyTestObserver:(id)observer;
- (void)CDR_original_addTestObserver:(id<XCTestObservation>)observer;
- (void)CDR_original__addLegacyTestObserver:(id)observer;
@end
55 changes: 41 additions & 14 deletions Source/XCTest/CDRXCTestFunctions.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,19 @@ id CDRCreateXCTestSuite() {
return [[[testSuiteSubclass alloc] initWithSpecRun:run] autorelease];
}

void CDRInjectIntoXCTestRunner() {
void CDRAddCedarTestObserver(id observationCenter) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[observationCenter CDR_original_addTestObserver:[[CDRXCTestObserver alloc] init]];
});
}

void CDRSwizzleTestSuiteAllTestsMethod() {
Class testSuiteClass = NSClassFromString(@"XCTestSuite") ?: NSClassFromString(@"SenTestSuite");
if (!testSuiteClass) {
[[NSException exceptionWithName:@"CedarNoTestFrameworkAvailable" reason:@"You must link against either the XCTest or SenTestingKit frameworks." userInfo:nil] raise];
}

// if possible, use the new XCTestObservation protocol available in Xcode 7
Class observationCenterClass = NSClassFromString(@"XCTestObservationCenter");
if (observationCenterClass && [observationCenterClass respondsToSelector:@selector(sharedTestObservationCenter)]) {
// Accessing the `sharedTestObservationCenter` too early causes XCTest console output to break when running
// on Xcode 7.3. Deferring adding the observer works around this issue. (rdar://25456276)
dispatch_async(dispatch_get_main_queue(), ^{
id observationCenter = [observationCenterClass sharedTestObservationCenter];
static CDRXCTestObserver *xcTestObserver;
xcTestObserver = [[CDRXCTestObserver alloc] init];
[observationCenter addTestObserver:xcTestObserver];
});
}

Class testSuiteMetaClass = object_getClass(testSuiteClass);
Method m = class_getClassMethod(testSuiteClass, @selector(allTests));

Expand All @@ -53,3 +47,36 @@ void CDRInjectIntoXCTestRunner() {
});
class_replaceMethod(testSuiteMetaClass, @selector(allTests), newImp, method_getTypeEncoding(m));
}

void CDRSwizzleTestObservationCenter() {
Class observationCenterClass = NSClassFromString(@"XCTestObservationCenter");
if (observationCenterClass && [observationCenterClass respondsToSelector:@selector(sharedTestObservationCenter)]) {
// Swizzle -addTestObserver:
Method addTestObserverMethod = class_getInstanceMethod(observationCenterClass, @selector(addTestObserver:));
class_addMethod(observationCenterClass, @selector(CDR_original_addTestObserver:), method_getImplementation(addTestObserverMethod), method_getTypeEncoding(addTestObserverMethod));

IMP newAddTestObserverImp = imp_implementationWithBlock(^void(id self, id observer){
[self CDR_original_addTestObserver:observer];
CDRAddCedarTestObserver(self);
});
class_replaceMethod(observationCenterClass, @selector(addTestObserver:), newAddTestObserverImp, method_getTypeEncoding(addTestObserverMethod));


// Swizzle -_addLegacyTestObserver:
Method addLegacyTestObserverMethod = class_getInstanceMethod(observationCenterClass, @selector(_addLegacyTestObserver:));
if (addLegacyTestObserverMethod) {
class_addMethod(observationCenterClass, @selector(CDR_original__addLegacyTestObserver:), method_getImplementation(addLegacyTestObserverMethod), method_getTypeEncoding(addLegacyTestObserverMethod));

IMP newAddLegacyTestObserverImp = imp_implementationWithBlock(^void(id self, id observer){
[self CDR_original__addLegacyTestObserver:observer];
CDRAddCedarTestObserver(self);
});
class_replaceMethod(observationCenterClass, @selector(_addLegacyTestObserver:), newAddLegacyTestObserverImp, method_getTypeEncoding(addLegacyTestObserverMethod));
}
}
}

void CDRInjectIntoXCTestRunner() {
CDRSwizzleTestSuiteAllTestsMethod();
CDRSwizzleTestObservationCenter();
}
2 changes: 2 additions & 0 deletions Spec/SpecBundle/SpecBundle-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSPrincipalClass</key>
<string>TestObservationHelper</string>
</dict>
</plist>
19 changes: 10 additions & 9 deletions Spec/SpecBundle/Support/TestObservationHelper.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ + (instancetype)defaultTestSuite;

@interface TestObservationHelper () <XCTestObservation> @end

// This class is loaded as the NSPrincipalClass of test bundles that require it, at which point
// it registers itself as a test observer.
@implementation TestObservationHelper

static NSMutableArray *_knownTestSuites;

+ (void)load {
Class observationCenterClass = NSClassFromString(@"XCTestObservationCenter");
if (observationCenterClass && [observationCenterClass respondsToSelector:@selector(sharedTestObservationCenter)]) {
_knownTestSuites = [NSMutableArray array];

// See comment in CDRXCTestFunctions.m for context on the dispatch_async
dispatch_async(dispatch_get_main_queue(), ^{
[[observationCenterClass sharedTestObservationCenter] addTestObserver:(id)[TestObservationHelper new]];
});
- (instancetype)init {
if (self = [super init]) {
Class observationCenterClass = NSClassFromString(@"XCTestObservationCenter");
if (observationCenterClass && [observationCenterClass respondsToSelector:@selector(sharedTestObservationCenter)]) {
_knownTestSuites = [NSMutableArray array];
[[observationCenterClass sharedTestObservationCenter] addTestObserver:self];
}
}
return self;
}

+ (NSArray *)knownTestSuites {
Expand Down

0 comments on commit 1bbc910

Please sign in to comment.