»
January 19, 2009
»

AMRunWhile

Here is tiny macro what runs NSRunLoop with step of 0.1 second until some condition is met. Useful for NSRunLoop dependent unittests:

#define kRunLoopTestTimeout 10.0

#define AMRunWhile(expr) {                                                                \
  NSDate *start = [NSDate date];                                                          \
  do {                                                                                    \
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]]; \
  } while ((expr) && ([start timeIntervalSinceNow] > -kRunLoopTestTimeout));              \
  STAssertTrue(!(expr), @"Loop timeout for %@", [NSString stringWithUTF8String: #expr]);  \
}

Usage example in OCUnit test case:

- (void)testShouldWaitUntilTimerFinishes
{
   Something *something = [[Something alloc] init];
   [something start];
   
   // should set finished later. see example implementation.
   STAssertFalse([something finished], nil);
   
   // wait until finished
   AMRunWhile([something finished] == NO);
   
   // this is not necessary in actual unittest
   STAssertTrue([something finished], nil);

   [something release];
}

And there is Something implementation with NSTimer:

@implementation Something
@synthesize finished;

- (id)init
{
   if(self = [super init]) {
      finished = false;
   }
   return self;
}

- (void)start
{
   // adds timer what will call -finish after one second
   NSTimer *timer = [NSTimer timerWithTimeInterval:1 
                                            target:self 
                                          selector:@selector(finish) 
                                          userInfo:nil 
                                           repeats:NO];

   [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

- (void)finish
{
   self.finished = YES;
}

@end
 
Internet Explorer 6
Are you serious?