Skip to content

Commit

Permalink
fix(coverage): v8 to use vm context offset from vite-node
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Feb 6, 2025
1 parent 3bfc4d1 commit 4e458b0
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 81 deletions.
15 changes: 12 additions & 3 deletions packages/coverage-v8/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { CoverageProviderModule } from 'vitest/node'
import type { V8CoverageProvider } from './provider'
import inspector, { type Profiler } from 'node:inspector'
import { fileURLToPath } from 'node:url'
import { provider } from 'std-env'
import { loadProvider } from './load-provider'

Expand All @@ -23,15 +24,19 @@ export default {
})
},

takeCoverage(): Promise<{ result: Profiler.ScriptCoverage[] }> {
takeCoverage({ moduleExecutionInfo }): Promise<{ result: Profiler.ScriptCoverage[] }> {
return new Promise((resolve, reject) => {
session.post('Profiler.takePreciseCoverage', async (error, coverage) => {
if (error) {
return reject(error)
}

// Reduce amount of data sent over rpc by doing some early result filtering
const result = coverage.result.filter(filterResult)
const result = coverage.result
.filter(filterResult)
.map(res => ({
...res,
startOffset: moduleExecutionInfo?.get(fileURLToPath(res.url))?.startOffset || 0,
}))

resolve({ result })
})
Expand Down Expand Up @@ -66,5 +71,9 @@ function filterResult(coverage: Profiler.ScriptCoverage): boolean {
return false
}

if (coverage.url.includes('/vitest/packages/')) {
return false
}

return true
}
24 changes: 15 additions & 9 deletions packages/coverage-v8/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ import { cleanUrl } from 'vite-node/utils'
import { BaseCoverageProvider } from 'vitest/coverage'
import { version } from '../package.json' with { type: 'json' }

type TransformResults = Map<string, FetchResult>
type RawCoverage = Profiler.TakePreciseCoverageReturnType
interface ScriptCoverageWithOffset extends Profiler.ScriptCoverage {
startOffset: number
}

// TODO: vite-node should export this
const WRAPPER_LENGTH = 185
type TransformResults = Map<string, FetchResult>
interface RawCoverage { result: ScriptCoverageWithOffset[] }

// Note that this needs to match the line ending as well
const VITE_EXPORTS_LINE_PATTERN
Expand Down Expand Up @@ -69,6 +70,14 @@ export class V8CoverageProvider extends BaseCoverageProvider<ResolvedCoverageOpt
await this.readCoverageFiles<RawCoverage>({
onFileRead(coverage) {
merged = mergeProcessCovs([merged, coverage])

// mergeProcessCovs sometimes loses startOffset, e.g. in vue
merged.result.forEach((result) => {
if (!result.startOffset) {
const original = coverage.result.find(r => r.url === result.url)
result.startOffset = original?.startOffset || 0
}
})
},
onFinished: async (project, transformMode) => {
const converted = await this.convertCoverage(
Expand Down Expand Up @@ -338,20 +347,17 @@ export class V8CoverageProvider extends BaseCoverageProvider<ResolvedCoverageOpt
}

await Promise.all(
chunk.map(async ({ url, functions }) => {
chunk.map(async ({ url, functions, startOffset }) => {
const sources = await this.getSources(
url,
transformResults,
onTransform,
functions,
)

// If file was executed by vite-node we'll need to add its wrapper
const wrapperLength = sources.isExecuted ? WRAPPER_LENGTH : 0

const converter = v8ToIstanbul(
url,
wrapperLength,
startOffset,
sources,
undefined,
this.options.ignoreEmptyLines,
Expand Down
19 changes: 7 additions & 12 deletions packages/vitest/src/integrations/coverage.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import type {
CoverageModuleLoader,
CoverageProvider,
CoverageProviderModule,
} from '../node/types/coverage'
import type { SerializedCoverageConfig } from '../runtime/config'

interface Loader {
executeId: (id: string) => Promise<{ default: CoverageProviderModule }>
isBrowser?: boolean
}

export const CoverageProviderMap: Record<string, string> = {
v8: '@vitest/coverage-v8',
istanbul: '@vitest/coverage-istanbul',
}

async function resolveCoverageProviderModule(
options: SerializedCoverageConfig | undefined,
loader: Loader,
loader: CoverageModuleLoader,
) {
if (!options?.enabled || !options.provider) {
return null
Expand Down Expand Up @@ -65,7 +60,7 @@ async function resolveCoverageProviderModule(

export async function getCoverageProvider(
options: SerializedCoverageConfig | undefined,
loader: Loader,
loader: CoverageModuleLoader,
): Promise<CoverageProvider | null> {
const coverageModule = await resolveCoverageProviderModule(options, loader)

Expand All @@ -78,7 +73,7 @@ export async function getCoverageProvider(

export async function startCoverageInsideWorker(
options: SerializedCoverageConfig | undefined,
loader: Loader,
loader: CoverageModuleLoader,
runtimeOptions: { isolate: boolean },
) {
const coverageModule = await resolveCoverageProviderModule(options, loader)
Expand All @@ -92,20 +87,20 @@ export async function startCoverageInsideWorker(

export async function takeCoverageInsideWorker(
options: SerializedCoverageConfig | undefined,
loader: Loader,
loader: CoverageModuleLoader,
) {
const coverageModule = await resolveCoverageProviderModule(options, loader)

if (coverageModule) {
return coverageModule.takeCoverage?.()
return coverageModule.takeCoverage?.({ moduleExecutionInfo: loader.moduleExecutionInfo })
}

return null
}

export async function stopCoverageInsideWorker(
options: SerializedCoverageConfig | undefined,
loader: Loader,
loader: CoverageModuleLoader,
runtimeOptions: { isolate: boolean },
) {
const coverageModule = await resolveCoverageProviderModule(options, loader)
Expand Down
9 changes: 8 additions & 1 deletion packages/vitest/src/node/types/coverage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ReportOptions } from 'istanbul-reports'
import type { TransformResult as ViteTransformResult } from 'vite'
import type { ModuleExecutionInfo } from 'vite-node'
import type { AfterSuiteRunMeta, Arrayable } from '../../types/general'
import type { Vitest } from '../core'

Expand Down Expand Up @@ -57,6 +58,12 @@ export interface ReportContext {
allTestsRun?: boolean
}

export interface CoverageModuleLoader {
executeId: (id: string) => Promise<{ default: CoverageProviderModule }>
isBrowser?: boolean
moduleExecutionInfo?: ModuleExecutionInfo
}

export interface CoverageProviderModule {
/**
* Factory for creating a new coverage provider
Expand All @@ -71,7 +78,7 @@ export interface CoverageProviderModule {
/**
* Executed on after each run in the worker thread. Possible to return a payload passed to the provider
*/
takeCoverage?: () => unknown | Promise<unknown>
takeCoverage?: (runtimeOptions: { moduleExecutionInfo?: ModuleExecutionInfo }) => unknown | Promise<unknown>

/**
* Executed after all tests have been run in the worker thread.
Expand Down
2 changes: 0 additions & 2 deletions test/coverage-test/fixtures/src/cjs-package/entry.js

This file was deleted.

5 changes: 0 additions & 5 deletions test/coverage-test/fixtures/src/cjs-package/package.json

This file was deleted.

9 changes: 0 additions & 9 deletions test/coverage-test/fixtures/src/cjs-package/target.js

This file was deleted.

40 changes: 0 additions & 40 deletions test/coverage-test/test/convert-failure.v8.test.ts

This file was deleted.

0 comments on commit 4e458b0

Please sign in to comment.