1*c8dee2aaSAndroid Build Coastguard Worker/** 2*c8dee2aaSAndroid Build Coastguard Worker * Command line application to test GMS and unit tests with puppeteer. 3*c8dee2aaSAndroid Build Coastguard Worker * node run-wasm-gm-tests --js_file ../../out/wasm_gm_tests/wasm_gm_tests.js --wasm_file ../../out/wasm_gm_tests/wasm_gm_tests.wasm --known_hashes /tmp/gold2/tests/hashes.txt --output /tmp/gold2/tests/ --use_gpu --timeout 180 4*c8dee2aaSAndroid Build Coastguard Worker */ 5*c8dee2aaSAndroid Build Coastguard Workerconst puppeteer = require('puppeteer'); 6*c8dee2aaSAndroid Build Coastguard Workerconst express = require('express'); 7*c8dee2aaSAndroid Build Coastguard Workerconst path = require('path'); 8*c8dee2aaSAndroid Build Coastguard Workerconst bodyParser = require('body-parser'); 9*c8dee2aaSAndroid Build Coastguard Workerconst fs = require('fs'); 10*c8dee2aaSAndroid Build Coastguard Workerconst commandLineArgs = require('command-line-args'); 11*c8dee2aaSAndroid Build Coastguard Workerconst commandLineUsage = require('command-line-usage'); 12*c8dee2aaSAndroid Build Coastguard Worker 13*c8dee2aaSAndroid Build Coastguard Workerconst opts = [ 14*c8dee2aaSAndroid Build Coastguard Worker { 15*c8dee2aaSAndroid Build Coastguard Worker name: 'js_file', 16*c8dee2aaSAndroid Build Coastguard Worker typeLabel: '{underline file}', 17*c8dee2aaSAndroid Build Coastguard Worker description: '(required) The path to wasm_gm_tests.js.' 18*c8dee2aaSAndroid Build Coastguard Worker }, 19*c8dee2aaSAndroid Build Coastguard Worker { 20*c8dee2aaSAndroid Build Coastguard Worker name: 'wasm_file', 21*c8dee2aaSAndroid Build Coastguard Worker typeLabel: '{underline file}', 22*c8dee2aaSAndroid Build Coastguard Worker description: '(required) The path to wasm_gm_tests.wasm.' 23*c8dee2aaSAndroid Build Coastguard Worker }, 24*c8dee2aaSAndroid Build Coastguard Worker { 25*c8dee2aaSAndroid Build Coastguard Worker name: 'known_hashes', 26*c8dee2aaSAndroid Build Coastguard Worker typeLabel: '{underline file}', 27*c8dee2aaSAndroid Build Coastguard Worker description: '(required) The hashes that should not be written to disk.' 28*c8dee2aaSAndroid Build Coastguard Worker }, 29*c8dee2aaSAndroid Build Coastguard Worker { 30*c8dee2aaSAndroid Build Coastguard Worker name: 'output', 31*c8dee2aaSAndroid Build Coastguard Worker typeLabel: '{underline file}', 32*c8dee2aaSAndroid Build Coastguard Worker description: '(required) The directory to write the output JSON and images to.', 33*c8dee2aaSAndroid Build Coastguard Worker }, 34*c8dee2aaSAndroid Build Coastguard Worker { 35*c8dee2aaSAndroid Build Coastguard Worker name: 'resources', 36*c8dee2aaSAndroid Build Coastguard Worker typeLabel: '{underline file}', 37*c8dee2aaSAndroid Build Coastguard Worker description: '(required) The directory that test images are stored in.', 38*c8dee2aaSAndroid Build Coastguard Worker }, 39*c8dee2aaSAndroid Build Coastguard Worker { 40*c8dee2aaSAndroid Build Coastguard Worker name: 'use_gpu', 41*c8dee2aaSAndroid Build Coastguard Worker description: 'Whether we should run in non-headless mode with GPU.', 42*c8dee2aaSAndroid Build Coastguard Worker type: Boolean, 43*c8dee2aaSAndroid Build Coastguard Worker }, 44*c8dee2aaSAndroid Build Coastguard Worker { 45*c8dee2aaSAndroid Build Coastguard Worker name: 'enable_simd', 46*c8dee2aaSAndroid Build Coastguard Worker description: 'enable execution of wasm SIMD operations in chromium', 47*c8dee2aaSAndroid Build Coastguard Worker type: Boolean 48*c8dee2aaSAndroid Build Coastguard Worker }, 49*c8dee2aaSAndroid Build Coastguard Worker { 50*c8dee2aaSAndroid Build Coastguard Worker name: 'port', 51*c8dee2aaSAndroid Build Coastguard Worker description: 'The port number to use, defaults to 8081.', 52*c8dee2aaSAndroid Build Coastguard Worker type: Number, 53*c8dee2aaSAndroid Build Coastguard Worker }, 54*c8dee2aaSAndroid Build Coastguard Worker { 55*c8dee2aaSAndroid Build Coastguard Worker name: 'help', 56*c8dee2aaSAndroid Build Coastguard Worker alias: 'h', 57*c8dee2aaSAndroid Build Coastguard Worker type: Boolean, 58*c8dee2aaSAndroid Build Coastguard Worker description: 'Print this usage guide.' 59*c8dee2aaSAndroid Build Coastguard Worker }, 60*c8dee2aaSAndroid Build Coastguard Worker { 61*c8dee2aaSAndroid Build Coastguard Worker name: 'timeout', 62*c8dee2aaSAndroid Build Coastguard Worker description: 'Number of seconds to allow test to run.', 63*c8dee2aaSAndroid Build Coastguard Worker type: Number, 64*c8dee2aaSAndroid Build Coastguard Worker }, 65*c8dee2aaSAndroid Build Coastguard Worker { 66*c8dee2aaSAndroid Build Coastguard Worker name: 'manual_mode', 67*c8dee2aaSAndroid Build Coastguard Worker description: 'If set, tests will not run automatically.', 68*c8dee2aaSAndroid Build Coastguard Worker type: Boolean, 69*c8dee2aaSAndroid Build Coastguard Worker }, 70*c8dee2aaSAndroid Build Coastguard Worker { 71*c8dee2aaSAndroid Build Coastguard Worker name: 'batch_size', 72*c8dee2aaSAndroid Build Coastguard Worker description: 'Number of gms (or unit tests) to run in a batch. The main thread ' + 73*c8dee2aaSAndroid Build Coastguard Worker 'of the page is only unlocked between batches. Default: 50. Use 1 for debugging.', 74*c8dee2aaSAndroid Build Coastguard Worker type: Number, 75*c8dee2aaSAndroid Build Coastguard Worker } 76*c8dee2aaSAndroid Build Coastguard Worker]; 77*c8dee2aaSAndroid Build Coastguard Worker 78*c8dee2aaSAndroid Build Coastguard Workerconst usage = [ 79*c8dee2aaSAndroid Build Coastguard Worker { 80*c8dee2aaSAndroid Build Coastguard Worker header: 'Measuring correctness of Skia WASM code', 81*c8dee2aaSAndroid Build Coastguard Worker content: 'Command line application to capture images drawn from tests', 82*c8dee2aaSAndroid Build Coastguard Worker }, 83*c8dee2aaSAndroid Build Coastguard Worker { 84*c8dee2aaSAndroid Build Coastguard Worker header: 'Options', 85*c8dee2aaSAndroid Build Coastguard Worker optionList: opts, 86*c8dee2aaSAndroid Build Coastguard Worker }, 87*c8dee2aaSAndroid Build Coastguard Worker]; 88*c8dee2aaSAndroid Build Coastguard Worker 89*c8dee2aaSAndroid Build Coastguard Worker// Parse and validate flags. 90*c8dee2aaSAndroid Build Coastguard Workerconst options = commandLineArgs(opts); 91*c8dee2aaSAndroid Build Coastguard Worker 92*c8dee2aaSAndroid Build Coastguard Workerif (!options.port) { 93*c8dee2aaSAndroid Build Coastguard Worker options.port = 8081; 94*c8dee2aaSAndroid Build Coastguard Worker} 95*c8dee2aaSAndroid Build Coastguard Workerif (!options.timeout) { 96*c8dee2aaSAndroid Build Coastguard Worker options.timeout = 60; 97*c8dee2aaSAndroid Build Coastguard Worker} 98*c8dee2aaSAndroid Build Coastguard Workerif (!options.batch_size) { 99*c8dee2aaSAndroid Build Coastguard Worker options.batch_size = 50; 100*c8dee2aaSAndroid Build Coastguard Worker} 101*c8dee2aaSAndroid Build Coastguard Worker 102*c8dee2aaSAndroid Build Coastguard Workerif (options.help) { 103*c8dee2aaSAndroid Build Coastguard Worker console.log(commandLineUsage(usage)); 104*c8dee2aaSAndroid Build Coastguard Worker process.exit(0); 105*c8dee2aaSAndroid Build Coastguard Worker} 106*c8dee2aaSAndroid Build Coastguard Worker 107*c8dee2aaSAndroid Build Coastguard Workerif (!options.output) { 108*c8dee2aaSAndroid Build Coastguard Worker console.error('You must supply an output directory.'); 109*c8dee2aaSAndroid Build Coastguard Worker console.log(commandLineUsage(usage)); 110*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 111*c8dee2aaSAndroid Build Coastguard Worker} 112*c8dee2aaSAndroid Build Coastguard Worker 113*c8dee2aaSAndroid Build Coastguard Workerif (!options.js_file) { 114*c8dee2aaSAndroid Build Coastguard Worker console.error('You must supply path to wasm_gm_tests.js.'); 115*c8dee2aaSAndroid Build Coastguard Worker console.log(commandLineUsage(usage)); 116*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 117*c8dee2aaSAndroid Build Coastguard Worker} 118*c8dee2aaSAndroid Build Coastguard Worker 119*c8dee2aaSAndroid Build Coastguard Workerif (!options.wasm_file) { 120*c8dee2aaSAndroid Build Coastguard Worker console.error('You must supply path to wasm_gm_tests.wasm.'); 121*c8dee2aaSAndroid Build Coastguard Worker console.log(commandLineUsage(usage)); 122*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 123*c8dee2aaSAndroid Build Coastguard Worker} 124*c8dee2aaSAndroid Build Coastguard Worker 125*c8dee2aaSAndroid Build Coastguard Workerif (!options.known_hashes) { 126*c8dee2aaSAndroid Build Coastguard Worker console.error('You must supply path to known_hashes.txt'); 127*c8dee2aaSAndroid Build Coastguard Worker console.log(commandLineUsage(usage)); 128*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 129*c8dee2aaSAndroid Build Coastguard Worker} 130*c8dee2aaSAndroid Build Coastguard Worker 131*c8dee2aaSAndroid Build Coastguard Workerif (!options.resources) { 132*c8dee2aaSAndroid Build Coastguard Worker console.error('You must supply resources directory'); 133*c8dee2aaSAndroid Build Coastguard Worker console.log(commandLineUsage(usage)); 134*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 135*c8dee2aaSAndroid Build Coastguard Worker} 136*c8dee2aaSAndroid Build Coastguard Worker 137*c8dee2aaSAndroid Build Coastguard Workerconst resourceBaseDir = path.resolve(options.resources) 138*c8dee2aaSAndroid Build Coastguard Worker// This executes recursively and synchronously. 139*c8dee2aaSAndroid Build Coastguard Workerconst recursivelyListFiles = (dir) => { 140*c8dee2aaSAndroid Build Coastguard Worker const absolutePaths = []; 141*c8dee2aaSAndroid Build Coastguard Worker const files = fs.readdirSync(dir); 142*c8dee2aaSAndroid Build Coastguard Worker files.forEach((file) => { 143*c8dee2aaSAndroid Build Coastguard Worker const filepath = path.join(dir, file); 144*c8dee2aaSAndroid Build Coastguard Worker const stats = fs.statSync(filepath); 145*c8dee2aaSAndroid Build Coastguard Worker if (stats.isDirectory()) { 146*c8dee2aaSAndroid Build Coastguard Worker absolutePaths.push(...recursivelyListFiles(filepath)); 147*c8dee2aaSAndroid Build Coastguard Worker } else if (stats.isFile()) { 148*c8dee2aaSAndroid Build Coastguard Worker absolutePaths.push(path.relative(resourceBaseDir, filepath)); 149*c8dee2aaSAndroid Build Coastguard Worker } 150*c8dee2aaSAndroid Build Coastguard Worker }); 151*c8dee2aaSAndroid Build Coastguard Worker return absolutePaths; 152*c8dee2aaSAndroid Build Coastguard Worker}; 153*c8dee2aaSAndroid Build Coastguard Worker 154*c8dee2aaSAndroid Build Coastguard Workerconst resourceListing = recursivelyListFiles(options.resources); 155*c8dee2aaSAndroid Build Coastguard Workerconsole.log('Saw resources', resourceListing); 156*c8dee2aaSAndroid Build Coastguard Worker 157*c8dee2aaSAndroid Build Coastguard Workerconst driverHTML = fs.readFileSync('run-wasm-gm-tests.html', 'utf8'); 158*c8dee2aaSAndroid Build Coastguard Workerconst testJS = fs.readFileSync(options.js_file, 'utf8'); 159*c8dee2aaSAndroid Build Coastguard Workerconst testWASM = fs.readFileSync(options.wasm_file, 'binary'); 160*c8dee2aaSAndroid Build Coastguard Workerconst knownHashes = fs.readFileSync(options.known_hashes, 'utf8'); 161*c8dee2aaSAndroid Build Coastguard Worker 162*c8dee2aaSAndroid Build Coastguard Worker// This express webserver will serve the HTML file running the benchmark and any additional assets 163*c8dee2aaSAndroid Build Coastguard Worker// needed to run the tests. 164*c8dee2aaSAndroid Build Coastguard Workerconst app = express(); 165*c8dee2aaSAndroid Build Coastguard Workerapp.get('/', (req, res) => res.send(driverHTML)); 166*c8dee2aaSAndroid Build Coastguard Worker 167*c8dee2aaSAndroid Build Coastguard Workerapp.use('/static/resources/', express.static(resourceBaseDir)); 168*c8dee2aaSAndroid Build Coastguard Workerconsole.log('resources served from', resourceBaseDir); 169*c8dee2aaSAndroid Build Coastguard Worker 170*c8dee2aaSAndroid Build Coastguard Worker// This allows the server to receive POST requests of up to 10MB for image/png and read the body 171*c8dee2aaSAndroid Build Coastguard Worker// as raw bytes, housed in a buffer. 172*c8dee2aaSAndroid Build Coastguard Workerapp.use(bodyParser.raw({ type: 'image/png', limit: '10mb' })); 173*c8dee2aaSAndroid Build Coastguard Worker 174*c8dee2aaSAndroid Build Coastguard Workerapp.get('/static/hashes.txt', (req, res) => res.send(knownHashes)); 175*c8dee2aaSAndroid Build Coastguard Workerapp.get('/static/resource_listing.json', (req, res) => res.send(JSON.stringify(resourceListing))); 176*c8dee2aaSAndroid Build Coastguard Workerapp.get('/static/wasm_gm_tests.js', (req, res) => res.send(testJS)); 177*c8dee2aaSAndroid Build Coastguard Workerapp.get('/static/wasm_gm_tests.wasm', function(req, res) { 178*c8dee2aaSAndroid Build Coastguard Worker // Set the MIME type so it can be streamed efficiently. 179*c8dee2aaSAndroid Build Coastguard Worker res.type('application/wasm'); 180*c8dee2aaSAndroid Build Coastguard Worker res.send(new Buffer(testWASM, 'binary')); 181*c8dee2aaSAndroid Build Coastguard Worker}); 182*c8dee2aaSAndroid Build Coastguard Workerapp.post('/write_png', (req, res) => { 183*c8dee2aaSAndroid Build Coastguard Worker const md5 = req.header('X-MD5-Hash'); 184*c8dee2aaSAndroid Build Coastguard Worker if (!md5) { 185*c8dee2aaSAndroid Build Coastguard Worker res.sendStatus(400); 186*c8dee2aaSAndroid Build Coastguard Worker return; 187*c8dee2aaSAndroid Build Coastguard Worker } 188*c8dee2aaSAndroid Build Coastguard Worker const data = req.body; 189*c8dee2aaSAndroid Build Coastguard Worker const newFile = path.join(options.output, md5 + '.png'); 190*c8dee2aaSAndroid Build Coastguard Worker fs.writeFileSync(newFile, data, { 191*c8dee2aaSAndroid Build Coastguard Worker encoding: 'binary', 192*c8dee2aaSAndroid Build Coastguard Worker }); 193*c8dee2aaSAndroid Build Coastguard Worker res.sendStatus(200); 194*c8dee2aaSAndroid Build Coastguard Worker}); 195*c8dee2aaSAndroid Build Coastguard Worker 196*c8dee2aaSAndroid Build Coastguard Workerconst server = app.listen(options.port, () => console.log('- Local web server started.')); 197*c8dee2aaSAndroid Build Coastguard Worker 198*c8dee2aaSAndroid Build Coastguard Workerconst hash = options.use_gpu? '#gpu': '#cpu'; 199*c8dee2aaSAndroid Build Coastguard Workerconst targetURL = `http://localhost:${options.port}/${hash}`; 200*c8dee2aaSAndroid Build Coastguard Workerconst viewPort = {width: 1000, height: 1000}; 201*c8dee2aaSAndroid Build Coastguard Worker 202*c8dee2aaSAndroid Build Coastguard Worker// Drive chrome to load the web page from the server we have running. 203*c8dee2aaSAndroid Build Coastguard Workerasync function driveBrowser() { 204*c8dee2aaSAndroid Build Coastguard Worker console.log('- Launching chrome for ' + options.input); 205*c8dee2aaSAndroid Build Coastguard Worker const browser_args = [ 206*c8dee2aaSAndroid Build Coastguard Worker '--no-sandbox', 207*c8dee2aaSAndroid Build Coastguard Worker '--disable-setuid-sandbox', 208*c8dee2aaSAndroid Build Coastguard Worker '--window-size=' + viewPort.width + ',' + viewPort.height, 209*c8dee2aaSAndroid Build Coastguard Worker // The following two params allow Chrome to run at an unlimited fps. Note, if there is 210*c8dee2aaSAndroid Build Coastguard Worker // already a chrome instance running, these arguments will have NO EFFECT, as the existing 211*c8dee2aaSAndroid Build Coastguard Worker // Chrome instance will be used instead of puppeteer spinning up a new one. 212*c8dee2aaSAndroid Build Coastguard Worker '--disable-frame-rate-limit', 213*c8dee2aaSAndroid Build Coastguard Worker '--disable-gpu-vsync', 214*c8dee2aaSAndroid Build Coastguard Worker ]; 215*c8dee2aaSAndroid Build Coastguard Worker if (options.enable_simd) { 216*c8dee2aaSAndroid Build Coastguard Worker browser_args.push('--enable-features=WebAssemblySimd'); 217*c8dee2aaSAndroid Build Coastguard Worker } 218*c8dee2aaSAndroid Build Coastguard Worker if (options.use_gpu) { 219*c8dee2aaSAndroid Build Coastguard Worker browser_args.push('--ignore-gpu-blacklist'); 220*c8dee2aaSAndroid Build Coastguard Worker browser_args.push('--ignore-gpu-blocklist'); 221*c8dee2aaSAndroid Build Coastguard Worker browser_args.push('--enable-gpu-rasterization'); 222*c8dee2aaSAndroid Build Coastguard Worker } 223*c8dee2aaSAndroid Build Coastguard Worker const headless = !options.use_gpu; 224*c8dee2aaSAndroid Build Coastguard Worker console.log("Running with headless: " + headless + " args: " + browser_args); 225*c8dee2aaSAndroid Build Coastguard Worker let browser; 226*c8dee2aaSAndroid Build Coastguard Worker let page; 227*c8dee2aaSAndroid Build Coastguard Worker try { 228*c8dee2aaSAndroid Build Coastguard Worker browser = await puppeteer.launch({ 229*c8dee2aaSAndroid Build Coastguard Worker headless: headless, 230*c8dee2aaSAndroid Build Coastguard Worker args: browser_args, 231*c8dee2aaSAndroid Build Coastguard Worker executablePath: options.chromium_executable_path 232*c8dee2aaSAndroid Build Coastguard Worker }); 233*c8dee2aaSAndroid Build Coastguard Worker page = await browser.newPage(); 234*c8dee2aaSAndroid Build Coastguard Worker await page.setViewport(viewPort); 235*c8dee2aaSAndroid Build Coastguard Worker } catch (e) { 236*c8dee2aaSAndroid Build Coastguard Worker console.log('Could not open the browser.', e); 237*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 238*c8dee2aaSAndroid Build Coastguard Worker } 239*c8dee2aaSAndroid Build Coastguard Worker console.log("Loading " + targetURL); 240*c8dee2aaSAndroid Build Coastguard Worker let failed = []; 241*c8dee2aaSAndroid Build Coastguard Worker try { 242*c8dee2aaSAndroid Build Coastguard Worker await page.goto(targetURL, { 243*c8dee2aaSAndroid Build Coastguard Worker timeout: 60000, 244*c8dee2aaSAndroid Build Coastguard Worker waitUntil: 'networkidle0' 245*c8dee2aaSAndroid Build Coastguard Worker }); 246*c8dee2aaSAndroid Build Coastguard Worker 247*c8dee2aaSAndroid Build Coastguard Worker if (options.manual_mode) { 248*c8dee2aaSAndroid Build Coastguard Worker console.log('Manual mode detected. Will hang'); 249*c8dee2aaSAndroid Build Coastguard Worker // Wait a very long time, with the web server running. 250*c8dee2aaSAndroid Build Coastguard Worker await page.waitForFunction(`window._abort_manual_mode`, { 251*c8dee2aaSAndroid Build Coastguard Worker timeout: 1000000000, 252*c8dee2aaSAndroid Build Coastguard Worker polling: 1000, 253*c8dee2aaSAndroid Build Coastguard Worker }); 254*c8dee2aaSAndroid Build Coastguard Worker } 255*c8dee2aaSAndroid Build Coastguard Worker 256*c8dee2aaSAndroid Build Coastguard Worker // Page is mostly loaded, wait for test harness page to report itself ready. Some resources 257*c8dee2aaSAndroid Build Coastguard Worker // may still be loading. 258*c8dee2aaSAndroid Build Coastguard Worker console.log('Waiting 30s for test harness to be ready'); 259*c8dee2aaSAndroid Build Coastguard Worker await page.waitForFunction(`(window._testsReady === true) || window._error`, { 260*c8dee2aaSAndroid Build Coastguard Worker timeout: 30000, 261*c8dee2aaSAndroid Build Coastguard Worker }); 262*c8dee2aaSAndroid Build Coastguard Worker 263*c8dee2aaSAndroid Build Coastguard Worker const err = await page.evaluate('window._error'); 264*c8dee2aaSAndroid Build Coastguard Worker if (err) { 265*c8dee2aaSAndroid Build Coastguard Worker const log = await page.evaluate('window._log'); 266*c8dee2aaSAndroid Build Coastguard Worker console.info(log); 267*c8dee2aaSAndroid Build Coastguard Worker console.error(`ERROR: ${err}`); 268*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 269*c8dee2aaSAndroid Build Coastguard Worker } 270*c8dee2aaSAndroid Build Coastguard Worker 271*c8dee2aaSAndroid Build Coastguard Worker // There is a button with id #start_tests to click (this also makes manual debugging easier). 272*c8dee2aaSAndroid Build Coastguard Worker await page.click('#start_tests'); 273*c8dee2aaSAndroid Build Coastguard Worker 274*c8dee2aaSAndroid Build Coastguard Worker // Rather than wait a long time for things to finish, we send progress updates every 50 tests. 275*c8dee2aaSAndroid Build Coastguard Worker let batch = options.batch_size; 276*c8dee2aaSAndroid Build Coastguard Worker while (true) { 277*c8dee2aaSAndroid Build Coastguard Worker console.log(`Waiting ${options.timeout}s for ${options.batch_size} tests to complete`); 278*c8dee2aaSAndroid Build Coastguard Worker await page.waitForFunction(`(window._testsProgress >= ${batch}) || window._testsDone || window._error`, { 279*c8dee2aaSAndroid Build Coastguard Worker timeout: options.timeout*1000, 280*c8dee2aaSAndroid Build Coastguard Worker }); 281*c8dee2aaSAndroid Build Coastguard Worker const progress = await page.evaluate(() => { 282*c8dee2aaSAndroid Build Coastguard Worker return { 283*c8dee2aaSAndroid Build Coastguard Worker err: window._error, 284*c8dee2aaSAndroid Build Coastguard Worker done: window._testsDone, 285*c8dee2aaSAndroid Build Coastguard Worker count: window._testsProgress, 286*c8dee2aaSAndroid Build Coastguard Worker }; 287*c8dee2aaSAndroid Build Coastguard Worker }); 288*c8dee2aaSAndroid Build Coastguard Worker if (progress.err) { 289*c8dee2aaSAndroid Build Coastguard Worker const log = await page.evaluate('window._log'); 290*c8dee2aaSAndroid Build Coastguard Worker console.info(log); 291*c8dee2aaSAndroid Build Coastguard Worker console.error(`ERROR: ${progress.err}`); 292*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 293*c8dee2aaSAndroid Build Coastguard Worker } 294*c8dee2aaSAndroid Build Coastguard Worker if (progress.done) { 295*c8dee2aaSAndroid Build Coastguard Worker console.log(`Completed ${progress.count} tests. Finished.`); 296*c8dee2aaSAndroid Build Coastguard Worker break; 297*c8dee2aaSAndroid Build Coastguard Worker } 298*c8dee2aaSAndroid Build Coastguard Worker console.log(`In Progress; completed ${progress.count} tests.`) 299*c8dee2aaSAndroid Build Coastguard Worker batch = progress.count + options.batch_size; 300*c8dee2aaSAndroid Build Coastguard Worker } 301*c8dee2aaSAndroid Build Coastguard Worker const goldResults = await page.evaluate('window._results'); 302*c8dee2aaSAndroid Build Coastguard Worker failed = await(page.evaluate('window._failed')); 303*c8dee2aaSAndroid Build Coastguard Worker 304*c8dee2aaSAndroid Build Coastguard Worker const log = await page.evaluate('window._log'); 305*c8dee2aaSAndroid Build Coastguard Worker console.info(log); 306*c8dee2aaSAndroid Build Coastguard Worker 307*c8dee2aaSAndroid Build Coastguard Worker 308*c8dee2aaSAndroid Build Coastguard Worker const jsonFile = path.join(options.output, 'gold_results.json'); 309*c8dee2aaSAndroid Build Coastguard Worker fs.writeFileSync(jsonFile, JSON.stringify(goldResults)); 310*c8dee2aaSAndroid Build Coastguard Worker } catch(e) { 311*c8dee2aaSAndroid Build Coastguard Worker console.log('Timed out while loading, drawing, or writing to disk.', e); 312*c8dee2aaSAndroid Build Coastguard Worker if (page) { 313*c8dee2aaSAndroid Build Coastguard Worker const log = await page.evaluate('window._log'); 314*c8dee2aaSAndroid Build Coastguard Worker console.error(log); 315*c8dee2aaSAndroid Build Coastguard Worker } 316*c8dee2aaSAndroid Build Coastguard Worker await browser.close(); 317*c8dee2aaSAndroid Build Coastguard Worker await new Promise((resolve) => server.close(resolve)); 318*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 319*c8dee2aaSAndroid Build Coastguard Worker } 320*c8dee2aaSAndroid Build Coastguard Worker 321*c8dee2aaSAndroid Build Coastguard Worker await browser.close(); 322*c8dee2aaSAndroid Build Coastguard Worker await new Promise((resolve) => server.close(resolve)); 323*c8dee2aaSAndroid Build Coastguard Worker 324*c8dee2aaSAndroid Build Coastguard Worker if (failed.length > 0) { 325*c8dee2aaSAndroid Build Coastguard Worker console.error('Failed tests', failed); 326*c8dee2aaSAndroid Build Coastguard Worker process.exit(1); 327*c8dee2aaSAndroid Build Coastguard Worker } else { 328*c8dee2aaSAndroid Build Coastguard Worker process.exit(0); 329*c8dee2aaSAndroid Build Coastguard Worker } 330*c8dee2aaSAndroid Build Coastguard Worker} 331*c8dee2aaSAndroid Build Coastguard Worker 332*c8dee2aaSAndroid Build Coastguard WorkerdriveBrowser(); 333