2 * Copyright (c) 2017 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@
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"
32 @interface CKKSNearFutureSchedulerTests : XCTestCase
33 @property NSOperationQueue* operationQueue;
36 @implementation CKKSNearFutureSchedulerTests
41 self.operationQueue = [[NSOperationQueue alloc] init];
48 #pragma mark - Block-based tests
50 - (void)testBlockOneShot {
51 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
53 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay:50*NSEC_PER_MSEC keepProcessAlive:true
54 dependencyDescriptionCode:CKKSResultDescriptionNone
56 [expectation fulfill];
61 [self waitForExpectationsWithTimeout:1 handler:nil];
64 - (void)testBlockOneShotDelay {
65 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
66 toofastexpectation.inverted = YES;
68 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
70 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:false
71 dependencyDescriptionCode:CKKSResultDescriptionNone
73 [toofastexpectation fulfill];
74 [expectation fulfill];
79 // Make sure it waits at least 0.1 seconds
80 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
82 // But finishes within 1.1s (total)
83 [self waitForExpectations: @[expectation] timeout:1];
86 - (void)testBlockOneShotManyTrigger {
87 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
88 toofastexpectation.inverted = YES;
90 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
91 expectation.assertForOverFulfill = YES;
93 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:true
94 dependencyDescriptionCode:CKKSResultDescriptionNone
96 [toofastexpectation fulfill];
97 [expectation fulfill];
109 // Make sure it waits at least 0.1 seconds
110 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
112 // But finishes within .6s (total)
113 [self waitForExpectations: @[expectation] timeout:0.5];
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];
122 - (void)testBlockMultiShot {
123 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
124 first.assertForOverFulfill = NO;
126 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
127 second.expectedFulfillmentCount = 2;
128 second.assertForOverFulfill = YES;
130 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:false
131 dependencyDescriptionCode:CKKSResultDescriptionNone
139 [self waitForExpectations: @[first] timeout:0.4];
145 [self waitForExpectations: @[second] timeout:0.4];
147 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
148 waitmore.inverted = YES;
149 [self waitForExpectations: @[waitmore] timeout: 0.4];
152 - (void)testBlockMultiShotDelays {
153 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
154 first.assertForOverFulfill = NO;
156 XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (long delay expectation)"];
157 longdelay.inverted = YES;
158 longdelay.expectedFulfillmentCount = 2;
160 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
161 second.expectedFulfillmentCount = 2;
162 second.assertForOverFulfill = YES;
164 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" initialDelay: 50*NSEC_PER_MSEC continuingDelay:600*NSEC_PER_MSEC keepProcessAlive:false
165 dependencyDescriptionCode:CKKSResultDescriptionNone
174 [self waitForExpectations: @[first] timeout:0.5];
180 // longdelay should NOT be fulfilled twice in the first 0.9 seconds
181 [self waitForExpectations: @[longdelay] timeout:0.4];
183 // But second should be fulfilled in the first 1.4 seconds
184 [self waitForExpectations: @[second] timeout:0.5];
186 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
187 waitmore.inverted = YES;
188 [self waitForExpectations: @[waitmore] timeout: 0.2];
191 - (void)testBlockMultiShotDelaysWithZeroInitialDelay {
192 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
193 first.assertForOverFulfill = NO;
195 XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (long delay expectation)"];
196 longdelay.inverted = YES;
197 longdelay.expectedFulfillmentCount = 2;
199 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
200 second.expectedFulfillmentCount = 2;
201 second.assertForOverFulfill = YES;
203 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName:@"test"
204 initialDelay:0*NSEC_PER_MSEC
205 continuingDelay:600*NSEC_PER_MSEC
206 keepProcessAlive:false
207 dependencyDescriptionCode:CKKSResultDescriptionNone
216 // Watches can be very slow. We expect this to come back immediately, but give them a lot of slack....
217 [self waitForExpectations: @[first] timeout:0.4];
223 // longdelay should NOT be fulfilled again within 0.3 seconds of the first run
224 [self waitForExpectations: @[longdelay] timeout:0.3];
226 // But second should be fulfilled in the first 1.0 seconds
227 [self waitForExpectations: @[second] timeout:0.6];
229 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
230 waitmore.inverted = YES;
231 [self waitForExpectations: @[waitmore] timeout: 0.2];
234 - (void)testBlockExponentialDelays {
235 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
236 first.assertForOverFulfill = NO;
238 XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (twice in 1s)"];
239 longdelay.inverted = YES;
240 longdelay.expectedFulfillmentCount = 2;
241 longdelay.assertForOverFulfill = NO;
243 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
244 second.expectedFulfillmentCount = 2;
245 second.assertForOverFulfill = NO;
247 XCTestExpectation *longdelay2 = [self expectationWithDescription:@"FutureScheduler fired (three in 2s)"];
248 longdelay2.inverted = YES;
249 longdelay2.expectedFulfillmentCount = 3;
250 longdelay2.assertForOverFulfill = NO;
252 XCTestExpectation *third = [self expectationWithDescription:@"FutureScheduler fired (three)"];
253 third.expectedFulfillmentCount = 3;
254 third.assertForOverFulfill = NO;
256 XCTestExpectation *final = [self expectationWithDescription:@"FutureScheduler fired (fourth)"];
257 final.expectedFulfillmentCount = 4;
258 final.assertForOverFulfill = YES;
260 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName:@"test"
261 initialDelay:500*NSEC_PER_MSEC
263 maximumDelay:30*NSEC_PER_SEC
264 keepProcessAlive:false
265 dependencyDescriptionCode:CKKSResultDescriptionNone
270 [longdelay2 fulfill];
277 // first should be fulfilled in the first 0.6 seconds
278 [self waitForExpectations: @[first] timeout:0.6];
282 // longdelay should NOT be fulfilled twice in the first 1.3-1.4 seconds
283 [self waitForExpectations: @[longdelay] timeout:0.8];
285 // But second should be fulfilled in the first 1.6 seconds
286 [self waitForExpectations: @[second] timeout:0.3];
290 // and longdelay2 should NOT be fulfilled three times in the first 2.3-2.4 seconds
291 [self waitForExpectations: @[longdelay2] timeout:0.9];
293 // But third should be fulfilled in the first 3.6 seconds
294 [self waitForExpectations: @[third] timeout:1.2];
296 // Wait out the 4s reset delay...
297 XCTestExpectation *reset = [self expectationWithDescription:@"reset"];
298 reset.inverted = YES;
299 [self waitForExpectations: @[reset] timeout:4.2];
301 // and it should use a 0.5s delay after trigger
304 [self waitForExpectations: @[final] timeout:0.6];
307 - (void)testBlockCancel {
308 XCTestExpectation *cancelexpectation = [self expectationWithDescription:@"FutureScheduler fired (after cancel)"];
309 cancelexpectation.inverted = YES;
311 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:true
312 dependencyDescriptionCode:CKKSResultDescriptionNone
314 [cancelexpectation fulfill];
320 // Make sure it does not fire in 0.5 s
321 [self waitForExpectations: @[cancelexpectation] timeout:0.2];
324 - (void)testBlockDelayedNoShot {
325 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
326 toofastexpectation.inverted = YES;
328 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
329 dependencyDescriptionCode:CKKSResultDescriptionNone
331 [toofastexpectation fulfill];
334 // Tell the scheduler to wait, but don't trigger it. It shouldn't fire.
335 [scheduler waitUntil: 50*NSEC_PER_MSEC];
337 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
340 - (void)testBlockDelayedOneShot {
341 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
342 first.assertForOverFulfill = NO;
344 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
345 toofastexpectation.inverted = YES;
347 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
348 dependencyDescriptionCode:CKKSResultDescriptionNone
351 [toofastexpectation fulfill];
354 [scheduler waitUntil: 150*NSEC_PER_MSEC];
357 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
358 [self waitForExpectations: @[first] timeout:0.5];
361 - (void)testBlockWaitedMultiShot {
362 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
363 first.assertForOverFulfill = NO;
365 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
366 toofastexpectation.expectedFulfillmentCount = 2;
367 toofastexpectation.inverted = YES;
369 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
370 second.expectedFulfillmentCount = 2;
371 second.assertForOverFulfill = YES;
373 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
374 dependencyDescriptionCode:CKKSResultDescriptionNone
378 [toofastexpectation fulfill];
382 [self waitForExpectations: @[first] timeout:0.5];
384 [scheduler waitUntil: 150*NSEC_PER_MSEC];
387 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
388 [self waitForExpectations: @[second] timeout:0.3];
391 #pragma mark - Operation-based tests
393 - (NSOperation*)operationFulfillingExpectations:(NSArray<XCTestExpectation*>*)expectations {
394 return [NSBlockOperation named:@"test" withBlock:^{
395 for(XCTestExpectation* e in expectations) {
401 - (void)addOperationFulfillingExpectations:(NSArray<XCTestExpectation*>*)expectations scheduler:(CKKSNearFutureScheduler*)scheduler {
402 NSOperation* op = [self operationFulfillingExpectations:expectations];
403 XCTAssertNotNil(scheduler.operationDependency, "Should be an operation dependency");
404 XCTAssertTrue([scheduler.operationDependency isPending], "operation dependency shouldn't have run yet");
405 [op addDependency:scheduler.operationDependency];
406 [self.operationQueue addOperation:op];
409 - (void)testOperationOneShot {
410 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
412 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay:50*NSEC_PER_MSEC keepProcessAlive:true
413 dependencyDescriptionCode:CKKSResultDescriptionNone
415 [self addOperationFulfillingExpectations:@[expectation] scheduler:scheduler];
419 [self waitForExpectationsWithTimeout:1 handler:nil];
422 - (void)testOperationOneShotDelay {
423 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
424 toofastexpectation.inverted = YES;
426 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
428 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:false
429 dependencyDescriptionCode:CKKSResultDescriptionNone
431 [self addOperationFulfillingExpectations:@[expectation,toofastexpectation] scheduler:scheduler];
435 // Make sure it waits at least 0.1 seconds
436 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
438 // But finishes within 1.1s (total)
439 [self waitForExpectations: @[expectation] timeout:1];
442 - (void)testOperationOneShotManyTrigger {
443 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
444 toofastexpectation.inverted = YES;
446 XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
447 expectation.assertForOverFulfill = YES;
449 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:true
450 dependencyDescriptionCode:CKKSResultDescriptionNone
452 [self addOperationFulfillingExpectations:@[expectation,toofastexpectation] scheduler:scheduler];
463 // Make sure it waits at least 0.1 seconds
464 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
466 // But finishes within .6s (total)
467 [self waitForExpectations: @[expectation] timeout:0.5];
469 // Ensure we don't get called again in the next 0.3 s
470 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
471 waitmore.inverted = YES;
472 [self waitForExpectations: @[waitmore] timeout: 0.3];
476 - (void)testOperationMultiShot {
477 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
479 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
481 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:false
482 dependencyDescriptionCode:CKKSResultDescriptionNone
485 [self addOperationFulfillingExpectations:@[first] scheduler:scheduler];
489 [self waitForExpectations: @[first] timeout:0.2];
491 [self addOperationFulfillingExpectations:@[second] scheduler:scheduler];
497 [self waitForExpectations: @[second] timeout:0.2];
499 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
500 waitmore.inverted = YES;
501 [self waitForExpectations: @[waitmore] timeout: 0.2];
504 - (void)testOperationMultiShotDelays {
505 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
507 XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (long delay expectation)"];
508 longdelay.inverted = YES;
509 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
511 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" initialDelay: 50*NSEC_PER_MSEC continuingDelay:300*NSEC_PER_MSEC keepProcessAlive:false
512 dependencyDescriptionCode:CKKSResultDescriptionNone
515 [self addOperationFulfillingExpectations:@[first] scheduler:scheduler];
519 [self waitForExpectations: @[first] timeout:0.2];
521 [self addOperationFulfillingExpectations:@[second,longdelay] scheduler:scheduler];
527 // longdelay shouldn't be fulfilled in the first 0.2 seconds
528 [self waitForExpectations: @[longdelay] timeout:0.2];
530 // But second should be fulfilled in the next 0.5 seconds
531 [self waitForExpectations: @[second] timeout:0.5];
533 XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
534 waitmore.inverted = YES;
535 [self waitForExpectations: @[waitmore] timeout: 0.2];
538 - (void)testOperationCancel {
539 XCTestExpectation *cancelexpectation = [self expectationWithDescription:@"FutureScheduler fired (after cancel)"];
540 cancelexpectation.inverted = YES;
542 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:true
543 dependencyDescriptionCode:CKKSResultDescriptionNone
546 [self addOperationFulfillingExpectations:@[cancelexpectation] scheduler:scheduler];
551 // Make sure it does not fire in 0.5 s
552 [self waitForExpectations: @[cancelexpectation] timeout:0.2];
555 - (void)testOperationDelayedNoShot {
556 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
557 toofastexpectation.inverted = YES;
559 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
560 dependencyDescriptionCode:CKKSResultDescriptionNone
562 [self addOperationFulfillingExpectations:@[toofastexpectation] scheduler:scheduler];
564 // Tell the scheduler to wait, but don't trigger it. It shouldn't fire.
565 [scheduler waitUntil: 50*NSEC_PER_MSEC];
567 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
570 - (void)testOperationDelayedOneShot {
571 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
572 first.assertForOverFulfill = NO;
574 XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
575 toofastexpectation.inverted = YES;
577 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false
578 dependencyDescriptionCode:CKKSResultDescriptionNone
580 [self addOperationFulfillingExpectations:@[first,toofastexpectation] scheduler:scheduler];
582 [scheduler waitUntil: 150*NSEC_PER_MSEC];
585 [self waitForExpectations: @[toofastexpectation] timeout:0.1];
586 [self waitForExpectations: @[first] timeout:0.5];
589 - (void)testChangeDelay {
590 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
591 first.assertForOverFulfill = NO;
593 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
594 second.expectedFulfillmentCount = 2;
595 second.assertForOverFulfill = YES;
597 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_SEC keepProcessAlive:false
598 dependencyDescriptionCode:CKKSResultDescriptionNone
604 [scheduler changeDelays:100*NSEC_PER_MSEC continuingDelay:100*NSEC_PER_MSEC];
607 [self waitForExpectations: @[first] timeout:0.4];
613 [self waitForExpectations: @[second] timeout:0.4];
616 - (void)testTriggerAtFromNoTimer {
617 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
618 first.inverted = YES;
620 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
621 second.assertForOverFulfill = YES;
623 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName:@"test"
624 delay:1*NSEC_PER_MSEC
625 keepProcessAlive:false
626 dependencyDescriptionCode:CKKSResultDescriptionNone
632 [scheduler triggerAt:300*NSEC_PER_MSEC];
633 [self waitForExpectations: @[first] timeout:0.1];
634 [self waitForExpectations: @[second] timeout:2];
637 - (void)testTriggerAtShortensTriggerDelay {
638 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
639 first.inverted = YES;
641 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
642 second.assertForOverFulfill = YES;
644 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName:@"test"
645 delay:10*NSEC_PER_SEC
646 keepProcessAlive:false
647 dependencyDescriptionCode:CKKSResultDescriptionNone
653 // Triggers a 10 second invocation, then invoke a triggerAt
655 [scheduler triggerAt:300*NSEC_PER_MSEC];
657 [self waitForExpectations: @[first] timeout:0.1];
658 [self waitForExpectations: @[second] timeout:2];
661 - (void)testTriggerAtLengthensTriggerDelay {
662 XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
663 first.inverted = YES;
665 XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
666 second.assertForOverFulfill = YES;
668 CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName:@"test"
669 delay:400*NSEC_PER_MSEC
670 keepProcessAlive:false
671 dependencyDescriptionCode:CKKSResultDescriptionNone
677 // Triggers a 400 millisecond invocation, then invoke a triggerAt
679 [scheduler triggerAt:1*NSEC_PER_SEC];
681 [self waitForExpectations: @[first] timeout:0.5];
682 [self waitForExpectations: @[second] timeout:2];