@kensio/yulin 1.21.14 → 1.21.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,10 +26,17 @@ export declare class BackgroundClockWaits {
26
26
  */
27
27
  around<T>(wait: BackgroundClockWait, work: () => Promise<T>): Promise<T>;
28
28
  /**
29
- * The work in a set that is neither waiting on the clock nor the work asking
30
- * about it.
29
+ * The work in a set that completion has anything to wait for.
30
+ *
31
+ * The work asking is always left out. Waiting for the invocation the asking
32
+ * code is part of would be waiting for itself.
33
+ *
34
+ * Work waiting on the clock is left out where simulated time stands still,
35
+ * since only a caller moving the clock brings the instant it waits for, and
36
+ * that caller is the one waiting here. A running clock reaches the instant on
37
+ * its own, and this waits for the work to get there.
31
38
  */
32
- runnable(held: ReadonlyMap<Promise<unknown>, BackgroundClockWait>): Promise<unknown>[];
39
+ runnable(held: ReadonlyMap<Promise<unknown>, BackgroundClockWait>, clockIsRunning?: boolean): Promise<unknown>[];
33
40
  /**
34
41
  * Whether the code running now is already inside outstanding work, and so
35
42
  * already counted.
@@ -23,14 +23,21 @@ export class BackgroundClockWaits {
23
23
  return await this.storage.run(wait, work);
24
24
  }
25
25
  /**
26
- * The work in a set that is neither waiting on the clock nor the work asking
27
- * about it.
26
+ * The work in a set that completion has anything to wait for.
27
+ *
28
+ * The work asking is always left out. Waiting for the invocation the asking
29
+ * code is part of would be waiting for itself.
30
+ *
31
+ * Work waiting on the clock is left out where simulated time stands still,
32
+ * since only a caller moving the clock brings the instant it waits for, and
33
+ * that caller is the one waiting here. A running clock reaches the instant on
34
+ * its own, and this waits for the work to get there.
28
35
  */
29
- runnable(held) {
36
+ runnable(held, clockIsRunning = false) {
30
37
  const own = this.storage.getStore();
31
38
  return held
32
39
  .entries()
33
- .filter(([, wait]) => wait.count === 0 && wait !== own)
40
+ .filter(([, wait]) => wait !== own && (clockIsRunning || wait.count === 0))
34
41
  .map(([promise]) => promise)
35
42
  .toArray();
36
43
  }
@@ -35,21 +35,27 @@ export declare class BackgroundPendingTasks {
35
35
  */
36
36
  get size(): number;
37
37
  /**
38
- * Wait until nothing is outstanding but work that is waiting on the clock.
38
+ * Wait until nothing is outstanding that can still get somewhere.
39
39
  *
40
- * Work that parks part way through ends the wait rather than extending it:
41
- * only moving the clock releases it, and moving the clock is what waits
42
- * here. The loop then looks again at what is left running, so work that
43
- * carries on is waited for again.
40
+ * Where simulated time stands still, work that parks on the clock ends the
41
+ * wait rather than extending it. Only moving the clock releases it, and
42
+ * moving the clock is what waits here. The loop then looks again at what is
43
+ * left running, so work that carries on is waited for again.
44
+ *
45
+ * A running clock reaches those instants by itself, and the work parked on
46
+ * them is waited for like any other. What bounds the wait is the instant
47
+ * itself, which arrives in the real time between here and there.
44
48
  */
45
- complete(): Promise<void>;
49
+ complete(clockIsRunning?: boolean): Promise<void>;
46
50
  /**
47
- * The work completion has anything to wait for, which is whatever is
48
- * neither waiting on the clock nor waiting for completion itself.
51
+ * The work completion has anything to wait for.
49
52
  *
50
53
  * Work that asks for the simulation to settle from inside itself is left
51
54
  * out. A handler advancing the clock is the ordinary case, and waiting for
52
55
  * the invocation it is part of would be waiting for itself.
56
+ *
57
+ * Work waiting on the clock joins it once simulated time is running, since
58
+ * a running clock is what brings the instant it waits for.
53
59
  */
54
- running(): Promise<unknown>[];
60
+ running(clockIsRunning?: boolean): Promise<unknown>[];
55
61
  }
@@ -52,15 +52,19 @@ export class BackgroundPendingTasks {
52
52
  return this.#held.size;
53
53
  }
54
54
  /**
55
- * Wait until nothing is outstanding but work that is waiting on the clock.
55
+ * Wait until nothing is outstanding that can still get somewhere.
56
56
  *
57
- * Work that parks part way through ends the wait rather than extending it:
58
- * only moving the clock releases it, and moving the clock is what waits
59
- * here. The loop then looks again at what is left running, so work that
60
- * carries on is waited for again.
57
+ * Where simulated time stands still, work that parks on the clock ends the
58
+ * wait rather than extending it. Only moving the clock releases it, and
59
+ * moving the clock is what waits here. The loop then looks again at what is
60
+ * left running, so work that carries on is waited for again.
61
+ *
62
+ * A running clock reaches those instants by itself, and the work parked on
63
+ * them is waited for like any other. What bounds the wait is the instant
64
+ * itself, which arrives in the real time between here and there.
61
65
  */
62
- async complete() {
63
- let running = this.running();
66
+ async complete(clockIsRunning = false) {
67
+ let running = this.running(clockIsRunning);
64
68
  while (running.length > 0) {
65
69
  // oxlint-disable-next-line no-await-in-loop
66
70
  const settled = await Promise.race([
@@ -68,19 +72,21 @@ export class BackgroundPendingTasks {
68
72
  this.#clockWaits.next,
69
73
  ]);
70
74
  new BackgroundSettledTasks(settled).throwFirstFailure();
71
- running = this.running();
75
+ running = this.running(clockIsRunning);
72
76
  }
73
77
  }
74
78
  /**
75
- * The work completion has anything to wait for, which is whatever is
76
- * neither waiting on the clock nor waiting for completion itself.
79
+ * The work completion has anything to wait for.
77
80
  *
78
81
  * Work that asks for the simulation to settle from inside itself is left
79
82
  * out. A handler advancing the clock is the ordinary case, and waiting for
80
83
  * the invocation it is part of would be waiting for itself.
84
+ *
85
+ * Work waiting on the clock joins it once simulated time is running, since
86
+ * a running clock is what brings the instant it waits for.
81
87
  */
82
- running() {
83
- return this.#clockWaits.runnable(this.#held);
88
+ running(clockIsRunning = false) {
89
+ return this.#clockWaits.runnable(this.#held, clockIsRunning);
84
90
  }
85
91
  /**
86
92
  * Keep work outstanding under its own record until it settles.
@@ -141,9 +141,11 @@ export declare class BackgroundTasks implements BackgroundScheduler, BackgroundC
141
141
  * Wait until all tasks currently scheduled have finished.
142
142
  * If tasks schedule more tasks, this will continue draining until idle.
143
143
  *
144
- * Work waiting on the clock is not waited for: it is not outstanding, it is
145
- * scheduled for a simulated instant that has not arrived. Only moving the
146
- * clock releases it.
144
+ * What happens to work waiting on the clock follows the clock. Where
145
+ * simulated time stands still the work stays where it is, scheduled for an
146
+ * instant only a caller can bring, and this returns without it. Where
147
+ * simulated time runs, the instant arrives by itself and this waits for the
148
+ * work to get through it.
147
149
  */
148
150
  complete(): Promise<void>;
149
151
  /**
@@ -154,5 +156,11 @@ export declare class BackgroundTasks implements BackgroundScheduler, BackgroundC
154
156
  * See how many tasks are waiting for simulated time to reach them.
155
157
  */
156
158
  get dueTaskCount(): number;
159
+ /**
160
+ * Whether simulated time reaches a scheduled instant on its own.
161
+ *
162
+ * A clock with nothing to say about it advances, as the host clock does.
163
+ */
164
+ private get clockIsRunning();
157
165
  }
158
166
  export {};
@@ -79,12 +79,14 @@ export class BackgroundTasks {
79
79
  * Wait until all tasks currently scheduled have finished.
80
80
  * If tasks schedule more tasks, this will continue draining until idle.
81
81
  *
82
- * Work waiting on the clock is not waited for: it is not outstanding, it is
83
- * scheduled for a simulated instant that has not arrived. Only moving the
84
- * clock releases it.
82
+ * What happens to work waiting on the clock follows the clock. Where
83
+ * simulated time stands still the work stays where it is, scheduled for an
84
+ * instant only a caller can bring, and this returns without it. Where
85
+ * simulated time runs, the instant arrives by itself and this waits for the
86
+ * work to get through it.
85
87
  */
86
88
  async complete() {
87
- await this.pending.complete();
89
+ await this.pending.complete(this.clockIsRunning);
88
90
  }
89
91
  /**
90
92
  * See how many outstanding background tasks are scheduled.
@@ -98,4 +100,12 @@ export class BackgroundTasks {
98
100
  get dueTaskCount() {
99
101
  return this.dueTasks.size;
100
102
  }
103
+ /**
104
+ * Whether simulated time reaches a scheduled instant on its own.
105
+ *
106
+ * A clock with nothing to say about it advances, as the host clock does.
107
+ */
108
+ get clockIsRunning() {
109
+ return this.clock.advances !== false;
110
+ }
101
111
  }
@@ -55,6 +55,10 @@ export declare class NonDeterministicBackgroundTasks implements BackgroundSchedu
55
55
  /**
56
56
  * Wait until all tasks currently scheduled have finished.
57
57
  * If tasks schedule more tasks, this will continue draining until idle.
58
+ *
59
+ * Work waiting on the clock is waited for where simulated time runs, and
60
+ * left where it is where simulated time stands still, exactly as it is on
61
+ * the deterministic scheduler.
58
62
  */
59
63
  complete(): Promise<void>;
60
64
  /**
@@ -79,9 +79,13 @@ export class NonDeterministicBackgroundTasks {
79
79
  /**
80
80
  * Wait until all tasks currently scheduled have finished.
81
81
  * If tasks schedule more tasks, this will continue draining until idle.
82
+ *
83
+ * Work waiting on the clock is waited for where simulated time runs, and
84
+ * left where it is where simulated time stands still, exactly as it is on
85
+ * the deterministic scheduler.
82
86
  */
83
87
  async complete() {
84
- await this.pending.complete();
88
+ await this.pending.complete(this.clock.advances !== false);
85
89
  }
86
90
  /**
87
91
  * See how many outstanding background tasks are scheduled.
@@ -11,6 +11,18 @@ export interface SimClock {
11
11
  * Get the current time in this simulation.
12
12
  */
13
13
  now(): Date;
14
+ /**
15
+ * Whether simulated time moves on this clock by itself.
16
+ *
17
+ * A scheduler reads this to decide what waiting for a simulation to settle
18
+ * means. Time that advances brings the instant a task is scheduled for on
19
+ * its own, and settling waits for that task. Time that stands still leaves
20
+ * the instant to whoever moves the clock, and settling leaves the task where
21
+ * it is.
22
+ *
23
+ * A clock that says nothing advances, as the host clock does.
24
+ */
25
+ readonly advances?: boolean;
14
26
  }
15
27
  /**
16
28
  * Clock that reports the real system time.
@@ -19,6 +31,8 @@ export interface SimClock {
19
31
  * something deliberately replaces it.
20
32
  */
21
33
  export declare class SimRealClock implements SimClock {
34
+ /** Real time moves by itself. */
35
+ readonly advances = true;
22
36
  /**
23
37
  * Get the current real system time.
24
38
  */
@@ -31,6 +45,8 @@ export declare class SimRealClock implements SimClock {
31
45
  * reaching for a library that replaces the clock for the whole process.
32
46
  */
33
47
  export declare class SimFixedClock implements SimClock {
48
+ /** A fixed instant is the whole of this clock. Time stands still on it. */
49
+ readonly advances = false;
34
50
  private readonly instant;
35
51
  constructor(instant: Date);
36
52
  /**
@@ -5,6 +5,8 @@
5
5
  * something deliberately replaces it.
6
6
  */
7
7
  export class SimRealClock {
8
+ /** Real time moves by itself. */
9
+ advances = true;
8
10
  /**
9
11
  * Get the current real system time.
10
12
  */
@@ -19,6 +21,8 @@ export class SimRealClock {
19
21
  * reaching for a library that replaces the clock for the whole process.
20
22
  */
21
23
  export class SimFixedClock {
24
+ /** A fixed instant is the whole of this clock. Time stands still on it. */
25
+ advances = false;
22
26
  instant;
23
27
  constructor(instant) {
24
28
  this.instant = new Date(instant);
@@ -33,6 +33,17 @@ export declare class SimControllableClock implements SimClock {
33
33
  * Whether simulated time is currently standing still.
34
34
  */
35
35
  get isFrozen(): boolean;
36
+ /**
37
+ * Whether simulated time moves here by itself.
38
+ *
39
+ * A frozen clock moves only where something moves it. So does a running
40
+ * clock over a base that stands still. The offset a running mode applies is
41
+ * fixed, so simulated time here moves exactly as far as the clock underneath
42
+ * moves, and a fixed clock underneath moves nowhere. Freezing and resuming
43
+ * such a clock changes which instant it reports and leaves it as still as it
44
+ * was.
45
+ */
46
+ get advances(): boolean;
36
47
  /**
37
48
  * Stop simulated time where it currently reads.
38
49
  */
@@ -32,6 +32,19 @@ export class SimControllableClock {
32
32
  get isFrozen() {
33
33
  return this.mode.isFrozen;
34
34
  }
35
+ /**
36
+ * Whether simulated time moves here by itself.
37
+ *
38
+ * A frozen clock moves only where something moves it. So does a running
39
+ * clock over a base that stands still. The offset a running mode applies is
40
+ * fixed, so simulated time here moves exactly as far as the clock underneath
41
+ * moves, and a fixed clock underneath moves nowhere. Freezing and resuming
42
+ * such a clock changes which instant it reports and leaves it as still as it
43
+ * was.
44
+ */
45
+ get advances() {
46
+ return !this.mode.isFrozen && this.base.advances !== false;
47
+ }
35
48
  /**
36
49
  * Stop simulated time where it currently reads.
37
50
  */
@@ -3269,6 +3269,17 @@ An interval runs once for each period an advance covers. Advancing eleven second
3269
3269
  interval runs it five times. A delay of zero, or none at all, is due at the instant it was asked
3270
3270
  for, and a handler yielding with `setTimeout(resolve, 0)` gets going again without the clock moving.
3271
3271
 
3272
+ Where the clock is left running, the delay passes in real time and nothing has to move it.
3273
+ `simAws.backgroundTasksComplete()` waits for a handler sleeping on a clock that is moving. An S3
3274
+ event notification, a stream record or an asynchronous invocation whose handler uses a timer has
3275
+ finished by the time the drain returns, and the function's `Timeout` bounds how long that wait can
3276
+ last.
3277
+
3278
+ Two things leave a clock standing still. `freeze()`, `advanceBy(...)` and `setTo(...)` all leave it
3279
+ frozen, and a `SimAws` built on a `SimFixedClock` reports one instant however long the host runs.
3280
+ Under either the drain comes back while the handler sleeps, and moving the clock is what releases
3281
+ it.
3282
+
3272
3283
  The function's `Timeout` is a deadline on the same clock. Where it arrives before the handler
3273
3284
  answers, the invocation ends in the error the real runtime reports.
3274
3285
 
@@ -63,6 +63,13 @@ example, a simulation advanced by one hour continues to run one hour ahead of th
63
63
 
64
64
  Read `simAws.clock().isFrozen` to check the current mode.
65
65
 
66
+ The mode also decides what waiting for the simulation to settle means.
67
+ `simAws.backgroundTasksComplete()` waits for background work sleeping on a clock that is moving,
68
+ because the instant that work sleeps until arrives by itself. Under a clock standing still the same
69
+ work is left where it is, and `advanceBy(...)` or a forward `setTo(...)` is what brings the instant
70
+ and waits for what follows from it. A simulation built on a `SimFixedClock` stands still under a
71
+ running mode too, since simulated time here moves only as far as the clock underneath moves.
72
+
66
73
  ## Advancing time
67
74
 
68
75
  `advanceBy(...)` accepts days, hours, minutes, seconds, and milliseconds. The values are added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.21.14",
3
+ "version": "1.21.15",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",