in Shims/Shimulator/TestReporterShim/XCTestReporterShim.m [377:433]
static void XCPerformTestWithSuppressedExpectedAssertionFailures(id self, SEL origSel, id arg1)
{
void (*msgsend)(id, SEL, id) = (void *) objc_msgSend;
int timeout = [@(getenv("TEST_SHIM_TEST_TIMEOUT") ?: "0") intValue];
NSAssertionHandler *handler = [[XCToolAssertionHandler alloc] init];
NSThread *currentThread = [NSThread currentThread];
NSMutableDictionary *currentThreadDict = [currentThread threadDictionary];
[currentThreadDict setObject:handler forKey:NSAssertionHandlerKey];
if (timeout > 0) {
BOOL isSuite = [self isKindOfClass:objc_getClass("XCTestCaseSuite")];
// If running in a suite, time out if we run longer than the combined timeouts of all tests + a fudge factor.
int64_t testCount = isSuite ? [[self tests] count] : 1;
// When in a suite, add a second per test to help account for the time required to switch tests in a suite.
int64_t fudgeFactor = isSuite ? MAX(testCount, 1) : 0;
int64_t interval = (timeout * testCount + fudgeFactor) * NSEC_PER_SEC ;
NSString *queueName = [NSString stringWithFormat:@"test.timer.%p", self];
dispatch_queue_t queue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(source, dispatch_time(DISPATCH_TIME_NOW, interval), 0, 0);
dispatch_source_set_event_handler(source, ^{
if (isSuite) {
NSString *additionalInformation = @"";
if ([self respondsToSelector:@selector(testRun)]) {
XCTestRun *run = [self testRun];
NSUInteger executedTests = [run executionCount];
if (executedTests == 0) {
additionalInformation = [NSString stringWithFormat:@"(No tests ran, likely stalled in +[%@ setUp])", [self name]];
} else if (executedTests == testCount) {
additionalInformation = [NSString stringWithFormat:@"(All tests ran, likely stalled in +[%@ tearDown])", [self name]];
}
}
[NSException raise:NSInternalInconsistencyException
format:@"*** Suite %@ ran longer than combined test time limit: %lld second(s) %@", [self name], testCount * timeout, additionalInformation];
} else {
[NSException raise:NSInternalInconsistencyException
format:@"*** Test %@ ran longer than specified test time limit: %d second(s)", self, timeout];
}
});
dispatch_resume(source);
// Call through original implementation
msgsend(self, origSel, arg1);
dispatch_source_cancel(source);
} else {
// Call through original implementation
msgsend(self, origSel, arg1);
}
// The assertion handler hasn't been touched for our test, so we can safely remove it.
[currentThreadDict removeObjectForKey:NSAssertionHandlerKey];
}