aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMNSThread+BlocksTest.m
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2012-12-18 00:30:10 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2012-12-18 00:30:10 +0000
commita18f91633f5bc0d805f167c340832b4515f5d682 (patch)
tree92cae0424db673064daad791a2ac92c5c55af006 /Foundation/GTMNSThread+BlocksTest.m
parente352172c303e12a7807d779042a74d314f76e277 (diff)
[Author: aharper]
Improve worker thread implementation: - Handle more of the NSThread interface. - Allow cancel or stop before we've started. - Cache looked up pthread_setname_np symbol in thread local storage. R=dmaclach,thomasvl APPROVED=dmaclach DELTA=441 (339 added, 38 deleted, 64 changed)
Diffstat (limited to 'Foundation/GTMNSThread+BlocksTest.m')
-rw-r--r--Foundation/GTMNSThread+BlocksTest.m267
1 files changed, 209 insertions, 58 deletions
diff --git a/Foundation/GTMNSThread+BlocksTest.m b/Foundation/GTMNSThread+BlocksTest.m
index e900b5b..606de6d 100644
--- a/Foundation/GTMNSThread+BlocksTest.m
+++ b/Foundation/GTMNSThread+BlocksTest.m
@@ -16,6 +16,7 @@
// under the License.
//
+#import <pthread.h>
#import "GTMSenTestCase.h"
#import "GTMNSThread+Blocks.h"
@@ -25,90 +26,240 @@
@interface GTMNSThread_BlocksTest : GTMTestCase {
@private
- NSThread *workerThread_;
- BOOL workerRunning_;
+ GTMSimpleWorkerThread *workerThread_;
}
-
-@property (nonatomic, readwrite, getter=isWorkerRunning) BOOL workerRunning;
@end
@implementation GTMNSThread_BlocksTest
-@synthesize workerRunning = workerRunning_;
-
-- (void)stopTestRunning:(GTMUnitTestingBooleanRunLoopContext *)context{
- [context setShouldStop:YES];
+- (void)setUp {
+ workerThread_ = [[GTMSimpleWorkerThread alloc] init];
+ [workerThread_ start];
}
-- (void)workerMain:(id)object {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- while ([self isWorkerRunning]) {
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
- beforeDate:[NSDate distantFuture]];
- }
- [pool drain];
+- (void)tearDown {
+ [workerThread_ stop];
+ [workerThread_ release];
}
-- (void)killWorkerThread:(GTMUnitTestingBooleanRunLoopContext *)context {
- [self setWorkerRunning:NO];
- [context setShouldStop:YES];
+- (void)testPerformBlockOnCurrentThread {
+ NSThread *currentThread = [NSThread currentThread];
+
+ GTMUnitTestingBooleanRunLoopContext *context =
+ [GTMUnitTestingBooleanRunLoopContext context];
+ __block NSThread *runThread = nil;
+
+ // Straight block runs right away (no runloop spin)
+ runThread = nil;
+ [context setShouldStop:NO];
+ [currentThread gtm_performBlock:^{
+ runThread = [NSThread currentThread];
+ [context setShouldStop:YES];
+ }];
+ STAssertEqualObjects(runThread, currentThread, nil);
+ STAssertTrue([context shouldStop], nil);
+
+ // Block with waiting runs immediately as well.
+ runThread = nil;
+ [context setShouldStop:NO];
+ [currentThread gtm_performWaitingUntilDone:YES block:^{
+ runThread = [NSThread currentThread];
+ [context setShouldStop:YES];
+ }];
+ STAssertEqualObjects(runThread, currentThread, nil);
+ STAssertTrue([context shouldStop], nil);
+
+ // Block without waiting requires a runloop spin.
+ runThread = nil;
+ [context setShouldStop:NO];
+ [currentThread gtm_performWaitingUntilDone:NO block:^{
+ runThread = [NSThread currentThread];
+ [context setShouldStop:YES];
+ }];
+ STAssertTrue([[NSRunLoop currentRunLoop]
+ gtm_runUpToSixtySecondsWithContext:context], nil);
+ STAssertEqualObjects(runThread, currentThread, nil);
+ STAssertTrue([context shouldStop], nil);
}
-- (void)setUp {
- [self setWorkerRunning:YES];
- workerThread_ = [[NSThread alloc] initWithTarget:self
- selector:@selector(workerMain:)
- object:nil];
- [workerThread_ start];
+- (void)testPerformBlockInBackground {
+ GTMUnitTestingBooleanRunLoopContext *context =
+ [GTMUnitTestingBooleanRunLoopContext context];
+ __block NSThread *runThread = nil;
+ [NSThread gtm_performBlockInBackground:^{
+ runThread = [NSThread currentThread];
+ [context setShouldStop:YES];
+ }];
+ STAssertTrue([[NSRunLoop currentRunLoop]
+ gtm_runUpToSixtySecondsWithContext:context], nil);
+ STAssertNotNil(runThread, nil);
+ STAssertNotEqualObjects(runThread, [NSThread currentThread], nil);
}
-- (void)tearDown {
- GTMUnitTestingBooleanRunLoopContext *context
- = [GTMUnitTestingBooleanRunLoopContext context];
- [self performSelector:@selector(killWorkerThread:)
- onThread:workerThread_
- withObject:context
- waitUntilDone:NO];
- NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
- STAssertTrue([runLoop gtm_runUpToSixtySecondsWithContext:context], nil);
- [workerThread_ release];
+- (void)testWorkerThreadBasics {
+ // Unstarted worker isn't running.
+ GTMSimpleWorkerThread *worker = [[GTMSimpleWorkerThread alloc] init];
+ STAssertFalse([worker isExecuting], nil);
+ STAssertFalse([worker isFinished], nil);
+
+ // Unstarted worker can be stopped without error.
+ [worker stop];
+ STAssertFalse([worker isExecuting], nil);
+ STAssertTrue([worker isFinished], nil);
+
+ // And can be stopped again
+ [worker stop];
+ STAssertFalse([worker isExecuting], nil);
+ STAssertTrue([worker isFinished], nil);
+
+ // A thread we start can be stopped with correct state.
+ worker = [[GTMSimpleWorkerThread alloc] init];
+ STAssertFalse([worker isExecuting], nil);
+ STAssertFalse([worker isFinished], nil);
+ [worker start];
+ STAssertTrue([worker isExecuting], nil);
+ STAssertFalse([worker isFinished], nil);
+ [worker stop];
+ STAssertFalse([worker isExecuting], nil);
+ STAssertTrue([worker isFinished], nil);
+
+ // A cancel is also honored
+ worker = [[GTMSimpleWorkerThread alloc] init];
+ STAssertFalse([worker isExecuting], nil);
+ STAssertFalse([worker isFinished], nil);
+ [worker start];
+ STAssertTrue([worker isExecuting], nil);
+ STAssertFalse([worker isFinished], nil);
+ [worker cancel];
+ // And after some time we're done. We're generous here, this needs to
+ // exceed the worker thread's runloop timeout.
+ sleep(5);
+ STAssertFalse([worker isExecuting], nil);
+ STAssertTrue([worker isFinished], nil);
}
-- (void)testPerformBlock {
- NSThread *currentThread = [NSThread currentThread];
- GTMUnitTestingBooleanRunLoopContext *context
- = [GTMUnitTestingBooleanRunLoopContext context];
+- (void)testWorkerThreadStopTiming {
+ // Throw a sleep and make sure that we stop as soon as we can.
+ NSDate *start = [NSDate date];
+ NSConditionLock *threadLock = [[[NSConditionLock alloc] initWithCondition:0]
+ autorelease];
[workerThread_ gtm_performBlock:^{
- [self performSelector:@selector(stopTestRunning:)
- onThread:currentThread
- withObject:context
- waitUntilDone:YES];
+ [threadLock lock];
+ [threadLock unlockWithCondition:1];
+ sleep(10);
}];
- NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
- STAssertTrue([runLoop gtm_runUpToSixtySecondsWithContext:context], nil);
+ [threadLock lockWhenCondition:1];
+ [threadLock unlock];
+ [workerThread_ stop];
+ STAssertFalse([workerThread_ isExecuting], nil);
+ STAssertTrue([workerThread_ isFinished], nil);
+ STAssertEqualsWithAccuracy(-[start timeIntervalSinceNow], 10.0, 2.0, nil);
}
-- (void)testPerformBlockWaitUntilDone {
- GTMUnitTestingBooleanRunLoopContext *context
- = [GTMUnitTestingBooleanRunLoopContext context];
+- (void)testPerformBlockOnWorkerThread {
+ GTMUnitTestingBooleanRunLoopContext *context =
+ [GTMUnitTestingBooleanRunLoopContext context];
+ __block NSThread *runThread = nil;
+
+ // Runs on the other thread
+ runThread = nil;
+ [context setShouldStop:NO];
+ [workerThread_ gtm_performBlock:^{
+ runThread = [NSThread currentThread];
+ [context setShouldStop:YES];
+ }];
+ STAssertTrue([[NSRunLoop currentRunLoop]
+ gtm_runUpToSixtySecondsWithContext:context], nil);
+ STAssertNotNil(runThread, nil);
+ STAssertEqualObjects(runThread, workerThread_, nil);
+
+ // Other thread no wait.
+ runThread = nil;
+ [context setShouldStop:NO];
+ [workerThread_ gtm_performWaitingUntilDone:NO block:^{
+ runThread = [NSThread currentThread];
+ [context setShouldStop:YES];
+ }];
+ STAssertTrue([[NSRunLoop currentRunLoop]
+ gtm_runUpToSixtySecondsWithContext:context], nil);
+ STAssertNotNil(runThread, nil);
+ STAssertEqualObjects(runThread, workerThread_, nil);
+
+ // Waiting requires no runloop spin
+ runThread = nil;
+ [context setShouldStop:NO];
[workerThread_ gtm_performWaitingUntilDone:YES block:^{
+ runThread = [NSThread currentThread];
[context setShouldStop:YES];
}];
STAssertTrue([context shouldStop], nil);
+ STAssertNotNil(runThread, nil);
+ STAssertEqualObjects(runThread, workerThread_, nil);
}
-- (void)testPerformBlockInBackground {
- NSThread *currentThread = [NSThread currentThread];
- GTMUnitTestingBooleanRunLoopContext *context
- = [GTMUnitTestingBooleanRunLoopContext context];
- [NSThread gtm_performBlockInBackground:^{
- [self performSelector:@selector(stopTestRunning:)
- onThread:currentThread
- withObject:context
- waitUntilDone:YES];
+- (void)testExitingBlockIsExecuting {
+ NSConditionLock *threadLock = [[[NSConditionLock alloc] initWithCondition:0]
+ autorelease];
+ [workerThread_ gtm_performWaitingUntilDone:NO block:^{
+ [threadLock lock];
+ [threadLock unlockWithCondition:1];
+ pthread_exit(NULL);
+ }];
+ [threadLock lockWhenCondition:1];
+ [threadLock unlock];
+ // Give the pthread_exit() a bit of time
+ sleep(5);
+ // Did we notice the thread died? Does [... isExecuting] clean up?
+ STAssertFalse([workerThread_ isExecuting], nil);
+ STAssertTrue([workerThread_ isFinished], nil);
+}
+
+- (void)testExitingBlockCancel {
+ NSConditionLock *threadLock = [[[NSConditionLock alloc] initWithCondition:0]
+ autorelease];
+ [workerThread_ gtm_performWaitingUntilDone:NO block:^{
+ [threadLock lock];
+ [threadLock unlockWithCondition:1];
+ pthread_exit(NULL);
+ }];
+ [threadLock lockWhenCondition:1];
+ [threadLock unlock];
+ // Give the pthread_exit() a bit of time
+ sleep(5);
+ // Cancel/stop the thread
+ [workerThread_ stop];
+ // Did we notice the thread died? Did we clean up?
+ STAssertFalse([workerThread_ isExecuting], nil);
+ STAssertTrue([workerThread_ isFinished], nil);
+}
+
+- (void)testStopFromThread {
+ NSConditionLock *threadLock = [[[NSConditionLock alloc] initWithCondition:0]
+ autorelease];
+ [workerThread_ gtm_performWaitingUntilDone:NO block:^{
+ [threadLock lock];
+ [workerThread_ stop]; // Shold not block.
+ [threadLock unlockWithCondition:1];
+ }];
+ // Block should complete before the stop occurs.
+ [threadLock lockWhenCondition:1];
+ [threadLock unlock];
+ // Still need to give the thread a moment to not be executing
+ sleep(5);
+ STAssertFalse([workerThread_ isExecuting], nil);
+ STAssertTrue([workerThread_ isFinished], nil);
+}
+
+- (void)testPThreadName {
+ NSString *testName = @"InigoMontoya";
+ [workerThread_ setName:testName];
+ [workerThread_ gtm_performWaitingUntilDone:NO block:^{
+ STAssertEqualObjects([workerThread_ name], testName, nil);
+ char threadName[100];
+ pthread_getname_np(pthread_self(), threadName, 100);
+ STAssertEqualObjects([NSString stringWithUTF8String:threadName],
+ testName, nil);
}];
- NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
- STAssertTrue([runLoop gtm_runUpToSixtySecondsWithContext:context], nil);
}
@end