]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSOperationTests.m
1eab4c61a681498c78944cdd24f7b16fe21d3282
[apple/security.git] / keychain / ckks / tests / CKKSOperationTests.m
1 /*
2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import <XCTest/XCTest.h>
25
26 #import "keychain/ckks/CKKSGroupOperation.h"
27
28 // Helper Operations
29 @interface CKKSResultCancelOperation : CKKSResultOperation
30 - (instancetype) init;
31 @end
32
33 @implementation CKKSResultCancelOperation
34 - (instancetype)init {
35 if(self = [super init]) {
36 __weak __typeof(self) weakSelf = self;
37 [self addExecutionBlock:^{
38 [weakSelf cancel];
39 }];
40 }
41 return self;
42 }
43 @end
44
45
46
47 @interface CKKSResultErrorOperation : CKKSResultOperation
48 - (instancetype) init;
49 @end
50
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];
57 }];
58 }
59 return self;
60 }
61 @end
62
63
64 @interface CKKSOperationTests : XCTestCase
65 @property NSOperationQueue* queue;
66 @end
67
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
72
73 @implementation CKKSOperationTests
74
75 - (void)setUp {
76 [super setUp];
77
78 self.queue = [[NSOperationQueue alloc] init];
79 }
80
81 - (void)tearDown {
82 [self.queue cancelAllOperations];
83 self.queue = nil;
84
85 [super tearDown];
86 }
87
88 - (void)testResultOperation {
89 CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
90 __weak __typeof(op) weakOp = op;
91
92 [op addExecutionBlock:^{
93 weakOp.error = [NSError errorWithDomain:@"test domain" code:0 userInfo:nil];
94 }];
95
96 [self.queue addOperation: op];
97
98 [op waitUntilFinished];
99
100 XCTAssertNotNil(op.error, "errors can persist");
101 }
102
103 - (void)testResultSuccessDependency {
104 __block bool firstRun = false;
105 __block bool secondRun = false;
106
107 CKKSResultOperation* first = [[CKKSResultOperation alloc] init];
108 [first addExecutionBlock:^{
109 firstRun = true;
110 }];
111
112 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
113 [second addExecutionBlock:^{
114 XCTAssertTrue(firstRun);
115 secondRun = true;
116 }];
117 [second addSuccessDependency: first];
118
119 [self.queue addOperation: second];
120 [self.queue addOperation: first];
121
122 [self.queue waitUntilAllOperationsAreFinished];
123
124 XCTAssertTrue(firstRun);
125 XCTAssertTrue(secondRun);
126
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");
131 }
132
133 - (void)testResultSuccessDependencyCancel {
134 CKKSResultCancelOperation* first = [[CKKSResultCancelOperation alloc] init];
135
136 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
137 [second addExecutionBlock:^{
138 XCTFail("Second operation should never run");
139 }];
140 [second addSuccessDependency: first];
141
142 [self.queue addOperation: second];
143 [self.queue addOperation: first];
144
145 [self.queue waitUntilAllOperationsAreFinished];
146
147 XCTAssertTrue(first.finished, "First operation finished");
148 XCTAssertTrue(first.cancelled, "First operation is canceled (as requested)");
149
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");
154 }
155
156 - (void)testResultSuccessDependencyError {
157 CKKSResultErrorOperation* first = [[CKKSResultErrorOperation alloc] init];
158
159 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
160 [second addExecutionBlock:^{
161 XCTFail("Second operation should never run");
162 }];
163 [second addSuccessDependency: first];
164
165 [self.queue addOperation: second];
166 [self.queue addOperation: first];
167
168 [self.queue waitUntilAllOperationsAreFinished];
169
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");
173
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");
178
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");
181 }
182
183 - (void)testResultTimeout {
184 __block bool firstRun = false;
185 __block bool secondRun = false;
186
187 CKKSResultOperation* first = [[CKKSResultOperation alloc] init];
188 [first addExecutionBlock:^{
189 firstRun = true;
190 }];
191
192 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
193 [second addExecutionBlock:^{
194 XCTAssertTrue(firstRun);
195 secondRun = true;
196 }];
197 [second addDependency: first];
198
199 [self.queue addOperation: [second timeout:(50)* NSEC_PER_MSEC]];
200 [self.queue waitUntilAllOperationsAreFinished];
201
202 XCTAssertFalse(firstRun);
203 XCTAssertFalse(secondRun);
204
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");
211 }
212
213 - (void)testResultNoTimeout {
214 __block bool firstRun = false;
215 __block bool secondRun = false;
216
217 CKKSResultOperation* first = [[CKKSResultOperation alloc] init];
218 [first addExecutionBlock:^{
219 firstRun = true;
220 }];
221
222 CKKSResultOperation* second = [[CKKSResultOperation alloc] init];
223 [second addExecutionBlock:^{
224 XCTAssertTrue(firstRun);
225 secondRun = true;
226 }];
227 [second addDependency: first];
228
229 [self.queue addOperation: [second timeout:(100)* NSEC_PER_MSEC]];
230 [self.queue addOperation: first];
231 [self.queue waitUntilAllOperationsAreFinished];
232
233 XCTAssertTrue(firstRun);
234 XCTAssertTrue(secondRun);
235
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");
241 }
242
243 - (void)testResultFinishDate
244 {
245 CKKSResultOperation* operation = [[CKKSResultOperation alloc] init];
246 XCTAssertNil(operation.finishDate, "Result operation does not have a finish date before it is run");
247
248 [operation addExecutionBlock:^{
249 NSLog(@"test execution block");
250 }];
251
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");
258 }
259
260 - (void)testGroupOperation {
261 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
262
263 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
264 [group runBeforeGroupFinished: op1];
265
266 CKKSResultOperation* op2 = [[CKKSResultOperation alloc] init];
267 [group runBeforeGroupFinished: op2];
268
269 [self.queue addOperation: group];
270
271 [self.queue waitUntilAllOperationsAreFinished];
272
273 XCTAssertEqual(op1.finished, YES, "First operation finished");
274 XCTAssertEqual(op2.finished, YES, "Second operation finished");
275 XCTAssertEqual(group.finished, YES, "Group operation finished");
276
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");
280
281 XCTAssertNil(op1.error, "First operation: no error");
282 XCTAssertNil(op2.error, "Second operation: no error");
283 XCTAssertNil(group.error, "Group operation: no error");
284 }
285
286 - (void)testGroupOperationCancel {
287 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
288
289 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
290 [group runBeforeGroupFinished: op1];
291
292 CKKSResultCancelOperation* op2 = [[CKKSResultCancelOperation alloc] init];
293 [group runBeforeGroupFinished: op2];
294
295 [self.queue addOperation: group];
296
297 [self.queue waitUntilAllOperationsAreFinished];
298
299 XCTAssertEqual(op1.finished, YES, "First operation finished");
300 XCTAssertEqual(op2.finished, YES, "Second operation finished");
301 XCTAssertEqual(group.finished, YES, "Group operation finished");
302
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");
306
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");
311 }
312
313 - (void)testGroupOperationTimeout {
314 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
315
316 __block bool run1 = false;
317 CKKSResultOperation* op1 = [CKKSResultOperation operationWithBlock: ^{
318 run1 = true;
319 }];
320 [group runBeforeGroupFinished: op1];
321
322 __block bool run2 = false;
323 CKKSResultOperation* op2 = [CKKSResultOperation operationWithBlock: ^{
324 run2 = true;
325 }];
326 [group runBeforeGroupFinished: op2];
327
328 CKKSResultOperation* never = [[CKKSResultOperation alloc] init];
329 [group addDependency: never];
330
331 [group timeout:50*NSEC_PER_MSEC];
332 [self.queue addOperation: group];
333
334 [self.queue waitUntilAllOperationsAreFinished];
335
336 XCTAssertEqual(op1.finished, YES, "First operation finished");
337 XCTAssertEqual(op2.finished, YES, "Second operation finished");
338 XCTAssertEqual(group.finished, YES, "Group operation finished");
339
340 XCTAssertEqual(op1.cancelled, YES, "First operation cancelled");
341 XCTAssertEqual(op2.cancelled, YES, "Second operation cancelled");
342 XCTAssertEqual(group.cancelled, YES, "Group operation cancelled");
343
344 XCTAssertFalse(run1, "First operation did not run");
345 XCTAssertFalse(run2, "Second operation did not run");
346
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");
351 }
352
353 - (void)testGroupOperationError {
354 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
355
356 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
357 [group runBeforeGroupFinished: op1];
358
359 CKKSResultErrorOperation* op2 = [[CKKSResultErrorOperation alloc] init];
360 [group runBeforeGroupFinished: op2];
361
362 [self.queue addOperation: group];
363
364 [self.queue waitUntilAllOperationsAreFinished];
365
366 XCTAssertEqual(op1.finished, YES, "First operation finished");
367 XCTAssertEqual(op2.finished, YES, "Second operation finished");
368 XCTAssertEqual(group.finished, YES, "Group operation finished");
369
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");
373
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");
377
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");
382 }
383
384 - (void)testGroupOperationPending {
385 CKKSGroupOperation* group = [[CKKSGroupOperation alloc] init];
386
387 CKKSResultOperation* op1 = [[CKKSResultOperation alloc] init];
388 [group runBeforeGroupFinished: op1];
389
390 CKKSResultOperation* op2 = [[CKKSResultOperation alloc] init];
391 [group addDependency: op2];
392
393 [self.queue addOperation: group];
394
395 XCTAssertTrue([group isPending], "group operation hasn't started yet");
396
397 [self.queue addOperation: op2];
398 [self.queue waitUntilAllOperationsAreFinished];
399 XCTAssertFalse([group isPending], "group operation has started");
400
401 XCTAssertEqual(op1.finished, YES, "First operation finished");
402 XCTAssertEqual(op2.finished, YES, "Second operation finished");
403 XCTAssertEqual(group.finished, YES, "Group operation finished");
404
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");
408
409 XCTAssertNil(op1.error, "First operation: no error");
410 XCTAssertNil(op2.error, "Second operation: no error");
411 XCTAssertNil(group.error, "Group operation: no error");
412 }
413
414 @end