static void XCPerformTestWithSuppressedExpectedAssertionFailures()

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];
}