1
+ const startTime = Date . now ( ) ;
1
2
const fs = require ( "fs" ) ;
2
3
const path = require ( "path" ) ;
3
4
const os = require ( "os" ) ;
@@ -8,6 +9,8 @@ const optionsUtil = require("../cli/util/options");
8
9
const diff = require ( "./util/diff" ) ;
9
10
const asc = require ( "../cli/asc.js" ) ;
10
11
const rtrace = require ( "../lib/rtrace" ) ;
12
+ const cluster = require ( "cluster" ) ;
13
+ const coreCount = require ( "physical-cpu-count" ) ;
11
14
12
15
const config = {
13
16
"create" : {
@@ -34,13 +37,18 @@ const config = {
34
37
"Enables verbose rtrace output."
35
38
]
36
39
} ,
40
+ "parallel" : {
41
+ "description" : [
42
+ "Runs tests in parallel."
43
+ ]
44
+ } ,
37
45
"help" : {
38
46
"description" : "Prints this message and exits." ,
39
47
"type" : "b" ,
40
48
"alias" : "h"
41
49
}
42
50
} ;
43
- const opts = optionsUtil . parse ( process . argv . slice ( 2 ) , config ) ;
51
+ const opts = optionsUtil . parse ( process . argv . slice ( 2 ) , config ) ;
44
52
const args = opts . options ;
45
53
const argv = opts . arguments ;
46
54
@@ -65,23 +73,23 @@ var skippedMessages = new Map();
65
73
66
74
const basedir = path . join ( __dirname , "compiler" ) ;
67
75
68
- // Get a list of all tests
69
- var tests = glob . sync ( "**/!(_*).ts" , { cwd : basedir } ) ;
70
-
71
- // Run specific tests only if arguments are provided
72
- if ( argv . length ) {
73
- tests = tests . filter ( filename => argv . indexOf ( filename . replace ( / \. t s $ / , "" ) ) >= 0 ) ;
74
- if ( ! tests . length ) {
75
- console . error ( "No matching tests: " + argv . join ( " " ) ) ;
76
- process . exit ( 1 ) ;
76
+ // Gets a list of all relevant tests
77
+ function getTests ( ) {
78
+ var tests = glob . sync ( "**/!(_*).ts" , { cwd : basedir } ) . map ( name => name . replace ( / \. t s $ / , "" ) ) ;
79
+ if ( argv . length ) { // run matching tests only
80
+ tests = tests . filter ( filename => argv . indexOf ( filename . replace ( / \. t s $ / , "" ) ) >= 0 ) ;
81
+ if ( ! tests . length ) {
82
+ console . error ( "No matching tests: " + argv . join ( " " ) ) ;
83
+ process . exit ( 1 ) ;
84
+ }
77
85
}
86
+ return tests ;
78
87
}
79
88
80
- // TODO: asc's callback is synchronous here. This might change.
81
- tests . forEach ( filename => {
82
- console . log ( colorsUtil . white ( "Testing compiler/" + filename ) + "\n" ) ;
89
+ // Runs a single test
90
+ function runTest ( basename ) {
91
+ console . log ( colorsUtil . white ( "Testing compiler/" + basename ) + "\n" ) ;
83
92
84
- const basename = filename . replace ( / \. t s $ / , "" ) ;
85
93
const configPath = path . join ( basedir , basename + ".json" ) ;
86
94
const config = fs . existsSync ( configPath )
87
95
? require ( configPath )
@@ -118,6 +126,7 @@ tests.forEach(filename => {
118
126
console . log ( "- " + colorsUtil . yellow ( "feature SKIPPED" ) + " (" + missing_features . join ( ", " ) + ")\n" ) ;
119
127
skippedTests . add ( basename ) ;
120
128
skippedMessages . set ( basename , "feature not enabled" ) ;
129
+ if ( cluster . isWorker ) process . send ( { cmd : "skipped" , message : skippedMessages . get ( basename ) } ) ;
121
130
return ;
122
131
}
123
132
}
@@ -129,11 +138,9 @@ tests.forEach(filename => {
129
138
130
139
var failed = false ;
131
140
132
- // TODO: also save stdout/stderr and diff it (-> expected failures)
133
-
134
141
// Build unoptimized
135
142
var cmd = [
136
- filename ,
143
+ basename + ".ts" ,
137
144
"--baseDir" , basedir ,
138
145
"--validate" ,
139
146
"--measure" ,
@@ -142,10 +149,7 @@ tests.forEach(filename => {
142
149
] ;
143
150
if ( asc_flags )
144
151
Array . prototype . push . apply ( cmd , asc_flags ) ;
145
- if ( args . createBinary )
146
- cmd . push ( "--binaryFile" , basename + ".untouched.wasm" ) ;
147
- else
148
- cmd . push ( "--binaryFile" , "temp.wasm" ) ;
152
+ cmd . push ( "--binaryFile" , basename + ".untouched.wasm" ) ;
149
153
asc . main ( cmd , {
150
154
stdout : stdout ,
151
155
stderr : stderr
@@ -214,7 +218,7 @@ tests.forEach(filename => {
214
218
215
219
// Build optimized
216
220
var cmd = [
217
- filename ,
221
+ basename + ".ts" ,
218
222
"--baseDir" , basedir ,
219
223
"--validate" ,
220
224
"--measure" ,
@@ -237,7 +241,7 @@ tests.forEach(filename => {
237
241
failedTests . add ( basename ) ;
238
242
return 1 ;
239
243
}
240
- let untouchedBuffer = fs . readFileSync ( path . join ( basedir , "temp .wasm") ) ;
244
+ let untouchedBuffer = fs . readFileSync ( path . join ( basedir , basename + ".untouched .wasm") ) ;
241
245
let optimizedBuffer = stdout . toBuffer ( ) ;
242
246
const gluePath = path . join ( basedir , basename + ".js" ) ;
243
247
var glue = { } ;
@@ -258,29 +262,11 @@ tests.forEach(filename => {
258
262
if ( failed ) return 1 ;
259
263
} ) ;
260
264
if ( v8_no_flags ) v8 . setFlagsFromString ( v8_no_flags ) ;
261
- } ) ;
262
-
263
- if ( skippedTests . size ) {
264
- console . log ( colorsUtil . yellow ( "WARNING: " ) + colorsUtil . white ( skippedTests . size + " compiler tests have been skipped:\n" ) ) ;
265
- skippedTests . forEach ( name => {
266
- var message = skippedMessages . has ( name ) ? colorsUtil . gray ( "[" + skippedMessages . get ( name ) + "]" ) : "" ;
267
- console . log ( " " + name + " " + message ) ;
268
- } ) ;
269
- console . log ( ) ;
270
- }
271
- if ( failedTests . size ) {
272
- process . exitCode = 1 ;
273
- console . log ( colorsUtil . red ( "ERROR: " ) + colorsUtil . white ( failedTests . size + " compiler tests had failures:\n" ) ) ;
274
- failedTests . forEach ( name => {
275
- var message = failedMessages . has ( name ) ? colorsUtil . gray ( "[" + failedMessages . get ( name ) + "]" ) : "" ;
276
- console . log ( " " + name + " " + message ) ;
277
- } ) ;
278
- console . log ( ) ;
279
- }
280
- if ( ! process . exitCode ) {
281
- console . log ( "[ " + colorsUtil . white ( "OK" ) + " ]" ) ;
265
+ if ( ! args . createBinary ) fs . unlink ( path . join ( basedir , basename + ".untouched.wasm" ) , err => { } ) ;
266
+ if ( cluster . isWorker ) process . send ( { cmd : "done" , failed : failed , message : failedMessages . get ( basename ) } ) ;
282
267
}
283
268
269
+ // Tests if instantiation of a module succeeds
284
270
function testInstantiate ( basename , binaryBuffer , name , glue ) {
285
271
var failed = false ;
286
272
try {
@@ -370,3 +356,94 @@ function testInstantiate(basename, binaryBuffer, name, glue) {
370
356
}
371
357
return false ;
372
358
}
359
+
360
+ // Evaluates the overall test result
361
+ function evaluateResult ( ) {
362
+ if ( skippedTests . size ) {
363
+ console . log ( colorsUtil . yellow ( "WARNING: " ) + colorsUtil . white ( skippedTests . size + " compiler tests have been skipped:\n" ) ) ;
364
+ skippedTests . forEach ( name => {
365
+ var message = skippedMessages . has ( name ) ? colorsUtil . gray ( "[" + skippedMessages . get ( name ) + "]" ) : "" ;
366
+ console . log ( " " + name + " " + message ) ;
367
+ } ) ;
368
+ console . log ( ) ;
369
+ }
370
+ if ( failedTests . size ) {
371
+ process . exitCode = 1 ;
372
+ console . log ( colorsUtil . red ( "ERROR: " ) + colorsUtil . white ( failedTests . size + " compiler tests had failures:\n" ) ) ;
373
+ failedTests . forEach ( name => {
374
+ var message = failedMessages . has ( name ) ? colorsUtil . gray ( "[" + failedMessages . get ( name ) + "]" ) : "" ;
375
+ console . log ( " " + name + " " + message ) ;
376
+ } ) ;
377
+ console . log ( ) ;
378
+ }
379
+ console . log ( "Time: " + ( Date . now ( ) - startTime ) + " ms\n" ) ;
380
+ if ( ! process . exitCode ) {
381
+ console . log ( "[ " + colorsUtil . white ( "OK" ) + " ]" ) ;
382
+ }
383
+ }
384
+
385
+ // Run tests in parallel if requested
386
+ if ( args . parallel && coreCount > 1 ) {
387
+ if ( cluster . isWorker ) {
388
+ colorsUtil . supported = true ;
389
+ process . on ( "message" , msg => {
390
+ if ( msg . cmd != "run" ) throw Error ( "invalid command: " + msg . cmd ) ;
391
+ try {
392
+ runTest ( msg . test ) ;
393
+ } catch ( e ) {
394
+ process . send ( { cmd : "done" , failed : true , message : e . message } ) ;
395
+ }
396
+ } ) ;
397
+ process . send ( { cmd : "ready" } ) ;
398
+ } else {
399
+ const tests = getTests ( ) ;
400
+ // const sizes = new Map();
401
+ // tests.forEach(name => sizes.set(name, fs.statSync(path.join(basedir, name + ".ts")).size));
402
+ // tests.sort((a, b) => sizes.get(b) - sizes.get(a));
403
+ const workers = [ ] ;
404
+ const current = [ ] ;
405
+ const outputs = [ ] ;
406
+ let numWorkers = Math . min ( coreCount - 1 , tests . length ) ;
407
+ console . log ( "Spawning " + numWorkers + " workers ..." ) ;
408
+ cluster . settings . silent = true ;
409
+ let index = 0 ;
410
+ for ( let i = 0 ; i < numWorkers ; ++ i ) {
411
+ let worker = cluster . fork ( ) ;
412
+ workers [ i ] = worker ;
413
+ current [ i ] = null ;
414
+ outputs [ i ] = [ ] ;
415
+ worker . process . stdout . on ( "data" , buf => outputs [ i ] . push ( buf ) ) ;
416
+ worker . process . stderr . on ( "data" , buf => outputs [ i ] . push ( buf ) ) ;
417
+ worker . on ( "message" , msg => {
418
+ if ( msg . cmd == "done" ) {
419
+ process . stdout . write ( Buffer . concat ( outputs [ i ] ) . toString ( ) ) ;
420
+ if ( msg . failed ) failedTests . add ( current [ i ] ) ;
421
+ if ( msg . message ) failedMessages . set ( current [ i ] , msg . message ) ;
422
+ } else if ( msg . cmd == "skipped" ) {
423
+ process . stdout . write ( Buffer . concat ( outputs [ i ] ) . toString ( ) ) ;
424
+ skippedTests . add ( current [ i ] ) ;
425
+ if ( msg . message ) skippedMessages . set ( current [ i ] , msg . message ) ;
426
+ } else if ( msg . cmd != "ready" ) {
427
+ throw Error ( "invalid command: " + msg . cmd ) ;
428
+ }
429
+ if ( index >= tests . length ) {
430
+ workers [ i ] = null ;
431
+ worker . kill ( ) ;
432
+ return ;
433
+ }
434
+ current [ i ] = tests [ index ++ ] ;
435
+ outputs [ i ] = [ ] ;
436
+ worker . send ( { cmd : "run" , test : current [ i ] } ) ;
437
+ } ) ;
438
+ worker . on ( "disconnect" , ( ) => {
439
+ if ( workers [ i ] ) throw Error ( "worker#" + i + " died unexpectedly" ) ;
440
+ if ( ! -- numWorkers ) evaluateResult ( ) ;
441
+ } ) ;
442
+ }
443
+ }
444
+
445
+ // Otherwise run tests sequentially
446
+ } else {
447
+ getTests ( ) . forEach ( runTest ) ;
448
+ evaluateResult ( ) ;
449
+ }
0 commit comments