Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
dmaclach opened this issue Jul 9, 2021 · 3 comments
Open

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

dmaclach opened this issue Jul 9, 2021 · 3 comments

Comments

@dmaclach
Copy link
Contributor

dmaclach commented Jul 9, 2021

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.

dmaclach added a commit to dmaclach/ocmock that referenced this issue Jul 9, 2021
Partial fix for erikdoe#501. Wraps up a bunch of locations with synchronization blocks to attempt
to make sure that the class is coherent before it starts receiving messages.
dmaclach added a commit to dmaclach/ocmock that referenced this issue Jul 9, 2021
Partial fix for erikdoe#501. Wraps up a bunch of locations with synchronization blocks to attempt
to make sure that the class is coherent before it starts receiving messages.
Repository owner deleted a comment from hgoyal6 Oct 26, 2021
@erikdoe
Copy link
Owner

erikdoe commented Oct 26, 2021

I can reproduce this problem. Will look into it.

@alkhimey
Copy link

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
Copy link

manojmahapatra commented Oct 24, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants