ocmock
ocmock copied to clipboard
ocmock tests have problems with Xcode 13.0b2 on iOS15
If I open b9c7fb949d7afc7087165b9c57fb5d7ddbe2ce4d in Xcode 13 and execute the tests on an iOS15 simulator multiple times, I eventually get a crash in something like:
Test Case '-[OCMockObjectTests testRaisesExceptionWhenMethodWithWrongVoidPointerArgumentIsCalled]' started.
2021-07-08 14:35:17.501008-0700 xctest[68194:138191625] +[NSMutableData ocmock_replaced_allocWithZone:]: unrecognized selector sent to class 0x7fff86364520
Test Case '-[OCMockObjectTests testRaisesExceptionWhenMethodWithWrongVoidPointerArgumentIsCalled]' passed (0.001 seconds).
Test Case '-[OCMockObjectTests testRaisesExceptionWhenSequenceIsWrongAndOrderMatters]' started.
2021-07-08 14:35:17.502242-0700 xctest[68194:138191625] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSMutableData ocmock_replaced_allocWithZone:]: unrecognized selector sent to class 0x7fff86364520'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff203fae07 __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff2019ebe7 objc_exception_throw + 48
2 CoreFoundation 0x00007fff2040994a __CFExceptionProem + 0
3 CoreFoundation 0x00007fff203ff2cb ___forwarding___ + 1412
4 CoreFoundation 0x00007fff204013f8 _CF_forwarding_prep_0 + 120
5 CoreFoundation 0x00007fff2040168c __invoking___ + 140
6 CoreFoundation 0x00007fff203fea96 -[NSInvocation invoke] + 305
7 OCMockLibTests 0x0000000111739ab3 -[OCClassMockObject forwardInvocationForClassObject:] + 275
8 CoreFoundation 0x00007fff203ff066 ___forwarding___ + 799
9 CoreFoundation 0x00007fff204013f8 _CF_forwarding_prep_0 + 120
10 libobjc.A.dylib 0x00007fff2019dd71 objc_opt_new + 76
11 Foundation 0x00007fff207acc11 -[NSKeyedArchiver init] + 21
12 Foundation 0x00007fff207ac9b4 -[NSKeyedArchiver initRequiringSecureCoding:] + 30
13 Foundation 0x00007fff207aca1b +[NSKeyedArchiver archivedDataWithRootObject:requiringSecureCoding:error:] + 70
14 DTXConnectionServices 0x000000010f7a5bae DTXPrimitiveDictionaryReferencingSerialized + 63742
15 DTXConnectionServices 0x000000010f7a6456 DTXPrimitiveDictionaryReferencingSerialized + 65958
16 DTXConnectionServices 0x000000010f7a5fa8 DTXPrimitiveDictionaryReferencingSerialized + 64760
17 DTXConnectionServices 0x000000010f7916bb DTXConnectionServices + 14011
18 CoreFoundation 0x00007fff203ff066 ___forwarding___ + 799
19 CoreFoundation 0x00007fff204013f8 _CF_forwarding_prep_0 + 120
20 XCTest 0x000000010f4b46f9 -[XCTRunnerIDESession logDebugMessage:] + 79
21 XCTest 0x000000010f487a71 -[XCTDefaultDebugLogHandler _queue_flushDebugMessageBufferWithBlock:] + 274
22 XCTest 0x000000010f487c7f __45-[XCTDefaultDebugLogHandler logDebugMessage:]_block_invoke + 185
23 libdispatch.dylib 0x00007fff20110b50 _dispatch_call_block_and_release + 12
24 libdispatch.dylib 0x00007fff20111d57 _dispatch_client_callout + 8
25 libdispatch.dylib 0x00007fff20118377 _dispatch_lane_serial_drain + 710
26 libdispatch.dylib 0x00007fff20118f1d _dispatch_lane_invoke + 400
27 libdispatch.dylib 0x00007fff20123a12 _dispatch_workloop_worker_thread + 772
28 libsystem_pthread.dylib 0x00007fff6b0234c0 _pthread_wqthread + 314
29 libsystem_pthread.dylib 0x00007fff6b022493 start_wqthread + 15
)
Test Case '-[OCMockObjectTests testRaisesExceptionWhenSequenceIsWrongAndOrderMatters]' passed (0.001 seconds).
libc++abi: Test Case '-[OCMockObjectTests testRaisesExceptionWhenStubbedMockArgIsNotUsed]' started.
terminating with uncaught exception of type NSException
It appears that Apple now uses a separate thread to deal with debug logs Thread 6 Queue : com.apple.dt.xctest.default-debug-log-handler (serial) which assumes that the NSNumber and NSMutableData classes aren't going to be messed with.
This exposes a bunch of potential race conditions we have:
- (void)forwardInvocationForClassObject:(NSInvocation *)anInvocation
{
// in here "self" is a reference to the real class, not the mock
OCClassMockObject *mock = OCMGetAssociatedMockForClass((Class)self, YES);
>>> 1
if(mock == nil)
{
[NSException raise:NSInternalInconsistencyException format:@"No mock for class %@", NSStringFromClass((Class)self)];
}
if([mock handleInvocation:anInvocation] == NO)
{
[anInvocation setSelector:OCMAliasForOriginalSelector([anInvocation selector])];
>>> 2
[anInvocation invoke];
}
}
- (void)stopMockingClassMethods
{
OCMSetAssociatedMockForClass(nil, mockedClass);
object_setClass(mockedClass, originalMetaClass);
originalMetaClass = nil;
/* created meta class will be disposed later because partial mocks create another subclass depending on it */
}
If stopMockingClassMethods executes before point >>>1, mock will be nil and the exception will fire. If stopMockingClassMethods executes between >>>1 and >>>2 and the mock is dealloc'd, the invocation to mock will fail. if stopMockingClassMethods executes between >>>1 and >>>2, the class will have been changed and the selector will not be recognized (this is likely what happened in the crash log scenario above).
Also, if a class method gets called while the mock is being set up in prepareClassForClassMethodMocking, we also run into problems.
I can reproduce this problem. Will look into it.
It is reproducible on ARM Macs as well.
When running natively, the error is like @dmaclach provided. When running under Rosetta, the error is a little different:
Thread 3: "+[NSMutableData allocWithZone:]: unrecognized selector sent to class 0x7fff801cd790"
I'm seeing the same issue while running against Xcode 15. while I can't repro this locally, but in our pipeline this occurs sometimes. Is there a fix that went earlier where I can track? cc @erikdoe