Skip to content

Commit 49eccbb

Browse files
authored
Puppeteer E2E test: Multi-page in-browser parallelism (#25386)
* Puppeteer: Multi-page parallelism * Remove CI parallelism * Some fixes * Fix the 'Execution context was destroyed' error * Update logging * Restore CI parallelism in 2 threads * More exceptions * Oops * Add Mac's own exception list * Update exceptions * Update exceptions * Test headful on Mac * Test mutiple browsers instead of multiple pages on Mac * Fix * Fix * Fix * Fix * Revert * Try to increase networkTimeout * Increase CI threads to 4 * Should be fixed now * Further increase networkTimeout * Further increase networkTimeout * TEST: numPages: 8, networkTimeout: 1.5 * TEST: numPages: 8, networkTimeout: 5 * TEST: numPages: 4, networkTimeout: 1.5 * Return to 8-5 * Accidentally removed one exception * New exception
1 parent b64399b commit 49eccbb

File tree

2 files changed

+86
-18
lines changed

2 files changed

+86
-18
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ jobs:
5858
strategy:
5959
fail-fast: false
6060
matrix:
61-
os: [ windows-latest, ubuntu-latest ]
62-
CI: [ 0, 1, 2, 3, 4, 5, 6, 7 ]
61+
os: [ windows-latest, ubuntu-latest, macos-latest ]
62+
CI: [ 0, 1, 2, 3 ]
6363
env:
6464
CI: ${{ matrix.CI }}
6565
steps:

test/e2e/puppeteer.js

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,35 @@ import jimp from 'jimp';
77
import * as fs from 'fs/promises';
88
import fetch from 'node-fetch';
99

10+
class PromiseQueue {
11+
12+
constructor( func, ...args ) {
13+
14+
this.func = func.bind( this, ...args );
15+
this.promises = [];
16+
17+
}
18+
19+
add( ...args ) {
20+
21+
const promise = this.func( ...args );
22+
this.promises.push( promise );
23+
promise.then( () => this.promises.splice( this.promises.indexOf( promise ), 1 ) );
24+
25+
}
26+
27+
async waitForAll() {
28+
29+
while ( this.promises.length > 0 ) {
30+
31+
await Promise.all( this.promises );
32+
33+
}
34+
35+
}
36+
37+
}
38+
1039
/* CONFIG VARIABLES START */
1140

1241
const idleTime = 9; // 9 seconds - for how long there should be no network requests
@@ -42,6 +71,8 @@ const exceptionList = [
4271

4372
// Unknown
4473
// TODO: most of these can be fixed just by increasing idleTime and parseTime
74+
'webgl_animation_skinning_blending',
75+
'webgl_buffergeometry_glbufferattribute',
4576
'webgl_clipping_advanced',
4677
'webgl_lensflares',
4778
'webgl_lines_sphere',
@@ -53,8 +84,10 @@ const exceptionList = [
5384
'webgl_morphtargets_face',
5485
'webgl_nodes_materials_standard',
5586
'webgl_postprocessing_crossfade',
87+
'webgl_postprocessing_dof2',
5688
'webgl_raymarching_reflect',
5789
'webgl_renderer_pathtracer',
90+
'webgl_shadowmap',
5891
'webgl_shadowmap_progressive',
5992
'webgl_test_memory2',
6093
'webgl_tiled_forward'
@@ -78,12 +111,14 @@ const port = 1234;
78111
const pixelThreshold = 0.1; // threshold error in one pixel
79112
const maxDifferentPixels = 0.3; // at most 0.3% different pixels
80113

81-
const networkTimeout = 90; // 90 seconds, set to 0 to disable
82-
const renderTimeout = 4.5; // 4.5 seconds, set to 0 to disable
114+
const networkTimeout = 5; // 5 minutes, set to 0 to disable
115+
const renderTimeout = 5; // 5 seconds, set to 0 to disable
83116

84117
const numAttempts = 2; // perform 2 attempts before failing
85118

86-
const numCIJobs = 8; // GitHub Actions run the script in 8 threads
119+
const numPages = 8; // use 8 browser pages
120+
121+
const numCIJobs = 4; // GitHub Actions run the script in 4 threads
87122

88123
const width = 400;
89124
const height = 250;
@@ -177,21 +212,26 @@ async function main() {
177212
const injection = await fs.readFile( 'test/e2e/deterministic-injection.js', 'utf8' );
178213
const build = ( await fs.readFile( 'build/three.module.js', 'utf8' ) ).replace( /Math\.random\(\) \* 0xffffffff/g, 'Math._random() * 0xffffffff' );
179214

180-
/* Prepare page */
215+
/* Prepare pages */
181216

182217
const errorMessagesCache = [];
183218

184-
const page = ( await browser.pages() )[ 0 ];
185-
await preparePage( page, injection, build, errorMessagesCache );
219+
const pages = await browser.pages();
220+
while ( pages.length < numPages && pages.length < files.length ) pages.push( await browser.newPage() );
221+
222+
for ( const page of pages ) await preparePage( page, injection, build, errorMessagesCache );
186223

187224
/* Loop for each file */
188225

189226
const failedScreenshots = [];
190227

191-
for ( const file of files ) await makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot, file );
228+
const queue = new PromiseQueue( makeAttempt, pages, failedScreenshots, cleanPage, isMakeScreenshot );
229+
for ( const file of files ) queue.add( file );
230+
await queue.waitForAll();
192231

193232
/* Finish */
194233

234+
failedScreenshots.sort();
195235
const list = failedScreenshots.join( ' ' );
196236

197237
if ( isMakeScreenshot && failedScreenshots.length ) {
@@ -281,7 +321,7 @@ async function preparePage( page, injection, build, errorMessages ) {
281321
const args = await Promise.all( msg.args().map( async arg => {
282322
try {
283323
return await arg.executionContext().evaluate( arg => arg instanceof Error ? arg.message : arg, arg );
284-
} catch (e) { // Execution context might have been already destroyed
324+
} catch ( e ) { // Execution context might have been already destroyed
285325
return arg;
286326
}
287327
} ) );
@@ -293,9 +333,9 @@ async function preparePage( page, injection, build, errorMessages ) {
293333

294334
text = file + ': ' + text.replace( /\[\.WebGL-(.+?)\] /g, '' );
295335

296-
if ( errorMessages.includes( text ) ) {
336+
if ( text === `${ file }: JSHandle@error` ) {
297337

298-
return;
338+
text = `${ file }: Unknown error`;
299339

300340
}
301341

@@ -305,6 +345,12 @@ async function preparePage( page, injection, build, errorMessages ) {
305345

306346
}
307347

348+
if ( errorMessages.includes( text ) ) {
349+
350+
return;
351+
352+
}
353+
308354
errorMessages.push( text );
309355

310356
if ( type === 'warning' ) {
@@ -353,11 +399,31 @@ async function preparePage( page, injection, build, errorMessages ) {
353399

354400
}
355401

356-
async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot, file, attemptID = 0 ) {
402+
async function makeAttempt( pages, failedScreenshots, cleanPage, isMakeScreenshot, file, attemptID = 0 ) {
403+
404+
const page = await new Promise( ( resolve, reject ) => {
405+
406+
const interval = setInterval( () => {
407+
408+
for ( const page of pages ) {
409+
410+
if ( page.file === undefined ) {
411+
412+
page.file = file; // acquire lock
413+
clearInterval( interval );
414+
resolve( page );
415+
break;
416+
417+
}
418+
419+
}
420+
421+
}, 100 );
422+
423+
} );
357424

358425
try {
359426

360-
page.file = file;
361427
page.pageSize = 0;
362428
page.error = undefined;
363429

@@ -367,7 +433,7 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
367433

368434
await page.goto( `http://localhost:${ port }/examples/${ file }.html`, {
369435
waitUntil: 'networkidle0',
370-
timeout: networkTimeout * 1000
436+
timeout: networkTimeout * 60000
371437
} );
372438

373439
} catch ( e ) {
@@ -383,7 +449,7 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
383449
await page.evaluate( cleanPage );
384450

385451
await page.waitForNetworkIdle( {
386-
timeout: networkTimeout * 1000,
452+
timeout: networkTimeout * 60000,
387453
idleTime: idleTime * 1000
388454
} );
389455

@@ -431,7 +497,7 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
431497
432498
console.yellow( `Render timeout exceeded in file ${ file }` );
433499
434-
} */
500+
} */ // TODO: fix this
435501

436502
}
437503

@@ -507,12 +573,14 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
507573
} else {
508574

509575
console.yellow( `${ e }, another attempt...` );
510-
await makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot, file, attemptID + 1 );
576+
this.add( file, attemptID + 1 );
511577

512578
}
513579

514580
}
515581

582+
page.file = undefined; // release lock
583+
516584
}
517585

518586
function close( exitCode = 1 ) {

0 commit comments

Comments
 (0)