BoltsTests/TaskTests.m (613 lines of code) (raw):

/* * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ @import XCTest; #import <Bolts/Bolts.h> @interface TaskTests : XCTestCase @end @implementation TaskTests - (void)testBasicOnSuccess { [[[BFTask taskWithResult:@"foo"] continueWithSuccessBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"foo", t.result); return nil; }] waitUntilFinished]; } - (void)testBasicOnSuccessWithExecutor { __block BOOL completed = NO; BFTask *task = [[BFTask taskWithDelay:100] continueWithExecutor:[BFExecutor immediateExecutor] withSuccessBlock:^id _Nullable(BFTask * _Nonnull _) { completed = YES; return nil; }]; [task waitUntilFinished]; XCTAssertTrue(completed); XCTAssertTrue(task.completed); XCTAssertFalse(task.faulted); XCTAssertFalse(task.cancelled); XCTAssertNil(task.result); } - (void)testBasicOnSuccessWithToken { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; BFTask *task = [BFTask taskWithDelay:100]; task = [task continueWithSuccessBlock:^id(BFTask *t) { XCTFail(@"Success block should not be triggered"); return nil; } cancellationToken:cts.token]; [cts cancel]; [task waitUntilFinished]; XCTAssertTrue(task.cancelled); } - (void)testBasicOnSuccessWithExecutorToken { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; BFTask *task = [BFTask taskWithDelay:100]; task = [task continueWithExecutor:[BFExecutor immediateExecutor] successBlock:^id(BFTask *t) { XCTFail(@"Success block should not be triggered"); return nil; } cancellationToken:cts.token]; [cts cancel]; [task waitUntilFinished]; XCTAssertTrue(task.cancelled); } - (void)testBasicOnSuccessWithCancelledToken { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; BFTask *task = [BFTask taskWithResult:nil]; [cts cancel]; task = [task continueWithExecutor:[BFExecutor immediateExecutor] successBlock:^id(BFTask *t) { XCTFail(@"Success block should not be triggered"); return nil; } cancellationToken:cts.token]; XCTAssertTrue(task.isCancelled); } - (void)testBasicContinueWithError { NSError *originalError = [NSError errorWithDomain:@"Bolts" code:22 userInfo:nil]; [[[BFTask taskWithError:originalError] continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error, @"Task should have failed."); XCTAssertEqual((NSInteger)22, t.error.code); return nil; }] waitUntilFinished]; } - (void)testBasicContinueWithToken { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; BFTask *task = [BFTask taskWithDelay:100]; task = [task continueWithExecutor:[BFExecutor immediateExecutor] block:^id(BFTask *t) { XCTFail(@"Continuation block should not be triggered"); return nil; } cancellationToken:cts.token]; [cts cancel]; [task waitUntilFinished]; XCTAssertTrue(task.isCancelled); } - (void)testBasicContinueWithCancelledToken { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; BFTask *task = [BFTask taskWithResult:nil]; [cts cancel]; task = [task continueWithExecutor:[BFExecutor immediateExecutor] block:^id(BFTask *t) { XCTFail(@"Continuation block should not be triggered"); return nil; } cancellationToken:cts.token]; XCTAssertTrue(task.isCancelled); } - (void)testFinishLaterWithSuccess { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"bar", t.result); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.result = @"bar"; return nil; }]; [task waitUntilFinished]; } - (void)testFinishLaterWithError { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)23, t.error.code); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.error = [NSError errorWithDomain:@"Bolts" code:23 userInfo:nil]; return nil; }]; [task waitUntilFinished]; } - (void)testTransformConstantToConstant { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [[tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"foo", t.result); return @"bar"; }] continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"bar", t.result); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.result = @"foo"; return nil; }]; [task waitUntilFinished]; } - (void)testTransformErrorToConstant { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [[tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)23, t.error.code); return @"bar"; }] continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"bar", t.result); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.error = [NSError errorWithDomain:@"Bolts" code:23 userInfo:nil]; return nil; }]; [task waitUntilFinished]; } - (void)testReturnSuccessfulTaskFromContinuation { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [[tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"foo", t.result); return [BFTask taskWithResult:@"bar"]; }] continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"bar", t.result); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.result = @"foo"; return nil; }]; [task waitUntilFinished]; } - (void)testReturnSuccessfulTaskFromContinuationAfterError { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [[tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)23, t.error.code); return [BFTask taskWithResult:@"bar"]; }] continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"bar", t.result); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.error = [NSError errorWithDomain:@"Bolts" code:23 userInfo:nil]; return nil; }]; [task waitUntilFinished]; } - (void)testReturnErrorTaskFromContinuation { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [[tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"foo", t.result); NSError *originalError = [NSError errorWithDomain:@"Bolts" code:24 userInfo:nil]; return [BFTask taskWithError:originalError]; }] continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)24, t.error.code); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.result = @"foo"; return nil; }]; [task waitUntilFinished]; } - (void)testReturnErrorTaskFromContinuationAfterError { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [[tcs.task continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)23, t.error.code); NSError *originalError = [NSError errorWithDomain:@"Bolts" code:24 userInfo:nil]; return [BFTask taskWithError:originalError]; }] continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)24, t.error.code); return nil; }]; [[BFTask taskWithDelay:0] continueWithBlock:^id(BFTask *t) { tcs.error = [NSError errorWithDomain:@"Bolts" code:23 userInfo:nil]; return nil; }]; [task waitUntilFinished]; } - (void)testPassOnError { NSError *originalError = [NSError errorWithDomain:@"Bolts" code:30 userInfo:nil]; [[[[[[[[BFTask taskWithError:originalError] continueWithSuccessBlock:^id(BFTask *t) { XCTFail(@"This callback should be skipped."); return nil; }] continueWithSuccessBlock:^id(BFTask *t) { XCTFail(@"This callback should be skipped."); return nil; }] continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)30, t.error.code); NSError *newError = [NSError errorWithDomain:@"Bolts" code:31 userInfo:nil]; return [BFTask taskWithError:newError]; }] continueWithSuccessBlock:^id(BFTask *t) { XCTFail(@"This callback should be skipped."); return nil; }] continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertEqual((NSInteger)31, t.error.code); return [BFTask taskWithResult:@"okay"]; }] continueWithSuccessBlock:^id(BFTask *t) { XCTAssertEqualObjects(@"okay", t.result); return nil; }] waitUntilFinished]; } - (void)testCancellation { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; BFTask *task = [[BFTask taskWithDelay:100] continueWithBlock:^id(BFTask *t) { return tcs.task; }]; [tcs cancel]; [task waitUntilFinished]; XCTAssertTrue(task.isCancelled); } - (void)testTaskForCompletionOfAllTasksSuccess { NSMutableArray *tasks = [NSMutableArray array]; const int kTaskCount = 20; for (int i = 0; i < kTaskCount; ++i) { double sleepTimeInMs = rand() % 100; [tasks addObject:[[BFTask taskWithDelay:(int)sleepTimeInMs] continueWithBlock:^id(BFTask *t) { return @(i); }]]; } [[[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *t) { XCTAssertNil(t.error); XCTAssertFalse(t.isCancelled); for (int i = 0; i < kTaskCount; ++i) { XCTAssertEqual(i, [((BFTask *)[tasks objectAtIndex:i]).result intValue]); } return nil; }] waitUntilFinished]; } - (void)testTaskForCompletionOfAllTasksOneError { NSMutableArray *tasks = [NSMutableArray array]; const int kTaskCount = 20; for (int i = 0; i < kTaskCount; ++i) { double sleepTimeInMs = rand() % 100; [tasks addObject:[[BFTask taskWithDelay:(int)sleepTimeInMs] continueWithBlock:^id(BFTask *t) { if (i == 10) { return [BFTask taskWithError:[NSError errorWithDomain:@"BoltsTests" code:35 userInfo:nil]]; } return @(i); }]]; } [[[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertFalse(t.isCancelled); XCTAssertEqualObjects(@"BoltsTests", t.error.domain); XCTAssertEqual(35, (int)t.error.code); for (int i = 0; i < kTaskCount; ++i) { if (i == 10) { XCTAssertNotNil(((BFTask *)[tasks objectAtIndex:i]).error); } else { XCTAssertEqual(i, [((BFTask *)[tasks objectAtIndex:i]).result intValue]); } } return nil; }] waitUntilFinished]; } - (void)testTaskForCompletionOfAllTasksTwoErrors { NSMutableArray *tasks = [NSMutableArray array]; const int kTaskCount = 20; for (int i = 0; i < kTaskCount; ++i) { double sleepTimeInMs = rand() % 100; [tasks addObject:[[BFTask taskWithDelay:(int)sleepTimeInMs] continueWithBlock:^id(BFTask *t) { if (i == 10 || i == 11) { return [BFTask taskWithError:[NSError errorWithDomain:@"BoltsTests" code:35 userInfo:nil]]; } return @(i); }]]; } [[[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *t) { XCTAssertNotNil(t.error); XCTAssertFalse(t.isCancelled); XCTAssertEqualObjects(@"bolts", t.error.domain); XCTAssertEqual(kBFMultipleErrorsError, t.error.code); NSArray *errors = [t.error.userInfo objectForKey:BFTaskMultipleErrorsUserInfoKey]; XCTAssertEqualObjects(@"BoltsTests", [[errors objectAtIndex:0] domain]); XCTAssertEqual(35, (int)[[errors objectAtIndex:0] code]); XCTAssertEqualObjects(@"BoltsTests", [[errors objectAtIndex:1] domain]); XCTAssertEqual(35, (int)[[errors objectAtIndex:1] code]); for (int i = 0; i < kTaskCount; ++i) { if (i == 10 || i == 11) { XCTAssertNotNil(((BFTask *)[tasks objectAtIndex:i]).error); } else { XCTAssertEqual(i, [((BFTask *)[tasks objectAtIndex:i]).result intValue]); } } return nil; }] waitUntilFinished]; } - (void)testTaskForCompletionOfAllTasksCancelled { NSMutableArray *tasks = [NSMutableArray array]; const int kTaskCount = 20; for (int i = 0; i < kTaskCount; ++i) { double sleepTimeInMs = rand() % 100; [tasks addObject:[[BFTask taskWithDelay:(int)sleepTimeInMs] continueWithBlock:^id(BFTask *t) { if (i == 10) { return [BFTask cancelledTask]; } return @(i); }]]; } [[[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *t) { XCTAssertNil(t.error); XCTAssertTrue(t.isCancelled); for (int i = 0; i < kTaskCount; ++i) { if (i == 10) { XCTAssertTrue(((BFTask *)[tasks objectAtIndex:i]).isCancelled); } else { XCTAssertEqual(i, [((BFTask *)[tasks objectAtIndex:i]).result intValue]); } } return nil; }] waitUntilFinished]; } - (void)testTaskForCompletionOfAllTasksNoTasksImmediateCompletion { NSMutableArray *tasks = [NSMutableArray array]; BFTask *task = [BFTask taskForCompletionOfAllTasks:tasks]; XCTAssertTrue(task.completed); XCTAssertFalse(task.cancelled); XCTAssertFalse(task.faulted); } - (void)testTaskForCompletionOfAllTasksWithResultsSuccess { NSMutableArray *tasks = [NSMutableArray array]; const int kTaskCount = 20; for (int i = 0; i < kTaskCount; ++i) { double sleepTimeInMs = i * 10; int result = i + 1; [tasks addObject:[[BFTask taskWithDelay:(int)sleepTimeInMs] continueWithBlock:^id(BFTask *__unused t) { return @(result); }]]; } [[[BFTask taskForCompletionOfAllTasksWithResults:tasks] continueWithBlock:^id(BFTask *t) { XCTAssertFalse(t.cancelled); XCTAssertFalse(t.faulted); NSArray *results = t.result; for (int i = 0; i < kTaskCount; ++i) { NSNumber *individualResult = [results objectAtIndex:i]; XCTAssertEqual([individualResult intValue], [((BFTask *)[tasks objectAtIndex:i]).result intValue]); } return nil; }] waitUntilFinished]; } - (void)testTaskForCompletionOfAllTasksErrorCancelledSuccess { BFTask *errorTask = [BFTask taskWithError:[NSError new]]; BFTask *cancelledTask = [BFTask cancelledTask]; BFTask *successfulTask = [BFTask taskWithResult:[NSNumber numberWithInt:2]]; BFTask *allTasks = [BFTask taskForCompletionOfAllTasks:@[ successfulTask, cancelledTask, errorTask ]]; XCTAssertTrue(allTasks.faulted, @"Task should be faulted"); } - (void)testTaskForCompletionOfAllTasksExceptionErrorCancelledSuccess { BFTask *errorTask = [BFTask taskWithError:[NSError new]]; BFTask *cancelledTask = [BFTask cancelledTask]; BFTask *successfulTask = [BFTask taskWithResult:[NSNumber numberWithInt:2]]; BFTask *allTasks = [BFTask taskForCompletionOfAllTasks:@[ successfulTask, cancelledTask, errorTask ]]; XCTAssertTrue(allTasks.faulted, @"Task should be faulted"); XCTAssertNotNil(allTasks.error, @"Task should have error"); } - (void)testTaskForCompletionOfAllTasksErrorCancelled { BFTask *errorTask = [BFTask taskWithError:[NSError new]]; BFTask *cancelledTask = [BFTask cancelledTask]; BFTask *allTasks = [BFTask taskForCompletionOfAllTasks:@[ cancelledTask, errorTask ]]; XCTAssertTrue(allTasks.faulted, @"Task should be faulted"); } - (void)testTaskForCompletionOfAllTasksSuccessCancelled { BFTask *cancelledTask = [BFTask cancelledTask]; BFTask *successfulTask = [BFTask taskWithResult:[NSNumber numberWithInt:2]]; BFTask *allTasks = [BFTask taskForCompletionOfAllTasks:@[ successfulTask, cancelledTask ]]; XCTAssertTrue(allTasks.cancelled, @"Task should be cancelled"); } - (void)testTaskForCompletionOfAllTasksSuccessError { BFTask *errorTask = [BFTask taskWithError:[NSError new]]; BFTask *successfulTask = [BFTask taskWithResult:[NSNumber numberWithInt:2]]; BFTask *allTasks = [BFTask taskForCompletionOfAllTasks:@[ successfulTask, errorTask ]]; XCTAssertTrue(allTasks.faulted, @"Task should be faulted"); } - (void)testTaskForCompletionOfAllTasksWithResultsNoTasksImmediateCompletion { NSMutableArray *tasks = [NSMutableArray array]; BFTask *task = [BFTask taskForCompletionOfAllTasksWithResults:tasks]; XCTAssertTrue(task.completed); XCTAssertFalse(task.cancelled); XCTAssertFalse(task.faulted); XCTAssertTrue(task.result != nil); } - (void)testTasksForTaskForCompletionOfAnyTasksWithSuccess { BFTask *task = [BFTask taskForCompletionOfAnyTask:@[[BFTask taskWithDelay:20], [BFTask taskWithResult:@"success"]]]; [task waitUntilFinished]; XCTAssertEqualObjects(@"success", task.result); } - (void)testTasksForTaskForCompletionOfAnyTasksWithRacing { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); BFExecutor *executor = [BFExecutor executorWithDispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; BFTask *first = [BFTask taskFromExecutor:executor withBlock:^id _Nullable { return @"first"; }]; BFTask *second = [BFTask taskFromExecutor:executor withBlock:^id _Nullable { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); return @"second"; }]; BFTask *task = [BFTask taskForCompletionOfAnyTask:@[first, second]]; [task waitUntilFinished]; dispatch_semaphore_signal(semaphore); XCTAssertEqualObjects(@"first", task.result); } - (void)testTasksForTaskForCompletionOfAnyTasksWithErrorAndSuccess { NSError *error = [NSError errorWithDomain:@"BoltsTests" code:35 userInfo:nil]; BFTask *task = [BFTask taskForCompletionOfAnyTask:@[[BFTask taskWithError:error], [BFTask taskWithResult:@"success"]]]; [task waitUntilFinished]; XCTAssertEqualObjects(@"success", task.result); XCTAssertNil(task.error); } - (void)testTasksForTaskForCompletionOfAnyTasksWithError { NSError *error = [NSError errorWithDomain:@"BoltsTests" code:35 userInfo:nil]; BFTask *task = [BFTask taskForCompletionOfAnyTask:@[[BFTask taskWithError:error]]]; [task waitUntilFinished]; XCTAssertEqualObjects(error, task.error); XCTAssertNotNil(task.error); } - (void)testTasksForTaskForCompletionOfAnyTasksWithNilArray { BFTask *task = [BFTask taskForCompletionOfAnyTask:nil]; [task waitUntilFinished]; XCTAssertNil(task.result); XCTAssertNil(task.error); } - (void)testTasksForTaskForCompletionOfAnyTasksAllErrors { NSError *error = [NSError errorWithDomain:@"BoltsTests" code:35 userInfo:nil]; BFTask *task = [BFTask taskForCompletionOfAnyTask:@[[BFTask taskWithError:error], [BFTask taskWithError:error]]]; [task waitUntilFinished]; XCTAssertNil(task.result); XCTAssertNotNil(task.error); XCTAssertNotNil(task.error.userInfo); XCTAssertEqualObjects(@"bolts", task.error.domain); XCTAssertTrue([task.error.userInfo[@"errors"] isKindOfClass:[NSArray class]]); XCTAssertEqual(2, [task.error.userInfo[@"errors"] count]); } - (void)testWaitUntilFinished { BFTask *task = [[BFTask taskWithDelay:50] continueWithBlock:^id(BFTask *t) { return @"foo"; }]; [task waitUntilFinished]; XCTAssertEqualObjects(@"foo", task.result); } - (void)testDelayWithToken { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; BFTask *task = [BFTask taskWithDelay:100 cancellationToken:cts.token]; [cts cancel]; [task waitUntilFinished]; XCTAssertTrue(task.cancelled, @"Task should be cancelled immediately"); } - (void)testDelayWithCancelledToken { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; [cts cancel]; BFTask *task = [BFTask taskWithDelay:100 cancellationToken:cts.token]; XCTAssertTrue(task.cancelled, @"Task should be cancelled immediately"); } - (void)testTaskFromExecutor { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0L); BFExecutor *queueExecutor = [BFExecutor executorWithDispatchQueue:queue]; BFTask *task = [BFTask taskFromExecutor:queueExecutor withBlock:^id() { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" XCTAssertEqual(queue, dispatch_get_current_queue()); #pragma clang diagnostic pop return @"foo"; }]; [task waitUntilFinished]; XCTAssertEqual(@"foo", task.result); } - (void)testDescription { BFTask *task = [BFTask taskWithResult:nil]; NSString *expected = [NSString stringWithFormat:@"<BFTask: %p; completed = YES; cancelled = NO; faulted = NO; result = (null)>", task]; NSString *description = task.description; XCTAssertTrue([expected isEqualToString:description]); } - (void)testReturnTaskFromContinuationWithCancellation { BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; XCTestExpectation *expectation = [self expectationWithDescription:@"task"]; [[[BFTask taskWithDelay:1] continueWithBlock:^id(BFTask *t) { [cts cancel]; return [BFTask taskWithDelay:10]; } cancellationToken:cts.token] continueWithBlock:^id(BFTask *t) { XCTAssertTrue(t.cancelled); [expectation fulfill]; return nil; }]; [self waitForExpectationsWithTimeout:10.0 handler:nil]; } - (void)testSetResult { BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; taskCompletionSource.result = @"a"; XCTAssertThrowsSpecificNamed([taskCompletionSource setResult:@"b"], NSException, NSInternalInconsistencyException); XCTAssertTrue(taskCompletionSource.task.completed); XCTAssertEqualObjects(taskCompletionSource.task.result, @"a"); } - (void)testTrySetResult { BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; [taskCompletionSource trySetResult:@"a"]; [taskCompletionSource trySetResult:@"b"]; XCTAssertTrue(taskCompletionSource.task.completed); XCTAssertEqualObjects(taskCompletionSource.task.result, @"a"); } - (void)testSetError { BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; NSError *error = [NSError errorWithDomain:@"TestDomain" code:100500 userInfo:nil]; taskCompletionSource.error = error; XCTAssertThrowsSpecificNamed([taskCompletionSource setError:error], NSException, NSInternalInconsistencyException); XCTAssertTrue(taskCompletionSource.task.completed); XCTAssertTrue(taskCompletionSource.task.faulted); XCTAssertEqualObjects(taskCompletionSource.task.error, error); } - (void)testTrySetError { BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; NSError *error = [NSError errorWithDomain:@"TestDomain" code:100500 userInfo:nil]; [taskCompletionSource trySetError:error]; [taskCompletionSource trySetError:error]; XCTAssertTrue(taskCompletionSource.task.completed); XCTAssertTrue(taskCompletionSource.task.faulted); XCTAssertEqualObjects(taskCompletionSource.task.error, error); } - (void)testSetCancelled { BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; [taskCompletionSource cancel]; XCTAssertThrowsSpecificNamed([taskCompletionSource cancel], NSException, NSInternalInconsistencyException); XCTAssertTrue(taskCompletionSource.task.completed); XCTAssertTrue(taskCompletionSource.task.cancelled); } - (void)testTrySetCancelled { BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; [taskCompletionSource trySetCancelled]; [taskCompletionSource trySetCancelled]; XCTAssertTrue(taskCompletionSource.task.completed); XCTAssertTrue(taskCompletionSource.task.cancelled); } - (void)testMultipleWaitUntilFinished { BFTask *task = [[BFTask taskWithDelay:50] continueWithBlock:^id(BFTask *t) { return @"foo"; }]; [task waitUntilFinished]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [task waitUntilFinished]; [expectation fulfill]; }); [self waitForExpectationsWithTimeout:10.0 handler:nil]; } - (void)testMultipleThreadsWaitUntilFinished { BFTask *task = [[BFTask taskWithDelay:500] continueWithBlock:^id(BFTask *t) { return @"foo"; }]; dispatch_queue_t queue = dispatch_queue_create("com.bolts.tests.wait", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_group_async(group, queue, ^{ [task waitUntilFinished]; }); dispatch_group_async(group, queue, ^{ [task waitUntilFinished]; }); [task waitUntilFinished]; dispatch_group_wait(group, DISPATCH_TIME_FOREVER); [expectation fulfill]; }); [self waitForExpectationsWithTimeout:10.0 handler:nil]; } @end