|
1 | 1 | 'use strict';
|
2 | 2 |
|
3 |
| -const common = require('../common'); |
| 3 | +require('../common'); |
4 | 4 | const { ok, strictEqual } = require('assert');
|
5 |
| -const { setImmediate: pause } = require('timers/promises'); |
| 5 | +const { setImmediate: sleep } = require('timers/promises'); |
6 | 6 | const {
|
7 | 7 | transferableAbortSignal,
|
8 | 8 | transferableAbortController,
|
9 | 9 | } = require('util');
|
| 10 | +const { |
| 11 | + test, |
| 12 | + mock, |
| 13 | +} = require('node:test'); |
10 | 14 |
|
11 |
| - |
12 |
| -function deferred() { |
13 |
| - let res; |
14 |
| - const promise = new Promise((resolve) => res = resolve); |
15 |
| - return { res, promise }; |
16 |
| -} |
17 |
| - |
18 |
| -(async () => { |
| 15 | +test('Can create a transferable abort controller', async () => { |
19 | 16 | const ac = transferableAbortController();
|
20 | 17 | const mc = new MessageChannel();
|
21 | 18 |
|
22 |
| - const deferred1 = deferred(); |
23 |
| - const deferred2 = deferred(); |
24 |
| - const resolvers = [deferred1, deferred2]; |
| 19 | + const setup1 = Promise.withResolvers(); |
| 20 | + const setup2 = Promise.withResolvers(); |
| 21 | + const setupResolvers = [setup1, setup2]; |
25 | 22 |
|
26 |
| - mc.port1.onmessage = common.mustCall(({ data }) => { |
27 |
| - data.addEventListener('abort', common.mustCall(() => { |
| 23 | + const abort1 = Promise.withResolvers(); |
| 24 | + const abort2 = Promise.withResolvers(); |
| 25 | + const abort3 = Promise.withResolvers(); |
| 26 | + const abortResolvers = [abort1, abort2, abort3]; |
| 27 | + |
| 28 | + mc.port1.onmessage = ({ data }) => { |
| 29 | + data.addEventListener('abort', () => { |
28 | 30 | strictEqual(data.reason, 'boom');
|
29 |
| - })); |
30 |
| - resolvers.shift().res(); |
31 |
| - }, 2); |
| 31 | + abortResolvers.shift().resolve(); |
| 32 | + }); |
| 33 | + setupResolvers.shift().resolve(); |
| 34 | + }; |
32 | 35 |
|
33 | 36 | mc.port2.postMessage(ac.signal, [ac.signal]);
|
34 | 37 |
|
35 | 38 | // Can be cloned/transferd multiple times and they all still work
|
36 | 39 | mc.port2.postMessage(ac.signal, [ac.signal]);
|
37 | 40 |
|
38 |
| - mc.port2.close(); |
39 |
| - |
40 | 41 | // Although we're using transfer semantics, the local AbortSignal
|
41 | 42 | // is still usable locally.
|
42 |
| - ac.signal.addEventListener('abort', common.mustCall(() => { |
| 43 | + ac.signal.addEventListener('abort', () => { |
43 | 44 | strictEqual(ac.signal.reason, 'boom');
|
44 |
| - })); |
| 45 | + abortResolvers.shift().resolve(); |
| 46 | + }); |
45 | 47 |
|
46 |
| - await Promise.all([ deferred1.promise, deferred2.promise ]); |
| 48 | + await Promise.all([ setup1.promise, setup2.promise ]); |
47 | 49 |
|
48 | 50 | ac.abort('boom');
|
49 | 51 |
|
50 |
| - // Because the postMessage used by the underlying AbortSignal |
51 |
| - // takes at least one turn of the event loop to be processed, |
52 |
| - // and because it is unref'd, it won't, by itself, keep the |
53 |
| - // event loop open long enough for the test to complete, so |
54 |
| - // we schedule two back to back turns of the event to ensure |
55 |
| - // the loop runs long enough for the test to complete. |
56 |
| - await pause(); |
57 |
| - await pause(); |
| 52 | + await Promise.all([ abort1.promise, abort2.promise, abort3.promise ]); |
| 53 | + |
| 54 | + mc.port2.close(); |
58 | 55 |
|
59 |
| -})().then(common.mustCall()); |
| 56 | +}); |
60 | 57 |
|
61 |
| -{ |
| 58 | +test('Can create a transferable abort signal', async () => { |
62 | 59 | const signal = transferableAbortSignal(AbortSignal.abort('boom'));
|
63 | 60 | ok(signal.aborted);
|
64 | 61 | strictEqual(signal.reason, 'boom');
|
65 | 62 | const mc = new MessageChannel();
|
66 |
| - mc.port1.onmessage = common.mustCall(({ data }) => { |
| 63 | + const { promise, resolve } = Promise.withResolvers(); |
| 64 | + mc.port1.onmessage = ({ data }) => { |
67 | 65 | ok(data instanceof AbortSignal);
|
68 | 66 | ok(data.aborted);
|
69 | 67 | strictEqual(data.reason, 'boom');
|
70 |
| - mc.port1.close(); |
71 |
| - }); |
| 68 | + resolve(); |
| 69 | + }; |
72 | 70 | mc.port2.postMessage(signal, [signal]);
|
73 |
| -} |
| 71 | + await promise; |
| 72 | + mc.port1.close(); |
| 73 | +}); |
74 | 74 |
|
75 |
| -{ |
76 |
| - // The cloned AbortSignal does not keep the event loop open |
77 |
| - // waiting for the abort to be triggered. |
| 75 | +test('A cloned AbortSignal does not keep the event loop open', async () => { |
78 | 76 | const ac = transferableAbortController();
|
79 | 77 | const mc = new MessageChannel();
|
80 |
| - mc.port1.onmessage = common.mustCall(); |
| 78 | + const fn = mock.fn(); |
| 79 | + mc.port1.onmessage = fn; |
81 | 80 | mc.port2.postMessage(ac.signal, [ac.signal]);
|
| 81 | + // Because the postMessage used by the underlying AbortSignal |
| 82 | + // takes at least one turn of the event loop to be processed, |
| 83 | + // and because it is unref'd, it won't, by itself, keep the |
| 84 | + // event loop open long enough for the test to complete, so |
| 85 | + // we schedule two back to back turns of the event to ensure |
| 86 | + // the loop runs long enough for the test to complete. |
| 87 | + await sleep(); |
| 88 | + await sleep(); |
| 89 | + strictEqual(fn.mock.calls.length, 1); |
82 | 90 | mc.port2.close();
|
83 |
| -} |
| 91 | +}); |
0 commit comments