From e4835e958c38992b0908f06fabf95acd26fc60bc Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Jan 2023 13:00:16 +0100 Subject: [PATCH 1/3] feat: display running processes, if vitest closes with timeout --- docs/guide/cli.md | 1 + packages/vitest/package.json | 3 ++- packages/vitest/src/node/cli.ts | 5 +++++ packages/vitest/src/node/core.ts | 2 ++ packages/vitest/src/node/track-process.ts | 14 ++++++++++++++ packages/vitest/src/types/config.ts | 5 +++++ pnpm-lock.yaml | 19 +++++++++++++++++++ 7 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 packages/vitest/src/node/track-process.ts diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 63a0754cce08..18b282e0967f 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -71,6 +71,7 @@ vitest related /src/index.ts /src/hello-world.js | `--dom` | Mock browser api with happy-dom | | `--browser` | Run tests in browser | | `--environment ` | Runner environment (default: `node`) | +| `--trackRunningProcesses` | Display running processes, if Vitest fails to exit | | `--passWithNoTests` | Pass when no tests found | | `--logHeapUsage` | Show the size of heap for each test | | `--allowOnly` | Allow tests and suites that are marked as `only` (default: false in CI, true otherwise) | diff --git a/packages/vitest/package.json b/packages/vitest/package.json index b906f7c67faf..47856a593163 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -122,7 +122,8 @@ "tinypool": "^0.3.0", "tinyspy": "^1.0.2", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "workspace:*" + "vite-node": "workspace:*", + "why-is-node-running": "^2.2.2" }, "devDependencies": { "@antfu/install-pkg": "^0.1.1", diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 54b0ca3d4204..7140db8e16da 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -6,6 +6,7 @@ import type { Vitest, VitestRunMode } from '../types' import type { CliOptions } from './cli-api' import { startVitest } from './cli-api' import { divider } from './reporters/renderers/utils' +import { startTrackingProcesses } from './track-process' const cli = cac('vitest') @@ -36,6 +37,7 @@ cli .option('--dom', 'mock browser api with happy-dom') .option('--browser', 'run tests in browser') .option('--environment ', 'runner environment (default: node)') + .option('--trackRunningProcesses', 'display running processes, if Vitest fails to exit') .option('--passWithNoTests', 'pass when no tests found') .option('--logHeapUsage', 'show the size of heap for each test') .option('--allowOnly', 'Allow tests and suites that are marked as only (default: !process.env.CI)') @@ -125,6 +127,9 @@ function normalizeCliOptions(argv: CliOptions): CliOptions { } async function start(mode: VitestRunMode, cliFilters: string[], options: CliOptions): Promise { + if (options.trackRunningProcesses) + startTrackingProcesses() + try { const ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(options)) if (!ctx?.config.watch) diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 74104d8df29d..4670ef99f0bd 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -13,6 +13,7 @@ import { clearTimeout, deepMerge, hasFailed, noop, setTimeout, slash, toArray } import { getCoverageProvider } from '../integrations/coverage' import { Typechecker } from '../typecheck/typechecker' import { createPool } from './pool' +import { logRunningProcesses } from './track-process' import type { WorkerPool } from './pool' import { createBenchmarkReporters, createReporters } from './reporters/utils' import { StateManager } from './state' @@ -549,6 +550,7 @@ export class Vitest { async exit(force = false) { setTimeout(() => { console.warn(`close timed out after ${this.config.teardownTimeout}ms`) + logRunningProcesses() process.exit() }, this.config.teardownTimeout).unref() diff --git a/packages/vitest/src/node/track-process.ts b/packages/vitest/src/node/track-process.ts new file mode 100644 index 000000000000..73af6830cfd5 --- /dev/null +++ b/packages/vitest/src/node/track-process.ts @@ -0,0 +1,14 @@ +import { createRequire } from 'module' + +const _require = createRequire(import.meta.url) + +let whyRunning: (() => void) | undefined + +export function startTrackingProcesses() { + whyRunning = _require('why-is-node-running') +} + +export function logRunningProcesses() { + if (whyRunning) + whyRunning() +} diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index edbed2d22833..c33cd0e3b58e 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -566,6 +566,11 @@ export interface UserConfig extends InlineConfig { * @example --shard=2/3 */ shard?: string + + /** + * Display running processes, if Vitest fails to exit + */ + trackRunningProcesses?: boolean } export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath' | 'benchmark' | 'shard' | 'cache' | 'sequence' | 'typecheck'> { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fedf0cb7336..89301758bfa3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -873,6 +873,7 @@ importers: typescript: ^4.9.4 vite: ^4.0.0 vite-node: workspace:* + why-is-node-running: ^2.2.2 ws: ^8.12.0 dependencies: '@types/chai': 4.3.4 @@ -892,6 +893,7 @@ importers: tinyspy: 1.0.2 vite: 4.0.0_@types+node@18.7.13 vite-node: link:../vite-node + why-is-node-running: 2.2.2 devDependencies: '@antfu/install-pkg': 0.1.1 '@edge-runtime/vm': 2.0.2 @@ -18764,6 +18766,10 @@ packages: object-inspect: 1.12.2 dev: true + /siginfo/2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: false + /sigmund/1.0.1: resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} dev: true @@ -19091,6 +19097,10 @@ packages: escape-string-regexp: 2.0.0 dev: true + /stackback/0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: false + /stackframe/1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} dev: true @@ -21415,6 +21425,15 @@ packages: dependencies: isexe: 2.0.0 + /why-is-node-running/2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: false + /wide-align/1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: From 0f1d4aac79d643a207adce0791d6aafbb2c96590 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Jan 2023 16:04:47 +0100 Subject: [PATCH 2/3] refactor: move hanging-process to reporter --- docs/config/index.md | 1 + docs/guide/cli.md | 1 - packages/vitest/src/node/cli.ts | 5 ----- packages/vitest/src/node/core.ts | 8 ++++---- .../vitest/src/node/reporters/hanging-process.ts | 15 +++++++++++++++ packages/vitest/src/node/reporters/index.ts | 2 ++ packages/vitest/src/node/track-process.ts | 14 -------------- packages/vitest/src/types/reporter.ts | 2 ++ 8 files changed, 24 insertions(+), 24 deletions(-) create mode 100644 packages/vitest/src/node/reporters/hanging-process.ts delete mode 100644 packages/vitest/src/node/track-process.ts diff --git a/docs/config/index.md b/docs/config/index.md index d77d3c911219..4317a4c8ccc5 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -358,6 +358,7 @@ Custom reporters for output. Reporters can be [a Reporter instance](https://gith - `'junit'` - JUnit XML reporter (you can configure `testsuites` tag name with `VITEST_JUNIT_SUITE_NAME` environmental variable) - `'json'` - give a simple JSON summary - `'html'` - outputs HTML report based on [`@vitest/ui`](/guide/ui) + - `'hanging-process'` - displays a list of hanging processes, if Vitest cannot exit process safely. This might be a heavy operation, enable it only if Vitest consistently cannot exit process - path of a custom reporter (e.g. `'./path/to/reporter.ts'`, `'@scope/reporter'`) ### outputTruncateLength diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 18b282e0967f..63a0754cce08 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -71,7 +71,6 @@ vitest related /src/index.ts /src/hello-world.js | `--dom` | Mock browser api with happy-dom | | `--browser` | Run tests in browser | | `--environment ` | Runner environment (default: `node`) | -| `--trackRunningProcesses` | Display running processes, if Vitest fails to exit | | `--passWithNoTests` | Pass when no tests found | | `--logHeapUsage` | Show the size of heap for each test | | `--allowOnly` | Allow tests and suites that are marked as `only` (default: false in CI, true otherwise) | diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 7140db8e16da..54b0ca3d4204 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -6,7 +6,6 @@ import type { Vitest, VitestRunMode } from '../types' import type { CliOptions } from './cli-api' import { startVitest } from './cli-api' import { divider } from './reporters/renderers/utils' -import { startTrackingProcesses } from './track-process' const cli = cac('vitest') @@ -37,7 +36,6 @@ cli .option('--dom', 'mock browser api with happy-dom') .option('--browser', 'run tests in browser') .option('--environment ', 'runner environment (default: node)') - .option('--trackRunningProcesses', 'display running processes, if Vitest fails to exit') .option('--passWithNoTests', 'pass when no tests found') .option('--logHeapUsage', 'show the size of heap for each test') .option('--allowOnly', 'Allow tests and suites that are marked as only (default: !process.env.CI)') @@ -127,9 +125,6 @@ function normalizeCliOptions(argv: CliOptions): CliOptions { } async function start(mode: VitestRunMode, cliFilters: string[], options: CliOptions): Promise { - if (options.trackRunningProcesses) - startTrackingProcesses() - try { const ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(options)) if (!ctx?.config.watch) diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 4670ef99f0bd..f84fd9caaca8 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -13,7 +13,6 @@ import { clearTimeout, deepMerge, hasFailed, noop, setTimeout, slash, toArray } import { getCoverageProvider } from '../integrations/coverage' import { Typechecker } from '../typecheck/typechecker' import { createPool } from './pool' -import { logRunningProcesses } from './track-process' import type { WorkerPool } from './pool' import { createBenchmarkReporters, createReporters } from './reporters/utils' import { StateManager } from './state' @@ -549,9 +548,10 @@ export class Vitest { */ async exit(force = false) { setTimeout(() => { - console.warn(`close timed out after ${this.config.teardownTimeout}ms`) - logRunningProcesses() - process.exit() + this.report('onProcessTimeout').then(() => { + console.warn(`close timed out after ${this.config.teardownTimeout}ms`) + process.exit() + }) }, this.config.teardownTimeout).unref() await this.close() diff --git a/packages/vitest/src/node/reporters/hanging-process.ts b/packages/vitest/src/node/reporters/hanging-process.ts new file mode 100644 index 000000000000..41c7b4938429 --- /dev/null +++ b/packages/vitest/src/node/reporters/hanging-process.ts @@ -0,0 +1,15 @@ +import { createRequire } from 'module' +import type { Reporter } from '../../types' + +export class HangingProcessReporter implements Reporter { + whyRunning: (() => void) | undefined + + onInit(): void { + const _require = createRequire(import.meta.url) + this.whyRunning = _require('why-is-node-running') + } + + onProcessTimeout() { + this.whyRunning?.() + } +} diff --git a/packages/vitest/src/node/reporters/index.ts b/packages/vitest/src/node/reporters/index.ts index 664f2afcad58..91e4020aba84 100644 --- a/packages/vitest/src/node/reporters/index.ts +++ b/packages/vitest/src/node/reporters/index.ts @@ -5,6 +5,7 @@ import { VerboseReporter } from './verbose' import { TapReporter } from './tap' import { JUnitReporter } from './junit' import { TapFlatReporter } from './tap-flat' +import { HangingProcessReporter } from './hanging-process' export { DefaultReporter } @@ -16,6 +17,7 @@ export const ReportersMap = { 'tap': TapReporter, 'tap-flat': TapFlatReporter, 'junit': JUnitReporter, + 'hanging-process': HangingProcessReporter, } export type BuiltinReporters = keyof typeof ReportersMap diff --git a/packages/vitest/src/node/track-process.ts b/packages/vitest/src/node/track-process.ts deleted file mode 100644 index 73af6830cfd5..000000000000 --- a/packages/vitest/src/node/track-process.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createRequire } from 'module' - -const _require = createRequire(import.meta.url) - -let whyRunning: (() => void) | undefined - -export function startTrackingProcesses() { - whyRunning = _require('why-is-node-running') -} - -export function logRunningProcesses() { - if (whyRunning) - whyRunning() -} diff --git a/packages/vitest/src/types/reporter.ts b/packages/vitest/src/types/reporter.ts index 9eb6b2b1292a..07e65804c450 100644 --- a/packages/vitest/src/types/reporter.ts +++ b/packages/vitest/src/types/reporter.ts @@ -16,6 +16,8 @@ export interface Reporter { onServerRestart?: (reason?: string) => Awaitable onUserConsoleLog?: (log: UserConsoleLog) => Awaitable + + onProcessTimeout?: () => Awaitable } export type { Vitest } From 47fa670645d1ecdc5e835fa52a04cd01d836a73a Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Jan 2023 16:05:58 +0100 Subject: [PATCH 3/3] chore: cleanup --- packages/vitest/src/types/config.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index c33cd0e3b58e..edbed2d22833 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -566,11 +566,6 @@ export interface UserConfig extends InlineConfig { * @example --shard=2/3 */ shard?: string - - /** - * Display running processes, if Vitest fails to exit - */ - trackRunningProcesses?: boolean } export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath' | 'benchmark' | 'shard' | 'cache' | 'sequence' | 'typecheck'> {