ocmock icon indicating copy to clipboard operation
ocmock copied to clipboard

ocmock tests have problems with Xcode 13.0b2 on iOS15

Open dmaclach opened this issue 3 years ago • 3 comments

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.

dmaclach avatar Jul 09 '21 01:07 dmaclach

I can reproduce this problem. Will look into it.

erikdoe avatar Oct 26 '21 20:10 erikdoe

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"

alkhimey avatar Jan 22 '22 21:01 alkhimey

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

manojmahapatra avatar Oct 24 '23 18:10 manojmahapatra