@@ -9,6 +9,9 @@ const path = require("path");
99const os = require ( 'os' ) ;
1010const { Options, runTest} = require ( 'browser-ui-test' ) ;
1111
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+
1215function showHelp ( ) {
1316 console . log ( "rustdoc-js options:" ) ;
1417 console . log ( " --doc-folder [PATH] : location of the generated doc folder" ) ;
@@ -129,11 +132,59 @@ function char_printer(n_tests) {
129132 } ;
130133}
131134
132- /// Sort array by .file_name property
135+ // Sort array by .file_name property
133136function by_filename ( a , b ) {
134137 return a . file_name - b . file_name ;
135138}
136139
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+
137188async function main ( argv ) {
138189 let opts = parseOptions ( argv . slice ( 2 ) ) ;
139190 if ( opts === null ) {
@@ -144,7 +195,7 @@ async function main(argv) {
144195 let debug = false ;
145196 // Run tests in sequentially
146197 let headless = true ;
147- const options = new Options ( ) ;
198+ const framework_options = new Options ( ) ;
148199 try {
149200 // This is more convenient that setting fields one by one.
150201 let args = [
@@ -169,13 +220,12 @@ async function main(argv) {
169220 args . push ( "--executable-path" ) ;
170221 args . push ( opts [ "executable_path" ] ) ;
171222 }
172- options . parseArguments ( args ) ;
223+ framework_options . parseArguments ( args ) ;
173224 } catch ( error ) {
174225 console . error ( `invalid argument: ${ error } ` ) ;
175226 process . exit ( 1 ) ;
176227 }
177228
178- let failed = false ;
179229 let files ;
180230 if ( opts [ "files" ] . length === 0 ) {
181231 files = fs . readdirSync ( opts [ "tests_folder" ] ) ;
@@ -187,6 +237,9 @@ async function main(argv) {
187237 console . error ( "rustdoc-gui: No test selected" ) ;
188238 process . exit ( 2 ) ;
189239 }
240+ files . forEach ( ( file_name , index ) => {
241+ files [ index ] = path . join ( opts [ "tests_folder" ] , file_name ) ;
242+ } ) ;
190243 files . sort ( ) ;
191244
192245 if ( ! headless ) {
@@ -215,52 +268,29 @@ async function main(argv) {
215268 } ;
216269 process . on ( 'exit' , exitHandling ) ;
217270
218- const tests_queue = [ ] ;
219- let results = {
220- successful : [ ] ,
221- failed : [ ] ,
222- errored : [ ] ,
223- } ;
271+ const originalFilesLen = files . length ;
272+ let results = createEmptyResults ( ) ;
224273 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 ;
257286 }
258287 }
259- if ( tests_queue . length > 0 ) {
260- await Promise . all ( tests_queue ) ;
261- }
288+
262289 status_bar . finish ( ) ;
263290
291+ Array . prototype . push . apply ( results . failed , new_results . failed ) ;
292+ Array . prototype . push . apply ( results . errored , new_results . errored ) ;
293+
264294 // We don't need this listener anymore.
265295 process . removeListener ( "exit" , exitHandling ) ;
266296
@@ -287,7 +317,7 @@ async function main(argv) {
287317 } ) ;
288318 }
289319
290- if ( failed ) {
320+ if ( results . failed . length > 0 || results . errored . length > 0 ) {
291321 process . exit ( 1 ) ;
292322 }
293323}
0 commit comments