Skip to content

Commit 19810a9

Browse files
fix(selectivity): remove stale test file hashes
1 parent ef3ff06 commit 19810a9

4 files changed

Lines changed: 519 additions & 6 deletions

File tree

src/browser/cdp/selectivity/fs-cache.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os from "node:os";
22
import path from "node:path";
3+
import { performance } from "node:perf_hooks";
34
import pLimit from "p-limit";
45
import lockfile from "proper-lockfile";
56
import fs from "fs-extra";
@@ -14,7 +15,6 @@ export const CacheType = {
1415
type CacheTypeValue = (typeof CacheType)[keyof typeof CacheType];
1516

1617
// Cache is considered fresh if it was created after process start
17-
const processStartTime = Number(new Date());
1818
const tmpDir = path.join(os.tmpdir(), SELECTIVITY_CACHE_DIRECTIRY);
1919

2020
// https://nodejs.org/api/cli.html#uv_threadpool_sizesize
@@ -27,7 +27,7 @@ const ensureSelectivityCacheDirectory = async (): Promise<void> => {
2727
const wasModifiedAfterProcessStart = async (flagFilePath: string): Promise<boolean> => {
2828
try {
2929
const stats = await libUVLimited(() => fs.stat(flagFilePath));
30-
return stats.mtimeMs >= processStartTime;
30+
return stats.mtimeMs >= performance.timeOrigin;
3131
} catch {
3232
return false;
3333
}

src/browser/cdp/selectivity/index.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
import path from "node:path";
2+
import fs from "fs-extra";
13
import { CSSSelectivity } from "./css-selectivity";
24
import { JSSelectivity } from "./js-selectivity";
35
import type { ExistingBrowser } from "../../existing-browser";
46
import { getTestDependenciesWriter } from "./test-dependencies-writer";
57
import type { Test, TestDepsContext, TestDepsData } from "../../../types";
6-
import { mergeSourceDependencies, transformSourceDependencies } from "./utils";
8+
import { getSelectivityTestsPath, mergeSourceDependencies, transformSourceDependencies } from "./utils";
79
import { getHashWriter } from "./hash-writer";
810
import { Compression } from "./types";
911
import { getCollectedTestplaneDependencies } from "./testplane-selectivity";
1012
import { getHashReader } from "./hash-reader";
1113
import type { Config } from "../../../config";
1214
import { MasterEvents } from "../../../events";
15+
import { debugSelectivity } from "./debug";
1316

1417
type StopSelectivityFn = (test: Test, shouldWrite: boolean) => Promise<void>;
1518

@@ -43,6 +46,71 @@ export const updateSelectivityHashes = async (config: Config): Promise<void> =>
4346
}
4447
};
4548

49+
export const clearUnusedSelectivityDumps = async (config: Config): Promise<void> => {
50+
const browserIds = config.getBrowserIds();
51+
const selectivityRoots: string[] = [];
52+
53+
for (const browserId of browserIds) {
54+
const browserConfig = config.forBrowser(browserId);
55+
const { enabled, testDependenciesPath } = browserConfig.selectivity;
56+
57+
if (enabled && !selectivityRoots.includes(testDependenciesPath)) {
58+
selectivityRoots.push(testDependenciesPath);
59+
}
60+
}
61+
62+
let filesTotal = 0;
63+
let filesDeleted = 0;
64+
65+
// eslint-disable-next-line no-bitwise
66+
const rwMode = fs.constants.R_OK | fs.constants.W_OK;
67+
68+
await Promise.all(
69+
selectivityRoots.map(async selectivityRoot => {
70+
const testsPath = getSelectivityTestsPath(selectivityRoot);
71+
const accessError = await fs.access(testsPath, rwMode).catch((err: Error) => err);
72+
73+
if (accessError) {
74+
if (!("code" in accessError && accessError.code === "ENOENT")) {
75+
debugSelectivity(`Couldn't access "${testsPath}" to clear stale files: %O`, accessError);
76+
}
77+
78+
return;
79+
}
80+
81+
const testsFileNames = await fs.readdir(testsPath);
82+
83+
filesTotal += testsFileNames.length;
84+
85+
for (const testFileName of testsFileNames) {
86+
const filePath = path.join(testsPath, testFileName);
87+
const fileStat = await fs.stat(filePath).catch(() => null);
88+
89+
if (!fileStat) {
90+
debugSelectivity(`Couldn't access file "${filePath}" to check if it was used. Skipping`);
91+
continue;
92+
}
93+
94+
// File was not used in this run
95+
if (fileStat.atimeMs < performance.timeOrigin && fileStat.isFile()) {
96+
await fs
97+
.unlink(filePath)
98+
.then(() => {
99+
filesDeleted++;
100+
})
101+
.catch(err => {
102+
debugSelectivity(`Couldn't remove stale file "${filePath}": %O`, err);
103+
});
104+
}
105+
}
106+
}),
107+
);
108+
109+
if (filesDeleted) {
110+
debugSelectivity(`Out of ${filesTotal} files, ${filesDeleted} were considered as outdated and deleted`);
111+
}
112+
};
113+
46114
export const startSelectivity = async (browser: ExistingBrowser): Promise<StopSelectivityFn> => {
47115
const { enabled, compression, sourceRoot, testDependenciesPath, mapDependencyRelativePath } =
48116
browser.config.selectivity;

src/testplane.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { initDevServer } from "./dev-server";
1818
import { ConfigInput } from "./config/types";
1919
import { MasterEventHandler, Test, TestResult } from "./types";
2020
import { preloadWebdriverIO } from "./utils/preload-utils";
21-
import { updateSelectivityHashes } from "./browser/cdp/selectivity";
21+
import { clearUnusedSelectivityDumps, updateSelectivityHashes } from "./browser/cdp/selectivity";
2222
import { TagFilter } from "./utils/cli";
2323
import { ViteServer } from "./runner/browser-env/vite/server";
2424
import { getGlobalFilesToRemove, initGlobalFilesToRemove } from "./globalFilesToRemove";
@@ -208,7 +208,18 @@ export class Testplane extends BaseTestplane {
208208
);
209209

210210
if (!shouldDisableSelectivity && !this.isFailed()) {
211-
await updateSelectivityHashes(this.config);
211+
const [updateResult, clearResult] = await Promise.allSettled([
212+
updateSelectivityHashes(this.config),
213+
clearUnusedSelectivityDumps(this.config),
214+
]);
215+
216+
if (updateResult.status === "rejected") {
217+
console.error("Couldn't update selectivity state: ", updateResult.reason);
218+
}
219+
220+
if (clearResult.status === "rejected") {
221+
console.error("Couldn't clear stale selectivity files: ", clearResult.reason);
222+
}
212223
}
213224

214225
if (this.config.afterAll) {

0 commit comments

Comments
 (0)