Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/common/async.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import * as async from '../../common/async.js';
8
import * as MicrotaskDelay from "../../common/symbols.js";
9
import { CancellationToken, CancellationTokenSource } from '../../common/cancellation.js';
10
import { isCancellationError } from '../../common/errors.js';
11
import { Event } from '../../common/event.js';
12
import { URI } from '../../common/uri.js';
13
import { runWithFakedTimers } from './timeTravelScheduler.js';
14
import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js';
15
import { DisposableStore } from '../../common/lifecycle.js';
16
import { Iterable } from '../../common/iterator.js';
17
18
suite('Async', () => {
19
20
const store = ensureNoDisposablesAreLeakedInTestSuite();
21
22
suite('cancelablePromise', function () {
23
test('set token, don\'t wait for inner promise', function () {
24
let canceled = 0;
25
const promise = async.createCancelablePromise(token => {
26
store.add(token.onCancellationRequested(_ => { canceled += 1; }));
27
return new Promise(resolve => { /*never*/ });
28
});
29
const result = promise.then(_ => assert.ok(false), err => {
30
assert.strictEqual(canceled, 1);
31
assert.ok(isCancellationError(err));
32
});
33
promise.cancel();
34
promise.cancel(); // cancel only once
35
return result;
36
});
37
38
test('cancel despite inner promise being resolved', function () {
39
let canceled = 0;
40
const promise = async.createCancelablePromise(token => {
41
store.add(token.onCancellationRequested(_ => { canceled += 1; }));
42
return Promise.resolve(1234);
43
});
44
const result = promise.then(_ => assert.ok(false), err => {
45
assert.strictEqual(canceled, 1);
46
assert.ok(isCancellationError(err));
47
});
48
promise.cancel();
49
return result;
50
});
51
52
test('cancel disposes result', function () {
53
54
const store = new DisposableStore();
55
56
const promise = async.createCancelablePromise(async token => {
57
return store;
58
});
59
promise.then(_ => assert.ok(false), err => {
60
61
assert.ok(isCancellationError(err));
62
assert.ok(store.isDisposed);
63
});
64
65
promise.cancel();
66
});
67
68
// Cancelling a sync cancelable promise will fire the cancelled token.
69
// Also, every `then` callback runs in another execution frame.
70
test('execution order (sync)', function () {
71
const order: string[] = [];
72
73
const cancellablePromise = async.createCancelablePromise(token => {
74
order.push('in callback');
75
store.add(token.onCancellationRequested(_ => order.push('cancelled')));
76
return Promise.resolve(1234);
77
});
78
79
order.push('afterCreate');
80
81
const promise = cancellablePromise
82
.then(undefined, err => null)
83
.then(() => order.push('finally'));
84
85
cancellablePromise.cancel();
86
order.push('afterCancel');
87
88
return promise.then(() => assert.deepStrictEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally']));
89
});
90
91
// Cancelling an async cancelable promise is just the same as a sync cancellable promise.
92
test('execution order (async)', function () {
93
const order: string[] = [];
94
95
const cancellablePromise = async.createCancelablePromise(token => {
96
order.push('in callback');
97
store.add(token.onCancellationRequested(_ => order.push('cancelled')));
98
return new Promise(c => setTimeout(c.bind(1234), 0));
99
});
100
101
order.push('afterCreate');
102
103
const promise = cancellablePromise
104
.then(undefined, err => null)
105
.then(() => order.push('finally'));
106
107
cancellablePromise.cancel();
108
order.push('afterCancel');
109
110
return promise.then(() => assert.deepStrictEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally']));
111
});
112
113
test('execution order (async with late listener)', async function () {
114
const order: string[] = [];
115
116
const cancellablePromise = async.createCancelablePromise(async token => {
117
order.push('in callback');
118
119
await async.timeout(0);
120
store.add(token.onCancellationRequested(_ => order.push('cancelled')));
121
cancellablePromise.cancel();
122
order.push('afterCancel');
123
});
124
125
order.push('afterCreate');
126
127
const promise = cancellablePromise
128
.then(undefined, err => null)
129
.then(() => order.push('finally'));
130
131
return promise.then(() => assert.deepStrictEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally']));
132
});
133
134
test('get inner result', async function () {
135
const promise = async.createCancelablePromise(token => {
136
return async.timeout(12).then(_ => 1234);
137
});
138
139
const result = await promise;
140
assert.strictEqual(result, 1234);
141
});
142
});
143
144
suite('Throttler', function () {
145
test('non async', function () {
146
let count = 0;
147
const factory = () => {
148
return Promise.resolve(++count);
149
};
150
151
const throttler = new async.Throttler();
152
153
return Promise.all([
154
throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }),
155
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }),
156
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }),
157
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }),
158
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); })
159
]).then(() => assert.strictEqual(count, 2));
160
});
161
162
test('async', () => {
163
let count = 0;
164
const factory = () => async.timeout(0).then(() => ++count);
165
166
const throttler = new async.Throttler();
167
168
return Promise.all([
169
throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }),
170
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }),
171
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }),
172
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); }),
173
throttler.queue(factory).then((result) => { assert.strictEqual(result, 2); })
174
]).then(() => {
175
return Promise.all([
176
throttler.queue(factory).then((result) => { assert.strictEqual(result, 3); }),
177
throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }),
178
throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }),
179
throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); }),
180
throttler.queue(factory).then((result) => { assert.strictEqual(result, 4); })
181
]);
182
});
183
});
184
185
test('last factory should be the one getting called', function () {
186
const factoryFactory = (n: number) => () => {
187
return async.timeout(0).then(() => n);
188
};
189
190
const throttler = new async.Throttler();
191
192
const promises: Promise<any>[] = [];
193
194
promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.strictEqual(n, 1); }));
195
promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.strictEqual(n, 3); }));
196
promises.push(throttler.queue(factoryFactory(3)).then((n) => { assert.strictEqual(n, 3); }));
197
198
return Promise.all(promises);
199
});
200
201
test('disposal after queueing', async () => {
202
let factoryCalls = 0;
203
const factory = async () => {
204
factoryCalls++;
205
return async.timeout(0);
206
};
207
208
const throttler = new async.Throttler();
209
const promises: Promise<any>[] = [];
210
211
promises.push(throttler.queue(factory));
212
promises.push(throttler.queue(factory));
213
throttler.dispose();
214
215
await Promise.all(promises);
216
assert.strictEqual(factoryCalls, 1);
217
});
218
219
test('disposal before queueing', async () => {
220
let factoryCalls = 0;
221
const factory = async () => {
222
factoryCalls++;
223
return async.timeout(0);
224
};
225
226
const throttler = new async.Throttler();
227
const promises: Promise<any>[] = [];
228
229
throttler.dispose();
230
promises.push(throttler.queue(factory));
231
232
try {
233
await Promise.all(promises);
234
assert.fail('should fail');
235
} catch (err) {
236
assert.strictEqual(factoryCalls, 0);
237
}
238
});
239
});
240
241
suite('Delayer', function () {
242
test('simple', () => {
243
let count = 0;
244
const factory = () => {
245
return Promise.resolve(++count);
246
};
247
248
const delayer = new async.Delayer(0);
249
const promises: Promise<any>[] = [];
250
251
assert(!delayer.isTriggered());
252
253
promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
254
assert(delayer.isTriggered());
255
256
promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
257
assert(delayer.isTriggered());
258
259
promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
260
assert(delayer.isTriggered());
261
262
return Promise.all(promises).then(() => {
263
assert(!delayer.isTriggered());
264
});
265
});
266
267
test('microtask delay simple', () => {
268
let count = 0;
269
const factory = () => {
270
return Promise.resolve(++count);
271
};
272
273
const delayer = new async.Delayer(MicrotaskDelay.MicrotaskDelay);
274
const promises: Promise<any>[] = [];
275
276
assert(!delayer.isTriggered());
277
278
promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
279
assert(delayer.isTriggered());
280
281
promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
282
assert(delayer.isTriggered());
283
284
promises.push(delayer.trigger(factory).then((result) => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
285
assert(delayer.isTriggered());
286
287
return Promise.all(promises).then(() => {
288
assert(!delayer.isTriggered());
289
});
290
});
291
292
suite('ThrottledDelayer', () => {
293
test('promise should resolve if disposed', async () => {
294
const throttledDelayer = new async.ThrottledDelayer<void>(100);
295
const promise = throttledDelayer.trigger(async () => { }, 0);
296
throttledDelayer.dispose();
297
298
try {
299
await promise;
300
assert.fail('SHOULD NOT BE HERE');
301
} catch (err) {
302
// OK
303
}
304
});
305
306
test('trigger after dispose throws', async () => {
307
const throttledDelayer = new async.ThrottledDelayer<void>(100);
308
throttledDelayer.dispose();
309
await assert.rejects(() => throttledDelayer.trigger(async () => { }, 0));
310
});
311
});
312
313
test('simple cancel', function () {
314
let count = 0;
315
const factory = () => {
316
return Promise.resolve(++count);
317
};
318
319
const delayer = new async.Delayer(0);
320
321
assert(!delayer.isTriggered());
322
323
const p = delayer.trigger(factory).then(() => {
324
assert(false);
325
}, () => {
326
assert(true, 'yes, it was cancelled');
327
});
328
329
assert(delayer.isTriggered());
330
delayer.cancel();
331
assert(!delayer.isTriggered());
332
333
return p;
334
});
335
336
test('simple cancel microtask', function () {
337
let count = 0;
338
const factory = () => {
339
return Promise.resolve(++count);
340
};
341
342
const delayer = new async.Delayer(MicrotaskDelay.MicrotaskDelay);
343
344
assert(!delayer.isTriggered());
345
346
const p = delayer.trigger(factory).then(() => {
347
assert(false);
348
}, () => {
349
assert(true, 'yes, it was cancelled');
350
});
351
352
assert(delayer.isTriggered());
353
delayer.cancel();
354
assert(!delayer.isTriggered());
355
356
return p;
357
});
358
359
test('cancel should cancel all calls to trigger', function () {
360
let count = 0;
361
const factory = () => {
362
return Promise.resolve(++count);
363
};
364
365
const delayer = new async.Delayer(0);
366
const promises: Promise<any>[] = [];
367
368
assert(!delayer.isTriggered());
369
370
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
371
assert(delayer.isTriggered());
372
373
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
374
assert(delayer.isTriggered());
375
376
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
377
assert(delayer.isTriggered());
378
379
delayer.cancel();
380
381
return Promise.all(promises).then(() => {
382
assert(!delayer.isTriggered());
383
});
384
});
385
386
test('trigger, cancel, then trigger again', function () {
387
let count = 0;
388
const factory = () => {
389
return Promise.resolve(++count);
390
};
391
392
const delayer = new async.Delayer(0);
393
let promises: Promise<any>[] = [];
394
395
assert(!delayer.isTriggered());
396
397
const p = delayer.trigger(factory).then((result) => {
398
assert.strictEqual(result, 1);
399
assert(!delayer.isTriggered());
400
401
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
402
assert(delayer.isTriggered());
403
404
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
405
assert(delayer.isTriggered());
406
407
delayer.cancel();
408
409
const p = Promise.all(promises).then(() => {
410
promises = [];
411
412
assert(!delayer.isTriggered());
413
414
promises.push(delayer.trigger(factory).then(() => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
415
assert(delayer.isTriggered());
416
417
promises.push(delayer.trigger(factory).then(() => { assert.strictEqual(result, 1); assert(!delayer.isTriggered()); }));
418
assert(delayer.isTriggered());
419
420
const p = Promise.all(promises).then(() => {
421
assert(!delayer.isTriggered());
422
});
423
424
assert(delayer.isTriggered());
425
426
return p;
427
});
428
429
return p;
430
});
431
432
assert(delayer.isTriggered());
433
434
return p;
435
});
436
437
test('last task should be the one getting called', function () {
438
const factoryFactory = (n: number) => () => {
439
return Promise.resolve(n);
440
};
441
442
const delayer = new async.Delayer(0);
443
const promises: Promise<any>[] = [];
444
445
assert(!delayer.isTriggered());
446
447
promises.push(delayer.trigger(factoryFactory(1)).then((n) => { assert.strictEqual(n, 3); }));
448
promises.push(delayer.trigger(factoryFactory(2)).then((n) => { assert.strictEqual(n, 3); }));
449
promises.push(delayer.trigger(factoryFactory(3)).then((n) => { assert.strictEqual(n, 3); }));
450
451
const p = Promise.all(promises).then(() => {
452
assert(!delayer.isTriggered());
453
});
454
455
assert(delayer.isTriggered());
456
457
return p;
458
});
459
});
460
461
suite('sequence', () => {
462
test('simple', () => {
463
const factoryFactory = (n: number) => () => {
464
return Promise.resolve(n);
465
};
466
467
return async.sequence([
468
factoryFactory(1),
469
factoryFactory(2),
470
factoryFactory(3),
471
factoryFactory(4),
472
factoryFactory(5),
473
]).then((result) => {
474
assert.strictEqual(5, result.length);
475
assert.strictEqual(1, result[0]);
476
assert.strictEqual(2, result[1]);
477
assert.strictEqual(3, result[2]);
478
assert.strictEqual(4, result[3]);
479
assert.strictEqual(5, result[4]);
480
});
481
});
482
});
483
484
suite('Limiter', () => {
485
test('assert degree of paralellism', function () {
486
let activePromises = 0;
487
const factoryFactory = (n: number) => () => {
488
activePromises++;
489
assert(activePromises < 6);
490
return async.timeout(0).then(() => { activePromises--; return n; });
491
};
492
493
const limiter = new async.Limiter(5);
494
495
const promises: Promise<any>[] = [];
496
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n))));
497
498
return Promise.all(promises).then((res) => {
499
assert.strictEqual(10, res.length);
500
assert.deepStrictEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], res);
501
});
502
});
503
});
504
505
506
suite('Queue', () => {
507
test('simple', function () {
508
const queue = new async.Queue();
509
510
let syncPromise = false;
511
const f1 = () => Promise.resolve(true).then(() => syncPromise = true);
512
513
let asyncPromise = false;
514
const f2 = () => async.timeout(10).then(() => asyncPromise = true);
515
516
assert.strictEqual(queue.size, 0);
517
518
queue.queue(f1);
519
assert.strictEqual(queue.size, 1);
520
521
const p = queue.queue(f2);
522
assert.strictEqual(queue.size, 2);
523
return p.then(() => {
524
assert.strictEqual(queue.size, 0);
525
assert.ok(syncPromise);
526
assert.ok(asyncPromise);
527
});
528
});
529
530
test('stop processing on dispose', async function () {
531
const queue = new async.Queue();
532
533
let workCounter = 0;
534
const task = async () => {
535
await async.timeout(0);
536
workCounter++;
537
queue.dispose(); // DISPOSE HERE
538
};
539
540
const p1 = queue.queue(task);
541
queue.queue(task);
542
queue.queue(task);
543
assert.strictEqual(queue.size, 3);
544
545
546
await p1;
547
548
assert.strictEqual(workCounter, 1);
549
});
550
551
test('stop on clear', async function () {
552
const queue = new async.Queue();
553
554
let workCounter = 0;
555
const task = async () => {
556
await async.timeout(0);
557
workCounter++;
558
queue.clear(); // CLEAR HERE
559
assert.strictEqual(queue.size, 1); // THIS task is still running
560
};
561
562
const p1 = queue.queue(task);
563
queue.queue(task);
564
queue.queue(task);
565
assert.strictEqual(queue.size, 3);
566
567
await p1;
568
assert.strictEqual(workCounter, 1);
569
assert.strictEqual(queue.size, 0); // has been cleared
570
571
572
const p2 = queue.queue(task);
573
await p2;
574
assert.strictEqual(workCounter, 2);
575
});
576
577
test('clear and drain (1)', async function () {
578
const queue = new async.Queue();
579
580
let workCounter = 0;
581
const task = async () => {
582
await async.timeout(0);
583
workCounter++;
584
queue.clear(); // CLEAR HERE
585
};
586
587
const p0 = Event.toPromise(queue.onDrained);
588
const p1 = queue.queue(task);
589
590
await p1;
591
await p0; // expect drain to fire because a task was running
592
assert.strictEqual(workCounter, 1);
593
queue.dispose();
594
});
595
596
test('clear and drain (2)', async function () {
597
const queue = new async.Queue();
598
599
let didFire = false;
600
const d = queue.onDrained(() => {
601
didFire = true;
602
});
603
604
queue.clear();
605
606
assert.strictEqual(didFire, false); // no work, no drain!
607
d.dispose();
608
queue.dispose();
609
});
610
611
test('drain timing', async function () {
612
const queue = new async.Queue();
613
614
const logicClock = new class {
615
private time = 0;
616
tick() {
617
return this.time++;
618
}
619
};
620
621
let didDrainTime = 0;
622
let didFinishTime1 = 0;
623
let didFinishTime2 = 0;
624
const d = queue.onDrained(() => {
625
didDrainTime = logicClock.tick();
626
});
627
628
const p1 = queue.queue(() => {
629
// await async.timeout(10);
630
didFinishTime1 = logicClock.tick();
631
return Promise.resolve();
632
});
633
634
const p2 = queue.queue(async () => {
635
await async.timeout(10);
636
didFinishTime2 = logicClock.tick();
637
});
638
639
640
await Promise.all([p1, p2]);
641
642
assert.strictEqual(didFinishTime1, 0);
643
assert.strictEqual(didFinishTime2, 1);
644
assert.strictEqual(didDrainTime, 2);
645
646
d.dispose();
647
queue.dispose();
648
});
649
650
test('drain event is send only once', async function () {
651
const queue = new async.Queue();
652
653
let drainCount = 0;
654
const d = queue.onDrained(() => { drainCount++; });
655
queue.queue(async () => { });
656
queue.queue(async () => { });
657
queue.queue(async () => { });
658
queue.queue(async () => { });
659
assert.strictEqual(drainCount, 0);
660
assert.strictEqual(queue.size, 4);
661
662
await queue.whenIdle();
663
664
assert.strictEqual(drainCount, 1);
665
666
d.dispose();
667
queue.dispose();
668
});
669
670
test('order is kept', function () {
671
return runWithFakedTimers({}, () => {
672
const queue = new async.Queue();
673
674
const res: number[] = [];
675
676
const f1 = () => Promise.resolve(true).then(() => res.push(1));
677
const f2 = () => async.timeout(10).then(() => res.push(2));
678
const f3 = () => Promise.resolve(true).then(() => res.push(3));
679
const f4 = () => async.timeout(20).then(() => res.push(4));
680
const f5 = () => async.timeout(0).then(() => res.push(5));
681
682
queue.queue(f1);
683
queue.queue(f2);
684
queue.queue(f3);
685
queue.queue(f4);
686
return queue.queue(f5).then(() => {
687
assert.strictEqual(res[0], 1);
688
assert.strictEqual(res[1], 2);
689
assert.strictEqual(res[2], 3);
690
assert.strictEqual(res[3], 4);
691
assert.strictEqual(res[4], 5);
692
});
693
});
694
});
695
696
test('errors bubble individually but not cause stop', function () {
697
const queue = new async.Queue();
698
699
const res: number[] = [];
700
let error = false;
701
702
const f1 = () => Promise.resolve(true).then(() => res.push(1));
703
const f2 = () => async.timeout(10).then(() => res.push(2));
704
const f3 = () => Promise.resolve(true).then(() => Promise.reject(new Error('error')));
705
const f4 = () => async.timeout(20).then(() => res.push(4));
706
const f5 = () => async.timeout(0).then(() => res.push(5));
707
708
queue.queue(f1);
709
queue.queue(f2);
710
queue.queue(f3).then(undefined, () => error = true);
711
queue.queue(f4);
712
return queue.queue(f5).then(() => {
713
assert.strictEqual(res[0], 1);
714
assert.strictEqual(res[1], 2);
715
assert.ok(error);
716
assert.strictEqual(res[2], 4);
717
assert.strictEqual(res[3], 5);
718
});
719
});
720
721
test('order is kept (chained)', function () {
722
const queue = new async.Queue();
723
724
const res: number[] = [];
725
726
const f1 = () => Promise.resolve(true).then(() => res.push(1));
727
const f2 = () => async.timeout(10).then(() => res.push(2));
728
const f3 = () => Promise.resolve(true).then(() => res.push(3));
729
const f4 = () => async.timeout(20).then(() => res.push(4));
730
const f5 = () => async.timeout(0).then(() => res.push(5));
731
732
return queue.queue(f1).then(() => {
733
return queue.queue(f2).then(() => {
734
return queue.queue(f3).then(() => {
735
return queue.queue(f4).then(() => {
736
return queue.queue(f5).then(() => {
737
assert.strictEqual(res[0], 1);
738
assert.strictEqual(res[1], 2);
739
assert.strictEqual(res[2], 3);
740
assert.strictEqual(res[3], 4);
741
assert.strictEqual(res[4], 5);
742
});
743
});
744
});
745
});
746
});
747
});
748
749
test('events', async function () {
750
const queue = new async.Queue();
751
752
let drained = false;
753
const onDrained = Event.toPromise(queue.onDrained).then(() => drained = true);
754
755
const res: number[] = [];
756
757
const f1 = () => async.timeout(10).then(() => res.push(2));
758
const f2 = () => async.timeout(20).then(() => res.push(4));
759
const f3 = () => async.timeout(0).then(() => res.push(5));
760
761
const q1 = queue.queue(f1);
762
const q2 = queue.queue(f2);
763
queue.queue(f3);
764
765
q1.then(() => {
766
assert.ok(!drained);
767
q2.then(() => {
768
assert.ok(!drained);
769
});
770
});
771
772
await onDrained;
773
assert.ok(drained);
774
});
775
});
776
777
suite('ResourceQueue', () => {
778
test('simple', async function () {
779
const queue = new async.ResourceQueue();
780
781
await queue.whenDrained(); // returns immediately since empty
782
783
let done1 = false;
784
queue.queueFor(URI.file('/some/path'), async () => { done1 = true; });
785
await queue.whenDrained(); // returns immediately since no work scheduled
786
assert.strictEqual(done1, true);
787
788
let done2 = false;
789
queue.queueFor(URI.file('/some/other/path'), async () => { done2 = true; });
790
await queue.whenDrained(); // returns immediately since no work scheduled
791
assert.strictEqual(done2, true);
792
793
// schedule some work
794
const w1 = new async.DeferredPromise<void>();
795
queue.queueFor(URI.file('/some/path'), () => w1.p);
796
797
let drained = false;
798
queue.whenDrained().then(() => drained = true);
799
assert.strictEqual(drained, false);
800
await w1.complete();
801
await async.timeout(0);
802
assert.strictEqual(drained, true);
803
804
// schedule some work
805
const w2 = new async.DeferredPromise<void>();
806
const w3 = new async.DeferredPromise<void>();
807
queue.queueFor(URI.file('/some/path'), () => w2.p);
808
queue.queueFor(URI.file('/some/other/path'), () => w3.p);
809
810
drained = false;
811
queue.whenDrained().then(() => drained = true);
812
813
queue.dispose();
814
await async.timeout(0);
815
assert.strictEqual(drained, true);
816
});
817
});
818
819
suite('retry', () => {
820
test('success case', async () => {
821
return runWithFakedTimers({ useFakeTimers: true }, async () => {
822
let counter = 0;
823
824
const res = await async.retry(() => {
825
counter++;
826
if (counter < 2) {
827
return Promise.reject(new Error('fail'));
828
}
829
830
return Promise.resolve(true);
831
}, 10, 3);
832
833
assert.strictEqual(res, true);
834
});
835
});
836
837
test('error case', async () => {
838
return runWithFakedTimers({ useFakeTimers: true }, async () => {
839
const expectedError = new Error('fail');
840
try {
841
await async.retry(() => {
842
return Promise.reject(expectedError);
843
}, 10, 3);
844
} catch (error) {
845
assert.strictEqual(error, error);
846
}
847
});
848
});
849
});
850
851
suite('TaskSequentializer', () => {
852
test('execution basics', async function () {
853
const sequentializer = new async.TaskSequentializer();
854
855
assert.ok(!sequentializer.isRunning());
856
assert.ok(!sequentializer.hasQueued());
857
assert.ok(!sequentializer.isRunning(2323));
858
assert.ok(!sequentializer.running);
859
860
// pending removes itself after done
861
await sequentializer.run(1, Promise.resolve());
862
assert.ok(!sequentializer.isRunning());
863
assert.ok(!sequentializer.isRunning(1));
864
assert.ok(!sequentializer.running);
865
assert.ok(!sequentializer.hasQueued());
866
867
// pending removes itself after done (use async.timeout)
868
sequentializer.run(2, async.timeout(1));
869
assert.ok(sequentializer.isRunning());
870
assert.ok(sequentializer.isRunning(2));
871
assert.ok(!sequentializer.hasQueued());
872
assert.strictEqual(sequentializer.isRunning(1), false);
873
assert.ok(sequentializer.running);
874
875
await async.timeout(2);
876
assert.strictEqual(sequentializer.isRunning(), false);
877
assert.strictEqual(sequentializer.isRunning(2), false);
878
assert.ok(!sequentializer.running);
879
});
880
881
test('executing and queued (finishes instantly)', async function () {
882
const sequentializer = new async.TaskSequentializer();
883
884
let pendingDone = false;
885
sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; }));
886
887
// queued finishes instantly
888
let queuedDone = false;
889
const res = sequentializer.queue(() => Promise.resolve(null).then(() => { queuedDone = true; return; }));
890
891
assert.ok(sequentializer.hasQueued());
892
893
await res;
894
assert.ok(pendingDone);
895
assert.ok(queuedDone);
896
assert.ok(!sequentializer.hasQueued());
897
});
898
899
test('executing and queued (finishes after timeout)', async function () {
900
const sequentializer = new async.TaskSequentializer();
901
902
let pendingDone = false;
903
sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; }));
904
905
// queued finishes after async.timeout
906
let queuedDone = false;
907
const res = sequentializer.queue(() => async.timeout(1).then(() => { queuedDone = true; return; }));
908
909
await res;
910
assert.ok(pendingDone);
911
assert.ok(queuedDone);
912
assert.ok(!sequentializer.hasQueued());
913
});
914
915
test('join (without executing or queued)', async function () {
916
const sequentializer = new async.TaskSequentializer();
917
918
await sequentializer.join();
919
assert.ok(!sequentializer.hasQueued());
920
});
921
922
test('join (without queued)', async function () {
923
const sequentializer = new async.TaskSequentializer();
924
925
let pendingDone = false;
926
sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; }));
927
928
await sequentializer.join();
929
assert.ok(pendingDone);
930
assert.ok(!sequentializer.isRunning());
931
});
932
933
test('join (with executing and queued)', async function () {
934
const sequentializer = new async.TaskSequentializer();
935
936
let pendingDone = false;
937
sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; }));
938
939
// queued finishes after async.timeout
940
let queuedDone = false;
941
sequentializer.queue(() => async.timeout(1).then(() => { queuedDone = true; return; }));
942
943
await sequentializer.join();
944
assert.ok(pendingDone);
945
assert.ok(queuedDone);
946
assert.ok(!sequentializer.isRunning());
947
assert.ok(!sequentializer.hasQueued());
948
});
949
950
test('executing and multiple queued (last one wins)', async function () {
951
const sequentializer = new async.TaskSequentializer();
952
953
let pendingDone = false;
954
sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; }));
955
956
// queued finishes after async.timeout
957
let firstDone = false;
958
const firstRes = sequentializer.queue(() => async.timeout(2).then(() => { firstDone = true; return; }));
959
960
let secondDone = false;
961
const secondRes = sequentializer.queue(() => async.timeout(3).then(() => { secondDone = true; return; }));
962
963
let thirdDone = false;
964
const thirdRes = sequentializer.queue(() => async.timeout(4).then(() => { thirdDone = true; return; }));
965
966
await Promise.all([firstRes, secondRes, thirdRes]);
967
assert.ok(pendingDone);
968
assert.ok(!firstDone);
969
assert.ok(!secondDone);
970
assert.ok(thirdDone);
971
});
972
973
test('cancel executing', async function () {
974
const sequentializer = new async.TaskSequentializer();
975
const ctsTimeout = store.add(new CancellationTokenSource());
976
977
let pendingCancelled = false;
978
const timeout = async.timeout(1, ctsTimeout.token);
979
sequentializer.run(1, timeout, () => pendingCancelled = true);
980
sequentializer.cancelRunning();
981
982
assert.ok(pendingCancelled);
983
ctsTimeout.cancel();
984
});
985
});
986
987
suite('disposableTimeout', () => {
988
test('handler only success', async () => {
989
let cb = false;
990
const t = async.disposableTimeout(() => cb = true);
991
992
await async.timeout(0);
993
994
assert.strictEqual(cb, true);
995
996
t.dispose();
997
});
998
999
test('handler only cancel', async () => {
1000
let cb = false;
1001
const t = async.disposableTimeout(() => cb = true);
1002
t.dispose();
1003
1004
await async.timeout(0);
1005
1006
assert.strictEqual(cb, false);
1007
});
1008
1009
test('store managed success', async () => {
1010
let cb = false;
1011
const s = new DisposableStore();
1012
async.disposableTimeout(() => cb = true, 0, s);
1013
1014
await async.timeout(0);
1015
1016
assert.strictEqual(cb, true);
1017
1018
s.dispose();
1019
});
1020
1021
test('store managed cancel via disposable', async () => {
1022
let cb = false;
1023
const s = new DisposableStore();
1024
const t = async.disposableTimeout(() => cb = true, 0, s);
1025
t.dispose();
1026
1027
await async.timeout(0);
1028
1029
assert.strictEqual(cb, false);
1030
1031
s.dispose();
1032
});
1033
1034
test('store managed cancel via store', async () => {
1035
let cb = false;
1036
const s = new DisposableStore();
1037
async.disposableTimeout(() => cb = true, 0, s);
1038
s.dispose();
1039
1040
await async.timeout(0);
1041
1042
assert.strictEqual(cb, false);
1043
});
1044
});
1045
1046
test('raceCancellation', async () => {
1047
const cts = store.add(new CancellationTokenSource());
1048
const ctsTimeout = store.add(new CancellationTokenSource());
1049
1050
let triggered = false;
1051
const timeout = async.timeout(100, ctsTimeout.token);
1052
const p = async.raceCancellation(timeout.then(() => triggered = true), cts.token);
1053
cts.cancel();
1054
1055
await p;
1056
1057
assert.ok(!triggered);
1058
ctsTimeout.cancel();
1059
});
1060
1061
test('raceTimeout', async () => {
1062
const cts = store.add(new CancellationTokenSource());
1063
1064
// timeout wins
1065
let timedout = false;
1066
let triggered = false;
1067
1068
const ctsTimeout1 = store.add(new CancellationTokenSource());
1069
const timeout1 = async.timeout(100, ctsTimeout1.token);
1070
const p1 = async.raceTimeout(timeout1.then(() => triggered = true), 1, () => timedout = true);
1071
cts.cancel();
1072
1073
await p1;
1074
1075
assert.ok(!triggered);
1076
assert.strictEqual(timedout, true);
1077
ctsTimeout1.cancel();
1078
1079
// promise wins
1080
timedout = false;
1081
1082
const ctsTimeout2 = store.add(new CancellationTokenSource());
1083
const timeout2 = async.timeout(1, ctsTimeout2.token);
1084
const p2 = async.raceTimeout(timeout2.then(() => triggered = true), 100, () => timedout = true);
1085
cts.cancel();
1086
1087
await p2;
1088
1089
assert.ok(triggered);
1090
assert.strictEqual(timedout, false);
1091
ctsTimeout2.cancel();
1092
});
1093
1094
test('SequencerByKey', async () => {
1095
const s = new async.SequencerByKey<string>();
1096
1097
const r1 = await s.queue('key1', () => Promise.resolve('hello'));
1098
assert.strictEqual(r1, 'hello');
1099
1100
await s.queue('key2', () => Promise.reject(new Error('failed'))).then(() => {
1101
throw new Error('should not be resolved');
1102
}, err => {
1103
// Expected error
1104
assert.strictEqual(err.message, 'failed');
1105
});
1106
1107
// Still works after a queued promise is rejected
1108
const r3 = await s.queue('key2', () => Promise.resolve('hello'));
1109
assert.strictEqual(r3, 'hello');
1110
});
1111
1112
test('IntervalCounter', async () => {
1113
let now = 0;
1114
const counter = new async.IntervalCounter(5, () => now);
1115
1116
assert.strictEqual(counter.increment(), 1);
1117
assert.strictEqual(counter.increment(), 2);
1118
assert.strictEqual(counter.increment(), 3);
1119
1120
now = 10;
1121
1122
assert.strictEqual(counter.increment(), 1);
1123
assert.strictEqual(counter.increment(), 2);
1124
assert.strictEqual(counter.increment(), 3);
1125
});
1126
1127
suite('firstParallel', () => {
1128
test('simple', async () => {
1129
const a = await async.firstParallel([
1130
Promise.resolve(1),
1131
Promise.resolve(2),
1132
Promise.resolve(3),
1133
], v => v === 2);
1134
assert.strictEqual(a, 2);
1135
});
1136
1137
test('uses null default', async () => {
1138
assert.strictEqual(await async.firstParallel([Promise.resolve(1)], v => v === 2), null);
1139
});
1140
1141
test('uses value default', async () => {
1142
assert.strictEqual(await async.firstParallel([Promise.resolve(1)], v => v === 2, 4), 4);
1143
});
1144
1145
test('empty', async () => {
1146
assert.strictEqual(await async.firstParallel([], v => v === 2, 4), 4);
1147
});
1148
1149
test('cancels', async () => {
1150
let ct1: CancellationToken;
1151
const p1 = async.createCancelablePromise(async (ct) => {
1152
ct1 = ct;
1153
await async.timeout(200, ct);
1154
return 1;
1155
});
1156
let ct2: CancellationToken;
1157
const p2 = async.createCancelablePromise(async (ct) => {
1158
ct2 = ct;
1159
await async.timeout(2, ct);
1160
return 2;
1161
});
1162
1163
assert.strictEqual(await async.firstParallel([p1, p2], v => v === 2, 4), 2);
1164
assert.strictEqual(ct1!.isCancellationRequested, true, 'should cancel a');
1165
assert.strictEqual(ct2!.isCancellationRequested, true, 'should cancel b');
1166
});
1167
1168
test('rejection handling', async () => {
1169
let ct1: CancellationToken;
1170
const p1 = async.createCancelablePromise(async (ct) => {
1171
ct1 = ct;
1172
await async.timeout(200, ct);
1173
return 1;
1174
});
1175
let ct2: CancellationToken;
1176
const p2 = async.createCancelablePromise(async (ct) => {
1177
ct2 = ct;
1178
await async.timeout(2, ct);
1179
throw new Error('oh no');
1180
});
1181
1182
assert.strictEqual(await async.firstParallel([p1, p2], v => v === 2, 4).catch(() => 'ok'), 'ok');
1183
assert.strictEqual(ct1!.isCancellationRequested, true, 'should cancel a');
1184
assert.strictEqual(ct2!.isCancellationRequested, true, 'should cancel b');
1185
});
1186
});
1187
1188
suite('DeferredPromise', () => {
1189
test('resolves', async () => {
1190
const deferred = new async.DeferredPromise<number>();
1191
assert.strictEqual(deferred.isResolved, false);
1192
deferred.complete(42);
1193
assert.strictEqual(await deferred.p, 42);
1194
assert.strictEqual(deferred.isResolved, true);
1195
});
1196
1197
test('rejects', async () => {
1198
const deferred = new async.DeferredPromise<number>();
1199
assert.strictEqual(deferred.isRejected, false);
1200
const err = new Error('oh no!');
1201
deferred.error(err);
1202
assert.strictEqual(await deferred.p.catch(e => e), err);
1203
assert.strictEqual(deferred.isRejected, true);
1204
});
1205
1206
test('cancels', async () => {
1207
const deferred = new async.DeferredPromise<number>();
1208
assert.strictEqual(deferred.isRejected, false);
1209
deferred.cancel();
1210
assert.strictEqual((await deferred.p.catch(e => e)).name, 'Canceled');
1211
assert.strictEqual(deferred.isRejected, true);
1212
});
1213
1214
test('retains the original settled value', async () => {
1215
const deferred = new async.DeferredPromise<number>();
1216
assert.strictEqual(deferred.isResolved, false);
1217
assert.strictEqual(deferred.value, undefined);
1218
1219
deferred.complete(42);
1220
assert.strictEqual(await deferred.p, 42);
1221
assert.strictEqual(deferred.value, 42);
1222
assert.strictEqual(deferred.isResolved, true);
1223
1224
deferred.complete(-1);
1225
assert.strictEqual(await deferred.p, 42);
1226
assert.strictEqual(deferred.value, 42);
1227
assert.strictEqual(deferred.isResolved, true);
1228
});
1229
});
1230
1231
suite('Promises.settled', () => {
1232
test('resolves', async () => {
1233
const p1 = Promise.resolve(1);
1234
const p2 = async.timeout(1).then(() => 2);
1235
const p3 = async.timeout(2).then(() => 3);
1236
1237
const result = await async.Promises.settled<number>([p1, p2, p3]);
1238
1239
assert.strictEqual(result.length, 3);
1240
assert.deepStrictEqual(result[0], 1);
1241
assert.deepStrictEqual(result[1], 2);
1242
assert.deepStrictEqual(result[2], 3);
1243
});
1244
1245
test('resolves in order', async () => {
1246
const p1 = async.timeout(2).then(() => 1);
1247
const p2 = async.timeout(1).then(() => 2);
1248
const p3 = Promise.resolve(3);
1249
1250
const result = await async.Promises.settled<number>([p1, p2, p3]);
1251
1252
assert.strictEqual(result.length, 3);
1253
assert.deepStrictEqual(result[0], 1);
1254
assert.deepStrictEqual(result[1], 2);
1255
assert.deepStrictEqual(result[2], 3);
1256
});
1257
1258
test('rejects with first error but handles all promises (all errors)', async () => {
1259
const p1 = Promise.reject(1);
1260
1261
let p2Handled = false;
1262
const p2Error = new Error('2');
1263
const p2 = async.timeout(1).then(() => {
1264
p2Handled = true;
1265
throw p2Error;
1266
});
1267
1268
let p3Handled = false;
1269
const p3Error = new Error('3');
1270
const p3 = async.timeout(2).then(() => {
1271
p3Handled = true;
1272
throw p3Error;
1273
});
1274
1275
let error: Error | undefined = undefined;
1276
try {
1277
await async.Promises.settled<number>([p1, p2, p3]);
1278
} catch (e) {
1279
error = e;
1280
}
1281
1282
assert.ok(error);
1283
assert.notStrictEqual(error, p2Error);
1284
assert.notStrictEqual(error, p3Error);
1285
assert.ok(p2Handled);
1286
assert.ok(p3Handled);
1287
});
1288
1289
test('rejects with first error but handles all promises (1 error)', async () => {
1290
const p1 = Promise.resolve(1);
1291
1292
let p2Handled = false;
1293
const p2Error = new Error('2');
1294
const p2 = async.timeout(1).then(() => {
1295
p2Handled = true;
1296
throw p2Error;
1297
});
1298
1299
let p3Handled = false;
1300
const p3 = async.timeout(2).then(() => {
1301
p3Handled = true;
1302
return 3;
1303
});
1304
1305
let error: Error | undefined = undefined;
1306
try {
1307
await async.Promises.settled<number>([p1, p2, p3]);
1308
} catch (e) {
1309
error = e;
1310
}
1311
1312
assert.strictEqual(error, p2Error);
1313
assert.ok(p2Handled);
1314
assert.ok(p3Handled);
1315
});
1316
});
1317
1318
suite('Promises.withAsyncBody', () => {
1319
test('basics', async () => {
1320
1321
const p1 = async.Promises.withAsyncBody(async (resolve, reject) => {
1322
resolve(1);
1323
});
1324
1325
const p2 = async.Promises.withAsyncBody(async (resolve, reject) => {
1326
reject(new Error('error'));
1327
});
1328
1329
const p3 = async.Promises.withAsyncBody(async (resolve, reject) => {
1330
throw new Error('error');
1331
});
1332
1333
const r1 = await p1;
1334
assert.strictEqual(r1, 1);
1335
1336
let e2: Error | undefined = undefined;
1337
try {
1338
await p2;
1339
} catch (error) {
1340
e2 = error;
1341
}
1342
1343
assert.ok(e2 instanceof Error);
1344
1345
let e3: Error | undefined = undefined;
1346
try {
1347
await p3;
1348
} catch (error) {
1349
e3 = error;
1350
}
1351
1352
assert.ok(e3 instanceof Error);
1353
});
1354
});
1355
1356
suite('ThrottledWorker', () => {
1357
1358
function assertArrayEquals(actual: unknown[], expected: unknown[]) {
1359
assert.strictEqual(actual.length, expected.length);
1360
1361
for (let i = 0; i < actual.length; i++) {
1362
assert.strictEqual(actual[i], expected[i]);
1363
}
1364
}
1365
1366
test('basics', async () => {
1367
let handled: number[] = [];
1368
1369
let handledCallback: Function;
1370
let handledPromise = new Promise(resolve => handledCallback = resolve);
1371
let handledCounterToResolve = 1;
1372
let currentHandledCounter = 0;
1373
1374
const handler = (units: readonly number[]) => {
1375
handled.push(...units);
1376
1377
currentHandledCounter++;
1378
if (currentHandledCounter === handledCounterToResolve) {
1379
handledCallback();
1380
1381
handledPromise = new Promise(resolve => handledCallback = resolve);
1382
currentHandledCounter = 0;
1383
}
1384
};
1385
1386
const worker = store.add(new async.ThrottledWorker<number>({
1387
maxWorkChunkSize: 5,
1388
maxBufferedWork: undefined,
1389
throttleDelay: 1
1390
}, handler));
1391
1392
// Work less than chunk size
1393
1394
let worked = worker.work([1, 2, 3]);
1395
1396
assertArrayEquals(handled, [1, 2, 3]);
1397
assert.strictEqual(worker.pending, 0);
1398
assert.strictEqual(worked, true);
1399
1400
worker.work([4, 5]);
1401
worked = worker.work([6]);
1402
1403
assertArrayEquals(handled, [1, 2, 3, 4, 5, 6]);
1404
assert.strictEqual(worker.pending, 0);
1405
assert.strictEqual(worked, true);
1406
1407
// Work more than chunk size (variant 1)
1408
1409
handled = [];
1410
handledCounterToResolve = 2;
1411
1412
worked = worker.work([1, 2, 3, 4, 5, 6, 7]);
1413
1414
assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1415
assert.strictEqual(worker.pending, 2);
1416
assert.strictEqual(worked, true);
1417
1418
await handledPromise;
1419
1420
assertArrayEquals(handled, [1, 2, 3, 4, 5, 6, 7]);
1421
1422
handled = [];
1423
handledCounterToResolve = 4;
1424
1425
worked = worker.work([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
1426
1427
assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1428
assert.strictEqual(worker.pending, 14);
1429
assert.strictEqual(worked, true);
1430
1431
await handledPromise;
1432
1433
assertArrayEquals(handled, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
1434
1435
// Work more than chunk size (variant 2)
1436
1437
handled = [];
1438
handledCounterToResolve = 2;
1439
1440
worked = worker.work([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1441
1442
assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1443
assert.strictEqual(worker.pending, 5);
1444
assert.strictEqual(worked, true);
1445
1446
await handledPromise;
1447
1448
assertArrayEquals(handled, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1449
1450
// Work more while throttled (variant 1)
1451
1452
handled = [];
1453
handledCounterToResolve = 3;
1454
1455
worked = worker.work([1, 2, 3, 4, 5, 6, 7]);
1456
1457
assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1458
assert.strictEqual(worker.pending, 2);
1459
assert.strictEqual(worked, true);
1460
1461
worker.work([8]);
1462
worked = worker.work([9, 10, 11]);
1463
1464
assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1465
assert.strictEqual(worker.pending, 6);
1466
assert.strictEqual(worked, true);
1467
1468
await handledPromise;
1469
1470
assertArrayEquals(handled, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
1471
assert.strictEqual(worker.pending, 0);
1472
1473
// Work more while throttled (variant 2)
1474
1475
handled = [];
1476
handledCounterToResolve = 2;
1477
1478
worked = worker.work([1, 2, 3, 4, 5, 6, 7]);
1479
1480
assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1481
assert.strictEqual(worked, true);
1482
1483
worker.work([8]);
1484
worked = worker.work([9, 10]);
1485
1486
assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1487
assert.strictEqual(worked, true);
1488
1489
await handledPromise;
1490
1491
assertArrayEquals(handled, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1492
});
1493
1494
test('do not accept too much work', async () => {
1495
const handled: number[] = [];
1496
const handler = (units: readonly number[]) => handled.push(...units);
1497
1498
const worker = store.add(new async.ThrottledWorker<number>({
1499
maxWorkChunkSize: 5,
1500
maxBufferedWork: 5,
1501
throttleDelay: 1
1502
}, handler));
1503
1504
let worked = worker.work([1, 2, 3]);
1505
assert.strictEqual(worked, true);
1506
1507
worked = worker.work([1, 2, 3, 4, 5, 6]);
1508
assert.strictEqual(worked, true);
1509
assert.strictEqual(worker.pending, 1);
1510
1511
worked = worker.work([7]);
1512
assert.strictEqual(worked, true);
1513
assert.strictEqual(worker.pending, 2);
1514
1515
worked = worker.work([8, 9, 10, 11]);
1516
assert.strictEqual(worked, false);
1517
assert.strictEqual(worker.pending, 2);
1518
});
1519
1520
test('do not accept too much work (account for max chunk size', async () => {
1521
const handled: number[] = [];
1522
const handler = (units: readonly number[]) => handled.push(...units);
1523
1524
const worker = store.add(new async.ThrottledWorker<number>({
1525
maxWorkChunkSize: 5,
1526
maxBufferedWork: 5,
1527
throttleDelay: 1
1528
}, handler));
1529
1530
let worked = worker.work([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
1531
assert.strictEqual(worked, false);
1532
assert.strictEqual(worker.pending, 0);
1533
1534
worked = worker.work([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1535
assert.strictEqual(worked, true);
1536
assert.strictEqual(worker.pending, 5);
1537
});
1538
1539
test('disposed', async () => {
1540
const handled: number[] = [];
1541
const handler = (units: readonly number[]) => handled.push(...units);
1542
1543
const worker = store.add(new async.ThrottledWorker<number>({
1544
maxWorkChunkSize: 5,
1545
maxBufferedWork: undefined,
1546
throttleDelay: 1
1547
}, handler));
1548
worker.dispose();
1549
const worked = worker.work([1, 2, 3]);
1550
1551
assertArrayEquals(handled, []);
1552
assert.strictEqual(worker.pending, 0);
1553
assert.strictEqual(worked, false);
1554
});
1555
1556
// https://github.com/microsoft/vscode/issues/230366
1557
// test('waitThrottleDelayBetweenWorkUnits option', async () => {
1558
// const handled: number[] = [];
1559
// let handledCallback: Function;
1560
// let handledPromise = new Promise(resolve => handledCallback = resolve);
1561
// let currentTime = 0;
1562
1563
// const handler = (units: readonly number[]) => {
1564
// handled.push(...units);
1565
// handledCallback();
1566
// handledPromise = new Promise(resolve => handledCallback = resolve);
1567
// };
1568
1569
// const worker = store.add(new async.ThrottledWorker<number>({
1570
// maxWorkChunkSize: 5,
1571
// maxBufferedWork: undefined,
1572
// throttleDelay: 5,
1573
// waitThrottleDelayBetweenWorkUnits: true
1574
// }, handler));
1575
1576
// // Schedule work, it should execute immediately
1577
// currentTime = Date.now();
1578
// let worked = worker.work([1, 2, 3]);
1579
// assert.strictEqual(worked, true);
1580
// assertArrayEquals(handled, [1, 2, 3]);
1581
// assert.strictEqual(Date.now() - currentTime < 5, true);
1582
1583
// // Schedule work again, it should wait at least throttle delay before executing
1584
// currentTime = Date.now();
1585
// worked = worker.work([4, 5]);
1586
// assert.strictEqual(worked, true);
1587
// // Throttle delay hasn't reset so we still must wait
1588
// assertArrayEquals(handled, [1, 2, 3]);
1589
// await handledPromise;
1590
// assert.strictEqual(Date.now() - currentTime >= 5, true);
1591
// assertArrayEquals(handled, [1, 2, 3, 4, 5]);
1592
// });
1593
});
1594
1595
suite('LimitedQueue', () => {
1596
1597
test('basics (with long running task)', async () => {
1598
const limitedQueue = new async.LimitedQueue();
1599
1600
let counter = 0;
1601
const promises = [];
1602
for (let i = 0; i < 5; i++) {
1603
promises.push(limitedQueue.queue(async () => {
1604
counter = i;
1605
await async.timeout(1);
1606
}));
1607
}
1608
1609
await Promise.all(promises);
1610
1611
// only the last task executed
1612
assert.strictEqual(counter, 4);
1613
});
1614
1615
test('basics (with sync running task)', async () => {
1616
const limitedQueue = new async.LimitedQueue();
1617
1618
let counter = 0;
1619
const promises = [];
1620
for (let i = 0; i < 5; i++) {
1621
promises.push(limitedQueue.queue(async () => {
1622
counter = i;
1623
}));
1624
}
1625
1626
await Promise.all(promises);
1627
1628
// only the last task executed
1629
assert.strictEqual(counter, 4);
1630
});
1631
});
1632
1633
suite('AsyncIterableObject', function () {
1634
1635
1636
test('onReturn NOT called', async function () {
1637
1638
let calledOnReturn = false;
1639
const iter = new async.AsyncIterableObject<number>(writer => {
1640
writer.emitMany([1, 2, 3, 4, 5]);
1641
}, () => {
1642
calledOnReturn = true;
1643
});
1644
1645
for await (const item of iter) {
1646
assert.strictEqual(typeof item, 'number');
1647
}
1648
1649
assert.strictEqual(calledOnReturn, false);
1650
1651
});
1652
1653
test('onReturn called on break', async function () {
1654
1655
let calledOnReturn = false;
1656
const iter = new async.AsyncIterableObject<number>(writer => {
1657
writer.emitMany([1, 2, 3, 4, 5]);
1658
}, () => {
1659
calledOnReturn = true;
1660
});
1661
1662
for await (const item of iter) {
1663
assert.strictEqual(item, 1);
1664
break;
1665
}
1666
1667
assert.strictEqual(calledOnReturn, true);
1668
1669
});
1670
1671
test('onReturn called on return', async function () {
1672
1673
let calledOnReturn = false;
1674
const iter = new async.AsyncIterableObject<number>(writer => {
1675
writer.emitMany([1, 2, 3, 4, 5]);
1676
}, () => {
1677
calledOnReturn = true;
1678
});
1679
1680
await (async function test() {
1681
for await (const item of iter) {
1682
assert.strictEqual(item, 1);
1683
return;
1684
}
1685
})();
1686
1687
1688
assert.strictEqual(calledOnReturn, true);
1689
1690
});
1691
1692
1693
test('onReturn called on throwing', async function () {
1694
1695
let calledOnReturn = false;
1696
const iter = new async.AsyncIterableObject<number>(writer => {
1697
writer.emitMany([1, 2, 3, 4, 5]);
1698
}, () => {
1699
calledOnReturn = true;
1700
});
1701
1702
try {
1703
for await (const item of iter) {
1704
assert.strictEqual(item, 1);
1705
throw new Error();
1706
}
1707
} catch (e) {
1708
1709
}
1710
1711
assert.strictEqual(calledOnReturn, true);
1712
});
1713
});
1714
1715
suite('AsyncIterableSource', function () {
1716
1717
test('onReturn is wired up', async function () {
1718
let calledOnReturn = false;
1719
const source = new async.AsyncIterableSource<number>(() => { calledOnReturn = true; });
1720
1721
source.emitOne(1);
1722
source.emitOne(2);
1723
source.emitOne(3);
1724
source.resolve();
1725
1726
for await (const item of source.asyncIterable) {
1727
assert.strictEqual(item, 1);
1728
break;
1729
}
1730
1731
assert.strictEqual(calledOnReturn, true);
1732
1733
});
1734
1735
test('onReturn is wired up 2', async function () {
1736
let calledOnReturn = false;
1737
const source = new async.AsyncIterableSource<number>(() => { calledOnReturn = true; });
1738
1739
source.emitOne(1);
1740
source.emitOne(2);
1741
source.emitOne(3);
1742
source.resolve();
1743
1744
for await (const item of source.asyncIterable) {
1745
assert.strictEqual(typeof item, 'number');
1746
}
1747
1748
assert.strictEqual(calledOnReturn, false);
1749
});
1750
1751
test('emitMany emits all items', async function () {
1752
const source = new async.AsyncIterableSource<number>();
1753
const values = [10, 20, 30, 40];
1754
source.emitMany(values);
1755
source.resolve();
1756
1757
const result: number[] = [];
1758
for await (const item of source.asyncIterable) {
1759
result.push(item);
1760
}
1761
1762
assert.deepStrictEqual(result, values);
1763
});
1764
});
1765
1766
suite('cancellableIterable', () => {
1767
let cts: CancellationTokenSource;
1768
setup(() => {
1769
cts = store.add(new CancellationTokenSource());
1770
});
1771
1772
test('should iterate through all values when not canceled', async function () {
1773
const asyncIterable = {
1774
async *[Symbol.asyncIterator]() {
1775
yield 'a';
1776
yield 'b';
1777
yield 'c';
1778
}
1779
};
1780
1781
const cancelableIterable = async.cancellableIterable(asyncIterable, cts.token);
1782
1783
const result = await Iterable.asyncToArray(cancelableIterable);
1784
assert.deepStrictEqual(result, ['a', 'b', 'c']);
1785
});
1786
1787
test('should stop iteration immediately when cancelled before starting', async function () {
1788
const values: string[] = [];
1789
1790
const asyncIterable = {
1791
async *[Symbol.asyncIterator]() {
1792
values.push('iterator created');
1793
yield 'a';
1794
values.push('after a');
1795
yield 'b';
1796
values.push('after b');
1797
yield 'c';
1798
values.push('after c');
1799
}
1800
};
1801
1802
// Cancel before iteration starts
1803
cts.cancel();
1804
const cancelableIterable = async.cancellableIterable(asyncIterable, cts.token);
1805
1806
const result = await Iterable.asyncToArray(cancelableIterable);
1807
assert.deepStrictEqual(result, []);
1808
assert.deepStrictEqual(values, []);
1809
});
1810
1811
test('should stop iteration when cancelled during iteration', async function () {
1812
const cts = new CancellationTokenSource();
1813
const deferredA = new async.DeferredPromise<void>();
1814
const deferredB = new async.DeferredPromise<void>();
1815
const deferredC = new async.DeferredPromise<void>();
1816
1817
const values: string[] = [];
1818
1819
const asyncIterable = {
1820
async *[Symbol.asyncIterator]() {
1821
values.push('a yielded');
1822
yield 'a';
1823
await deferredA.p;
1824
1825
values.push('b yielded');
1826
yield 'b';
1827
await deferredB.p;
1828
1829
values.push('c yielded');
1830
yield 'c';
1831
await deferredC.p;
1832
}
1833
};
1834
1835
for await (const value of async.cancellableIterable(asyncIterable, cts.token)) {
1836
if (value === 'a') {
1837
deferredA.complete();
1838
} else if (value === 'b') {
1839
cts.cancel();
1840
deferredB.complete();
1841
} else {
1842
throw new Error('Unexpected value');
1843
}
1844
}
1845
1846
assert.deepStrictEqual(values, ['a yielded', 'b yielded']);
1847
});
1848
1849
test('should handle return method correctly', async function () {
1850
let returnCalled = false;
1851
let n = 0;
1852
const asyncIterable = {
1853
async *[Symbol.asyncIterator]() {
1854
try {
1855
yield 'a'; n++;
1856
yield 'b'; n++;
1857
yield 'c'; n++;
1858
} finally {
1859
returnCalled = true;
1860
}
1861
},
1862
};
1863
1864
// Add a return method to the iterator
1865
const originalIterable = asyncIterable[Symbol.asyncIterator]();
1866
originalIterable.return = async function () {
1867
returnCalled = true;
1868
return Promise.resolve({ done: true, value: undefined });
1869
};
1870
1871
// Create a test-specific iterable with our mocked iterator
1872
const testIterable = {
1873
[Symbol.asyncIterator]: () => originalIterable
1874
};
1875
1876
for await (const value of async.cancellableIterable(testIterable, cts.token)) {
1877
if (value === 'b') {
1878
break;
1879
}
1880
}
1881
1882
assert.strictEqual(returnCalled, true);
1883
assert.strictEqual(n < 2, true);
1884
});
1885
});
1886
1887
1888
suite('AsyncIterableProducer', () => {
1889
test('emitOne produces single values', async () => {
1890
const producer = new async.AsyncIterableProducer<number>(emitter => {
1891
emitter.emitOne(1);
1892
emitter.emitOne(2);
1893
emitter.emitOne(3);
1894
});
1895
1896
const result: number[] = [];
1897
for await (const item of producer) {
1898
result.push(item);
1899
}
1900
1901
assert.deepStrictEqual(result, [1, 2, 3]);
1902
});
1903
1904
test('emitMany produces multiple values', async () => {
1905
const producer = new async.AsyncIterableProducer<number>(emitter => {
1906
emitter.emitMany([1, 2, 3]);
1907
emitter.emitMany([4, 5]);
1908
});
1909
1910
const result: number[] = [];
1911
for await (const item of producer) {
1912
result.push(item);
1913
}
1914
1915
assert.deepStrictEqual(result, [1, 2, 3, 4, 5]);
1916
});
1917
1918
test('mixed emitOne and emitMany', async () => {
1919
const producer = new async.AsyncIterableProducer<number>(emitter => {
1920
emitter.emitOne(1);
1921
emitter.emitMany([2, 3]);
1922
emitter.emitOne(4);
1923
});
1924
1925
const result: number[] = [];
1926
for await (const item of producer) {
1927
result.push(item);
1928
}
1929
1930
assert.deepStrictEqual(result, [1, 2, 3, 4]);
1931
});
1932
1933
test('async executor with emitOne', async () => {
1934
const producer = new async.AsyncIterableProducer<number>(async emitter => {
1935
emitter.emitOne(1);
1936
await async.timeout(1);
1937
emitter.emitOne(2);
1938
await async.timeout(1);
1939
emitter.emitOne(3);
1940
});
1941
1942
const result: number[] = [];
1943
for await (const item of producer) {
1944
result.push(item);
1945
}
1946
1947
assert.deepStrictEqual(result, [1, 2, 3]);
1948
});
1949
1950
test('async executor with emitMany', async () => {
1951
const producer = new async.AsyncIterableProducer<number>(async emitter => {
1952
emitter.emitMany([1, 2]);
1953
await async.timeout(1);
1954
emitter.emitMany([3, 4]);
1955
});
1956
1957
const result: number[] = [];
1958
for await (const item of producer) {
1959
result.push(item);
1960
}
1961
1962
assert.deepStrictEqual(result, [1, 2, 3, 4]);
1963
});
1964
1965
test('reject with error', async () => {
1966
const expectedError = new Error('test error');
1967
const producer = new async.AsyncIterableProducer<number>(emitter => {
1968
emitter.emitOne(1);
1969
emitter.reject(expectedError);
1970
});
1971
1972
const result: number[] = [];
1973
let caughtError: Error | undefined;
1974
1975
try {
1976
for await (const item of producer) {
1977
result.push(item);
1978
}
1979
} catch (error) {
1980
caughtError = error as Error;
1981
}
1982
1983
assert.deepStrictEqual(result, [1]);
1984
assert.strictEqual(caughtError, expectedError);
1985
});
1986
1987
test('async executor throws error', async () => {
1988
const expectedError = new Error('executor error');
1989
const producer = new async.AsyncIterableProducer<number>(async emitter => {
1990
emitter.emitOne(1);
1991
throw expectedError;
1992
});
1993
1994
const result: number[] = [];
1995
let caughtError: Error | undefined;
1996
1997
try {
1998
for await (const item of producer) {
1999
result.push(item);
2000
}
2001
} catch (error) {
2002
caughtError = error as Error;
2003
}
2004
2005
assert.deepStrictEqual(result, [1]);
2006
assert.strictEqual(caughtError, expectedError);
2007
});
2008
2009
test('empty producer', async () => {
2010
const producer = new async.AsyncIterableProducer<number>(emitter => {
2011
// Don't emit anything
2012
});
2013
2014
const result: number[] = [];
2015
for await (const item of producer) {
2016
result.push(item);
2017
}
2018
2019
assert.deepStrictEqual(result, []);
2020
});
2021
2022
test('async executor resolves without emitting', async () => {
2023
const producer = new async.AsyncIterableProducer<number>(async emitter => {
2024
await async.timeout(1);
2025
// Don't emit anything
2026
});
2027
2028
const result: number[] = [];
2029
for await (const item of producer) {
2030
result.push(item);
2031
}
2032
2033
assert.deepStrictEqual(result, []);
2034
});
2035
2036
test('multiple iterators on same producer', async () => {
2037
const producer = new async.AsyncIterableProducer<number>(emitter => {
2038
emitter.emitMany([1, 2, 3]);
2039
});
2040
2041
// First iterator should consume all values
2042
const result1: number[] = [];
2043
for await (const item of producer) {
2044
result1.push(item);
2045
}
2046
2047
// Second iterator should not see any values (already consumed)
2048
const result2: number[] = [];
2049
for await (const item of producer) {
2050
result2.push(item);
2051
}
2052
2053
assert.deepStrictEqual(result1, [1, 2, 3]);
2054
assert.deepStrictEqual(result2, []);
2055
});
2056
2057
test('concurrent iteration', async () => {
2058
const producer = new async.AsyncIterableProducer<number>(async emitter => {
2059
emitter.emitOne(1);
2060
await async.timeout(1);
2061
emitter.emitOne(2);
2062
await async.timeout(1);
2063
emitter.emitOne(3);
2064
});
2065
2066
const iterator1 = producer[Symbol.asyncIterator]();
2067
const iterator2 = producer[Symbol.asyncIterator]();
2068
2069
// Both iterators share the same underlying producer
2070
const first1 = await iterator1.next();
2071
const first2 = await iterator2.next();
2072
const second1 = await iterator1.next();
2073
const second2 = await iterator2.next();
2074
2075
// Since they share the same producer, values are consumed in order
2076
assert.strictEqual(first1.value, 1);
2077
assert.strictEqual(first2.value, 2);
2078
assert.strictEqual(second1.value, 3);
2079
assert.strictEqual(second2.done, true);
2080
});
2081
2082
test('executor with promise return value', async () => {
2083
const producer = new async.AsyncIterableProducer<number>(emitter => {
2084
emitter.emitOne(1);
2085
emitter.emitOne(2);
2086
return Promise.resolve();
2087
});
2088
2089
const result: number[] = [];
2090
for await (const item of producer) {
2091
result.push(item);
2092
}
2093
2094
assert.deepStrictEqual(result, [1, 2]);
2095
});
2096
2097
test('executor with non-promise return value', async () => {
2098
const producer = new async.AsyncIterableProducer<number>(emitter => {
2099
emitter.emitOne(1);
2100
emitter.emitOne(2);
2101
return 'some value';
2102
});
2103
2104
const result: number[] = [];
2105
for await (const item of producer) {
2106
result.push(item);
2107
}
2108
2109
assert.deepStrictEqual(result, [1, 2]);
2110
});
2111
2112
test('emitMany with empty array', async () => {
2113
const producer = new async.AsyncIterableProducer<number>(emitter => {
2114
emitter.emitOne(1);
2115
emitter.emitMany([]);
2116
emitter.emitOne(2);
2117
});
2118
2119
const result: number[] = [];
2120
for await (const item of producer) {
2121
result.push(item);
2122
}
2123
2124
assert.deepStrictEqual(result, [1, 2]);
2125
});
2126
2127
test('reject immediately without emitting', async () => {
2128
const expectedError = new Error('immediate error');
2129
const producer = new async.AsyncIterableProducer<number>(emitter => {
2130
emitter.reject(expectedError);
2131
});
2132
2133
let caughtError: Error | undefined;
2134
try {
2135
for await (const _item of producer) {
2136
assert.fail('Should not iterate when rejected immediately');
2137
}
2138
} catch (error) {
2139
caughtError = error as Error;
2140
}
2141
2142
assert.strictEqual(caughtError, expectedError);
2143
});
2144
2145
test('string values', async () => {
2146
const producer = new async.AsyncIterableProducer<string>(emitter => {
2147
emitter.emitOne('hello');
2148
emitter.emitMany(['world', 'test']);
2149
});
2150
2151
const result: string[] = [];
2152
for await (const item of producer) {
2153
result.push(item);
2154
}
2155
2156
assert.deepStrictEqual(result, ['hello', 'world', 'test']);
2157
});
2158
2159
test('object values', async () => {
2160
interface TestObject {
2161
id: number;
2162
name: string;
2163
}
2164
2165
const producer = new async.AsyncIterableProducer<TestObject>(emitter => {
2166
emitter.emitOne({ id: 1, name: 'first' });
2167
emitter.emitMany([
2168
{ id: 2, name: 'second' },
2169
{ id: 3, name: 'third' }
2170
]);
2171
});
2172
2173
const result: TestObject[] = [];
2174
for await (const item of producer) {
2175
result.push(item);
2176
}
2177
2178
assert.deepStrictEqual(result, [
2179
{ id: 1, name: 'first' },
2180
{ id: 2, name: 'second' },
2181
{ id: 3, name: 'third' }
2182
]);
2183
});
2184
});
2185
2186
suite('AsyncReader', () => {
2187
async function* createAsyncIterator<T>(values: T[]): AsyncIterator<T> {
2188
for (const value of values) {
2189
yield value;
2190
}
2191
}
2192
2193
async function* createDelayedAsyncIterator<T>(values: T[], delayMs: number = 1): AsyncIterator<T> {
2194
for (const value of values) {
2195
await async.timeout(delayMs);
2196
yield value;
2197
}
2198
}
2199
2200
test('read - basic functionality', async () => {
2201
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3]));
2202
2203
assert.strictEqual(await reader.read(), 1);
2204
assert.strictEqual(await reader.read(), 2);
2205
assert.strictEqual(await reader.read(), 3);
2206
assert.strictEqual(await reader.read(), async.AsyncReaderEndOfStream);
2207
});
2208
2209
test('read - empty iterator', async () => {
2210
const reader = new async.AsyncReader(createAsyncIterator([]));
2211
2212
assert.strictEqual(await reader.read(), async.AsyncReaderEndOfStream);
2213
assert.strictEqual(await reader.read(), async.AsyncReaderEndOfStream);
2214
});
2215
2216
test('endOfStream property', async () => {
2217
const reader = new async.AsyncReader(createAsyncIterator([1, 2]));
2218
2219
assert.strictEqual(reader.endOfStream, false);
2220
2221
await reader.read(); // 1
2222
assert.strictEqual(reader.endOfStream, false);
2223
2224
await reader.read(); // 2
2225
assert.strictEqual(reader.endOfStream, false);
2226
2227
await reader.read(); // end
2228
assert.strictEqual(reader.endOfStream, true);
2229
});
2230
2231
test('peek - basic functionality', async () => {
2232
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3]));
2233
2234
assert.strictEqual(await reader.peek(), 1);
2235
assert.strictEqual(await reader.peek(), 1); // Should return same value
2236
assert.strictEqual(await reader.read(), 1); // Should consume the peeked value
2237
2238
assert.strictEqual(await reader.peek(), 2);
2239
assert.strictEqual(await reader.read(), 2);
2240
});
2241
2242
test('peek - empty iterator', async () => {
2243
const reader = new async.AsyncReader(createAsyncIterator([]));
2244
2245
assert.strictEqual(await reader.peek(), async.AsyncReaderEndOfStream);
2246
});
2247
2248
test('readSyncOrThrow - throws when no data available', async () => {
2249
const reader = new async.AsyncReader(createAsyncIterator([1]));
2250
2251
// Read the only item
2252
await reader.read();
2253
2254
// Should throw since no more data and not at end yet
2255
assert.throws(() => reader.readBufferedOrThrow());
2256
});
2257
2258
test('readSyncOrThrow - returns end of stream when at end', async () => {
2259
const reader = new async.AsyncReader(createAsyncIterator([]));
2260
2261
// Trigger end detection
2262
await reader.read();
2263
2264
assert.strictEqual(reader.readBufferedOrThrow(), async.AsyncReaderEndOfStream);
2265
});
2266
2267
test('peekSyncOrThrow - with buffered data', async () => {
2268
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3]));
2269
2270
// First peek to populate buffer
2271
await reader.peek();
2272
2273
// Should be able to peek sync now
2274
assert.strictEqual(reader.peekBufferedOrThrow(), 1);
2275
assert.strictEqual(reader.peekBufferedOrThrow(), 1); // Should return same value
2276
});
2277
2278
test('peekSyncOrThrow - throws when no data available', async () => {
2279
const reader = new async.AsyncReader(createAsyncIterator([1]));
2280
2281
// Should throw since buffer is empty and we haven't loaded anything
2282
assert.throws(() => reader.peekBufferedOrThrow());
2283
});
2284
2285
test('peekSyncOrThrow - returns end of stream when at end', async () => {
2286
const reader = new async.AsyncReader(createAsyncIterator([]));
2287
2288
// Trigger end detection
2289
await reader.peek();
2290
2291
assert.strictEqual(reader.peekBufferedOrThrow(), async.AsyncReaderEndOfStream);
2292
});
2293
2294
test('consumeToEnd - consumes all remaining data', async () => {
2295
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3, 4, 5]));
2296
2297
// Read some data first
2298
assert.strictEqual(await reader.read(), 1);
2299
assert.strictEqual(await reader.read(), 2);
2300
2301
// Consume the rest
2302
await reader.consumeToEnd();
2303
2304
assert.strictEqual(reader.endOfStream, true);
2305
assert.strictEqual(await reader.read(), async.AsyncReaderEndOfStream);
2306
});
2307
2308
test('consumeToEnd - on empty reader', async () => {
2309
const reader = new async.AsyncReader(createAsyncIterator([]));
2310
2311
await reader.consumeToEnd();
2312
2313
assert.strictEqual(reader.endOfStream, true);
2314
});
2315
2316
test('readWhile - basic functionality', async () => {
2317
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3, 4, 5]));
2318
const collected: number[] = [];
2319
2320
await reader.readWhile(
2321
value => value < 4,
2322
async value => {
2323
collected.push(value);
2324
}
2325
);
2326
2327
assert.deepStrictEqual(collected, [1, 2, 3]);
2328
2329
// Next read should return 4
2330
assert.strictEqual(await reader.read(), 4);
2331
});
2332
2333
test('readWhile - stops at end of stream', async () => {
2334
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3]));
2335
const collected: number[] = [];
2336
2337
await reader.readWhile(
2338
value => value < 10, // Always true
2339
async value => {
2340
collected.push(value);
2341
}
2342
);
2343
2344
assert.deepStrictEqual(collected, [1, 2, 3]);
2345
assert.strictEqual(reader.endOfStream, true);
2346
});
2347
2348
test('readWhile - empty iterator', async () => {
2349
const reader = new async.AsyncReader(createAsyncIterator([]));
2350
const collected: number[] = [];
2351
2352
await reader.readWhile(
2353
value => true,
2354
async value => {
2355
collected.push(value);
2356
}
2357
);
2358
2359
assert.deepStrictEqual(collected, []);
2360
});
2361
2362
test('readWhile - predicate returns false immediately', async () => {
2363
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3]));
2364
const collected: number[] = [];
2365
2366
await reader.readWhile(
2367
value => false, // Always false
2368
async value => {
2369
collected.push(value);
2370
}
2371
);
2372
2373
assert.deepStrictEqual(collected, []);
2374
2375
// First item should still be available
2376
assert.strictEqual(await reader.read(), 1);
2377
});
2378
2379
test('peekTimeout - with immediate data', async () => {
2380
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3]));
2381
2382
const result = await reader.peekTimeout(100);
2383
assert.strictEqual(result, 1);
2384
});
2385
2386
test('peekTimeout - with delayed data', async () => {
2387
const reader = new async.AsyncReader(createDelayedAsyncIterator([1, 2, 3], 10));
2388
2389
const result = await reader.peekTimeout(50);
2390
assert.strictEqual(result, 1);
2391
});
2392
2393
test('peekTimeout - timeout occurs', async () => {
2394
const reader = new async.AsyncReader(createDelayedAsyncIterator([1, 2, 3], 50));
2395
2396
const result = await reader.peekTimeout(10);
2397
assert.strictEqual(result, undefined);
2398
2399
await reader.consumeToEnd();
2400
});
2401
2402
test('peekTimeout - empty iterator', async () => {
2403
const reader = new async.AsyncReader(createAsyncIterator([]));
2404
2405
const result = await reader.peekTimeout(10);
2406
assert.strictEqual(result, async.AsyncReaderEndOfStream);
2407
});
2408
2409
test('peekTimeout - after consuming all data', async () => {
2410
const reader = new async.AsyncReader(createAsyncIterator([1]));
2411
2412
await reader.consumeToEnd();
2413
const result = await reader.peekTimeout(10);
2414
assert.strictEqual(result, async.AsyncReaderEndOfStream);
2415
});
2416
2417
test('mixed operations - complex scenario', async () => {
2418
const reader = new async.AsyncReader(createAsyncIterator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
2419
2420
// Peek first
2421
assert.strictEqual(await reader.peek(), 1);
2422
2423
// Read some
2424
assert.strictEqual(await reader.read(), 1);
2425
assert.strictEqual(await reader.read(), 2);
2426
2427
// Peek again
2428
assert.strictEqual(await reader.peek(), 3);
2429
2430
// Read while
2431
const collected: number[] = [];
2432
await reader.readWhile(
2433
value => value <= 5,
2434
async value => collected.push(value)
2435
);
2436
assert.deepStrictEqual(collected, [3, 4, 5]);
2437
2438
// Use sync operations
2439
assert.strictEqual(await reader.peek(), 6);
2440
assert.strictEqual(reader.peekBufferedOrThrow(), 6);
2441
assert.strictEqual(reader.readBufferedOrThrow(), 6);
2442
2443
// Consume rest
2444
await reader.consumeToEnd();
2445
assert.strictEqual(reader.endOfStream, true);
2446
});
2447
2448
test('string values', async () => {
2449
const reader = new async.AsyncReader(createAsyncIterator(['hello', 'world', 'test']));
2450
2451
assert.strictEqual(await reader.read(), 'hello');
2452
assert.strictEqual(await reader.peek(), 'world');
2453
assert.strictEqual(await reader.read(), 'world');
2454
assert.strictEqual(await reader.read(), 'test');
2455
assert.strictEqual(await reader.read(), async.AsyncReaderEndOfStream);
2456
});
2457
2458
test('object values', async () => {
2459
interface TestObj {
2460
id: number;
2461
name: string;
2462
}
2463
2464
const objects: TestObj[] = [
2465
{ id: 1, name: 'first' },
2466
{ id: 2, name: 'second' },
2467
{ id: 3, name: 'third' }
2468
];
2469
2470
const reader = new async.AsyncReader(createAsyncIterator(objects));
2471
2472
assert.deepStrictEqual(await reader.read(), { id: 1, name: 'first' });
2473
assert.deepStrictEqual(await reader.peek(), { id: 2, name: 'second' });
2474
assert.deepStrictEqual(await reader.read(), { id: 2, name: 'second' });
2475
});
2476
2477
test('concurrent operations', async () => {
2478
const reader = new async.AsyncReader(createDelayedAsyncIterator([1, 2, 3], 5));
2479
2480
// Start multiple operations concurrently
2481
const peekPromise = reader.peek();
2482
const readPromise = reader.read();
2483
2484
const [peekResult, readResult] = await Promise.all([peekPromise, readPromise]);
2485
2486
// Both should return the same first value
2487
assert.strictEqual(peekResult, 1);
2488
assert.strictEqual(readResult, 1);
2489
2490
// Next read should get the second value
2491
assert.strictEqual(await reader.read(), 2);
2492
});
2493
2494
test('buffer management - single extend buffer call', async () => {
2495
let nextCallCount = 0;
2496
const mockIterator: AsyncIterator<number> = {
2497
async next() {
2498
nextCallCount++;
2499
if (nextCallCount === 1) {
2500
await async.timeout(1);
2501
return { value: 1, done: false };
2502
}
2503
return { value: undefined, done: true };
2504
}
2505
};
2506
2507
const reader = new async.AsyncReader(mockIterator);
2508
2509
// Multiple concurrent operations should only trigger one extend buffer call
2510
const promises = [
2511
reader.peek(),
2512
reader.peek(),
2513
reader.read()
2514
];
2515
2516
await Promise.all(promises);
2517
2518
// Should have called next() only once despite multiple concurrent operations
2519
assert.strictEqual(nextCallCount, 1);
2520
});
2521
});
2522
});
2523
2524