1
- // Flags: --no-warnings -- expose-gc --expose-internals
1
+ // Flags: --expose-gc
2
2
'use strict' ;
3
3
4
- const common = require ( '../common' ) ;
4
+ require ( '../common' ) ;
5
5
const { inspect } = require ( 'util' ) ;
6
6
7
7
const {
@@ -12,80 +12,93 @@ const {
12
12
} = require ( 'assert' ) ;
13
13
14
14
const {
15
- kWeakHandler,
16
- } = require ( 'internal/event_target' ) ;
15
+ test,
16
+ mock,
17
+ } = require ( 'node:test' ) ;
17
18
18
19
const { setTimeout : sleep } = require ( 'timers/promises' ) ;
19
20
20
- {
21
+ // All of the the tests in this file depend on public-facing Node.js APIs.
22
+ // For tests that depend on Node.js internal APIs, please add them to
23
+ // test-abortcontroller-internal.js instead.
24
+
25
+ test ( 'Abort is fired with the correct event type on AbortControllers' , ( ) => {
21
26
// Tests that abort is fired with the correct event type on AbortControllers
22
27
const ac = new AbortController ( ) ;
23
28
ok ( ac . signal ) ;
24
- ac . signal . onabort = common . mustCall ( ( event ) => {
29
+
30
+ const fn = mock . fn ( ( event ) => {
25
31
ok ( event ) ;
26
32
strictEqual ( event . type , 'abort' ) ;
27
33
} ) ;
28
- ac . signal . addEventListener ( 'abort' , common . mustCall ( ( event ) => {
29
- ok ( event ) ;
30
- strictEqual ( event . type , 'abort' ) ;
31
- } ) , { once : true } ) ;
34
+
35
+ ac . signal . onabort = fn ;
36
+ ac . signal . addEventListener ( 'abort' , fn ) ;
37
+
32
38
ac . abort ( ) ;
33
39
ac . abort ( ) ;
34
40
ok ( ac . signal . aborted ) ;
35
- }
36
41
37
- {
42
+ strictEqual ( fn . mock . calls . length , 2 ) ;
43
+ } ) ;
44
+
45
+ test ( 'Abort events are trusted' , ( ) => {
38
46
// Tests that abort events are trusted
39
47
const ac = new AbortController ( ) ;
40
- ac . signal . addEventListener ( 'abort' , common . mustCall ( ( event ) => {
48
+
49
+ const fn = mock . fn ( ( event ) => {
41
50
ok ( event . isTrusted ) ;
42
- } ) ) ;
51
+ } ) ;
52
+
53
+ ac . signal . onabort = fn ;
43
54
ac . abort ( ) ;
44
- }
55
+ strictEqual ( fn . mock . calls . length , 1 ) ;
56
+ } ) ;
45
57
46
- {
58
+ test ( 'Abort events have the same isTrusted reference' , ( ) => {
47
59
// Tests that abort events have the same `isTrusted` reference
48
60
const first = new AbortController ( ) ;
49
61
const second = new AbortController ( ) ;
50
62
let ev1 , ev2 ;
51
63
const ev3 = new Event ( 'abort' ) ;
52
- first . signal . addEventListener ( 'abort' , common . mustCall ( ( event ) => {
64
+
65
+ first . signal . addEventListener ( 'abort' , ( event ) => {
53
66
ev1 = event ;
54
- } ) ) ;
55
- second . signal . addEventListener ( 'abort' , common . mustCall ( ( event ) => {
67
+ } ) ;
68
+ second . signal . addEventListener ( 'abort' , ( event ) => {
56
69
ev2 = event ;
57
- } ) ) ;
70
+ } ) ;
58
71
first . abort ( ) ;
59
72
second . abort ( ) ;
60
73
const firstTrusted = Reflect . getOwnPropertyDescriptor ( Object . getPrototypeOf ( ev1 ) , 'isTrusted' ) . get ;
61
74
const secondTrusted = Reflect . getOwnPropertyDescriptor ( Object . getPrototypeOf ( ev2 ) , 'isTrusted' ) . get ;
62
75
const untrusted = Reflect . getOwnPropertyDescriptor ( Object . getPrototypeOf ( ev3 ) , 'isTrusted' ) . get ;
63
76
strictEqual ( firstTrusted , secondTrusted ) ;
64
77
strictEqual ( untrusted , firstTrusted ) ;
65
- }
78
+ } ) ;
66
79
67
- {
80
+ test ( 'AbortSignal is impossible to construct manually' , ( ) => {
68
81
// Tests that AbortSignal is impossible to construct manually
69
82
const ac = new AbortController ( ) ;
70
83
throws ( ( ) => new ac . signal . constructor ( ) , {
71
84
code : 'ERR_ILLEGAL_CONSTRUCTOR' ,
72
85
} ) ;
73
- }
74
- {
86
+ } ) ;
87
+
88
+ test ( 'Symbol.toStringTag is correct' , ( ) => {
75
89
// Symbol.toStringTag
76
90
const toString = ( o ) => Object . prototype . toString . call ( o ) ;
77
91
const ac = new AbortController ( ) ;
78
92
strictEqual ( toString ( ac ) , '[object AbortController]' ) ;
79
93
strictEqual ( toString ( ac . signal ) , '[object AbortSignal]' ) ;
80
- }
94
+ } ) ;
81
95
82
- {
96
+ test ( 'AbortSignal.abort() creates an already aborted signal' , ( ) => {
83
97
const signal = AbortSignal . abort ( ) ;
84
98
ok ( signal . aborted ) ;
85
- }
99
+ } ) ;
86
100
87
- {
88
- // Test that AbortController properties and methods validate the receiver
101
+ test ( 'AbortController properties and methods valiate the receiver' , ( ) => {
89
102
const acSignalGet = Object . getOwnPropertyDescriptor (
90
103
AbortController . prototype ,
91
104
'signal'
@@ -115,10 +128,9 @@ const { setTimeout: sleep } = require('timers/promises');
115
128
{ name : 'TypeError' }
116
129
) ;
117
130
}
118
- }
131
+ } ) ;
119
132
120
- {
121
- // Test that AbortSignal properties validate the receiver
133
+ test ( 'AbortSignal properties validate the receiver' , ( ) => {
122
134
const signalAbortedGet = Object . getOwnPropertyDescriptor (
123
135
AbortSignal . prototype ,
124
136
'aborted'
@@ -142,96 +154,87 @@ const { setTimeout: sleep } = require('timers/promises');
142
154
{ name : 'TypeError' }
143
155
) ;
144
156
}
145
- }
157
+ } ) ;
146
158
147
- {
159
+ test ( 'AbortController inspection depth 1 or null works' , ( ) => {
148
160
const ac = new AbortController ( ) ;
149
161
strictEqual ( inspect ( ac , { depth : 1 } ) ,
150
162
'AbortController { signal: [AbortSignal] }' ) ;
151
163
strictEqual ( inspect ( ac , { depth : null } ) ,
152
164
'AbortController { signal: AbortSignal { aborted: false } }' ) ;
153
- }
165
+ } ) ;
154
166
155
- {
167
+ test ( 'AbortSignal reason is set correctly' , ( ) => {
156
168
// Test AbortSignal.reason
157
169
const ac = new AbortController ( ) ;
158
170
ac . abort ( 'reason' ) ;
159
171
strictEqual ( ac . signal . reason , 'reason' ) ;
160
- }
172
+ } ) ;
161
173
162
- {
174
+ test ( 'AbortSignal reasonable is set correctly with AbortSignal.abort()' , ( ) => {
163
175
// Test AbortSignal.reason
164
176
const signal = AbortSignal . abort ( 'reason' ) ;
165
177
strictEqual ( signal . reason , 'reason' ) ;
166
- }
178
+ } ) ;
167
179
168
- {
180
+ test ( 'AbortSignal.timeout() works as expected' , async ( ) => {
169
181
// Test AbortSignal timeout
170
182
const signal = AbortSignal . timeout ( 10 ) ;
171
183
ok ( ! signal . aborted ) ;
172
- setTimeout ( common . mustCall ( ( ) => {
184
+
185
+ const { promise, resolve } = Promise . withResolvers ( ) ;
186
+
187
+ const fn = mock . fn ( ( ) => {
173
188
ok ( signal . aborted ) ;
174
189
strictEqual ( signal . reason . name , 'TimeoutError' ) ;
175
190
strictEqual ( signal . reason . code , 23 ) ;
176
- } ) , 20 ) ;
177
- }
178
-
179
- {
180
- ( async ( ) => {
181
- // Test AbortSignal timeout doesn't prevent the signal
182
- // from being garbage collected.
183
- let ref ;
184
- {
185
- ref = new globalThis . WeakRef ( AbortSignal . timeout ( 1_200_000 ) ) ;
186
- }
187
-
188
- await sleep ( 10 ) ;
189
- globalThis . gc ( ) ;
190
- strictEqual ( ref . deref ( ) , undefined ) ;
191
- } ) ( ) . then ( common . mustCall ( ) ) ;
192
-
193
- ( async ( ) => {
194
- // Test that an AbortSignal with a timeout is not gc'd while
195
- // there is an active listener on it.
196
- let ref ;
197
- function handler ( ) { }
198
- {
199
- ref = new globalThis . WeakRef ( AbortSignal . timeout ( 1_200_000 ) ) ;
200
- ref . deref ( ) . addEventListener ( 'abort' , handler ) ;
201
- }
202
-
203
- await sleep ( 10 ) ;
204
- globalThis . gc ( ) ;
205
- notStrictEqual ( ref . deref ( ) , undefined ) ;
206
- ok ( ref . deref ( ) instanceof AbortSignal ) ;
207
-
208
- ref . deref ( ) . removeEventListener ( 'abort' , handler ) ;
209
-
210
- await sleep ( 10 ) ;
211
- globalThis . gc ( ) ;
212
- strictEqual ( ref . deref ( ) , undefined ) ;
213
- } ) ( ) . then ( common . mustCall ( ) ) ;
214
-
215
- ( async ( ) => {
216
- // If the event listener is weak, however, it should not prevent gc
217
- let ref ;
218
- function handler ( ) { }
219
- {
220
- ref = new globalThis . WeakRef ( AbortSignal . timeout ( 1_200_000 ) ) ;
221
- ref . deref ( ) . addEventListener ( 'abort' , handler , { [ kWeakHandler ] : { } } ) ;
222
- }
223
-
224
- await sleep ( 10 ) ;
225
- globalThis . gc ( ) ;
226
- strictEqual ( ref . deref ( ) , undefined ) ;
227
- } ) ( ) . then ( common . mustCall ( ) ) ;
228
-
229
- // Setting a long timeout (20 minutes here) should not
230
- // keep the Node.js process open (the timer is unref'd)
191
+ resolve ( ) ;
192
+ } ) ;
193
+
194
+ setTimeout ( fn , 20 ) ;
195
+ await promise ;
196
+ } ) ;
197
+
198
+ test ( 'AbortSignal.timeout() does not prevent the signal from being collected' , async ( ) => {
199
+ // Test AbortSignal timeout doesn't prevent the signal
200
+ // from being garbage collected.
201
+ let ref ;
202
+ {
203
+ ref = new globalThis . WeakRef ( AbortSignal . timeout ( 1_200_000 ) ) ;
204
+ }
205
+
206
+ await sleep ( 10 ) ;
207
+ globalThis . gc ( ) ;
208
+ strictEqual ( ref . deref ( ) , undefined ) ;
209
+ } ) ;
210
+
211
+ test ( 'AbortSignal with a timeout is not collected while there is an active listener' , async ( ) => {
212
+ // Test that an AbortSignal with a timeout is not gc'd while
213
+ // there is an active listener on it.
214
+ let ref ;
215
+ function handler ( ) { }
216
+ {
217
+ ref = new globalThis . WeakRef ( AbortSignal . timeout ( 1_200_000 ) ) ;
218
+ ref . deref ( ) . addEventListener ( 'abort' , handler ) ;
219
+ }
220
+
221
+ await sleep ( 10 ) ;
222
+ globalThis . gc ( ) ;
223
+ notStrictEqual ( ref . deref ( ) , undefined ) ;
224
+ ok ( ref . deref ( ) instanceof AbortSignal ) ;
225
+
226
+ ref . deref ( ) . removeEventListener ( 'abort' , handler ) ;
227
+
228
+ await sleep ( 10 ) ;
229
+ globalThis . gc ( ) ;
230
+ strictEqual ( ref . deref ( ) , undefined ) ;
231
+ } ) ;
232
+
233
+ test ( 'Setting a long timeout should not keep the process open' , ( ) => {
231
234
AbortSignal . timeout ( 1_200_000 ) ;
232
- }
235
+ } ) ;
233
236
234
- {
237
+ test ( 'AbortSignal.reason should default' , ( ) => {
235
238
// Test AbortSignal.reason default
236
239
const signal = AbortSignal . abort ( ) ;
237
240
ok ( signal . reason instanceof DOMException ) ;
@@ -241,9 +244,9 @@ const { setTimeout: sleep } = require('timers/promises');
241
244
ac . abort ( ) ;
242
245
ok ( ac . signal . reason instanceof DOMException ) ;
243
246
strictEqual ( ac . signal . reason . code , 20 ) ;
244
- }
247
+ } ) ;
245
248
246
- {
249
+ test ( 'abortSignal.throwIfAborted() works as expected' , ( ) => {
247
250
// Test abortSignal.throwIfAborted()
248
251
throws ( ( ) => AbortSignal . abort ( ) . throwIfAborted ( ) , {
249
252
code : 20 ,
@@ -253,21 +256,21 @@ const { setTimeout: sleep } = require('timers/promises');
253
256
// Does not throw because it's not aborted.
254
257
const ac = new AbortController ( ) ;
255
258
ac . signal . throwIfAborted ( ) ;
256
- }
259
+ } ) ;
257
260
258
- {
261
+ test ( 'abortSignal.throwIfAobrted() works as expected (2)' , ( ) => {
259
262
const originalDesc = Reflect . getOwnPropertyDescriptor ( AbortSignal . prototype , 'aborted' ) ;
260
263
const actualReason = new Error ( ) ;
261
264
Reflect . defineProperty ( AbortSignal . prototype , 'aborted' , { value : false } ) ;
262
265
throws ( ( ) => AbortSignal . abort ( actualReason ) . throwIfAborted ( ) , actualReason ) ;
263
266
Reflect . defineProperty ( AbortSignal . prototype , 'aborted' , originalDesc ) ;
264
- }
267
+ } ) ;
265
268
266
- {
269
+ test ( 'abortSignal.throwIfAobrted() works as expected (3)' , ( ) => {
267
270
const originalDesc = Reflect . getOwnPropertyDescriptor ( AbortSignal . prototype , 'reason' ) ;
268
271
const actualReason = new Error ( ) ;
269
272
const fakeExcuse = new Error ( ) ;
270
273
Reflect . defineProperty ( AbortSignal . prototype , 'reason' , { value : fakeExcuse } ) ;
271
274
throws ( ( ) => AbortSignal . abort ( actualReason ) . throwIfAborted ( ) , actualReason ) ;
272
275
Reflect . defineProperty ( AbortSignal . prototype , 'reason' , originalDesc ) ;
273
- }
276
+ } ) ;
0 commit comments