Question

I'm trying to use the pattern described in: Grand Central Dispatch and unit testing, Pattern for unit testing async queue that calls main queue on completion and mainly in: https://github.com/AFNetworking/AFNetworking/issues/466#issuecomment-7917445.

In my unit tests I need to "straighten" an asynchronous flow of some of the methods I have (fx AFNetworking requests operations):

This is what I use inside my tests for such "forced synchronous operations":

@property (nonatomic, assign) dispatch_semaphore_t semaphore;
// ...
- (void)runTestWithBlock:(void (^)(void))block {
    self.semaphore = dispatch_semaphore_create(0);

    block();

    while (dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_NOW))
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:[NSDate dateWithTimeIntervalSinceNow:2]];

    dispatch_release(self.semaphore);
}

- (void)blockTestCompletedWithBlock:(void (^)(void))block {
    dispatch_semaphore_signal(self.semaphore);

    if (block) {
        block();
    }
}

If use fx dateWithTimeIntervalSinceNow:10 i.e. with a value more than zero (like quoted SO topics and Github comment state) I get equivalent superfluous harmful delays in my tests. If I set ...SinceNow:0... all my tests work without any delays and I don't see any problems with this 0 value.

Apple doc says:

Runs the loop once, blocking for input in the specified mode until a given date.
If no input sources or timers are attached to the run loop, this method exits immediately and returns NO; otherwise, it returns after either the first input source is processed *or* limitDate is reached.

This "or" makes me guessing whether I may use 0 seconds without affecting my code in any bad way.

I would also be thankful for any alternative to using exactly this variation of runMode... method or for a completely different solution for solving the problem described in quoted links.

Was it helpful?

Solution

Just to note, I still don't know the answer to the original question.

The intermediate solution to this problem (inspired by the code GHUnit uses to test asynchronous methods) was to use small time interval like [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeItervalSinceNow:0.05].

Currently I use the following loop:

while (dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_NOW))
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.05, YES);

Delays have gone.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top