From ab5c593ab6446368315b0efe1d40ee92723d3cb7 Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Wed, 11 Nov 2026 15:55:12 +0400 Subject: [PATCH] Launch QA browsers on SwiftShader where there is no Metal The shared launcher always asked for ANGLE's Metal backend, which exists only on macOS; on CI's Linux runners WebGL failed and the Street Level gate never saw the app start. The default is now Metal on macOS and SwiftShader elsewhere; QA_ANGLE still overrides it. --- scripts/qa-strict-harness.mjs | 9 +++++++-- scripts/qa/browser.mjs | 10 +++++++++- scripts/qa/browser.test.mjs | 10 ++++++++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/scripts/qa-strict-harness.mjs b/scripts/qa-strict-harness.mjs index 3ca041030..59990074a 100644 --- a/scripts/qa-strict-harness.mjs +++ b/scripts/qa-strict-harness.mjs @@ -27,7 +27,12 @@ import { STRICT_TILE_HOSTS, } from './qa-map-policy.mjs'; import { createMissLoader, tileCacheFile } from './qa/tileCache.mjs'; -import { closeBrowser, diagnoseExit, exitAfterFlush } from './qa/browser.mjs'; +import { + closeBrowser, + diagnoseExit, + exitAfterFlush, + qaAngle, +} from './qa/browser.mjs'; import { DAYTIME, shiftTime } from './qa/time.mjs'; import { PLANESPOTTERS_EMPTY_BODY, @@ -78,7 +83,7 @@ export const RESERVED_PORTS = Object.freeze([ .split(',').map((port) => port.trim()).filter(Boolean), ]); /** Chrome flags every scenario's browser starts with (the GPU path). */ -export const CHROME_ARGS = Object.freeze([`--use-angle=${process.env.QA_ANGLE || 'metal'}`, '--enable-gpu']); +export const CHROME_ARGS = Object.freeze([`--use-angle=${qaAngle()}`, '--enable-gpu']); /** The values of --layers=a,b and --evidence=FILE (null when absent). */ export function strictArguments(argv = process.argv) { diff --git a/scripts/qa/browser.mjs b/scripts/qa/browser.mjs index cf4ceb528..549809767 100644 --- a/scripts/qa/browser.mjs +++ b/scripts/qa/browser.mjs @@ -1,6 +1,14 @@ import { processTree, readProcesses } from './process.mjs'; import puppeteer from 'puppeteer'; import { chromeExecutablePath } from '../qa-chrome.mjs'; +/** + * The ANGLE backend: QA_ANGLE, else Metal on macOS and SwiftShader elsewhere + * (CI's Linux runners have no GPU, and Metal does not exist there). + */ +export function qaAngle(env = process.env, platform = process.platform) { + return env.QA_ANGLE || (platform === 'darwin' ? 'metal' : 'swiftshader'); +} + /** Keep every scenario on the same headless GPU path. */ export function launchBrowser({ args = [], ...options } = {}) { return puppeteer.launch({ @@ -8,7 +16,7 @@ export function launchBrowser({ args = [], ...options } = {}) { ...options, headless: true, args: [ - `--use-angle=${process.env.QA_ANGLE || 'metal'}`, + `--use-angle=${qaAngle()}`, '--enable-gpu', ...args, ], diff --git a/scripts/qa/browser.test.mjs b/scripts/qa/browser.test.mjs index 9af359739..75bb2ec98 100644 --- a/scripts/qa/browser.test.mjs +++ b/scripts/qa/browser.test.mjs @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; import puppeteer from 'puppeteer'; -import { launchBrowser } from './browser.mjs'; +import { launchBrowser, qaAngle } from './browser.mjs'; test('scenario launch uses the selected ANGLE backend and stays headless', async (t) => { const prior = process.env.QA_ANGLE; @@ -35,5 +35,11 @@ test('scenario launch uses the selected ANGLE backend and stays headless', async assert.equal(options.protocolTimeout, 120000); delete process.env.QA_ANGLE; await launchBrowser(); - assert.equal(options.args[0], '--use-angle=metal'); + assert.equal(options.args[0], `--use-angle=${qaAngle({})}`); +}); + +test('the default backend is Metal on macOS and SwiftShader elsewhere', () => { + assert.equal(qaAngle({}, 'darwin'), 'metal'); + assert.equal(qaAngle({}, 'linux'), 'swiftshader'); + assert.equal(qaAngle({ QA_ANGLE: 'vulkan' }, 'linux'), 'vulkan'); });