@@ -9,6 +9,9 @@ const path = require("path");
9
9
const os = require ( 'os' ) ;
10
10
const { Options, runTest} = require ( 'browser-ui-test' ) ;
11
11
12
+ // If a test fails or errors, we will retry it two more times in case it was a flaky failure.
13
+ const NB_RETRY = 3 ;
14
+
12
15
function showHelp ( ) {
13
16
console . log ( "rustdoc-js options:" ) ;
14
17
console . log ( " --doc-folder [PATH] : location of the generated doc folder" ) ;
@@ -129,11 +132,59 @@ function char_printer(n_tests) {
129
132
} ;
130
133
}
131
134
132
- /// Sort array by .file_name property
135
+ // Sort array by .file_name property
133
136
function by_filename ( a , b ) {
134
137
return a . file_name - b . file_name ;
135
138
}
136
139
140
+ async function runTests ( opts , framework_options , files , results , status_bar , showTestFailures ) {
141
+ const tests_queue = [ ] ;
142
+
143
+ for ( const testPath of files ) {
144
+ const callback = runTest ( testPath , framework_options )
145
+ . then ( out => {
146
+ const [ output , nb_failures ] = out ;
147
+ results [ nb_failures === 0 ? "successful" : "failed" ] . push ( {
148
+ file_name : testPath ,
149
+ output : output ,
150
+ } ) ;
151
+ if ( nb_failures === 0 ) {
152
+ status_bar . successful ( ) ;
153
+ } else if ( showTestFailures ) {
154
+ status_bar . erroneous ( ) ;
155
+ }
156
+ } )
157
+ . catch ( err => {
158
+ results . errored . push ( {
159
+ file_name : testPath ,
160
+ output : err ,
161
+ } ) ;
162
+ if ( showTestFailures ) {
163
+ status_bar . erroneous ( ) ;
164
+ }
165
+ } )
166
+ . finally ( ( ) => {
167
+ // We now remove the promise from the tests_queue.
168
+ tests_queue . splice ( tests_queue . indexOf ( callback ) , 1 ) ;
169
+ } ) ;
170
+ tests_queue . push ( callback ) ;
171
+ if ( opts [ "jobs" ] > 0 && tests_queue . length >= opts [ "jobs" ] ) {
172
+ await Promise . race ( tests_queue ) ;
173
+ }
174
+ }
175
+ if ( tests_queue . length > 0 ) {
176
+ await Promise . all ( tests_queue ) ;
177
+ }
178
+ }
179
+
180
+ function createEmptyResults ( ) {
181
+ return {
182
+ successful : [ ] ,
183
+ failed : [ ] ,
184
+ errored : [ ] ,
185
+ } ;
186
+ }
187
+
137
188
async function main ( argv ) {
138
189
let opts = parseOptions ( argv . slice ( 2 ) ) ;
139
190
if ( opts === null ) {
@@ -144,7 +195,7 @@ async function main(argv) {
144
195
let debug = false ;
145
196
// Run tests in sequentially
146
197
let headless = true ;
147
- const options = new Options ( ) ;
198
+ const framework_options = new Options ( ) ;
148
199
try {
149
200
// This is more convenient that setting fields one by one.
150
201
let args = [
@@ -169,13 +220,12 @@ async function main(argv) {
169
220
args . push ( "--executable-path" ) ;
170
221
args . push ( opts [ "executable_path" ] ) ;
171
222
}
172
- options . parseArguments ( args ) ;
223
+ framework_options . parseArguments ( args ) ;
173
224
} catch ( error ) {
174
225
console . error ( `invalid argument: ${ error } ` ) ;
175
226
process . exit ( 1 ) ;
176
227
}
177
228
178
- let failed = false ;
179
229
let files ;
180
230
if ( opts [ "files" ] . length === 0 ) {
181
231
files = fs . readdirSync ( opts [ "tests_folder" ] ) ;
@@ -187,6 +237,9 @@ async function main(argv) {
187
237
console . error ( "rustdoc-gui: No test selected" ) ;
188
238
process . exit ( 2 ) ;
189
239
}
240
+ files . forEach ( ( file_name , index ) => {
241
+ files [ index ] = path . join ( opts [ "tests_folder" ] , file_name ) ;
242
+ } ) ;
190
243
files . sort ( ) ;
191
244
192
245
if ( ! headless ) {
@@ -215,52 +268,29 @@ async function main(argv) {
215
268
} ;
216
269
process . on ( 'exit' , exitHandling ) ;
217
270
218
- const tests_queue = [ ] ;
219
- let results = {
220
- successful : [ ] ,
221
- failed : [ ] ,
222
- errored : [ ] ,
223
- } ;
271
+ const originalFilesLen = files . length ;
272
+ let results = createEmptyResults ( ) ;
224
273
const status_bar = char_printer ( files . length ) ;
225
- for ( let i = 0 ; i < files . length ; ++ i ) {
226
- const file_name = files [ i ] ;
227
- const testPath = path . join ( opts [ "tests_folder" ] , file_name ) ;
228
- const callback = runTest ( testPath , options )
229
- . then ( out => {
230
- const [ output , nb_failures ] = out ;
231
- results [ nb_failures === 0 ? "successful" : "failed" ] . push ( {
232
- file_name : testPath ,
233
- output : output ,
234
- } ) ;
235
- if ( nb_failures > 0 ) {
236
- status_bar . erroneous ( ) ;
237
- failed = true ;
238
- } else {
239
- status_bar . successful ( ) ;
240
- }
241
- } )
242
- . catch ( err => {
243
- results . errored . push ( {
244
- file_name : testPath + file_name ,
245
- output : err ,
246
- } ) ;
247
- status_bar . erroneous ( ) ;
248
- failed = true ;
249
- } )
250
- . finally ( ( ) => {
251
- // We now remove the promise from the tests_queue.
252
- tests_queue . splice ( tests_queue . indexOf ( callback ) , 1 ) ;
253
- } ) ;
254
- tests_queue . push ( callback ) ;
255
- if ( opts [ "jobs" ] > 0 && tests_queue . length >= opts [ "jobs" ] ) {
256
- await Promise . race ( tests_queue ) ;
274
+
275
+ let new_results ;
276
+ for ( let it = 0 ; it < NB_RETRY && files . length > 0 ; ++ it ) {
277
+ new_results = createEmptyResults ( ) ;
278
+ await runTests ( opts , framework_options , files , new_results , status_bar , it + 1 >= NB_RETRY ) ;
279
+ Array . prototype . push . apply ( results . successful , new_results . successful ) ;
280
+ // We generate the new list of files with the previously failing tests.
281
+ files = Array . prototype . concat ( new_results . failed , new_results . errored ) ;
282
+ if ( files . length > originalFilesLen / 2 ) {
283
+ // If we have too many failing tests, it's very likely not flaky failures anymore so
284
+ // no need to retry.
285
+ break ;
257
286
}
258
287
}
259
- if ( tests_queue . length > 0 ) {
260
- await Promise . all ( tests_queue ) ;
261
- }
288
+
262
289
status_bar . finish ( ) ;
263
290
291
+ Array . prototype . push . apply ( results . failed , new_results . failed ) ;
292
+ Array . prototype . push . apply ( results . errored , new_results . errored ) ;
293
+
264
294
// We don't need this listener anymore.
265
295
process . removeListener ( "exit" , exitHandling ) ;
266
296
@@ -287,7 +317,7 @@ async function main(argv) {
287
317
} ) ;
288
318
}
289
319
290
- if ( failed ) {
320
+ if ( results . failed . length > 0 || results . errored . length > 0 ) {
291
321
process . exit ( 1 ) ;
292
322
}
293
323
}
0 commit comments