22#import < XCTest/XCTest.h>
33#import < objc/runtime.h>
44
5- #pragma mark - Private
6-
7- @interface XCTestObservationCenter (Private)
8- - (void )_addLegacyTestObserver : (id )observer ;
9- @end
10-
11- @implementation XCTestObservationCenter (Register)
12-
13- // / Uses objc method swizzling to register `CurrentTestCaseTracker` as a test observer. This is necessary
14- // / because Xcode 7.3 introduced timing issues where if a custom `XCTestObservation` is registered too early
15- // / it suppresses all console output (generated by `XCTestLog`), breaking any tools that depend on this output.
16- // / This approach waits to register our custom test observer until XCTest adds its first "legacy" observer,
17- // / falling back to registering after the first normal observer if this private method ever changes.
18- + (void )load {
19- if (class_getInstanceMethod ([self class ], @selector (_addLegacyTestObserver: ))) {
20- // Swizzle -_addLegacyTestObserver:
21- [self swizzleSelector: @selector (_addLegacyTestObserver: ) withSelector: @selector (NMB_original__addLegacyTestObserver: )];
22- } else {
23- // Swizzle -addTestObserver:, only if -_addLegacyTestObserver: is not implemented
24- [self swizzleSelector: @selector (addTestObserver: ) withSelector: @selector (NMB_original_addTestObserver: )];
25- }
26- }
27-
285#pragma mark - Method Swizzling
296
307// / Swaps the implementations between two instance methods.
318// /
9+ // / @param class The class containing `originalSelector`.
3210// / @param originalSelector Original method to replace.
3311// / @param replacementSelector Replacement method.
34- + (void )swizzleSelector : (SEL )originalSelector withSelector : (SEL )replacementSelector {
35- Class class = [self class ];
36-
12+ void swizzleSelectors (Class class, SEL originalSelector, SEL replacementSelector) {
3713 Method originalMethod = class_getInstanceMethod (class, originalSelector);
3814 Method replacementMethod = class_getInstanceMethod (class, replacementSelector);
3915
@@ -53,6 +29,29 @@ + (void)swizzleSelector:(SEL)originalSelector withSelector:(SEL)replacementSelec
5329 }
5430}
5531
32+ #pragma mark - Private
33+
34+ @interface XCTestObservationCenter (Private)
35+ - (void )_addLegacyTestObserver : (id )observer ;
36+ @end
37+
38+ @implementation XCTestObservationCenter (Register)
39+
40+ // / Uses objc method swizzling to register `CurrentTestCaseTracker` as a test observer. This is necessary
41+ // / because Xcode 7.3 introduced timing issues where if a custom `XCTestObservation` is registered too early
42+ // / it suppresses all console output (generated by `XCTestLog`), breaking any tools that depend on this output.
43+ // / This approach waits to register our custom test observer until XCTest adds its first "legacy" observer,
44+ // / falling back to registering after the first normal observer if this private method ever changes.
45+ + (void )load {
46+ if (class_getInstanceMethod ([self class ], @selector (_addLegacyTestObserver: ))) {
47+ // Swizzle -_addLegacyTestObserver:
48+ swizzleSelectors ([self class ], @selector (_addLegacyTestObserver: ), @selector (NMB_original__addLegacyTestObserver: ));
49+ } else {
50+ // Swizzle -addTestObserver:, only if -_addLegacyTestObserver: is not implemented
51+ swizzleSelectors ([self class ], @selector (addTestObserver: ), @selector (NMB_original_addTestObserver: ));
52+ }
53+ }
54+
5655#pragma mark - Replacement Methods
5756
5857// / Registers `CurrentTestCaseTracker` as a test observer after `XCTestLog` has been added.
0 commit comments