1
1
// Copyright 2018-2025 the Deno authors. MIT license.
2
2
// deno-lint-ignore-file no-console
3
3
4
+ // This script runs all Node.js test cases without modification
5
+ // It saves the test results in `tests/node_compat/report.json` when
6
+ // filtering is not specified.
7
+ // The saved results are uploaded to cloud storage bucket `dl.deno.land`
8
+ // daily, and can be viewed at the web page:
9
+ // https://node-test-viewer.deno.dev/
10
+
4
11
import { deadline } from "@std/async/deadline" ;
5
12
import { expandGlob } from "@std/fs/expand-glob" ;
6
13
import { toFileUrl } from "@std/path/to-file-url" ;
7
- import { basename } from "@std/path/basename" ;
8
14
import { pooledMap } from "@std/async/pool" ;
9
15
import { partition } from "@std/collections/partition" ;
10
16
import { stripAnsiCode } from "@std/fmt/colors" ;
@@ -61,152 +67,12 @@ const NODE_IGNORED_TEST_DIRS = [
61
67
"wpt" ,
62
68
] ;
63
69
64
- // Category names are usually one word, but there are some exceptions.
65
- // This list contains the exceptions.
66
- const multiWordsCategoryNames = [
67
- "async-hooks" ,
68
- "async-local-storage" ,
69
- "async-wrap" ,
70
- "child-process" ,
71
- "cpu-prof" ,
72
- "double-tls" ,
73
- "diagnostics-channel" ,
74
- "force-repl" ,
75
- "listen-fd" ,
76
- "memory-usage" ,
77
- "next-tick" ,
78
- "outgoing-message" ,
79
- "shadow-realm" ,
80
- "single-executable" ,
81
- "string-decoder" ,
82
- ] ;
83
-
84
- const categoryMap = {
85
- cjs : "module" ,
86
- cwd : "process" ,
87
- diagnostic : "diagnostics-channel" ,
88
- "double-tls" : "net" ,
89
- event : "events" ,
90
- eventsource : "events" ,
91
- eventtarget : "events" ,
92
- esm : "module" ,
93
- file : "fs" ,
94
- filehandle : "fs" ,
95
- "force-repl" : "repl" ,
96
- inspect : "util" ,
97
- "listen-fd" : "net" ,
98
- "next-tick" : "process" ,
99
- "outgoing-message" : "http" ,
100
- promises : "promise" ,
101
- readable : "stream" ,
102
- require : "module" ,
103
- socket : "net" ,
104
- stdin : "stdio" ,
105
- stdout : "stdio" ,
106
- stream2 : "stream" ,
107
- stream3 : "stream" ,
108
- tcp : "net" ,
109
- ttywrap : "tty" ,
110
- webstream : "webstreams" ,
111
- } as Record < string , string > ;
112
-
113
- // These name could appear as category name, but they are actually not.
114
- // If the category name is one of these, it should be categorized as "others".
115
- const otherCategories = [
116
- "common" ,
117
- "compile" ,
118
- "corepack" ,
119
- "disable" ,
120
- "env" ,
121
- "error" ,
122
- "errors" ,
123
- "eslint" ,
124
- "eval" ,
125
- "exception" ,
126
- "handle" ,
127
- "heap" ,
128
- "heapdump" ,
129
- "heapsnapshot" ,
130
- "internal" ,
131
- "memory" ,
132
- "no" ,
133
- "queue" ,
134
- "release" ,
135
- "set" ,
136
- "source" ,
137
- "startup" ,
138
- "sync" ,
139
- "trace" ,
140
- "tick" ,
141
- "unhandled" ,
142
- "uv" ,
143
- "warn" ,
144
- "windows" ,
145
- "wrap" ,
146
- ] ;
147
-
148
- /**
149
- * The test files in these dirs seem categorized in the form
150
- * test-[category-name]-test-case.js
151
- */
152
- const categorizedTestGroups = [
153
- "es-module" ,
154
- "parallel" ,
155
- "pummel" ,
156
- "sequential" ,
157
- "internet" ,
158
- ] ;
159
-
160
70
/** The group is the directory name of the test file.
161
71
* e.g. parallel, internet, pummel, sequential, pseudo-tty, etc */
162
72
function getGroupRelUrl ( str : string ) {
163
73
return str . split ( "/" ) [ 0 ] ;
164
74
}
165
75
166
- /** Gets the category name from the test path
167
- * e.g.
168
- * - parallel/test-async-hooks-destroyed-context.js -> async-hooks
169
- * - sequential/test-child-process-exec-stderr.js -> child-process
170
- * - internet/test-http-keep-alive.js -> http
171
- * - pseudo-tty/test-stdin.js -> tty
172
- * - module-hooks/test-require.js -> module
173
- */
174
- function getCategoryFromPath ( str : string ) {
175
- const group = getGroupRelUrl ( str ) ;
176
- if ( group === "pseudo-tty" ) {
177
- return "tty" ;
178
- } else if ( group === "module-hooks" ) {
179
- return "module" ;
180
- } else if ( categorizedTestGroups . includes ( group ) ) {
181
- const name = basename ( str ) . replace ( / \. j s / , "" ) ;
182
- let category = name . split ( "-" ) [ 1 ] ;
183
- for ( const multiWord of multiWordsCategoryNames ) {
184
- if ( name . startsWith ( "test-" + multiWord ) ) {
185
- category = multiWord ;
186
- }
187
- }
188
- category = categoryMap [ category ] ?? category ;
189
- if ( otherCategories . includes ( category ) ) {
190
- return "others" ;
191
- }
192
- return category ;
193
- } else {
194
- return "others" ;
195
- }
196
- }
197
-
198
- /** Collect the items that are not categorized into the "others" category. */
199
- function collectNonCategorizedItems ( categories : Record < string , string [ ] > ) {
200
- const others = [ ] as string [ ] ;
201
- for ( const [ category , items ] of Object . entries ( categories ) ) {
202
- if ( items . length === 1 ) {
203
- delete categories [ category ] ;
204
- others . push ( ...items ) ;
205
- }
206
- }
207
- ( categories [ "others" ] ??= [ ] ) . push ( ...others ) ;
208
- }
209
-
210
76
function truncateTestOutput ( output : string ) : string {
211
77
output = stripAnsiCode ( output ) ;
212
78
if ( output . length > 2000 ) {
@@ -382,7 +248,6 @@ async function main() {
382
248
383
249
const start = Date . now ( ) ;
384
250
const tests = [ ] as string [ ] ;
385
- const categories = { } as Record < string , string [ ] > ;
386
251
for await (
387
252
const test of expandGlob (
388
253
"tests/node_compat/runner/suite/**/test-*{.mjs,.cjs.,.js,.ts}" ,
@@ -392,12 +257,8 @@ async function main() {
392
257
const relUrl = toFileUrl ( test . path ) . href . replace ( testDirUrl , "" ) ;
393
258
if ( NODE_IGNORED_TEST_DIRS . every ( ( dir ) => ! relUrl . startsWith ( dir ) ) ) {
394
259
tests . push ( relUrl ) ;
395
- ( categories [ getCategoryFromPath ( relUrl ) ] ??= [ ] ) . push ( relUrl ) ;
396
260
}
397
261
}
398
- collectNonCategorizedItems ( categories ) ;
399
- const categoryList = Object . entries ( categories )
400
- . sort ( ( [ c0 ] , [ c1 ] ) => c0 . localeCompare ( c1 ) ) ;
401
262
const reports = { } as TestReports ;
402
263
let i = 0 ;
403
264
@@ -420,7 +281,6 @@ async function main() {
420
281
( test ) => getGroupRelUrl ( test ) === "sequential" ,
421
282
) ;
422
283
423
- console . log ;
424
284
if ( filterTerm ) {
425
285
sequential = sequential . filter ( ( term ) => {
426
286
if ( term . includes ( filterTerm ) ) {
@@ -454,54 +314,35 @@ async function main() {
454
314
// pass
455
315
}
456
316
457
- // Reporting to stdout
458
- console . log ( `Result by categories (${ categoryList . length } ):` ) ;
459
- for ( const [ category , tests ] of categoryList ) {
460
- if (
461
- tests . every ( ( test ) => reports [ test ] . result === NodeTestFileResult . SKIP )
462
- ) {
463
- continue ;
464
- }
465
- const s = tests . filter ( ( test ) =>
466
- reports [ test ] . result === NodeTestFileResult . PASS
467
- ) . length ;
468
- const all = filterTerm
469
- ? tests . map ( ( testPath ) => reports [ testPath ] . result ) . filter ( ( result ) =>
470
- result !== NodeTestFileResult . SKIP
471
- ) . length
472
- : tests . length ;
473
- console . log ( ` ${ category } ${ s } /${ all } (${ ( s / all * 100 ) . toFixed ( 2 ) } %)` ) ;
474
- for ( const testPath of tests ) {
475
- switch ( reports [ testPath ] . result ) {
476
- case NodeTestFileResult . PASS : {
477
- console . log ( ` %cPASS` , "color: green" , testPath ) ;
478
- break ;
479
- }
480
- case NodeTestFileResult . FAIL : {
481
- // deno-lint-ignore no-explicit-any
482
- let elements : any [ ] = [ ] ;
483
- const error = reports [ testPath ] . error ! ;
484
- if ( error . code ) {
485
- elements = [ "exit code:" , error . code , "\n " , error . stderr ] ;
486
- } else if ( error . timeout ) {
487
- elements = [ "timeout out after" , error . timeout , "seconds" ] ;
488
- } else {
489
- elements = [ "errored with:" , error . message ] ;
490
- }
491
- console . log ( ` %cFAIL` , "color: red" , testPath ) ;
492
- console . log ( " " , ...elements ) ;
493
- break ;
494
- }
495
- case NodeTestFileResult . SKIP : {
496
- // Don't print message for "skip" for now, as it's too noisy
497
- // console.log(` %cSKIP`, "color: yellow", testPath);
498
- break ;
317
+ for ( const [ testPath , fileResult ] of Object . entries ( reports ) ) {
318
+ switch ( fileResult . result ) {
319
+ case NodeTestFileResult . PASS : {
320
+ console . log ( ` %cPASS` , "color: green" , testPath ) ;
321
+ break ;
322
+ }
323
+ case NodeTestFileResult . FAIL : {
324
+ let elements : string [ ] = [ ] ;
325
+ const error = fileResult . error ! ;
326
+ if ( "code" in error ) {
327
+ elements = [ `exit code: ${ error . code } \n ` , error . stderr ] ;
328
+ } else if ( "timeout" in error ) {
329
+ elements = [ `timeout out after ${ error . timeout } seconds` ] ;
330
+ } else {
331
+ elements = [ "errored with:" , error . message ] ;
499
332
}
500
- default :
501
- console . warn (
502
- `Unknown result (${ reports [ testPath ] . result } ) for ${ testPath } ` ,
503
- ) ;
333
+ console . log ( ` %cFAIL` , "color: red" , testPath ) ;
334
+ console . log ( " " , ...elements ) ;
335
+ break ;
336
+ }
337
+ case NodeTestFileResult . SKIP : {
338
+ // Don't print message for "skip" for now, as it's too noisy
339
+ // console.log(` %cSKIP`, "color: yellow", testPath);
340
+ break ;
504
341
}
342
+ default :
343
+ console . warn (
344
+ `Unknown result (${ fileResult . result } ) for ${ testPath } ` ,
345
+ ) ;
505
346
}
506
347
}
507
348
0 commit comments