diff --git a/packages/vitest/src/coverage.ts b/packages/vitest/src/coverage.ts index 6bc118420cc7..1aa0e9a53d95 100644 --- a/packages/vitest/src/coverage.ts +++ b/packages/vitest/src/coverage.ts @@ -95,7 +95,6 @@ export interface C8Options { } export interface ResolvedC8Options extends Required { - tempDirectory: string } export function resolveC8Options(options: C8Options, root: string): ResolvedC8Options { @@ -116,7 +115,6 @@ export function resolveC8Options(options: C8Options, root: string): ResolvedC8Op resolved.reporter = toArray(resolved.reporter) resolved.reportsDirectory = resolve(root, resolved.reportsDirectory) - resolved.tempDirectory = process.env.NODE_V8_COVERAGE || resolve(resolved.reportsDirectory, 'tmp') return resolved as ResolvedC8Options } @@ -124,9 +122,6 @@ export function resolveC8Options(options: C8Options, root: string): ResolvedC8Op export async function cleanCoverage(options: ResolvedC8Options, clean = true) { if (clean && existsSync(options.reportsDirectory)) await fs.rm(options.reportsDirectory, { recursive: true, force: true }) - - if (!existsSync(options.tempDirectory)) - await fs.mkdir(options.tempDirectory, { recursive: true }) } const require = createRequire(import.meta.url) @@ -136,6 +131,8 @@ export async function reportCoverage(ctx: Vitest) { const createReport = require('c8/lib/report') const report = createReport(ctx.config.coverage) + report._loadReports = () => ctx.coverage + const original = report._getMergedProcessCov report._getMergedProcessCov = () => { diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 721a54cc0779..ac55bd2ab7c3 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -1,5 +1,4 @@ import cac from 'cac' -import { execa } from 'execa' import type { UserConfig } from '../types' import { version } from '../../package.json' import { ensurePackageInstalled } from '../utils' @@ -80,12 +79,6 @@ async function run(cliFilters: string[], options: UserConfig) { if (ctx.config.coverage.enabled) { if (!await ensurePackageInstalled('c8')) process.exit(1) - - if (!process.env.NODE_V8_COVERAGE) { - process.env.NODE_V8_COVERAGE = ctx.config.coverage.tempDirectory - const { exitCode } = await execa(process.argv0, process.argv.slice(1), { stdio: 'inherit' }) - process.exit(exitCode) - } } if (ctx.config.environment && ctx.config.environment !== 'node') { diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 87b7a07571b0..4f1f20760f5b 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -1,4 +1,5 @@ import { existsSync } from 'fs' +import type { Profiler } from 'inspector' import type { ViteDevServer } from 'vite' import fg from 'fast-glob' import mm from 'micromatch' @@ -24,6 +25,7 @@ export class Vitest { server: ViteDevServer = undefined! state: StateManager = undefined! snapshot: SnapshotManager = undefined! + coverage: Profiler.TakePreciseCoverageReturnType[] = [] reporters: Reporter[] = undefined! console: Console pool: WorkerPool | undefined @@ -260,6 +262,7 @@ export class Vitest { // }) // } this.snapshot.clear() + this.coverage = [] const files = Array.from(this.changedTests) this.changedTests.clear() diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index 873cd8472650..99c7fd1c39ff 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -111,6 +111,9 @@ function createChannel(ctx: Vitest) { snapshotSaved(snapshot) { ctx.snapshot.add(snapshot) }, + coverageCollected(coverage) { + ctx.coverage.push(coverage) + }, async getSourceMap(id, force) { if (force) { const mod = ctx.server.moduleGraph.getModuleById(id) diff --git a/packages/vitest/src/runtime/run.ts b/packages/vitest/src/runtime/run.ts index c4df6b379e2e..9403d267a27b 100644 --- a/packages/vitest/src/runtime/run.ts +++ b/packages/vitest/src/runtime/run.ts @@ -1,4 +1,5 @@ import { performance } from 'perf_hooks' +import inspector from 'inspector' import type { HookListener, ResolvedConfig, Suite, SuiteHooks, Task, TaskResult, Test } from '../types' import { vi } from '../integrations/vi' import { getSnapshotClient } from '../integrations/snapshot/chai' @@ -188,8 +189,32 @@ export async function startTests(paths: string[], config: ResolvedConfig) { rpc().onCollected(files) + let session!: inspector.Session + if (config.coverage.enabled) { + inspector.open(0) + session = new inspector.Session() + session.connect() + + session.post('Profiler.enable') + session.post('Profiler.startPreciseCoverage', { detailed: true }) + } + await runSuites(files) + if (config.coverage.enabled) { + session.post('Profiler.takePreciseCoverage', (_, coverage) => { + rpc().coverageCollected(coverage) + }) + + session.disconnect() + try { + inspector.close() + } + catch { + // Fails inside workers for some reason + } + } + await getSnapshotClient().saveSnap() await sendTasksUpdate() diff --git a/packages/vitest/src/types/worker.ts b/packages/vitest/src/types/worker.ts index ec5c79700e82..febfdbaa57fa 100644 --- a/packages/vitest/src/types/worker.ts +++ b/packages/vitest/src/types/worker.ts @@ -1,3 +1,4 @@ +import type { Profiler } from 'inspector' import type { MessagePort } from 'worker_threads' import type { FetchFunction, ViteNodeResolveId } from 'vite-node' import type { RawSourceMap } from '../types' @@ -27,4 +28,5 @@ export interface WorkerRPC { onTaskUpdate: (pack: TaskResultPack[]) => void snapshotSaved: (snapshot: SnapshotResult) => void + coverageCollected: (coverage: Profiler.TakePreciseCoverageReturnType) => void }