Skip to content

ocmock tests have problems with Xcode 13.0b2 on iOS15 #501

Open
@dmaclach

Description

@dmaclach

If I open b9c7fb9 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions