erikdoe / ocmock

Mock objects for Objective-C
http://ocmock.org
Apache License 2.0
2.16k stars 606 forks source link

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

Open dmaclach opened 3 years ago

dmaclach commented 3 years ago

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.

erikdoe commented 2 years ago

I can reproduce this problem. Will look into it.

alkhimey commented 2 years ago

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"
manojmahapatra commented 11 months ago

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