Skip to content

Commit

Permalink
test: use patched vitest
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Nov 27, 2023
1 parent ed42879 commit f9cd8d3
Show file tree
Hide file tree
Showing 4 changed files with 1,335 additions and 5 deletions.
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@
"typescript": "^5.2.2",
"vite": "^5.0.2",
"vitest": "1.0.0-beta.5"
},
"pnpm": {
"patchedDependencies": {
"[email protected]": "patches/[email protected]",
"@vitest/[email protected]": "patches/@[email protected]"
}
}
}
279 changes: 279 additions & 0 deletions patches/@[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
diff --git a/dist/provider.d.ts b/dist/provider.d.ts
index fe3836e882d3b12bfbd270af3a28bc541c52da12..1f8268d03ff0e6104c0b05b3e071fbdc74623870 100644
--- a/dist/provider.d.ts
+++ b/dist/provider.d.ts
@@ -1,10 +1,11 @@
import { CoverageProvider, Vitest, AfterSuiteRunMeta, ReportContext, ResolvedCoverageOptions } from 'vitest';
import { BaseCoverageProvider } from 'vitest/coverage';
-import { CoverageMapData } from 'istanbul-lib-coverage';
+import { CoverageMap } from 'istanbul-lib-coverage';
import { Instrumenter } from 'istanbul-lib-instrument';

type Options = ResolvedCoverageOptions<'istanbul'>;
-type CoverageByTransformMode = Record<AfterSuiteRunMeta['transformMode'], CoverageMapData[]>;
+type Filename = string;
+type CoverageFilesByTransformMode = Record<AfterSuiteRunMeta['transformMode'], Filename[]>;
type ProjectName = NonNullable<AfterSuiteRunMeta['projectName']> | typeof DEFAULT_PROJECT;
interface TestExclude {
new (opts: {
@@ -26,13 +27,9 @@ declare class IstanbulCoverageProvider extends BaseCoverageProvider implements C
options: Options;
instrumenter: Instrumenter;
testExclude: InstanceType<TestExclude>;
- /**
- * Coverage objects collected from workers.
- * Some istanbul utilizers write these into file system instead of storing in memory.
- * If storing in memory causes issues, we can simply write these into fs in `onAfterSuiteRun`
- * and read them back when merging coverage objects in `onAfterAllFilesRun`.
- */
- coverages: Map<ProjectName, CoverageByTransformMode>;
+ coverageFiles: Map<ProjectName, CoverageFilesByTransformMode>;
+ coverageFilesDirectory: string;
+ pendingPromises: Promise<void>[];
initialize(ctx: Vitest): void;
resolveOptions(): Options;
onFileTransform(sourceCode: string, id: string, pluginCtx: any): {
@@ -42,7 +39,7 @@ declare class IstanbulCoverageProvider extends BaseCoverageProvider implements C
onAfterSuiteRun({ coverage, transformMode, projectName }: AfterSuiteRunMeta): void;
clean(clean?: boolean): Promise<void>;
reportCoverage({ allTestsRun }?: ReportContext): Promise<void>;
- getCoverageMapForUncoveredFiles(coveredFiles: string[]): Promise<CoverageMapData>;
+ getCoverageMapForUncoveredFiles(coveredFiles: string[]): Promise<CoverageMap>;
}

export { IstanbulCoverageProvider };
diff --git a/dist/provider.js b/dist/provider.js
index 0477bcd634d57857e8df1a9303aebf2a6f6f2469..7a5ea928ebe4d67d6f1475b9934cee4358864863 100644
--- a/dist/provider.js
+++ b/dist/provider.js
@@ -1,8 +1,9 @@
-import { existsSync, promises, writeFileSync } from 'node:fs';
+import { promises, existsSync, writeFileSync } from 'node:fs';
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config';
import { BaseCoverageProvider } from 'vitest/coverage';
import c from 'picocolors';
import { parseModule } from 'magicast';
+import createDebug from 'debug';
import libReport from 'istanbul-lib-report';
import reports from 'istanbul-reports';
import libCoverage from 'istanbul-lib-coverage';
@@ -106,19 +107,17 @@ const isAbsolute = function(p) {
};

const DEFAULT_PROJECT = Symbol.for("default-project");
+const debug = createDebug("vitest:coverage");
+let uniqueId = 0;
class IstanbulCoverageProvider extends BaseCoverageProvider {
name = "istanbul";
ctx;
options;
instrumenter;
testExclude;
- /**
- * Coverage objects collected from workers.
- * Some istanbul utilizers write these into file system instead of storing in memory.
- * If storing in memory causes issues, we can simply write these into fs in `onAfterSuiteRun`
- * and read them back when merging coverage objects in `onAfterAllFilesRun`.
- */
- coverages = /* @__PURE__ */ new Map();
+ coverageFiles = /* @__PURE__ */ new Map();
+ coverageFilesDirectory;
+ pendingPromises = [];
initialize(ctx) {
const config = ctx.config.coverage;
this.ctx = ctx;
@@ -157,6 +156,7 @@ class IstanbulCoverageProvider extends BaseCoverageProvider {
extension: this.options.extension,
relativePath: !this.options.allowExternal
});
+ this.coverageFilesDirectory = resolve(this.options.reportsDirectory, ".tmp");
}
resolveOptions() {
return this.options;
@@ -176,33 +176,58 @@ class IstanbulCoverageProvider extends BaseCoverageProvider {
* backwards compatibility is a breaking change.
*/
onAfterSuiteRun({ coverage, transformMode, projectName }) {
+ if (!coverage)
+ return;
if (transformMode !== "web" && transformMode !== "ssr")
throw new Error(`Invalid transform mode: ${transformMode}`);
- let entry = this.coverages.get(projectName || DEFAULT_PROJECT);
+ let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
if (!entry) {
entry = { web: [], ssr: [] };
- this.coverages.set(projectName || DEFAULT_PROJECT, entry);
+ this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
}
- entry[transformMode].push(coverage);
+ const filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
+ entry[transformMode].push(filename);
+ const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
+ this.pendingPromises.push(promise);
}
async clean(clean = true) {
if (clean && existsSync(this.options.reportsDirectory))
await promises.rm(this.options.reportsDirectory, { recursive: true, force: true, maxRetries: 10 });
- this.coverages = /* @__PURE__ */ new Map();
+ if (existsSync(this.coverageFilesDirectory))
+ await promises.rm(this.coverageFilesDirectory, { recursive: true, force: true, maxRetries: 10 });
+ await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
+ this.coverageFiles = /* @__PURE__ */ new Map();
+ this.pendingPromises = [];
}
async reportCoverage({ allTestsRun } = {}) {
- const coverageMaps = await Promise.all(
- Array.from(this.coverages.values()).map((coverages) => [
- mergeAndTransformCoverage(coverages.ssr),
- mergeAndTransformCoverage(coverages.web)
- ]).flat()
- );
+ const coverageMap = libCoverage.createCoverageMap({});
+ let index = 0;
+ const total = this.pendingPromises.length;
+ await Promise.all(this.pendingPromises);
+ this.pendingPromises = [];
+ for (const coveragePerProject of this.coverageFiles.values()) {
+ for (const filenames of [coveragePerProject.ssr, coveragePerProject.web]) {
+ const coverageMapByTransformMode = libCoverage.createCoverageMap({});
+ for (const chunk of toSlices(filenames, this.options.processingConcurrency)) {
+ if (debug.enabled) {
+ index += chunk.length;
+ debug("Covered files %d/%d", index, total);
+ }
+ await Promise.all(chunk.map(async (filename) => {
+ const contents = await promises.readFile(filename, "utf-8");
+ const coverage = JSON.parse(contents);
+ coverageMapByTransformMode.merge(coverage);
+ }));
+ }
+ const transformedCoverage = await transformCoverage(coverageMapByTransformMode);
+ coverageMap.merge(transformedCoverage);
+ }
+ }
if (this.options.all && allTestsRun) {
- const coveredFiles = coverageMaps.map((map) => map.files()).flat();
+ const coveredFiles = coverageMap.files();
const uncoveredCoverage = await this.getCoverageMapForUncoveredFiles(coveredFiles);
- coverageMaps.push(await mergeAndTransformCoverage([uncoveredCoverage]));
+ coverageMap.merge(await transformCoverage(uncoveredCoverage));
}
- const coverageMap = mergeCoverageMaps(...coverageMaps);
const context = libReport.createContext({
dir: this.options.reportsDirectory,
coverageMap,
@@ -242,43 +267,28 @@ class IstanbulCoverageProvider extends BaseCoverageProvider {
});
}
}
+ await promises.rm(this.coverageFilesDirectory, { recursive: true });
+ this.coverageFiles = /* @__PURE__ */ new Map();
}
async getCoverageMapForUncoveredFiles(coveredFiles) {
const includedFiles = await this.testExclude.glob(this.ctx.config.root);
const uncoveredFiles = includedFiles.map((file) => resolve(this.ctx.config.root, file)).filter((file) => !coveredFiles.includes(file));
- const transformResults = await Promise.all(uncoveredFiles.map(async (filename) => {
- const transformResult = await this.ctx.vitenode.transformRequest(filename);
- return { transformResult, filename };
- }));
const coverageMap = libCoverage.createCoverageMap({});
- for (const { transformResult, filename } of transformResults) {
- const sourceMap = transformResult?.map;
- if (sourceMap) {
- this.instrumenter.instrumentSync(
- transformResult.code,
- filename,
- sourceMap
- );
- const lastCoverage = this.instrumenter.lastFileCoverage();
- if (lastCoverage)
- coverageMap.addFileCoverage(lastCoverage);
- }
+ for (const [index, filename] of uncoveredFiles.entries()) {
+ debug("Uncovered file %s %d/%d", filename, index, uncoveredFiles.length);
+ if (this.ctx.vitenode.fetchCache.has(filename))
+ this.ctx.vitenode.fetchCache.delete(filename);
+ await this.ctx.vitenode.transformRequest(filename);
+ const lastCoverage = this.instrumenter.lastFileCoverage();
+ coverageMap.addFileCoverage(lastCoverage);
}
- return coverageMap.data;
+ return coverageMap;
}
}
-async function mergeAndTransformCoverage(coverages) {
- const mergedCoverage = mergeCoverageMaps(...coverages);
- includeImplicitElseBranches(mergedCoverage);
+async function transformCoverage(coverageMap) {
+ includeImplicitElseBranches(coverageMap);
const sourceMapStore = libSourceMaps.createSourceMapStore();
- return await sourceMapStore.transformCoverage(mergedCoverage);
-}
-function mergeCoverageMaps(...coverageMaps) {
- return coverageMaps.reduce((coverage, previousCoverageMap) => {
- const map = libCoverage.createCoverageMap(coverage);
- map.merge(previousCoverageMap);
- return map;
- }, libCoverage.createCoverageMap({}));
+ return await sourceMapStore.transformCoverage(coverageMap);
}
function removeQueryParameters(filename) {
return filename.split("?")[0];
@@ -307,5 +317,17 @@ function isEmptyCoverageRange(range) {
function hasTerminalReporter(reporters) {
return reporters.some(([reporter]) => reporter === "text" || reporter === "text-summary" || reporter === "text-lcov" || reporter === "teamcity");
}
+function toSlices(array, size) {
+ return array.reduce((chunks, item) => {
+ const index = Math.max(0, chunks.length - 1);
+ const lastChunk = chunks[index] || [];
+ chunks[index] = lastChunk;
+ if (lastChunk.length >= size)
+ chunks.push([item]);
+ else
+ lastChunk.push(item);
+ return chunks;
+ }, []);
+}

export { IstanbulCoverageProvider };
diff --git a/package.json b/package.json
index ade70d1005555d6badc43e3ed2c723410bacd589..7a3bc75d3ff12ec12853552ff194ee3a54d3ce3b 100644
--- a/package.json
+++ b/package.json
@@ -36,10 +36,16 @@
"files": [
"dist"
],
+ "scripts": {
+ "build": "rimraf dist && rollup -c",
+ "dev": "rollup -c --watch --watch.include 'src/**'",
+ "prepublishOnly": "pnpm build"
+ },
"peerDependencies": {
"vitest": "^1.0.0-0"
},
"dependencies": {
+ "debug": "^4.3.4",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-instrument": "^6.0.1",
"istanbul-lib-report": "^3.0.1",
@@ -50,16 +56,13 @@
"test-exclude": "^6.0.0"
},
"devDependencies": {
+ "@types/debug": "^4.1.12",
"@types/istanbul-lib-coverage": "^2.0.6",
"@types/istanbul-lib-instrument": "^1.7.7",
"@types/istanbul-lib-report": "^3.0.3",
"@types/istanbul-lib-source-maps": "^4.0.4",
"@types/istanbul-reports": "^3.0.4",
"pathe": "^1.1.1",
- "vitest": "1.0.0-beta.5"
- },
- "scripts": {
- "build": "rimraf dist && rollup -c",
- "dev": "rollup -c --watch --watch.include 'src/**'"
+ "vitest": "workspace:*"
}
}
Loading

0 comments on commit f9cd8d3

Please sign in to comment.