2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #import <XCTest/XCTest.h>
26 #import "keychain/ckks/CKKSGroupOperation.h"
29 @interface CKKSResultCancelOperation : CKKSResultOperation
30 - (instancetype) init;
33 @implementation CKKSResultCancelOperation
34 - (instancetype)init {
35 if(self = [super init]) {
36 __weak __typeof(self) weakSelf = self;
37 [self addExecutionBlock:^{
47 @interface CKKSResultErrorOperation : CKKSResultOperation
48 - (instancetype) init;
51 @implementation CKKSResultErrorOperation
52 - (instancetype)init {
53 if(self = [super init]) {
54 __weak __typeof(self) weakSelf = self;
55 [self addExecutionBlock:^{
56 weakSelf.error = [NSError errorWithDomain:@"test domain" code:5 userInfo:nil];
64 @interface CKKSOperationTests : XCTestCase
65 @property NSOperationQueue* queue;
68 // Remaining tests to write:
69 // TODO: subclass of CKKSResultOperation implementing main() respects addSuccessDependency without any special code
70 // TODO: chain of automatic dependencies
71 // TODO: test showing that CKKSGroupOperations don't start if they success-depend on a failed CKKSResultOperation
73 @implementation CKKSOperationTests
78 self.queue = [[NSOperationQueue alloc] init];
82 [self.queue cancelAllOperations];
88 - (void)testResultOperation {
89 CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
90 __weak __typeof(op) weakOp = op;
92 [op addExecutionBlock:^{
93 weakOp.error = [NSError errorWithDomain:@"test domain" code:0 userInfo:nil];
96 [self.queue addOperation: op];
98 [op waitUntilFinished];
100 XCTAssertNotNil(op.error, "errors can persist");
103 - (void)testResultSuccessDependency {
104 __block bool firstRun = false;
105 __block bool secondRun = false;
107 CKKSResultOperation* first = [[CKKSResultOperation alloc] init];
108 [first addExecutionBlock:^{
112 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
113 [second addExecutionBlock:^{
114 XCTAssertTrue(firstRun);
117 [second addSuccessDependency: first];
119 [self.queue addOperation: second];
120 [self.queue addOperation: first];
122 [self.queue waitUntilAllOperationsAreFinished];
124 XCTAssertTrue(firstRun);
125 XCTAssertTrue(secondRun);
127 XCTAssertTrue(first.finished, "First operation finished");
128 XCTAssertFalse(first.cancelled, "First operation not cancelled");
129 XCTAssertTrue(second.finished, "Second operation finished");
130 XCTAssertFalse(second.cancelled, "Second operation not cancelled");
133 - (void)testResultSuccessDependencyCancel {
134 CKKSResultCancelOperation* first = [[CKKSResultCancelOperation alloc] init];
136 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
137 [second addExecutionBlock:^{
138 XCTFail("Second operation should never run");
140 [second addSuccessDependency: first];
142 [self.queue addOperation: second];
143 [self.queue addOperation: first];
145 [self.queue waitUntilAllOperationsAreFinished];
147 XCTAssertTrue(first.finished, "First operation finished");
148 XCTAssertTrue(first.cancelled, "First operation is canceled (as requested)");
150 XCTAssertTrue(second.cancelled, "Second operation is canceled");
151 XCTAssertTrue(second.finished, "Second operation finished");
152 XCTAssertNotNil(second.error, "Error is generated when CKKSResultOperation is cancelled");
153 XCTAssertEqual(second.error.code, CKKSResultSubresultCancelled, "Error code is CKKSResultSubresultCancelled");
156 - (void)testResultSuccessDependencyError {
157 CKKSResultErrorOperation* first = [[CKKSResultErrorOperation alloc] init];
159 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
160 [second addExecutionBlock:^{
161 XCTFail("Second operation should never run");
163 [second addSuccessDependency: first];
165 [self.queue addOperation: second];
166 [self.queue addOperation: first];
168 [self.queue waitUntilAllOperationsAreFinished];
170 XCTAssertTrue(first.finished, "First operation finished");
171 XCTAssertFalse(first.cancelled, "First operation is not canceled");
172 XCTAssertNotNil(first.error, "First operation has an error");
174 XCTAssertTrue(second.cancelled, "Second operation is canceled");
175 XCTAssertTrue(second.finished, "Second operation finished");
176 XCTAssertNotNil(second.error, "Error is generated when dependent CKKSResultOperation has an error");
177 XCTAssertEqual(second.error.code, CKKSResultSubresultError, "Error code is CKKSResultSubresultError");
179 XCTAssertNotNil(second.error.userInfo[NSUnderlyingErrorKey], "Passed up the error from the first operation");
180 XCTAssertEqual([second.error.userInfo[NSUnderlyingErrorKey] code], 5, "Passed up the right error from the first operation");
183 - (void)testResultTimeout {
184 __block bool firstRun = false;
185 __block bool secondRun = false;
187 CKKSResultOperation* first = [[CKKSResultOperation alloc] init];
188 [first addExecutionBlock:^{
192 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
193 [second addExecutionBlock:^{
194 XCTAssertTrue(firstRun);
197 [second addDependency: first];
199 [self.queue addOperation: [second timeout:(50)* NSEC_PER_MSEC]];
200 [self.queue waitUntilAllOperationsAreFinished];
202 XCTAssertFalse(firstRun);
203 XCTAssertFalse(secondRun);
205 XCTAssertFalse(first.finished, "First operation not finished");
206 XCTAssertFalse(first.cancelled, "First operation not cancelled");
207 XCTAssertTrue(second.finished, "Second operation finished");
208 XCTAssertTrue(second.cancelled, "Second operation cancelled");
209 XCTAssertNotNil(second.error, "Second operation has an error");
210 XCTAssertEqual(second.error.code, CKKSResultTimedOut, "Second operation error is good");
213 - (void)testResultNoTimeout {
214 __block bool firstRun = false;
215 __block bool secondRun = false;
217 CKKSResultOperation* first = [[CKKSResultOperation alloc] init];
218 [first addExecutionBlock:^{
222 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
223 [second addExecutionBlock:^{
224 XCTAssertTrue(firstRun);
227 [second addDependency: first];
229 [self.queue addOperation: [second timeout:(100)* NSEC_PER_MSEC]];
230 [self.queue addOperation: first];
231 [self.queue waitUntilAllOperationsAreFinished];
233 XCTAssertTrue(firstRun);
234 XCTAssertTrue(secondRun);
236 XCTAssertTrue(first.finished, "First operation finished");
237 XCTAssertFalse(first.cancelled, "First operation not cancelled");
238 XCTAssertTrue(second.finished, "Second operation finished");
239 XCTAssertFalse(second.cancelled, "Second operation not cancelled");
240 XCTAssertNil(second.error, "Second operation has no error");
243 - (void)testResultFinishDate
245 CKKSResultOperation* operation = [[CKKSResultOperation alloc] init];
246 XCTAssertNil(operation.finishDate, "Result operation does not have a finish date before it is run");
248 [operation addExecutionBlock:^{
249 NSLog(@"test execution block");
252 [self.queue addOperation:operation];
253 [self.queue waitUntilAllOperationsAreFinished];
254 sleep(0.1); // wait for the completion block to have time to fire
255 XCTAssertNotNil(operation.finishDate, "Result operation has a finish date after everything is done");
256 NSTimeInterval timeIntervalSinceFinishDate = [[NSDate date] timeIntervalSinceDate:operation.finishDate];
257 XCTAssertTrue(timeIntervalSinceFinishDate >= 0.0 && timeIntervalSinceFinishDate <= 10.0, "Result operation finish datelooks reasonable");
260 - (void)testGroupOperation {
261 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
263 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
264 [group runBeforeGroupFinished: op1];
266 CKKSResultOperation* op2 = [[CKKSResultOperation alloc] init];
267 [group runBeforeGroupFinished: op2];
269 [self.queue addOperation: group];
271 [self.queue waitUntilAllOperationsAreFinished];
273 XCTAssertEqual(op1.finished, YES, "First operation finished");
274 XCTAssertEqual(op2.finished, YES, "Second operation finished");
275 XCTAssertEqual(group.finished, YES, "Group operation finished");
277 XCTAssertEqual(op1.cancelled, NO, "First operation not cancelled");
278 XCTAssertEqual(op2.cancelled, NO, "Second operation cancelled");
279 XCTAssertEqual(group.cancelled, NO, "Group operation not cancelled");
281 XCTAssertNil(op1.error, "First operation: no error");
282 XCTAssertNil(op2.error, "Second operation: no error");
283 XCTAssertNil(group.error, "Group operation: no error");
286 - (void)testGroupOperationCancel {
287 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
289 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
290 [group runBeforeGroupFinished: op1];
292 CKKSResultCancelOperation* op2 = [[CKKSResultCancelOperation alloc] init];
293 [group runBeforeGroupFinished: op2];
295 [self.queue addOperation: group];
297 [self.queue waitUntilAllOperationsAreFinished];
299 XCTAssertEqual(op1.finished, YES, "First operation finished");
300 XCTAssertEqual(op2.finished, YES, "Second operation finished");
301 XCTAssertEqual(group.finished, YES, "Group operation finished");
303 XCTAssertEqual(op1.cancelled, NO, "First operation not cancelled");
304 XCTAssertEqual(op2.cancelled, YES, "Second operation not cancelled");
305 XCTAssertEqual(group.cancelled, NO, "Group operation not cancelled");
307 XCTAssertNil(op1.error, "First operation: no error");
308 XCTAssertNil(op2.error, "Second operation: no error");
309 XCTAssertNotNil(group.error, "Group operation: no error");
310 XCTAssertEqual(group.error.code, CKKSResultSubresultCancelled, "Error code is CKKSResultSubresultCancelled");
313 - (void)testGroupOperationTimeout {
314 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
316 __block bool run1 = false;
317 CKKSResultOperation* op1 = [CKKSResultOperation operationWithBlock: ^{
320 [group runBeforeGroupFinished: op1];
322 __block bool run2 = false;
323 CKKSResultOperation* op2 = [CKKSResultOperation operationWithBlock: ^{
326 [group runBeforeGroupFinished: op2];
328 CKKSResultOperation* never = [[CKKSResultOperation alloc] init];
329 [group addDependency: never];
331 [group timeout:50*NSEC_PER_MSEC];
332 [self.queue addOperation: group];
334 [self.queue waitUntilAllOperationsAreFinished];
336 XCTAssertEqual(op1.finished, YES, "First operation finished");
337 XCTAssertEqual(op2.finished, YES, "Second operation finished");
338 XCTAssertEqual(group.finished, YES, "Group operation finished");
340 XCTAssertEqual(op1.cancelled, YES, "First operation cancelled");
341 XCTAssertEqual(op2.cancelled, YES, "Second operation cancelled");
342 XCTAssertEqual(group.cancelled, YES, "Group operation cancelled");
344 XCTAssertFalse(run1, "First operation did not run");
345 XCTAssertFalse(run2, "Second operation did not run");
347 XCTAssertNil(op1.error, "First operation: no error");
348 XCTAssertNil(op2.error, "Second operation: no error");
349 XCTAssertNotNil(group.error, "Group operation: error");
350 XCTAssertEqual(group.error.code, CKKSResultTimedOut, "Error code is CKKSResultTimedOut");
353 - (void)testGroupOperationError {
354 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
356 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
357 [group runBeforeGroupFinished: op1];
359 CKKSResultErrorOperation* op2 = [[CKKSResultErrorOperation alloc] init];
360 [group runBeforeGroupFinished: op2];
362 [self.queue addOperation: group];
364 [self.queue waitUntilAllOperationsAreFinished];
366 XCTAssertEqual(op1.finished, YES, "First operation finished");
367 XCTAssertEqual(op2.finished, YES, "Second operation finished");
368 XCTAssertEqual(group.finished, YES, "Group operation finished");
370 XCTAssertEqual(op1.cancelled, NO, "First operation not cancelled");
371 XCTAssertEqual(op2.cancelled, NO, "Second operation cancelled");
372 XCTAssertEqual(group.cancelled, NO, "Group operation not cancelled");
374 XCTAssertNil(op1.error, "First operation: no error");
375 XCTAssertNotNil(op2.error, "Second operation: error (as expected)");
376 XCTAssertEqual(op2.error.code, 5, "Rght error from the erroring operation");
378 XCTAssertNotNil(group.error, "Error is generated when dependent CKKSResultOperation has an error");
379 XCTAssertEqual(group.error.code, CKKSResultSubresultError, "Error code is CKKSResultSubresultError");
380 XCTAssertNotNil(group.error.userInfo[NSUnderlyingErrorKey], "Passed up the error from the first operation");
381 XCTAssertEqual([group.error.userInfo[NSUnderlyingErrorKey] code], 5, "Passed up the right error from the first operation");
384 - (void)testGroupOperationPending {
385 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
387 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
388 [group runBeforeGroupFinished: op1];
390 CKKSResultOperation* op2 = [[CKKSResultOperation alloc] init];
391 [group addDependency: op2];
393 [self.queue addOperation: group];
395 XCTAssertTrue([group isPending], "group operation hasn't started yet");
397 [self.queue addOperation: op2];
398 [self.queue waitUntilAllOperationsAreFinished];
399 XCTAssertFalse([group isPending], "group operation has started");
401 XCTAssertEqual(op1.finished, YES, "First operation finished");
402 XCTAssertEqual(op2.finished, YES, "Second operation finished");
403 XCTAssertEqual(group.finished, YES, "Group operation finished");
405 XCTAssertEqual(op1.cancelled, NO, "First operation not cancelled");
406 XCTAssertEqual(op2.cancelled, NO, "Second operation cancelled");
407 XCTAssertEqual(group.cancelled, NO, "Group operation not cancelled");
409 XCTAssertNil(op1.error, "First operation: no error");
410 XCTAssertNil(op2.error, "Second operation: no error");
411 XCTAssertNil(group.error, "Group operation: no error");