mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
Stop the strict acceptance from waiting for itself
A scenario whose body ran past about 19 s idled until 45 s after its start, and twice one never exited and hit the runner's 300 s timeout after it had passed: something of Chrome outlived Browser.close and kept its stdout and stderr open. - scripts/qa/browser.mjs closes Chrome within 5 s, kills its process group and closes its pipes; the harness then finishes and exits with the scenario's code. QA_EXIT_DIAGNOSTICS=1 keeps the old ending and prints the open handles and Chrome's process tree 5 s after finish. - Every strict page starts with its clock at the 18:30 UTC nearest to the real time, so Navara's sun is up over the test cities whenever the acceptance runs; a scenario can ask for its own instant or the real clock (Wildlife does, for its freshness fixtures). - The runner records a scenario that passed but did not exit as hung, kills the whole process group on a timeout, runs a failed scenario once more and reports one that passes then as flaky, and appends each run to --history=FILE; scripts/report-acceptance-flake.mjs reads it.
This commit is contained in:
1 parent
a913e5466e
commit
ed6ae54ef6
19 files changed
+1039
-104
No files matched your search
@@ -0,0 +1,73 @@
|
||||
import { processTree, readProcesses } from './process.mjs';
|
||||
|
||||
/** Bound shutdown even when Chrome leaves descendants holding its pipes. */
|
||||
export async function closeBrowser(browser, { deadlineMs = 5000 } = {}) {
|
||||
const start = performance.now();
|
||||
let child;
|
||||
let closed = false;
|
||||
let killed = false;
|
||||
let timer;
|
||||
try {
|
||||
child = browser.process();
|
||||
} catch {}
|
||||
try {
|
||||
closed = await Promise.race([
|
||||
Promise.resolve()
|
||||
.then(() => browser.close())
|
||||
.then(
|
||||
() => true,
|
||||
() => false,
|
||||
),
|
||||
new Promise((resolve) => {
|
||||
timer = setTimeout(() => resolve(false), deadlineMs);
|
||||
}),
|
||||
]);
|
||||
} catch {}
|
||||
clearTimeout(timer);
|
||||
if (process.env.QA_EXIT_DIAGNOSTICS !== '1') {
|
||||
try {
|
||||
if (Number.isInteger(child?.pid) && child.pid > 0) {
|
||||
// The group can outlive its leader, so probe the group itself.
|
||||
process.kill(-child.pid, 0);
|
||||
process.kill(-child.pid, 'SIGKILL');
|
||||
killed = true;
|
||||
}
|
||||
} catch {}
|
||||
for (const name of ['stdout', 'stderr']) {
|
||||
try {
|
||||
child?.[name]?.destroy();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
return { closed, killed, ms: performance.now() - start };
|
||||
}
|
||||
|
||||
export async function exitAfterFlush(code = process.exitCode ?? 0) {
|
||||
process.exitCode = code;
|
||||
if (process.env.QA_EXIT_DIAGNOSTICS === '1') return;
|
||||
await Promise.all(
|
||||
[process.stdout, process.stderr].map(
|
||||
(stream) =>
|
||||
new Promise((resolve) => {
|
||||
if (stream.destroyed) return resolve();
|
||||
stream.write('', resolve);
|
||||
}),
|
||||
),
|
||||
);
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
export function diagnoseExit(pids) {
|
||||
if (process.env.QA_EXIT_DIAGNOSTICS !== '1') return;
|
||||
setTimeout(() => {
|
||||
console.error('QA EXIT resources', process.getActiveResourcesInfo());
|
||||
const processes = readProcesses();
|
||||
console.error('QA EXIT Chrome: pid ppid pgid elapsed command');
|
||||
for (const pid of pids) {
|
||||
const rows = processTree(pid, processes);
|
||||
console.error(`Chrome ${pid}:`);
|
||||
for (const row of rows) console.error(row.line);
|
||||
if (!rows.length) console.error('(no remaining processes)');
|
||||
}
|
||||
}, 5000).unref();
|
||||
}
|
||||
Reference in new issue
Block a user