]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSNearFutureSchedulerTests.m
Security-58286.60.28.tar.gz
[apple/security.git] / keychain / ckks / tests / CKKSNearFutureSchedulerTests.m
1 /*
2 * Copyright (c) 2017 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 #if OCTAGON
25
26 #include <dispatch/dispatch.h>
27 #import <XCTest/XCTest.h>
28 #import "keychain/ckks/CKKSNearFutureScheduler.h"
29 #import "keychain/ckks/CKKSResultOperation.h"
30 #import "keychain/ckks/CKKS.h"
31
32 @interface CKKSNearFutureSchedulerTests : XCTestCase
33 @property NSOperationQueue* operationQueue;
34 @end
35
36 @implementation CKKSNearFutureSchedulerTests
37
38 - (void)setUp {
39 [super setUp];
40
41 self.operationQueue = [[NSOperationQueue alloc] init];
42 }
43
44 - (void)tearDown {
45 [super tearDown];
46 }
47
48 #pragma mark - Block-based tests
49
50 - (void)testBlockOneShot {
51 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
52
53 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay:50*NSEC_PER_MSEC keepProcessAlive:true
54 dependencyDescriptionCode:CKKSResultDescriptionNone
55 block:^{
56 [expectation fulfill];
57 }];
58
59 [scheduler trigger];
60
61 [self waitForExpectationsWithTimeout:1 handler:nil];
62 }
63
64 - (void)testBlockOneShotDelay {
65 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
66 toofastexpectation.inverted = YES;
67
68 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
69
70 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:false
71 dependencyDescriptionCode:CKKSResultDescriptionNone
72 block:^{
73 [toofastexpectation fulfill];
74 [expectation fulfill];
75 }];
76
77 [scheduler trigger];
78
79 // Make sure it waits at least 0.1 seconds
80 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
81
82 // But finishes within 1.1s (total)
83 [self waitForExpectations: @[expectation] timeout:1];
84 }
85
86 - (void)testBlockOneShotManyTrigger {
87 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
88 toofastexpectation.inverted = YES;
89
90 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
91 expectation.assertForOverFulfill = YES;
92
93 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:true
94 dependencyDescriptionCode:CKKSResultDescriptionNone
95 block:^{
96 [toofastexpectation fulfill];
97 [expectation fulfill];
98 }];
99
100 [scheduler trigger];
101 [scheduler trigger];
102 [scheduler trigger];
103 [scheduler trigger];
104 [scheduler trigger];
105 [scheduler trigger];
106 [scheduler trigger];
107 [scheduler trigger];
108
109 // Make sure it waits at least 0.1 seconds
110 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
111
112 // But finishes within .6s (total)
113 [self waitForExpectations: @[expectation] timeout:0.5];
114
115 // Ensure we don't get called again in the next 0.3 s
116 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
117 waitmore.inverted = YES;
118 [self waitForExpectations: @[waitmore] timeout: 0.3];
119 }
120
121
122 - (void)testBlockMultiShot {
123 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
124 first.assertForOverFulfill = NO;
125
126 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
127 second.expectedFulfillmentCount = 2;
128 second.assertForOverFulfill = YES;
129
130 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:false
131 dependencyDescriptionCode:CKKSResultDescriptionNone
132 block:^{
133 [first fulfill];
134 [second fulfill];
135 }];
136
137 [scheduler trigger];
138
139 [self waitForExpectations: @[first] timeout:0.4];
140
141 [scheduler trigger];
142 [scheduler trigger];
143 [scheduler trigger];
144
145 [self waitForExpectations: @[second] timeout:0.4];
146
147 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
148 waitmore.inverted = YES;
149 [self waitForExpectations: @[waitmore] timeout: 0.4];
150 }
151
152 - (void)testBlockMultiShotDelays {
153 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
154 first.assertForOverFulfill = NO;
155
156 XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (long delay expectation)"];
157 longdelay.inverted = YES;
158 longdelay.expectedFulfillmentCount = 2;
159
160 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
161 second.expectedFulfillmentCount = 2;
162 second.assertForOverFulfill = YES;
163
164 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" initialDelay: 50*NSEC_PER_MSEC continuingDelay:600*NSEC_PER_MSEC keepProcessAlive:false
165 dependencyDescriptionCode:CKKSResultDescriptionNone
166 block:^{
167 [first fulfill];
168 [longdelay fulfill];
169 [second fulfill];
170 }];
171
172 [scheduler trigger];
173
174 [self waitForExpectations: @[first] timeout:0.5];
175
176 [scheduler trigger];
177 [scheduler trigger];
178 [scheduler trigger];
179
180 // longdelay should NOT be fulfilled twice in the first 0.9 seconds
181 [self waitForExpectations: @[longdelay] timeout:0.4];
182
183 // But second should be fulfilled in the first 1.4 seconds
184 [self waitForExpectations: @[second] timeout:0.5];
185
186 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
187 waitmore.inverted = YES;
188 [self waitForExpectations: @[waitmore] timeout: 0.2];
189 }
190
191 - (void)testBlockCancel {
192 XCTestExpectation *cancelexpectation = [self expectationWithDescription:@"FutureScheduler fired (after cancel)"];
193 cancelexpectation.inverted = YES;
194
195 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:true
196 dependencyDescriptionCode:CKKSResultDescriptionNone
197 block:^{
198 [cancelexpectation fulfill];
199 }];
200
201 [scheduler trigger];
202 [scheduler cancel];
203
204 // Make sure it does not fire in 0.5 s
205 [self waitForExpectations: @[cancelexpectation] timeout:0.2];
206 }
207
208 - (void)testBlockDelayedNoShot {
209 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
210 toofastexpectation.inverted = YES;
211
212 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
213 dependencyDescriptionCode:CKKSResultDescriptionNone
214 block:^{
215 [toofastexpectation fulfill];
216 }];
217
218 // Tell the scheduler to wait, but don't trigger it. It shouldn't fire.
219 [scheduler waitUntil: 50*NSEC_PER_MSEC];
220
221 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
222 }
223
224 - (void)testBlockDelayedOneShot {
225 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
226 first.assertForOverFulfill = NO;
227
228 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
229 toofastexpectation.inverted = YES;
230
231 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
232 dependencyDescriptionCode:CKKSResultDescriptionNone
233 block:^{
234 [first fulfill];
235 [toofastexpectation fulfill];
236 }];
237
238 [scheduler waitUntil: 150*NSEC_PER_MSEC];
239 [scheduler trigger];
240
241 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
242 [self waitForExpectations: @[first] timeout:0.5];
243 }
244
245 - (void)testBlockWaitedMultiShot {
246 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
247 first.assertForOverFulfill = NO;
248
249 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
250 toofastexpectation.expectedFulfillmentCount = 2;
251 toofastexpectation.inverted = YES;
252
253 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
254 second.expectedFulfillmentCount = 2;
255 second.assertForOverFulfill = YES;
256
257 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
258 dependencyDescriptionCode:CKKSResultDescriptionNone
259 block:^{
260 [first fulfill];
261 [second fulfill];
262 [toofastexpectation fulfill];
263 }];
264
265 [scheduler trigger];
266 [self waitForExpectations: @[first] timeout:0.5];
267
268 [scheduler waitUntil: 150*NSEC_PER_MSEC];
269 [scheduler trigger];
270
271 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
272 [self waitForExpectations: @[second] timeout:0.3];
273 }
274
275 #pragma mark - Operation-based tests
276
277 - (NSOperation*)operationFulfillingExpectations:(NSArray<XCTestExpectation*>*)expectations {
278 return [NSBlockOperation named:@"test" withBlock:^{
279 for(XCTestExpectation* e in expectations) {
280 [e fulfill];
281 }
282 }];
283 }
284
285 - (void)addOperationFulfillingExpectations:(NSArray<XCTestExpectation*>*)expectations scheduler:(CKKSNearFutureScheduler*)scheduler {
286 NSOperation* op = [self operationFulfillingExpectations:expectations];
287 XCTAssertNotNil(scheduler.operationDependency, "Should be an operation dependency");
288 XCTAssertTrue([scheduler.operationDependency isPending], "operation dependency shouldn't have run yet");
289 [op addDependency:scheduler.operationDependency];
290 [self.operationQueue addOperation:op];
291 }
292
293 - (void)testOperationOneShot {
294 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
295
296 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay:50*NSEC_PER_MSEC keepProcessAlive:true
297 dependencyDescriptionCode:CKKSResultDescriptionNone
298 block:^{}];
299 [self addOperationFulfillingExpectations:@[expectation] scheduler:scheduler];
300
301 [scheduler trigger];
302
303 [self waitForExpectationsWithTimeout:1 handler:nil];
304 }
305
306 - (void)testOperationOneShotDelay {
307 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
308 toofastexpectation.inverted = YES;
309
310 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
311
312 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:false
313 dependencyDescriptionCode:CKKSResultDescriptionNone
314 block:^{}];
315 [self addOperationFulfillingExpectations:@[expectation,toofastexpectation] scheduler:scheduler];
316
317 [scheduler trigger];
318
319 // Make sure it waits at least 0.1 seconds
320 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
321
322 // But finishes within 1.1s (total)
323 [self waitForExpectations: @[expectation] timeout:1];
324 }
325
326 - (void)testOperationOneShotManyTrigger {
327 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
328 toofastexpectation.inverted = YES;
329
330 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
331 expectation.assertForOverFulfill = YES;
332
333 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:true
334 dependencyDescriptionCode:CKKSResultDescriptionNone
335 block:^{}];
336 [self addOperationFulfillingExpectations:@[expectation,toofastexpectation] scheduler:scheduler];
337
338 [scheduler trigger];
339 [scheduler trigger];
340 [scheduler trigger];
341 [scheduler trigger];
342 [scheduler trigger];
343 [scheduler trigger];
344 [scheduler trigger];
345 [scheduler trigger];
346
347 // Make sure it waits at least 0.1 seconds
348 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
349
350 // But finishes within .6s (total)
351 [self waitForExpectations: @[expectation] timeout:0.5];
352
353 // Ensure we don't get called again in the next 0.3 s
354 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
355 waitmore.inverted = YES;
356 [self waitForExpectations: @[waitmore] timeout: 0.3];
357 }
358
359
360 - (void)testOperationMultiShot {
361 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
362
363 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
364
365 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:false
366 dependencyDescriptionCode:CKKSResultDescriptionNone
367 block:^{}];
368
369 [self addOperationFulfillingExpectations:@[first] scheduler:scheduler];
370
371 [scheduler trigger];
372
373 [self waitForExpectations: @[first] timeout:0.2];
374
375 [self addOperationFulfillingExpectations:@[second] scheduler:scheduler];
376
377 [scheduler trigger];
378 [scheduler trigger];
379 [scheduler trigger];
380
381 [self waitForExpectations: @[second] timeout:0.2];
382
383 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
384 waitmore.inverted = YES;
385 [self waitForExpectations: @[waitmore] timeout: 0.2];
386 }
387
388 - (void)testOperationMultiShotDelays {
389 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
390
391 XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (long delay expectation)"];
392 longdelay.inverted = YES;
393 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
394
395 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" initialDelay: 50*NSEC_PER_MSEC continuingDelay:300*NSEC_PER_MSEC keepProcessAlive:false
396 dependencyDescriptionCode:CKKSResultDescriptionNone
397 block:^{}];
398
399 [self addOperationFulfillingExpectations:@[first] scheduler:scheduler];
400
401 [scheduler trigger];
402
403 [self waitForExpectations: @[first] timeout:0.2];
404
405 [self addOperationFulfillingExpectations:@[second,longdelay] scheduler:scheduler];
406
407 [scheduler trigger];
408 [scheduler trigger];
409 [scheduler trigger];
410
411 // longdelay shouldn't be fulfilled in the first 0.2 seconds
412 [self waitForExpectations: @[longdelay] timeout:0.2];
413
414 // But second should be fulfilled in the next 0.5 seconds
415 [self waitForExpectations: @[second] timeout:0.5];
416
417 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
418 waitmore.inverted = YES;
419 [self waitForExpectations: @[waitmore] timeout: 0.2];
420 }
421
422 - (void)testOperationCancel {
423 XCTestExpectation *cancelexpectation = [self expectationWithDescription:@"FutureScheduler fired (after cancel)"];
424 cancelexpectation.inverted = YES;
425
426 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:true
427 dependencyDescriptionCode:CKKSResultDescriptionNone
428 block:^{}];
429
430 [self addOperationFulfillingExpectations:@[cancelexpectation] scheduler:scheduler];
431
432 [scheduler trigger];
433 [scheduler cancel];
434
435 // Make sure it does not fire in 0.5 s
436 [self waitForExpectations: @[cancelexpectation] timeout:0.2];
437 }
438
439 - (void)testOperationDelayedNoShot {
440 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
441 toofastexpectation.inverted = YES;
442
443 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
444 dependencyDescriptionCode:CKKSResultDescriptionNone
445 block:^{}];
446 [self addOperationFulfillingExpectations:@[toofastexpectation] scheduler:scheduler];
447
448 // Tell the scheduler to wait, but don't trigger it. It shouldn't fire.
449 [scheduler waitUntil: 50*NSEC_PER_MSEC];
450
451 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
452 }
453
454 - (void)testOperationDelayedOneShot {
455 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
456 first.assertForOverFulfill = NO;
457
458 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
459 toofastexpectation.inverted = YES;
460
461 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
462 dependencyDescriptionCode:CKKSResultDescriptionNone
463 block:^{}];
464 [self addOperationFulfillingExpectations:@[first,toofastexpectation] scheduler:scheduler];
465
466 [scheduler waitUntil: 150*NSEC_PER_MSEC];
467 [scheduler trigger];
468
469 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
470 [self waitForExpectations: @[first] timeout:0.5];
471 }
472
473 @end
474
475 #endif /* OCTAGON */