|
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