Michał Hernas
Michał Hernas

Reputation: 416

iOS Test crashes with waitForExpectationsWithTimeout

Tests randomly crashes with waitForExpectation, always if I fulfill expectation in dispatch_after.

It happens both in objective-c and swift.

Totally randomly, sometimes it works sometimes not. Does anyone have any idea? ( I am running it using CMD+U on MacMini, but I also tried with MBP Retina, same results)

Example code:

func testBarcodeNotFound() {
        let exp = self.expectationWithDescription("store loading")
        OHHTTPStubs.stubRequestsPassingTest({ (request:NSURLRequest!) -> Bool in
            if request.URL.absoluteString == nil {
                return false
            }
            return request.URL.absoluteString!.hasSuffix("/products/barcode/1422/")
            }, withStubResponse: { (request:NSURLRequest!) -> OHHTTPStubsResponse! in
                let data = "".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
                return OHHTTPStubsResponse(data: data, statusCode: 404, headers: ["Content-Type":"application/json"])

        })
        self.productsVC.scanningVC.successScan("1422", "EAN13")
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
            exp.fulfill()
        }

        self.waitForExpectationsWithTimeout(1, handler: nil)

        XCTAssertTrue(self.delegateForProductsVC.barcodeNotFoundCalled)

    }

And backtrace

(lldb) bt
* thread #1: tid = 0x27e405, 0x06461ab0 libdispatch.dylib`_dispatch_semaphore_dispose + 92, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x06461ab0 libdispatch.dylib`_dispatch_semaphore_dispose + 92
    frame #1: 0x06463578 libdispatch.dylib`_dispatch_dispose + 43
    frame #2: 0x064759e1 libdispatch.dylib`_os_object_dispose + 33
    frame #3: 0x06475cb1 libdispatch.dylib`-[OS_dispatch_object _xref_dispose] + 58
    frame #4: 0x064759bb libdispatch.dylib`_os_object_xref_dispose + 33
    frame #5: 0x05759eb1 libobjc.A.dylib`objc_release + 65
    frame #6: 0x2011949d XCTest`__destroy_helper_block_95 + 29
    frame #7: 0x064e4793 libsystem_sim_blocks.dylib`_Block_release + 211
    frame #8: 0x0647603f libdispatch.dylib`_dispatch_client_callout + 14
    frame #9: 0x0645f764 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 470
    frame #10: 0x05af095e CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 14
    frame #11: 0x05aaf760 CoreFoundation`__CFRunLoopRun + 2256
    frame #12: 0x05aaebcb CoreFoundation`CFRunLoopRunSpecific + 443
    frame #13: 0x05aae9fb CoreFoundation`CFRunLoopRunInMode + 123
    frame #14: 0x04c2ed98 Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 309
    frame #15: 0x20119a31 XCTest`-[XCTestCase(AsynchronousTesting) waitForExpectationsWithTimeout:handler:] + 1192
  * frame #16: 0x1055be0b SomeAppNative`SomeApp.ProductsDelegatesViewControllerTest.testBarcodeNotFound (self=0x7b0dbdf0)() -> () + 5595 at ProductsDelegatesViewControllerTest.swift:108
    frame #17: 0x1055c1a2 SomeAppNative`@objc SomeApp.ProductsDelegatesViewControllerTest.testBarcodeNotFound (SomeApp.ProductsDelegatesViewControllerTest)() -> () + 34 at ProductsDelegatesViewControllerTest.swift:0
    frame #18: 0x05a7976d CoreFoundation`__invoking___ + 29
    frame #19: 0x05a79618 CoreFoundation`-[NSInvocation invoke] + 360
    frame #20: 0x2010897b XCTest`-[XCTestCase invokeTest] + 320
    frame #21: 0x20108bb9 XCTest`-[XCTestCase performTest:] + 184
    frame #22: 0x20114162 XCTest`-[XCTest run] + 314
    frame #23: 0x20107598 XCTest`-[XCTestSuite performTest:] + 406
    frame #24: 0x20114162 XCTest`-[XCTest run] + 314
    frame #25: 0x20107598 XCTest`-[XCTestSuite performTest:] + 406
    frame #26: 0x20114162 XCTest`-[XCTest run] + 314
    frame #27: 0x20107598 XCTest`-[XCTestSuite performTest:] + 406
    frame #28: 0x20114162 XCTest`-[XCTest run] + 314
    frame #29: 0x20103de2 XCTest`__25-[XCTestDriver _runSuite]_block_invoke + 61
    frame #30: 0x20110c82 XCTest`-[XCTestObservationCenter _observeTestExecutionForBlock:] + 184
    frame #31: 0x20103d06 XCTest`-[XCTestDriver _runSuite] + 285
    frame #32: 0x20104951 XCTest`-[XCTestDriver _checkForTestManager] + 272
    frame #33: 0x20104c6b XCTest`-[XCTestDriver runTestSuite:completionHandler:] + 378
    frame #34: 0x2011775c XCTest`+[XCTestProbe runTests:] + 216
    frame #35: 0x04c2ab57 Foundation`__NSFireDelayedPerform + 423
    frame #36: 0x05af08d6 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
    frame #37: 0x05af025d CoreFoundation`__CFRunLoopDoTimer + 1309
    frame #38: 0x05aaf6ba CoreFoundation`__CFRunLoopRun + 2090
    frame #39: 0x05aaebcb CoreFoundation`CFRunLoopRunSpecific + 443
    frame #40: 0x05aae9fb CoreFoundation`CFRunLoopRunInMode + 123
    frame #41: 0x0712e24f GraphicsServices`GSEventRunModal + 192
    frame #42: 0x0712e08c GraphicsServices`GSEventRun + 104
    frame #43: 0x034518b6 UIKit`UIApplicationMain + 1526
    frame #44: 0x0053dae5 SomeApp`main(argc=16, argv=0xbff6e4a4) + 213 at main.m:16
    frame #45: 0x0649cac9 libdyld.dylib`start + 1
(lldb) 

Upvotes: 7

Views: 3706

Answers (3)

Ben Lu
Ben Lu

Reputation: 3042

I experienced similar issues and it took me three days to figure out.

The documentation of -waitForExpectationsWithTimeout:handler: says:

 * -waitForExpectationsWithTimeout:handler: runs the run loop while handling events until all expectations
 * are fulfilled or the timeout is reached. Clients should not manipulate the run
 * loop while using this API.

Most likely your NSInputStream or NSOutputStream is trying to affect the runloop during waiting, which will cause a crash.

Upvotes: 2

Chris
Chris

Reputation: 1673

Swift has especial trouble to show the correct exception break point when closures are present in the same scope.

I've seen the same problem in an XCTestCase that used dispatch_after and even through the exception breakpoint was the same line as waitForExpectationsWithTimeout the test case was crashing because of a downcast on a nil object.

I know this is not your case but whenever this happens I suggest removing statements one line at a time and running the test after each removal. If the test does not crash, you have identified the culprit. This is unfortunately the best option at the time of this writing whenever Swift shows an exception breakpoint on a line that does not make sense, especially the infamous line 0 of a class that you might see in crash reporting tools.

Let us know if you have figured out your crash.

Upvotes: 0

Sean Greevers
Sean Greevers

Reputation: 235

I had the same issue. The fix for me appeared to be increasing the timeout interval. I originally had a timeout interval of 1 and increased it to 5 just to be on the safe side. Appears to be working fine now. I'm not sure how long your async call takes, or if you can afford a longer timeout interval than 1, but it's worth a shot.

Upvotes: 0

Related Questions