Yesterday, Apple released Xcode 15 β3 (15A195k) and I noticed that my tests started failing. These tests verified that an expectation was thrown from calling a method that should be unavailable. However, these tests were failing with an uncaught exception:

xctest(8933,0x1f2049e00) malloc: nano zone abandoned due to inability to reserve vm space.
Test Suite 'ExampleClassTest' started at 2023-07-06 07:40:43.904.
Test Case '-[ExampleClassTest testInit]' started.
2023-07-06 07:40:43.907915-0500 xctest[8933:99736] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'init is unavailable for class ExampleClass'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001971a3154 __exceptionPreprocess + 176
    1   libobjc.A.dylib                     0x0000000196cc24d4 objc_exception_throw + 60
    2   ExampleFramework                    0x000000010f091e1c -[ExampleClass init] + 348
    3   ExampleFrameworkTests               0x000000010f061bdc -[ExampleClassTest testInit] + 196
    4   CoreFoundation                      0x000000019710c7f4 __invoking___ + 148
    5   CoreFoundation                      0x000000019710c668 -[NSInvocation invoke] + 428
    6   XCTestCore                          0x0000000101dea1f4 +[XCTFailableInvocation invokeStandardConventionInvocation:completion:] + 68
    7   XCTestCore                          0x0000000101dea1a8 __90+[XCTFailableInvocation invokeInvocation:withTestMethodConvention:lastObservedErrorIssue:]_block_invoke_3 + 28
    8   XCTestCore                          0x0000000101de9900 __81+[XCTFailableInvocation invokeWithAsynchronousWait:lastObservedErrorIssue:block:]_block_invoke + 360
    9   XCTestCore                          0x0000000101da2a10 +[XCTSwiftErrorObservation observeErrorsInBlock:] + 280
    10  XCTestCore                          0x0000000101de9690 +[XCTFailableInvocation invokeWithAsynchronousWait:lastObservedErrorIssue:block:] + 228
    11  XCTestCore                          0x0000000101de9e90 +[XCTFailableInvocation invokeInvocation:withTestMethodConvention:lastObservedErrorIssue:] + 540
    12  XCTestCore                          0x0000000101dea298 +[XCTFailableInvocation invokeInvocation:lastObservedErrorIssue:] + 72
    13  XCTestCore                          0x0000000101ddb498 __24-[XCTestCase invokeTest]_block_invoke_2 + 88
    14  XCTestCore                          0x0000000101db1318 -[XCTMemoryChecker _assertInvalidObjectsDeallocatedAfterScope:] + 84
    15  XCTestCore                          0x0000000101dde974 -[XCTestCase assertInvalidObjectsDeallocatedAfterScope:] + 92
    16  XCTestCore                          0x0000000101ddb3f8 __24-[XCTestCase invokeTest]_block_invoke.96 + 172
    17  XCTestCore                          0x0000000101d99558 -[XCTestCase(XCTIssueHandling) _caughtUnhandledDeveloperExceptionPermittingControlFlowInterruptions:caughtInterruptionException:whileExecutingBlock:] + 168
    18  XCTestCore                          0x0000000101ddaf20 -[XCTestCase invokeTest] + 764
    19  XCTestCore                          0x0000000101ddc9a8 __26-[XCTestCase performTest:]_block_invoke.149 + 36
    20  XCTestCore                          0x0000000101d99558 -[XCTestCase(XCTIssueHandling) _caughtUnhandledDeveloperExceptionPermittingControlFlowInterruptions:caughtInterruptionException:whileExecutingBlock:] + 168
    21  XCTestCore                          0x0000000101ddc44c __26-[XCTestCase performTest:]_block_invoke.134 + 552
    22  XCTestCore                          0x0000000101dbd8e4 +[XCTContext _runInChildOfContext:forTestCase:markAsReportingBase:block:] + 180
    23  XCTestCore                          0x0000000101dbd7cc +[XCTContext runInContextForTestCase:markAsReportingBase:block:] + 104
    24  XCTestCore                          0x0000000101ddbea4 -[XCTestCase performTest:] + 308
    25  XCTestCore                          0x0000000101d84190 -[XCTest runTest] + 48
    26  XCTestCore                          0x0000000101dc0b7c -[XCTestSuite runTestBasedOnRepetitionPolicy:testRun:] + 68
    27  XCTestCore                          0x0000000101dc0a44 __27-[XCTestSuite performTest:]_block_invoke + 164
    28  XCTestCore                          0x0000000101dc04a8 __59-[XCTestSuite _performProtectedSectionForTest:testSection:]_block_invoke + 48
    29  XCTestCore                          0x0000000101dbd8e4 +[XCTContext _runInChildOfContext:forTestCase:markAsReportingBase:block:] + 180
    30  XCTestCore                          0x0000000101dbd7cc +[XCTContext runInContextForTestCase:markAsReportingBase:block:] + 104
    31  XCTestCore                          0x0000000101dc0418 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 180
    32  XCTestCore                          0x0000000101dc06f8 -[XCTestSuite performTest:] + 220
    33  XCTestCore                          0x0000000101d84190 -[XCTest runTest] + 48
    34  XCTestCore                          0x0000000101d867b4 __89-[XCTTestRunSession executeTestsWithIdentifiers:skippingTestsWithIdentifiers:completion:]_block_invoke + 580
    35  XCTestCore                          0x0000000101dbd8e4 +[XCTContext _runInChildOfContext:forTestCase:markAsReportingBase:block:] + 180
    36  XCTestCore                          0x0000000101dbd7cc +[XCTContext runInContextForTestCase:markAsReportingBase:block:] + 104
    37  XCTestCore                          0x0000000101d86498 -[XCTTestRunSession executeTestsWithIdentifiers:skippingTestsWithIdentifiers:completion:] + 296
    38  XCTestCore                          0x0000000101dff5fc __103-[XCTExecutionWorker executeTestIdentifiers:skippingTestIdentifiers:completionHandler:completionQueue:]_block_invoke_2 + 136
    39  XCTestCore                          0x0000000101dfe360 -[XCTExecutionWorker runWithError:] + 132
    40  XCTestCore                          0x0000000101dba0a0 __25-[XCTestDriver _runTests]_block_invoke.261 + 56
    41  XCTestCore                          0x0000000101d9143c -[XCTestObservationCenter _observeTestExecutionForTestBundle:inBlock:] + 212
    42  XCTestCore                          0x0000000101db9af4 -[XCTestDriver _runTests] + 1100
    43  XCTestCore                          0x0000000101d84874 _XCTestMain + 92
    44  xctest                              0x000000010000577c main + 156
    45  dyld                                0x0000000196cf3f28 start + 2236
)
libc++abi: terminating due to uncaught exception of type NSException

This stood out as weird immediately because the exception that was determined to be uncaught was the expectation that the XCTAssertThrows macro was supposed to be catching. Looking at the code, there wasn’t anything that seemed wrong considering this worked in Xcode 15 beta 2 and earlier:

// Class Definition
@import Foundation;

NS_ASSUME_NONNULL_BEGIN

@interface ExampleClass : NSObject

- (instancetype)init NS_UNAVAILABLE;

@end

NS_ASSUME_NONNULL_END

// Class Implementation
#import "ExampleClass.h"

@implementation ExampleClass

- (instancetype)__attribute__((noreturn)) init
{
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"init is unavailable for class %@", NSStringFromClass([self class])] userInfo:nil];
}

@end

// The Test
@import XCTest;
@import ExampleFramework;

@interface ExampleClassTest : XCTestCase

@end

@implementation ExampleClassTest

- (void)testInit
{
    ExampleClass *example = [ExampleClass alloc];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    XCTAssertThrows([example performSelector:NSSelectorFromString(@"init")]);
#pragma clang diagnostic pop
}

@end

My XCTestPlan includes configuration for running the tests with diagnostics enabled (ASan, TSan, and UBSan). I started turning them off one by one to see if that would resolve the issue and it did. Turns out, TSan is interfering somehow and causing the exception to not get caught appropriately.


I’ve logged this as FB12532865 and hopefully it is resolved soon. If not, an alternative could be to replace the macro and use a try/catch/finally block with a sentinel value that is checked and, if the exception is not thrown, call XCTFail.