#import #import #import "FutureUtil.h" #import "TestUtil.h" #import "ThreadManager.h" @interface FutureUtilTest : XCTestCase @end @implementation FutureUtilTest - (void)testOperationTry { TOCFuture *f = [TOCFuture operationTry:^TOCFuture *(TOCCancelToken *_) { @throw @"Fail"; }](nil); test([f isEqualToFuture:[TOCFuture futureWithFailure:@"Fail"]]); } - (void)testThenValue { test([[[TOCFuture futureWithFailure:@0] thenValue:@""] isEqualToFuture:[TOCFuture futureWithFailure:@0]]); test([[[TOCFuture futureWithResult:@1] thenValue:@""] isEqualToFuture:[TOCFuture futureWithResult:@""]]); test([[TOCFutureSource new].future thenValue:@""].isIncomplete); } - (void)testFinallyTry { test([[[TOCFuture futureWithResult:@1] finallyTry:^(TOCFuture *f) { return @""; }] isEqualToFuture:[TOCFuture futureWithResult:@""]]); test([[[TOCFuture futureWithFailure:@0] finallyTry:^(TOCFuture *f) { return @""; }] isEqualToFuture:[TOCFuture futureWithResult:@""]]); test([[TOCFutureSource new] .future finallyTry:^(TOCFuture *f) { return @""; }].isIncomplete); test([[[TOCFuture futureWithResult:@1] finallyTry:^id(TOCFuture *f) { @throw @""; }] isEqualToFuture:[TOCFuture futureWithFailure:@""]]); test([[[TOCFuture futureWithFailure:@0] finallyTry:^id(TOCFuture *f) { @throw @""; }] isEqualToFuture:[TOCFuture futureWithFailure:@""]]); test([[TOCFutureSource new] .future finallyTry:^id(TOCFuture *f) { @throw @""; }].isIncomplete); } - (void)testThenTry { test([[[TOCFuture futureWithResult:@1] thenTry:^(id f) { return @""; }] isEqualToFuture:[TOCFuture futureWithResult:@""]]); test([[[TOCFuture futureWithFailure:@0] thenTry:^(id f) { return @""; }] isEqualToFuture:[TOCFuture futureWithFailure:@0]]); test([[TOCFutureSource new] .future thenTry:^(id f) { return @""; }].isIncomplete); test([[[TOCFuture futureWithResult:@1] thenTry:^id(id f) { @throw @""; }] isEqualToFuture:[TOCFuture futureWithFailure:@""]]); test([[[TOCFuture futureWithFailure:@0] thenTry:^id(id f) { @throw @""; }] isEqualToFuture:[TOCFuture futureWithFailure:@0]]); test([[TOCFutureSource new] .future thenTry:^id(id f) { @throw @""; }].isIncomplete); } - (void)testCatchTry { test([[[TOCFuture futureWithResult:@1] catchTry:^(id f) { return @""; }] isEqualToFuture:[TOCFuture futureWithResult:@1]]); test([[[TOCFuture futureWithFailure:@0] catchTry:^(id f) { return @""; }] isEqualToFuture:[TOCFuture futureWithResult:@""]]); test([[TOCFutureSource new] .future catchTry:^(id f) { return @""; }].isIncomplete); test([[[TOCFuture futureWithResult:@1] catchTry:^id(id f) { @throw @""; }] isEqualToFuture:[TOCFuture futureWithResult:@1]]); test([[[TOCFuture futureWithFailure:@0] catchTry:^id(id f) { @throw @""; }] isEqualToFuture:[TOCFuture futureWithFailure:@""]]); test([[TOCFutureSource new] .future catchTry:^id(id f) { @throw @""; }].isIncomplete); } - (void)testRetry_pass { __block NSUInteger repeat = 0; __block NSUInteger evalCount = 0; TOCUntilOperation op = ^(TOCCancelToken *c) { repeat += 1; return [TimeUtil scheduleEvaluate:^id { evalCount++; return @YES; } afterDelay:0.35 onRunLoop:[ThreadManager normalLatencyThreadRunLoop] unlessCancelled:c]; }; TOCFuture *f = [TOCFuture retry:op upToNTimes:4 withBaseTimeout:0.5 / 8 andRetryFactor:2 untilCancelled:nil]; testChurnUntil(!f.isIncomplete, 500.0); test(repeat == 3 || repeat == 4); test(evalCount == 1); test(f.hasResult); test([[f forceGetResult] isEqual:@YES]); } - (void)testRetry_fail { __block NSUInteger repeat = 0; __block NSUInteger evalCount = 0; TOCUntilOperation op = ^(TOCCancelToken *c) { repeat += 1; return [TimeUtil scheduleEvaluate:^{ evalCount++; return [TOCFuture futureWithFailure:@13]; } afterDelay:0.1 onRunLoop:[ThreadManager normalLatencyThreadRunLoop] unlessCancelled:c]; }; TOCFuture *f = [TOCFuture retry:op upToNTimes:4 withBaseTimeout:0.5 / 8 andRetryFactor:2 untilCancelled:nil]; testChurnUntil(!f.isIncomplete, 5.0); test(repeat >= 1); test(evalCount >= 1); test(f.hasFailed); test([f.forceGetFailure isEqual:@13]); } - (void)testRetry_timeout { __block NSUInteger repeat = 0; __block NSUInteger evalCount = 0; TOCUntilOperation op = ^(TOCCancelToken *c) { repeat += 1; return [TimeUtil scheduleEvaluate:^id { evalCount++; return @YES; } afterDelay:0.5 onRunLoop:[ThreadManager normalLatencyThreadRunLoop] unlessCancelled:c]; }; TOCFuture *f = [TOCFuture retry:op upToNTimes:2 withBaseTimeout:0.5 / 8 andRetryFactor:2 untilCancelled:nil]; testChurnUntil(!f.isIncomplete, 5.0); test(repeat == 2); test(evalCount == 0); test(f.hasFailedWithTimeout); } - (void)testRetry_cancel { TOCCancelTokenSource *s = [TOCCancelTokenSource new]; __block NSUInteger repeat = 0; __block NSUInteger evalCount = 0; TOCUntilOperation op = ^(TOCCancelToken *c) { repeat += 1; [TimeUtil scheduleRun:^{ [s cancel]; } afterDelay:0.1 onRunLoop:[ThreadManager normalLatencyThreadRunLoop] unlessCancelled:nil]; return [TimeUtil scheduleEvaluate:^id { evalCount++; return @YES; } afterDelay:0.5 onRunLoop:[ThreadManager normalLatencyThreadRunLoop] unlessCancelled:c]; }; TOCFuture *f = [TOCFuture retry:op upToNTimes:2 withBaseTimeout:0.5 / 8 andRetryFactor:2 untilCancelled:s.token]; testChurnUntil(!f.isIncomplete, 5.0); test(repeat == 2); test(evalCount == 0); test(f.hasFailedWithCancel); } @end