Permalink
Browse files

Adding GHRunForInterval and GHRunUntilTimeoutWhileBlock as utilities …

…to help test asynchronous runloop tasks.
  • Loading branch information...
1 parent f04913c commit 76b55b7904271c04faa5d8706eaf8d5b5096cafb John Boiles committed Oct 23, 2012
Showing with 193 additions and 0 deletions.
  1. +51 −0 Classes/GHTestUtils.h
  2. +52 −0 Classes/GHTestUtils.m
  3. +16 −0 Project-iOS/GHUnitIOS.xcodeproj/project.pbxproj
  4. +74 −0 Tests/GHTestUtilsTest.m
View
@@ -0,0 +1,51 @@
+//
+// GHTestUtils.h
+// GHUnitIOS
+//
+// Created by John Boiles on 10/22/12.
+// Copyright 2012. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+#define GHRunWhile(__CONDITION__) GHRunUntilTimeoutWhileBlock(10.0, ^BOOL{ return (__CONDITION__); })
+
+/*!
+ Run the main run loop for a period of time. This is useful to give views time to
+ render any asynchronously rendered views. However when possible, GHRunUntilTimeoutWhileBlock
+ should be used instead since it will provide more determinate output.
+
+ @param interval Interval for the main loop to run
+ */
+void GHRunForInterval(NSTimeInterval interval);
+
+/*!
+ Keep running the main runloop until whileBlock returns NO or timeout is reached.
+ This is useful for waiting until certain parts of views render. This method should be
+ used instead of putting GHRunForInterval in a while loop.
+
+ @param timeout Maximum time to run the main loop before giving up
+ @param whileBlock Block that returns YES if the main runloop should keep running
+ */
+void GHRunUntilTimeoutWhileBlock(NSTimeInterval timeout, BOOL(^whileBlock)());
View
@@ -0,0 +1,52 @@
+//
+// GHTestUtils.m
+// GHUnitIOS
+//
+// Created by John Boiles on 10/22/12.
+// Copyright 2012. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "GHTestUtils.h"
+
+void GHRunForInterval(NSTimeInterval interval) {
+ NSTimeInterval checkEveryInterval = 0.01;
+ NSDate *runUntilDate = [NSDate dateWithTimeIntervalSinceNow:interval];
+ NSArray *runLoopModes = [NSArray arrayWithObjects:NSDefaultRunLoopMode, NSRunLoopCommonModes, nil];
+ NSInteger runIndex = 0;
+ while ([runUntilDate compare:[NSDate dateWithTimeIntervalSinceNow:0]] == NSOrderedDescending) {
+ NSString *mode = [runLoopModes objectAtIndex:(runIndex++ % [runLoopModes count])];
+ @autoreleasepool {
+ if (!mode || ![[NSRunLoop currentRunLoop] runMode:mode beforeDate:[NSDate dateWithTimeIntervalSinceNow:checkEveryInterval]])
+ // If there were no run loop sources or timers then we should sleep for the interval
+ [NSThread sleepForTimeInterval:checkEveryInterval];
+ }
+ }
+}
+
+void GHRunUntilTimeoutWhileBlock(NSTimeInterval timeout, BOOL(^whileBlock)()) {
+ NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate] + timeout;
+ while (whileBlock() && ([NSDate timeIntervalSinceReferenceDate] < endTime)) {
+ GHRunForInterval(0.1);
+ }
+}
@@ -158,6 +158,11 @@
4228635914595D0400BF3ED2 /* GHImageDiffView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4228635714595D0400BF3ED2 /* GHImageDiffView.m */; };
423AF3371460965C00F04DC7 /* GHImageDiffView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4228635714595D0400BF3ED2 /* GHImageDiffView.m */; };
423AF3391460969000F04DC7 /* GHImageDiffView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4228635614595D0400BF3ED2 /* GHImageDiffView.h */; };
+ 42A274AA16361D9500BA4C74 /* GHTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 42A274A816361D9500BA4C74 /* GHTestUtils.h */; };
+ 42A274AB16361D9500BA4C74 /* GHTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 42A274A816361D9500BA4C74 /* GHTestUtils.h */; };
+ 42A274AC16361D9500BA4C74 /* GHTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 42A274A916361D9500BA4C74 /* GHTestUtils.m */; };
+ 42A274AD16361D9500BA4C74 /* GHTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 42A274A916361D9500BA4C74 /* GHTestUtils.m */; };
+ 42A274B216361F4200BA4C74 /* GHTestUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 42A274B116361F4200BA4C74 /* GHTestUtilsTest.m */; };
42A678EF1401423D0060D1B5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42A678EE1401423D0060D1B5 /* QuartzCore.framework */; };
/* End PBXBuildFile section */
@@ -279,6 +284,9 @@
422863101458BC1200BF3ED2 /* GHViewTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHViewTestCase.m; sourceTree = "<group>"; };
4228635614595D0400BF3ED2 /* GHImageDiffView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GHImageDiffView.h; sourceTree = "<group>"; };
4228635714595D0400BF3ED2 /* GHImageDiffView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHImageDiffView.m; sourceTree = "<group>"; };
+ 42A274A816361D9500BA4C74 /* GHTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GHTestUtils.h; sourceTree = "<group>"; };
+ 42A274A916361D9500BA4C74 /* GHTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHTestUtils.m; sourceTree = "<group>"; };
+ 42A274B116361F4200BA4C74 /* GHTestUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHTestUtilsTest.m; sourceTree = "<group>"; };
42A678EE1401423D0060D1B5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
@@ -392,6 +400,8 @@
0041C62113BA7C9A0015FC22 /* GHAsyncTestCase.h */,
0041C62213BA7C9A0015FC22 /* GHAsyncTestCase.m */,
0041C63B13BA7C9A0015FC22 /* GHUnit.h */,
+ 42A274A816361D9500BA4C74 /* GHTestUtils.h */,
+ 42A274A916361D9500BA4C74 /* GHTestUtils.m */,
0041C63C13BA7C9A0015FC22 /* Mock */,
0041C64513BA7C9A0015FC22 /* SharedUI */,
);
@@ -547,6 +557,7 @@
00C9798C13BA809600FC285C /* GHTestOnMainThread.m */,
42187C1715BF472300E702B6 /* GHUnitIOSTestViewTest.m */,
00C9798D13BA809600FC285C /* GHUnitIOSTestMain.m */,
+ 42A274B116361F4200BA4C74 /* GHTestUtilsTest.m */,
00C9798E13BA809600FC285C /* GTM */,
);
name = Tests;
@@ -607,6 +618,7 @@
422863111458BC1200BF3ED2 /* GHViewTestCase.h in Headers */,
4228635814595D0400BF3ED2 /* GHImageDiffView.h in Headers */,
00CEA041156C2E0B00BA3276 /* GHUIImageViewControl.h in Headers */,
+ 42A274AA16361D9500BA4C74 /* GHTestUtils.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -644,6 +656,7 @@
422863121458BC1200BF3ED2 /* GHViewTestCase.h in Headers */,
00CEA042156C2E0B00BA3276 /* GHUIImageViewControl.h in Headers */,
423AF3391460969000F04DC7 /* GHImageDiffView.h in Headers */,
+ 42A274AB16361D9500BA4C74 /* GHTestUtils.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -822,6 +835,7 @@
422863131458BC1200BF3ED2 /* GHViewTestCase.m in Sources */,
4228635914595D0400BF3ED2 /* GHImageDiffView.m in Sources */,
00CEA043156C2E0B00BA3276 /* GHUIImageViewControl.m in Sources */,
+ 42A274AC16361D9500BA4C74 /* GHTestUtils.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -855,6 +869,7 @@
422863141458BC1200BF3ED2 /* GHViewTestCase.m in Sources */,
00CEA044156C2E0B00BA3276 /* GHUIImageViewControl.m in Sources */,
423AF3371460965C00F04DC7 /* GHImageDiffView.m in Sources */,
+ 42A274AD16361D9500BA4C74 /* GHTestUtils.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -880,6 +895,7 @@
00C979A213BA809600FC285C /* GHUnitIOSTestMain.m in Sources */,
00C979A313BA809600FC285C /* GTMSenTestCase.m in Sources */,
42187C1815BF472300E702B6 /* GHUnitIOSTestViewTest.m in Sources */,
+ 42A274B216361F4200BA4C74 /* GHTestUtilsTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
@@ -0,0 +1,74 @@
+//
+// GHTestUtilsTest.m
+// GHUnitIOS
+//
+// Created by John Boiles on 10/22/12.
+// Copyright (c) 2012. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "GHTestCase.h"
+#import "GHTestUtils.h"
+
+@interface GHTestUtilsTest : GHTestCase {
+ BOOL _value;
+}
+@end
+
+@implementation GHTestUtilsTest
+
+- (BOOL)shouldRunOnMainThread { return YES; }
+
+- (void)setUp {
+ _value = NO;
+}
+
+- (void)testRunForInterval {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _value = YES;
+ });
+ GHRunForInterval(0.1);
+ GHAssertTrue(_value, @"_value should have been set to true when running the main loop.");
+}
+
+- (void)testRunWhile {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _value = YES;
+ });
+ GHRunUntilTimeoutWhileBlock(10.0, ^BOOL{
+ return !_value;
+ });
+ GHAssertTrue(_value, @"_value should have been set to true when running the main loop.");
+}
+
+- (void)testRunWhileTimesOut {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
+ _value = YES;
+ });
+ GHRunUntilTimeoutWhileBlock(0.1, ^BOOL{
+ return !_value;
+ });
+ GHAssertFalse(_value, @"_value should not have been set to true since GHRunUntilTimeoutWhileBlock should have timed out.");
+}
+
+@end

0 comments on commit 76b55b7

Please sign in to comment.