Skip to content

Commit caede70

Browse files
Merge pull request #1211 from gemini-testing/TESTPLANE-904.calc_hash_in_worker
perf(selectivity): speed up pre-test checks
2 parents 0ff2890 + 6999fb8 commit caede70

File tree

6 files changed

+77
-44
lines changed

6 files changed

+77
-44
lines changed

src/browser/cdp/selectivity/hash-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const calculateFileMd5Hash = (filePath: string): Promise<string> =>
1818
export class HashProvider {
1919
private static readonly _fileHashStore: Map<string, Promise<string>> = new Map();
2020
private static readonly _patternHashStore: Map<string, Promise<string>> = new Map();
21-
private static readonly _limited = pLimit(10);
21+
private static readonly _limited = pLimit(16);
2222

2323
async calculateForFile(filePath: string): Promise<string> {
2424
const cachedHash = HashProvider._fileHashStore.get(filePath);

src/browser/cdp/selectivity/hash-reader.ts

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export class HashReader {
1010
private readonly _selectivityHashesPath: string;
1111
private readonly _compresion: SelectivityCompressionType;
1212
private _hashFileContents: Promise<HashFileContents> | null = null;
13+
private _fileStateCache = new Map<string, boolean>();
1314

1415
constructor(selectivityRootPath: string, compression: SelectivityCompressionType) {
1516
this._selectivityHashesPath = getSelectivityHashesPath(selectivityRootPath);
@@ -24,60 +25,64 @@ export class HashReader {
2425
return (this._hashFileContents = readHashFileContents(this._selectivityHashesPath, this._compresion));
2526
}
2627

27-
private _readHashForFile(filePath: string): Promise<string> {
28-
return this._getHashFileContents().then(hashFileContents => hashFileContents.files[filePath]);
29-
}
30-
31-
private _readHashForModule(moduleName: string): Promise<string> {
32-
return this._getHashFileContents().then(hashFileContents => hashFileContents.modules[moduleName]);
33-
}
34-
35-
private _readHashForPattern(pattern: string): Promise<string> {
36-
return this._getHashFileContents().then(hashFileContents => hashFileContents.patterns[pattern]);
37-
}
38-
3928
async patternHasChanged(pattern: string): Promise<boolean> {
40-
const [cachedPatternHash, calculatedPatternHash] = await Promise.all([
41-
this._readHashForPattern(pattern),
42-
this._hashProvider.calculateForPattern(pattern),
43-
]);
29+
const fileContents = await this._getHashFileContents();
30+
const cachedPatternHash = fileContents.patterns[pattern];
31+
const calculatedPatternHash = await this._hashProvider.calculateForPattern(pattern);
4432

4533
return cachedPatternHash !== calculatedPatternHash;
4634
}
4735

4836
/** @returns changed deps or null, if nothing changed */
4937
async getTestChangedDeps(testDeps: NormalizedDependencies): Promise<NormalizedDependencies | null> {
5038
const depFileTypes: Array<keyof NormalizedDependencies> = ["css", "js", "modules"] as const;
51-
const result: NormalizedDependencies = { css: [], js: [], modules: [] };
39+
const fileContents = await this._getHashFileContents();
5240

53-
let hasAnythingChanged = false;
41+
let result: NormalizedDependencies | null = null;
5442

5543
const checkForDepFileType = async (depFileType: keyof NormalizedDependencies): Promise<void> => {
56-
await Promise.all(
57-
testDeps[depFileType].map(async filePath => {
58-
const adjustedFilePath = depFileType === "modules" ? path.join(filePath, "package.json") : filePath;
59-
const [cachedFileHash, calculatedFileHash] = await Promise.all([
60-
depFileType === "modules" ? this._readHashForModule(filePath) : this._readHashForFile(filePath),
61-
this._hashProvider.calculateForFile(adjustedFilePath).catch((err: Error) => err),
62-
]);
63-
64-
if (calculatedFileHash instanceof Error) {
65-
debugSelectivity(
66-
`Couldn't calculate hash for ${adjustedFilePath}: ${calculatedFileHash.message}`,
67-
);
68-
}
69-
70-
if (cachedFileHash !== calculatedFileHash) {
71-
hasAnythingChanged = true;
72-
result[depFileType].push(filePath);
73-
}
74-
}),
75-
);
44+
for (const filePath of testDeps[depFileType]) {
45+
const isChanged = this._fileStateCache.get(filePath);
46+
47+
if (isChanged === false) {
48+
continue;
49+
} else if (isChanged === true) {
50+
result ||= { css: [], js: [], modules: [] };
51+
result[depFileType].push(filePath);
52+
continue;
53+
}
54+
55+
const adjustedFilePath = depFileType === "modules" ? path.join(filePath, "package.json") : filePath;
56+
const cachedFileHash =
57+
depFileType === "modules" ? fileContents.modules[filePath] : fileContents.files[filePath];
58+
59+
const calculatedFileHash = await this._hashProvider
60+
.calculateForFile(adjustedFilePath)
61+
.catch((err: Error) => err);
62+
63+
if (calculatedFileHash instanceof Error) {
64+
debugSelectivity(`${calculatedFileHash.message}: ${calculatedFileHash.cause}`);
65+
}
66+
67+
if (cachedFileHash !== calculatedFileHash) {
68+
result ||= { css: [], js: [], modules: [] };
69+
result[depFileType].push(filePath);
70+
this._fileStateCache.set(filePath, true);
71+
} else {
72+
this._fileStateCache.set(filePath, false);
73+
}
74+
}
7675
};
7776

78-
await Promise.all(depFileTypes.map(depFileType => checkForDepFileType(depFileType)));
77+
for (const depFileType of depFileTypes) {
78+
await checkForDepFileType(depFileType);
79+
}
80+
81+
return result;
82+
}
7983

80-
return hasAnythingChanged ? result : null;
84+
clearCache(): void {
85+
this._fileStateCache.clear();
8186
}
8287
}
8388

src/browser/cdp/selectivity/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ export const updateSelectivityHashes = async (config: Config): Promise<void> =>
3939
}
4040
}
4141

42-
await hashWriter.commit();
42+
try {
43+
await hashWriter.commit();
44+
} catch (cause) {
45+
throw new Error("Selectivity: couldn't save test dependencies hash", { cause });
46+
}
4347
}
4448
};
4549

src/browser/cdp/selectivity/runner.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export class SelectivityRunner {
119119
private readonly _opts?: SelectivityRunnerOptions;
120120
private readonly _browserSelectivityDisabledCache: Record<string, void | Promise<boolean>> = {};
121121
private readonly _testsToRun: [Test, string][] = [];
122-
private readonly _processingTestLimit = pLimit(10);
122+
private readonly _processingTestLimit = pLimit(16);
123123
private readonly _processingTestPromises: Array<Promise<void>> = [];
124124

125125
static create(...args: ConstructorParameters<typeof this>): SelectivityRunner {
@@ -197,5 +197,6 @@ export class SelectivityRunner {
197197

198198
shouldDisableBrowserSelectivity.cache.clear?.();
199199
shouldDisableTestBySelectivity.cache.clear?.();
200+
getHashReader(this._config.selectivity.testDependenciesPath, this._config.selectivity.compression).clearCache();
200201
}
201202
}

src/testplane.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,9 @@ export class Testplane extends BaseTestplane {
208208
);
209209

210210
if (!shouldDisableSelectivity && !this.isFailed()) {
211-
await updateSelectivityHashes(this.config);
211+
await updateSelectivityHashes(this.config).catch(err => {
212+
console.error("Skipping selectivity state update because of an error:", err);
213+
});
212214
}
213215

214216
if (this.config.afterAll) {

test/src/browser/cdp/selectivity/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,5 +412,26 @@ describe("CDP/Selectivity", () => {
412412
assert.calledTwice(hashWriterMock.addPatternDependencyHash);
413413
assert.calledTwice(hashWriterMock.commit);
414414
});
415+
416+
it("should throw error with cause when commit fails", async () => {
417+
configMock.getBrowserIds.returns(["chrome"]);
418+
configMock.forBrowser.withArgs("chrome").returns({
419+
selectivity: {
420+
enabled: true,
421+
testDependenciesPath: "/test/path",
422+
compression: "none",
423+
disableSelectivityPatterns: ["pattern1"],
424+
},
425+
});
426+
427+
hashReaderMock.patternHasChanged.resolves(true);
428+
const commitError = new Error("Failed to write file");
429+
hashWriterMock.commit.rejects(commitError);
430+
431+
await assert.isRejected(
432+
updateSelectivityHashes(configMock as any),
433+
/Selectivity: couldn't save test dependencies hash/,
434+
);
435+
});
415436
});
416437
});

0 commit comments

Comments
 (0)