8
8
var path = require ( 'path' ) ;
9
9
var glob = require ( 'glob' ) ;
10
10
var child_process = require ( 'child_process' ) ;
11
+ var fs = require ( 'fs-extra' ) ;
11
12
12
13
// Create a base for the output file
13
14
var originalMochaFile = process . env [ 'MOCHA_FILE' ] ;
14
15
var mochaFile = originalMochaFile || './test-results.xml' ;
15
16
var mochaBaseFile = path . join ( path . dirname ( mochaFile ) , path . basename ( mochaFile , '.xml' ) ) ;
16
17
var mochaFileExt = '.xml' ;
18
+ var groupCount = 4 ;
19
+
20
+ function gatherArgs ( extraArgs , file ) {
21
+ return [
22
+ file ,
23
+ '--require=out/test/unittests.js' ,
24
+ '--exclude=out/**/*.jsx' ,
25
+ '--reporter=mocha-multi-reporters' ,
26
+ '--reporter-option=configFile=build/.mocha-multi-reporters.config' ,
27
+ '--ui=tdd' ,
28
+ '--recursive' ,
29
+ '--colors' ,
30
+ '--exit' ,
31
+ '--timeout=180000' ,
32
+ ...extraArgs
33
+ ] ;
34
+ }
35
+
36
+ async function generateGroups ( files ) {
37
+ // Go through each file putting it into a bucket. Each bucket will attempt to
38
+ // have equal size
39
+
40
+ // Start with largest files first (sort by size)
41
+ var stats = await Promise . all ( files . map ( ( f ) => fs . stat ( f ) ) ) ;
42
+ var filesWithSize = files . map ( ( f , i ) => {
43
+ return {
44
+ file : f ,
45
+ size : stats [ i ] . size
46
+ } ;
47
+ } ) ;
48
+ var sorted = filesWithSize . sort ( ( a , b ) => b . size - a . size ) ;
49
+
50
+ // Generate buckets that try to hold the largest file first
51
+ var buckets = new Array ( groupCount ) . fill ( ) . map ( ( _ , i ) => {
52
+ return {
53
+ index : i ,
54
+ totalSize : 0 ,
55
+ files : [ ]
56
+ } ;
57
+ } ) ;
58
+ var lowestBucket = buckets [ 0 ] ;
59
+ sorted . forEach ( ( fs ) => {
60
+ buckets [ lowestBucket . index ] . totalSize += fs . size ;
61
+ buckets [ lowestBucket . index ] . files . push ( fs . file ) ;
62
+ lowestBucket = buckets . find ( ( b ) => b . totalSize < lowestBucket . totalSize ) || lowestBucket ;
63
+ } ) ;
64
+
65
+ // Return these groups of files
66
+ return buckets . map ( ( b ) => b . files ) ;
67
+ }
68
+
69
+ async function runIndividualTest ( extraArgs , file , index ) {
70
+ var subMochaFile = `${ mochaBaseFile } _${ index } _${ path . basename ( file ) } ${ mochaFileExt } ` ;
71
+ process . env [ 'MOCHA_FILE' ] = subMochaFile ;
72
+ var args = gatherArgs ( extraArgs , file ) ;
73
+ console . log ( `Running functional test for file ${ file } ...` ) ;
74
+ var exitCode = await new Promise ( ( resolve ) => {
75
+ // Spawn the sub node process
76
+ var proc = child_process . fork ( './node_modules/mocha/bin/_mocha' , args ) ;
77
+ proc . on ( 'exit' , resolve ) ;
78
+ } ) ;
79
+
80
+ // If failed keep track
81
+ if ( exitCode !== 0 ) {
82
+ console . log ( `Functional tests for ${ file } failed.` ) ;
83
+ } else {
84
+ console . log ( `Functional test for ${ file } succeeded` ) ;
85
+ }
86
+
87
+ return exitCode ;
88
+ }
17
89
18
90
// Wrap async code in a function so can wait till done
19
91
async function main ( ) {
20
92
console . log ( 'Globbing files for functional tests' ) ;
21
93
22
94
// Glob all of the files that we usually send to mocha as a group (see mocha.functional.opts.xml)
23
95
var files = await new Promise ( ( resolve , reject ) => {
24
- glob ( './out/test/**/*.functional.test.js' , ( ex , res ) => {
96
+ glob ( './out/test/datascience/ **/*.functional.test.js' , ( ex , res ) => {
25
97
if ( ex ) {
26
98
reject ( ex ) ;
27
99
} else {
@@ -30,38 +102,42 @@ async function main() {
30
102
} ) ;
31
103
} ) ;
32
104
105
+ // Figure out what group is running (should be something like --group1, --group2 etc.)
106
+ var groupArgIndex = process . argv . findIndex ( ( a ) => a . includes ( '--group' ) ) ;
107
+ var groupIndex = groupArgIndex >= 0 ? parseInt ( process . argv [ groupArgIndex ] . slice ( 7 ) , 10 ) - 1 : - 1 ;
108
+
109
+ // Generate 4 groups based on sorting by size
110
+ var groups = await generateGroups ( files ) ;
111
+ files = groupIndex >= 0 ? groups [ groupIndex ] : files ;
112
+ console . log ( `Running for group ${ groupIndex } ` ) ;
113
+
114
+ // Extract any extra args for the individual mocha processes
115
+ var extraArgs =
116
+ groupIndex >= 0 && process . argv . length > 3
117
+ ? process . argv . slice ( 3 )
118
+ : process . argv . length > 2
119
+ ? process . argv . slice ( 2 )
120
+ : [ ] ;
121
+
33
122
// Iterate over them, running mocha on each
34
123
var returnCode = 0 ;
35
124
36
- // Go through each one at a time
125
+ // Start timing now (don't care about glob time)
126
+ var startTime = Date . now ( ) ;
127
+
128
+ // Run all of the tests (in parallel or sync based on env)
37
129
try {
38
- for ( var index = 0 ; index < files . length ; index += 1 ) {
39
- // Each run with a file will expect a $MOCHA_FILE$ variable. Generate one for each
40
- // Note: this index is used as a pattern when setting mocha file in the test_phases.yml
41
- var subMochaFile = `${ mochaBaseFile } _${ index } _${ path . basename ( files [ index ] ) } ${ mochaFileExt } ` ;
42
- process . env [ 'MOCHA_FILE' ] = subMochaFile ;
43
- var exitCode = await new Promise ( ( resolve ) => {
44
- // Spawn the sub node process
45
- var proc = child_process . fork ( './node_modules/mocha/bin/_mocha' , [
46
- files [ index ] ,
47
- '--require=out/test/unittests.js' ,
48
- '--exclude=out/**/*.jsx' ,
49
- '--reporter=mocha-multi-reporters' ,
50
- '--reporter-option=configFile=build/.mocha-multi-reporters.config' ,
51
- '--ui=tdd' ,
52
- '--recursive' ,
53
- '--colors' ,
54
- '--exit' ,
55
- '--timeout=180000'
56
- ] ) ;
57
- proc . on ( 'exit' , resolve ) ;
58
- } ) ;
59
-
60
- // If failed keep track
61
- if ( exitCode !== 0 ) {
62
- console . log ( `Functional tests for ${ files [ index ] } failed.` ) ;
63
- returnCode = exitCode ;
130
+ if ( process . env . VSCODE_PYTHON_FORCE_TEST_SYNC ) {
131
+ for ( var i = 0 ; i < files . length ; i += 1 ) {
132
+ // Synchronous, one at a time
133
+ returnCode = returnCode | ( await runIndividualTest ( extraArgs , files [ i ] , i ) ) ;
64
134
}
135
+ } else {
136
+ // Parallel, all at once
137
+ const returnCodes = await Promise . all ( files . map ( runIndividualTest . bind ( undefined , extraArgs ) ) ) ;
138
+
139
+ // Or all of the codes together
140
+ returnCode = returnCodes . reduce ( ( p , c ) => p | c ) ;
65
141
}
66
142
} catch ( ex ) {
67
143
console . log ( `Functional tests run failure: ${ ex } .` ) ;
@@ -73,8 +149,10 @@ async function main() {
73
149
process . env [ 'MOCHA_FILE' ] = originalMochaFile ;
74
150
}
75
151
76
- // Indicate error code
77
- console . log ( `Functional test run result: ${ returnCode } ` ) ;
152
+ var endTime = Date . now ( ) ;
153
+
154
+ // Indicate error code and total time of the run
155
+ console . log ( `Functional test run result: ${ returnCode } after ${ ( endTime - startTime ) / 1_000 } seconds` ) ;
78
156
process . exit ( returnCode ) ;
79
157
}
80
158
0 commit comments