All files / testing / time.ts

91.55% Branches 65/71
94.16% Lines 274/291
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x31
 
x31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
 
 
 
 
x31
x40
x40
x40
x31
 
x161
x161
x161
 
x31
x31
x91
 
x91
x31
x31
x32
x31
x31
x876
x1848
x1848
x2435
x876
x31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
 
x91
 
x91
x91
 
x91
 
x91
x150
x91
 
x49
x49
x49
x58
x58
x49
 
x37
 
x37
x37
 
x37
 
x37
x42
x37
 
x37
x37
x37
x42
x42
x37
 
x95
 
x95
x95
x95
x95
 
x95
x95
x95
x285
x95
x612
x153
x153
x95
x95
x95
x95
x95
x95
x95
x95
x95
x95
x95
 
x38
x38
x38
x44
x38
x38
x38
 
x156
x156
x156
x156
x156
x156
x156
x156
 
x145
x145
x145
x145
x145
x145
x145
x145
 
x103
x103
x103
x103
 
x59
x59
x91
x91
x119
x91
x95
x95
x59
 
x31
x31
x31
x31
x31
x31
x31
x31
x31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
 
 
 
 
 
 
 
 
 
 
x31
x31
x31
x31
x105
x106
x106
x178
x105
x105
x105
x105
x105
x105
x105
x105
x106
x106
 
x106
x177
 
x177
x177
x177
x177
 
 
x177
x177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x62
x86
x86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x34
x37
x37
x36
x34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
 
x85
 
x85
x85
x85
x86
x86
 
x86
x138
x138
x138
x85
x87
x85
x135
x135
x135
x85
x86
x86
x86
x85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x222
x222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x111
x112
x112
 
x112
x190
x111
x251
x251
x318
x318
x337
x1011
x337
x1348
x337
x337
x1348
x337
x318
x366
x366
x318
x251
x391
x391
x391
x251
x190
x111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x33
x33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x75
 
 
 
 
 
x75
x119
x119
x119
x119
x121
x119
x119
x119
 
x119
 
x162
x119
x119
x119
x357
x75
x75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x71
x71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x68
x68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x39
x39
x39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x59
x59
x59
x59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x45
x45
x45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x34
x39
x39
x34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x35
x40
x40
x35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x31
x94
x158
x158
x217
x217
 
x94
x31




































































































































































































































































































































I



I

I






































































































































































































































I















I























































































































































































































































I

// Copyright 2018-2025 the Deno authors. MIT license.

/**
 * Utilities for mocking time while testing.
 *
 * ```ts
 * import {
 *   assertSpyCalls,
 *   spy,
 * } from "@std/testing/mock";
 * import { FakeTime } from "@std/testing/time";
 *
 * function secondInterval(cb: () => void): number {
 *   return setInterval(cb, 1000);
 * }
 *
 * Deno.test("secondInterval calls callback every second and stops after being cleared", () => {
 *   using time = new FakeTime();
 *
 *   const cb = spy();
 *   const intervalId = secondInterval(cb);
 *   assertSpyCalls(cb, 0);
 *   time.tick(500);
 *   assertSpyCalls(cb, 0);
 *   time.tick(500);
 *   assertSpyCalls(cb, 1);
 *   time.tick(3500);
 *   assertSpyCalls(cb, 4);
 *
 *   clearInterval(intervalId);
 *   time.tick(1000);
 *   assertSpyCalls(cb, 4);
 * });
 * ```
 *
 * @module
 */

import { RedBlackTree } from "@std/data-structures/red-black-tree";
import { ascend } from "@std/data-structures/comparators";
import type { DelayOptions } from "@std/async/delay";
import { _internals } from "./_time.ts";

export type { DelayOptions };

/**
 * Represents an error when trying to execute an invalid operation on fake time,
 * given the state fake time is in.
 *
 * @example Usage
 * ```ts
 * import { FakeTime, TimeError } from "@std/testing/time";
 * import { assertThrows } from "@std/assert";
 *
 * assertThrows(() => {
 *   const time = new FakeTime();
 *   time.restore();
 *   time.restore();
 * }, TimeError);
 * ```
 */
export class TimeError extends Error {
  /** Construct TimeError.
   *
   * @param message The error message
   */
  constructor(message: string) {
    super(message);
    this.name = "TimeError";
  }
}

function fakeTimeNow() {
  return time?.now ?? _internals.Date.now();
}

const FakeDate = new Proxy(Date, {
  construct(_target, args) {
    if (args.length === 0) args.push(FakeDate.now());
    // @ts-expect-error this is a passthrough
    return new _internals.Date(...args);
  },
  apply(_target, _thisArg, _args) {
    return new _internals.Date(fakeTimeNow()).toString();
  },
  get(target, prop, receiver) {
    if (prop === "now") {
      return fakeTimeNow;
    }
    return Reflect.get(target, prop, receiver);
  },
});

interface Timer {
  id: number;
  // deno-lint-ignore no-explicit-any
  callback: (...args: any[]) => void;
  delay: number;
  args: unknown[];
  due: number;
  repeat: boolean;
}

/** The option for {@linkcode FakeTime} */
export interface FakeTimeOptions {
  /**
   * The rate relative to real time at which fake time is updated.
   * By default time only moves forward through calling tick or setting now.
   * Set to 1 to have the fake time automatically tick forward at the same rate in milliseconds as real time.
   *
   * @default {0}
   */
  advanceRate: number;
  /**
   * The frequency in milliseconds at which fake time is updated.
   * If advanceRate is set, we will update the time every 10 milliseconds by default.
   *
   * @default {10}
   */
  advanceFrequency?: number;
}

interface DueNode {
  due: number;
  timers: Timer[];
}

let time: FakeTime | undefined = undefined;

function fakeSetTimeout(
  // deno-lint-ignore no-explicit-any
  callback: (...args: any[]) => void,
  delay = 0,
  // deno-lint-ignore no-explicit-any
  ...args: any[]
): number {
  if (!time) throw new TimeError("Cannot set timeout: time is not faked");
  return setTimer(callback, delay, args, false);
}

function fakeClearTimeout(id?: unknown) {
  if (!time) throw new TimeError("Cannot clear timeout: time is not faked");
  if (typeof id === "number" && dueNodes.has(id)) {
    dueNodes.delete(id);
  }
}

function fakeSetInterval(
  // deno-lint-ignore no-explicit-any
  callback: (...args: any[]) => unknown,
  delay = 0,
  // deno-lint-ignore no-explicit-any
  ...args: any[]
): number {
  if (!time) throw new TimeError("Cannot set interval: time is not faked");
  return setTimer(callback, delay, args, true);
}

function fakeClearInterval(id?: unknown) {
  if (!time) throw new TimeError("Cannot clear interval: time is not faked");
  if (typeof id === "number" && dueNodes.has(id)) {
    dueNodes.delete(id);
  }
}

function setTimer(
  // deno-lint-ignore no-explicit-any
  callback: (...args: any[]) => void,
  delay = 0,
  args: unknown[],
  repeat = false,
): number {
  const id: number = timerId.next().value;
  delay = Math.max(repeat ? 1 : 0, Math.floor(delay));
  const due: number = now + delay;
  let dueNode: DueNode | null = dueTree.find({ due } as DueNode);
  if (dueNode === null) {
    dueNode = { due, timers: [] };
    dueTree.insert(dueNode);
  }
  dueNode.timers.push({
    id,
    callback,
    args,
    delay,
    due,
    repeat,
  });
  dueNodes.set(id, dueNode);
  return id;
}

function fakeAbortSignalTimeout(delay: number): AbortSignal {
  const aborter = new AbortController();
  fakeSetTimeout(() => {
    aborter.abort(new DOMException("Signal timed out.", "TimeoutError"));
  }, delay);
  return aborter.signal;
}

function overrideGlobals() {
  globalThis.Date = FakeDate;
  globalThis.setTimeout = fakeSetTimeout;
  globalThis.clearTimeout = fakeClearTimeout;
  globalThis.setInterval = fakeSetInterval;
  globalThis.clearInterval = fakeClearInterval;
  AbortSignal.timeout = fakeAbortSignalTimeout;
}

function restoreGlobals() {
  globalThis.Date = _internals.Date;
  globalThis.setTimeout = _internals.setTimeout;
  globalThis.clearTimeout = _internals.clearTimeout;
  globalThis.setInterval = _internals.setInterval;
  globalThis.clearInterval = _internals.clearInterval;
  AbortSignal.timeout = _internals.AbortSignalTimeout;
}

function* timerIdGen() {
  let i = 1;
  while (true) yield i++;
}

function nextDueNode(): DueNode | null {
  for (;;) {
    const dueNode = dueTree.min();
    if (!dueNode) return null;
    const hasTimer = dueNode.timers.some((timer) => dueNodes.has(timer.id));
    if (hasTimer) return dueNode;
    dueTree.remove(dueNode);
  }
}

let startedAt: number;
let now: number;
let initializedAt: number;
let advanceRate: number;
let advanceFrequency: number;
let advanceIntervalId: number | undefined;
let timerId: Generator<number>;
let dueNodes: Map<number, DueNode>;
let dueTree: RedBlackTree<DueNode>;

/**
 * Overrides the real Date object and timer functions with fake ones that can be
 * controlled through the fake time instance.
 *
 * Note: there is no setter for the `start` property, as it cannot be changed
 * after initialization.
 *
 * @example Usage
 * ```ts
 * import {
 *   assertSpyCalls,
 *   spy,
 * } from "@std/testing/mock";
 * import { FakeTime } from "@std/testing/time";
 *
 * function secondInterval(cb: () => void): number {
 *   return setInterval(cb, 1000);
 * }
 *
 * Deno.test("secondInterval calls callback every second and stops after being cleared", () => {
 *   using time = new FakeTime();
 *
 *   const cb = spy();
 *   const intervalId = secondInterval(cb);
 *   assertSpyCalls(cb, 0);
 *   time.tick(500);
 *   assertSpyCalls(cb, 0);
 *   time.tick(500);
 *   assertSpyCalls(cb, 1);
 *   time.tick(3500);
 *   assertSpyCalls(cb, 4);
 *
 *   clearInterval(intervalId);
 *   time.tick(1000);
 *   assertSpyCalls(cb, 4);
 * });
 * ```
 */
export class FakeTime {
  /**
   * Construct a FakeTime object. This overrides the real Date object and timer functions with fake ones that can be
   * controlled through the fake time instance.
   *
   * @param start The time to simulate. The default is the current time.
   * @param options The options
   *
   * @throws {TimeError} If time is already faked
   * @throws {TypeError} If the start is invalid
   */
  constructor(
    start?: number | string | Date | null,
    options?: FakeTimeOptions,
  ) {
    if (time) {
      throw new TimeError("Cannot construct FakeTime: time is already faked");
    }
    initializedAt = _internals.Date.now();
    startedAt = start instanceof Date
      ? start.valueOf()
      : typeof start === "number"
      ? Math.floor(start)
      : typeof start === "string"
      ? (new Date(start)).valueOf()
      : initializedAt;
    if (Number.isNaN(startedAt)) {
      throw new TypeError(
        `Cannot construct FakeTime: invalid start time ${startedAt}`,
      );
    }
    now = startedAt;

    timerId = timerIdGen();
    dueNodes = new Map();
    dueTree = new RedBlackTree(
      (a: DueNode, b: DueNode) => ascend(a.due, b.due),
    );

    overrideGlobals();
    time = this;

    advanceRate = Math.max(
      0,
      options?.advanceRate ?? 0,
    );
    advanceFrequency = Math.max(
      0,
      options?.advanceFrequency ?? 10,
    );
    advanceIntervalId = advanceRate > 0
      ? _internals.setInterval.call(null, () => {
        this.tick(advanceRate * advanceFrequency);
      }, advanceFrequency)
      : undefined;
  }

  /**
   * Restores real time.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals, assertNotEquals } from "@std/assert";
   *
   * const setTimeout = globalThis.setTimeout;
   *
   * {
   *   using fakeTime = new FakeTime();
   *
   *   assertNotEquals(globalThis.setTimeout, setTimeout);
   *
   *   // test timer related things.
   *
   *   // You don't need to call fakeTime.restore() explicitly
   *   // as it's implicitly called via the [Symbol.dispose] method
   *   // when declared with `using`.
   * }
   *
   * assertEquals(globalThis.setTimeout, setTimeout);
   * ```
   */
  [Symbol.dispose]() {
    this.restore();
  }

  /**
   * Restores real time.
   *
   * @throws {TimeError} If time is already restored
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals, assertNotEquals } from "@std/assert"
   *
   * const setTimeout = globalThis.setTimeout;
   *
   * const fakeTime = new FakeTime();
   *
   * assertNotEquals(globalThis.setTimeout, setTimeout);
   *
   * FakeTime.restore();
   *
   * assertEquals(globalThis.setTimeout, setTimeout);
   * ```
   */
  static restore() {
    if (!time) {
      throw new TimeError("Cannot restore time: time is already restored");
    }
    time.restore();
  }

  /**
   * Restores real time temporarily until callback returns and resolves.
   *
   * @throws {TimeError} If time is not faked
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals, assertNotEquals } from "@std/assert"
   *
   * const setTimeout = globalThis.setTimeout;
   *
   * const fakeTime = new FakeTime();
   *
   * assertNotEquals(globalThis.setTimeout, setTimeout);
   *
   * FakeTime.restoreFor(() => {
   *   assertEquals(globalThis.setTimeout, setTimeout);
   * });
   * ```
   *
   * @typeParam T The returned value type of the callback
   * @param callback The callback to be called while FakeTime being restored
   * @param args The arguments to pass to the callback
   * @returns The returned value from the callback
   */
  static restoreFor<T>(
    // deno-lint-ignore no-explicit-any
    callback: (...args: any[]) => Promise<T> | T,
    // deno-lint-ignore no-explicit-any
    ...args: any[]
  ): Promise<T> {
    if (!time) {
      return Promise.reject(
        new TimeError("Cannot restore time: time is not faked"),
      );
    }
    restoreGlobals();
    try {
      const result = callback.apply(null, args);
      if (result instanceof Promise) {
        return result.finally(() => overrideGlobals());
      } else {
        overrideGlobals();
        return Promise.resolve(result);
      }
    } catch (e) {
      overrideGlobals();
      return Promise.reject(e);
    }
  }

  /**
   * The number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * assertEquals(fakeTime.now, 15_000);
   *
   * fakeTime.tick(5_000);
   *
   * assertEquals(fakeTime.now, 20_000);
   * ```
   *
   * @returns The number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time
   */
  get now(): number {
    return now;
  }
  /**
   * Set the current time. It will call any functions waiting to be called between the current and new fake time.
   * If the timer callback throws, time will stop advancing forward beyond that timer.
   *
   * @throws {RangeError} If the time goes backwards
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * assertEquals(fakeTime.now, 15_000);
   *
   * fakeTime.now = 35_000;
   *
   * assertEquals(fakeTime.now, 35_000);
   * ```
   *
   * @param value The current time (in milliseconds)
   */
  set now(value: number) {
    if (value < now) {
      throw new RangeError(
        `Cannot set current time in the past, time must be >= ${now}: received ${value}`,
      );
    }
    let dueNode: DueNode | null = dueTree.min();
    while (dueNode && dueNode.due <= value) {
      const timer: Timer | undefined = dueNode.timers.shift();
      if (timer && dueNodes.has(timer.id)) {
        now = timer.due;
        if (timer.repeat) {
          const due: number = timer.due + timer.delay;
          let dueNode: DueNode | null = dueTree.find({ due } as DueNode);
          if (dueNode === null) {
            dueNode = { due, timers: [] };
            dueTree.insert(dueNode);
          }
          dueNode.timers.push({ ...timer, due });
          dueNodes.set(timer.id, dueNode);
        } else {
          dueNodes.delete(timer.id);
        }
        timer.callback.apply(null, timer.args);
      } else if (!timer) {
        dueTree.remove(dueNode);
        dueNode = dueTree.min();
      }
    }
    now = value;
  }

  /**
   * The initial number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * assertEquals(fakeTime.start, 15_000);
   * ```
   *
   * @returns The initial number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time.
   */
  get start(): number {
    return startedAt;
  }

  /**
   * Resolves after the given number of milliseconds using real time.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * await fakeTime.delay(500); // wait 500 ms in real time.
   *
   * assertEquals(fakeTime.now, 15_000); // The simulated time doesn't advance.
   * ```
   *
   * @param ms The milliseconds to delay
   * @param options The options
   */
  async delay(ms: number, options: DelayOptions = {}): Promise<void> {
    const { signal } = options;
    if (signal?.aborted) {
      return Promise.reject(
        new DOMException("Delay was aborted.", "AbortError"),
      );
    }
    return await new Promise((resolve, reject) => {
      let timer: number | null = null;
      const abort = () =>
        FakeTime
          .restoreFor(() => {
            if (timer) clearTimeout(timer);
          })
          .then(() =>
            reject(new DOMException("Delay was aborted.", "AbortError"))
          );
      const done = () => {
        signal?.removeEventListener("abort", abort);
        resolve();
      };
      FakeTime.restoreFor(() => setTimeout(done, ms))
        .then((id) => timer = id);
      signal?.addEventListener("abort", abort, { once: true });
    });
  }

  /**
   * Runs all pending microtasks.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assert } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * let called = false;
   *
   * Promise.resolve().then(() => { called = true });
   *
   * await fakeTime.runMicrotasks();
   *
   * assert(called);
   * ```
   */
  async runMicrotasks() {
    await this.delay(0);
  }

  /**
   * Adds the specified number of milliseconds to the fake time.
   * This will call any functions waiting to be called between the current and new fake time.
   *
   * @example Usage
   * ```ts
   * import {
   *   assertSpyCalls,
   *   spy,
   * } from "@std/testing/mock";
   * import { FakeTime } from "@std/testing/time";
   *
   * function secondInterval(cb: () => void): number {
   *   return setInterval(cb, 1000);
   * }
   *
   * Deno.test("secondInterval calls callback every second and stops after being cleared", () => {
   *   using time = new FakeTime();
   *
   *   const cb = spy();
   *   const intervalId = secondInterval(cb);
   *   assertSpyCalls(cb, 0);
   *   time.tick(500);
   *   assertSpyCalls(cb, 0);
   *   time.tick(500);
   *   assertSpyCalls(cb, 1);
   *   time.tick(3500);
   *   assertSpyCalls(cb, 4);
   *
   *   clearInterval(intervalId);
   *   time.tick(1000);
   *   assertSpyCalls(cb, 4);
   * });
   * ```
   *
   * @param ms The milliseconds to advance
   */
  tick(ms = 0) {
    this.now += ms;
  }

  /**
   * Runs all pending microtasks then adds the specified number of milliseconds to the fake time.
   * This will call any functions waiting to be called between the current and new fake time.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assert, assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * let called = false;
   *
   * Promise.resolve().then(() => { called = true });
   *
   * await fakeTime.tickAsync(5_000);
   *
   * assert(called);
   * assertEquals(fakeTime.now, 20_000);
   * ```
   *
   * @param ms The milliseconds to advance
   */
  async tickAsync(ms = 0) {
    await this.runMicrotasks();
    this.now += ms;
  }

  /**
   * Advances time to when the next scheduled timer is due.
   * If there are no pending timers, time will not be changed.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assert, assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * let called = false;
   *
   * setTimeout(() => { called = true }, 5000);
   *
   * fakeTime.next();
   *
   * assert(called);
   * assertEquals(fakeTime.now, 20_000);
   * ```
   *
   * @returns `true` when there is a scheduled timer and `false` when there is not.
   */
  next(): boolean {
    const next = nextDueNode();
    if (next) this.now = next.due;
    return !!next;
  }

  /**
   * Runs all pending microtasks then advances time to when the next scheduled timer is due.
   * If there are no pending timers, time will not be changed.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assert, assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * let called0 = false;
   * let called1 = false;
   *
   * setTimeout(() => { called0 = true }, 5000);
   * Promise.resolve().then(() => { called1 = true });
   *
   * await fakeTime.nextAsync();
   *
   * assert(called0);
   * assert(called1);
   * assertEquals(fakeTime.now, 20_000);
   * ```
   *
   * @returns `true` if the pending timers existed and the time advanced, `false` if there was no pending timer and the time didn't advance.
   */
  async nextAsync(): Promise<boolean> {
    await this.runMicrotasks();
    return this.next();
  }

  /**
   * Advances time forward to the next due timer until there are no pending timers remaining.
   * If the timers create additional timers, they will be run too. If there is an interval,
   * time will keep advancing forward until the interval is cleared.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * let count = 0;
   *
   * setTimeout(() => { count++ }, 5_000);
   * setTimeout(() => { count++ }, 15_000);
   * setTimeout(() => { count++ }, 35_000);
   *
   * fakeTime.runAll();
   *
   * assertEquals(count, 3);
   * assertEquals(fakeTime.now, 50_000);
   * ```
   */
  runAll() {
    while (!dueTree.isEmpty()) {
      this.next();
    }
  }

  /**
   * Advances time forward to the next due timer until there are no pending timers remaining.
   * If the timers create additional timers, they will be run too. If there is an interval,
   * time will keep advancing forward until the interval is cleared.
   * Runs all pending microtasks before each timer.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals } from "@std/assert";
   *
   * const fakeTime = new FakeTime(15_000);
   *
   * let count = 0;
   *
   * setTimeout(() => { count++ }, 5_000);
   * setTimeout(() => { count++ }, 15_000);
   * setTimeout(() => { count++ }, 35_000);
   * Promise.resolve().then(() => { count++ });
   *
   * await fakeTime.runAllAsync();
   *
   * assertEquals(count, 4);
   * assertEquals(fakeTime.now, 50_000);
   * ```
   */
  async runAllAsync() {
    while (!dueTree.isEmpty()) {
      await this.nextAsync();
    }
  }

  /**
   * Restores time related global functions to their original state.
   *
   * @example Usage
   * ```ts
   * import { FakeTime } from "@std/testing/time";
   * import { assertEquals, assertNotEquals } from "@std/assert";
   *
   * const setTimeout = globalThis.setTimeout;
   *
   * const fakeTime = new FakeTime(); // global timers are now faked
   *
   * assertNotEquals(globalThis.setTimeout, setTimeout);
   *
   * fakeTime.restore(); // timers are restored
   *
   * assertEquals(globalThis.setTimeout, setTimeout);
   * ```
   */
  restore() {
    if (!time) {
      throw new TimeError("Cannot restore time: time is already restored");
    }
    time = undefined;
    restoreGlobals();
    if (advanceIntervalId) clearInterval(advanceIntervalId);
  }
}