From 0abcbaf66dace852b9f1a7e64b6bbffcdb256623 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 1 Mar 2025 13:26:05 -0800 Subject: [PATCH 01/47] refactor diagnostic for safer teardown --- packages/diagnostic/server/bun/fetch.js | 8 ++-- .../diagnostic/server/bun/socket-handler.js | 16 ++----- packages/diagnostic/server/bun/watch.js | 16 +++++-- packages/diagnostic/server/default-setup.js | 7 ++- packages/diagnostic/server/index.js | 43 ++++++++++++++++--- .../diagnostic/server/reporters/default.js | 7 ++- packages/diagnostic/server/utils/get-flags.js | 2 +- 7 files changed, 70 insertions(+), 29 deletions(-) diff --git a/packages/diagnostic/server/bun/fetch.js b/packages/diagnostic/server/bun/fetch.js index c8421eb4dd7..eb97b75848a 100644 --- a/packages/diagnostic/server/bun/fetch.js +++ b/packages/diagnostic/server/bun/fetch.js @@ -31,11 +31,13 @@ export function handleBunFetch(config, state, req, server) { if (INDEX_PATHS.includes(url.pathname)) { if (bId && wId) { // serve test index.html - if (config.entry.indexOf('?')) { - config._realEntry = config.entry.substr(0, config.entry.indexOf('?')); + if (!config._realEntry && config.entry.indexOf('?')) { + config._realEntry = path.join(process.cwd(), config.entry.substr(0, config.entry.indexOf('?'))); } debug(`Serving entry ${config._realEntry} for browser ${bId} window ${wId}`); - return new Response(Bun.file(config._realEntry)); + + const asset = Bun.file(config._realEntry); + return new Response(asset); } const _bId = bId ?? state.lastBowserId ?? state.browserId; const _wId = wId ?? state.lastWindowId ?? state.windowId; diff --git a/packages/diagnostic/server/bun/socket-handler.js b/packages/diagnostic/server/bun/socket-handler.js index 4f57799b95c..c161eecced8 100644 --- a/packages/diagnostic/server/bun/socket-handler.js +++ b/packages/diagnostic/server/bun/socket-handler.js @@ -7,7 +7,7 @@ import { watchAssets } from './watch.js'; export function buildHandler(config, state) { const Connections = new Set(); if (config.serve && !config.noWatch) { - watchAssets(config.assets, () => { + watchAssets(state, config.assets, () => { Connections.forEach((ws) => { ws.send(JSON.stringify({ name: 'reload' })); }); @@ -18,7 +18,8 @@ export function buildHandler(config, state) { perMessageDeflate: true, async message(ws, message) { const msg = JSON.parse(message); - msg.launcher = state.browsers.get(msg.browserId).launcher; + msg.launcher = state.browsers.get(msg.browserId)?.launcher ?? ''; + info(`${chalk.green('āž”')} [${chalk.cyan(msg.browserId)}/${chalk.cyan(msg.windowId)}] ${chalk.green(msg.name)}`); switch (msg.name) { @@ -53,16 +54,7 @@ export function buildHandler(config, state) { debug(`${chalk.green('āœ… [All Complete]')} ${chalk.yellow('@' + sinceStart())}`); if (!config.serve) { - state.browsers.forEach((browser) => { - browser.proc.kill(); - browser.proc.unref(); - }); - state.server.stop(); - if (config.cleanup) { - debug(`Running configured cleanup hook`); - await config.cleanup(); - debug(`Configured cleanup hook completed`); - } + await state.safeCleanup(); debug(`\n\nExiting with code ${exitCode}`); // 1. We expect all cleanup to have happened after // config.cleanup(), so exiting here should be safe. diff --git a/packages/diagnostic/server/bun/watch.js b/packages/diagnostic/server/bun/watch.js index e6be7cc4cc0..6bb61a623e3 100644 --- a/packages/diagnostic/server/bun/watch.js +++ b/packages/diagnostic/server/bun/watch.js @@ -1,6 +1,10 @@ import { watch } from 'fs'; -export function addCloseHandler(cb) { +export function addCloseHandler(state, cb) { + state.closeHandlers.push(createCloseHandler(cb)); +} + +function createCloseHandler(cb) { let executed = false; process.on('SIGINT', () => { @@ -26,14 +30,20 @@ export function addCloseHandler(cb) { executed = true; cb(); }); + + return () => { + if (executed) return; + executed = true; + cb(); + }; } -export function watchAssets(directory, onAssetChange) { +export function watchAssets(state, directory, onAssetChange) { const watcher = watch(directory, { recursive: true }, (event, filename) => { onAssetChange(event, filename); }); - addCloseHandler(() => { + addCloseHandler(state, () => { watcher.close(); }); } diff --git a/packages/diagnostic/server/default-setup.js b/packages/diagnostic/server/default-setup.js index e0ac49ce1f6..589be7b8266 100644 --- a/packages/diagnostic/server/default-setup.js +++ b/packages/diagnostic/server/default-setup.js @@ -3,7 +3,7 @@ import fs from 'fs'; import path from 'path'; import { getBrowser, recommendedArgs } from './browsers/index.js'; -import launch from './index.js'; +import { launch } from './index.js'; import DefaultReporter from './reporters/default.js'; import { getFlags } from './utils/get-flags.js'; @@ -79,6 +79,11 @@ export default async function launchDefault(overrides = {}) { debug: overrides.debug ?? false, headless: overrides.headless ?? false, useExisting: overrides.useExisting ?? false, + protocol: overrides.protocol ?? 'http', + key: overrides.key ?? null, + cert: overrides.cert ?? null, + hostname: overrides.hostname ?? 'localhost', + port: overrides.port ?? null, entry: overrides.entry ?? `./dist-test/tests/index.html?${TEST_PAGE_FLAGS.join('&')}`, assets: overrides.assets ?? './dist-test', diff --git a/packages/diagnostic/server/index.js b/packages/diagnostic/server/index.js index b75e3bcd5a4..1b00958933d 100644 --- a/packages/diagnostic/server/index.js +++ b/packages/diagnostic/server/index.js @@ -5,11 +5,12 @@ import { launchBrowsers } from './bun/launch-browser.js'; import { buildHandler } from './bun/socket-handler.js'; import { debug, error, print } from './utils/debug.js'; import { getPort } from './utils/port.js'; +import { addCloseHandler } from './bun/watch.js'; /** @type {import('bun-types')} */ const isBun = typeof Bun !== 'undefined'; -export default async function launch(config) { +export async function launch(config) { if (isBun) { debug(`Bun detected, using Bun.serve()`); @@ -34,6 +35,18 @@ export default async function launch(config) { browsers: new Map(), completed: 0, expected: config.parallel ?? 1, + closeHandlers: [], + }; + state.safeCleanup = async () => { + debug(`Running close handlers`); + for (const handler of state.closeHandlers) { + try { + await handler(); + } catch (e) { + error(`Error in close handler: ${e?.message ?? e}`); + } + } + debug(`All close handlers completed`); }; if (protocol === 'https') { @@ -56,6 +69,16 @@ export default async function launch(config) { }, websocket: buildHandler(config, state), }); + + addCloseHandler(state, () => { + state.browsers?.forEach((browser) => { + browser.proc.kill(); + browser.proc.unref(); + }); + state.server.stop(); + state.server.unref(); + }); + print(chalk.magenta(`šŸš€ Serving on ${chalk.white(protocol + '://' + hostname + ':')}${chalk.magenta(port)}`)); config.reporter.serverConfig = { port, @@ -66,6 +89,7 @@ export default async function launch(config) { if (config.setup) { debug(`Running configured setup hook`); + await config.setup({ port, hostname, @@ -73,15 +97,20 @@ export default async function launch(config) { }); debug(`Configured setup hook completed`); } + if (config.cleanup) { + addCloseHandler(state, async () => { + debug(`Running configured cleanup hook`); + await config.cleanup(); + debug(`Configured cleanup hook completed`); + }); + } - await launchBrowsers(config, state); + if (!config.noLaunch) { + await launchBrowsers(config, state); + } } catch (e) { error(`Error: ${e?.message ?? e}`); - if (config.cleanup) { - debug(`Running configured cleanup hook`); - await config.cleanup(); - debug(`Configured cleanup hook completed`); - } + await state.safeCleanup(); throw e; } } else { diff --git a/packages/diagnostic/server/reporters/default.js b/packages/diagnostic/server/reporters/default.js index df9eac22b4c..a3e0ad86926 100644 --- a/packages/diagnostic/server/reporters/default.js +++ b/packages/diagnostic/server/reporters/default.js @@ -181,14 +181,14 @@ export default class CustomDotReporter { if (this.failedTests.length) { this.write( chalk.red( - `\n\n${this.failedTests.length} Tests Failed. Complete stack traces for failures will print at the end.` + `\n\n\t${this.failedTests.length} Tests Failed. Complete stack traces for failures will print at the end.` ) ); } if (this.globalFailures.length) { this.write( chalk.red( - `\n\n${this.globalFailures.length} Global Failures were detected.. Complete stack traces for failures will print at the end.` + `\n\n\t${this.globalFailures.length} Global Failures were detected.. Complete stack traces for failures will print at the end.` ) ); } @@ -457,6 +457,9 @@ export default class CustomDotReporter { } reportFailedTests() { + if (this.failedTests.length) { + this.write(chalk.red(`\n\n\tPrinting ${this.failedTests.length} Failed Tests\n\n\t====================\n\n`)); + } this.failedTests.forEach((failure) => { const result = failure.data; this.write(chalk.red(`\n\tšŸ’„ Failed: ${result.runDuration.toLocaleString('en-US')}ms ${result.name}\n`)); diff --git a/packages/diagnostic/server/utils/get-flags.js b/packages/diagnostic/server/utils/get-flags.js index 2b76e754020..918ccd908e2 100644 --- a/packages/diagnostic/server/utils/get-flags.js +++ b/packages/diagnostic/server/utils/get-flags.js @@ -24,7 +24,7 @@ export function getFlags() { const filter = flags.has('--filter') || flags.has('-f'); const retry = flags.has('--retry') || flags.has('-r'); const headless = flags.has('--headless') || flags.has('-h'); - const useExisting = flags.has('--use-existing') || flags.has('-e'); + const useExisting = flags.has('--use-existing') || flags.has('-e') || flags.has('-b'); if (filter) { filtered['filter'] = true; From 4d21b599edcc4251c5cfd205b7e29f0aed0fceaa Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 1 Mar 2025 18:29:21 -0800 Subject: [PATCH 02/47] make holodeck cleanup appropriately --- .../diagnostic/server/NCC-1701-a-gold_100.svg | 1 + packages/diagnostic/server/bun/fetch.js | 7 +- packages/diagnostic/server/launcher.html | 1 + packages/holodeck/package.json | 4 +- packages/holodeck/server/index.js | 242 +++++++++++------- packages/holodeck/server/start-node.js | 3 + packages/holodeck/server/tsconfig.json | 12 + packages/holodeck/server/worker.js | 3 + pnpm-lock.yaml | 4 +- .../tests/{test-helper.js => test-helper.ts} | 8 +- 10 files changed, 181 insertions(+), 104 deletions(-) create mode 100644 packages/diagnostic/server/NCC-1701-a-gold_100.svg create mode 100644 packages/holodeck/server/start-node.js create mode 100644 packages/holodeck/server/tsconfig.json create mode 100644 packages/holodeck/server/worker.js rename tests/ember-data__request/tests/{test-helper.js => test-helper.ts} (78%) diff --git a/packages/diagnostic/server/NCC-1701-a-gold_100.svg b/packages/diagnostic/server/NCC-1701-a-gold_100.svg new file mode 100644 index 00000000000..024f6d7f905 --- /dev/null +++ b/packages/diagnostic/server/NCC-1701-a-gold_100.svg @@ -0,0 +1 @@ + diff --git a/packages/diagnostic/server/bun/fetch.js b/packages/diagnostic/server/bun/fetch.js index eb97b75848a..4ca776b0f7b 100644 --- a/packages/diagnostic/server/bun/fetch.js +++ b/packages/diagnostic/server/bun/fetch.js @@ -57,8 +57,11 @@ export function handleBunFetch(config, state, req, server) { } const route = pathParts.join('/'); - if (route === 'favicon.ico') { - return new Response('Not Found', { status: 404 }); + if (route === 'favicon.ico' || route === 'NCC-1701-a-gold_100.svg') { + const dir = import.meta.dir; + const asset = path.join(dir, '../NCC-1701-a-gold_100.svg'); + + return new Response(Bun.file(asset)); } // serve test assets diff --git a/packages/diagnostic/server/launcher.html b/packages/diagnostic/server/launcher.html index cea14159dba..363e7f05bc9 100644 --- a/packages/diagnostic/server/launcher.html +++ b/packages/diagnostic/server/launcher.html @@ -2,6 +2,7 @@ @warp-drive/diagnostic Parallel Test Launcher +