Skip to content

Commit a3bef80

Browse files
feat(selectivity): implement read-only and write-only selectivity modes
1 parent 73542e1 commit a3bef80

File tree

11 files changed

+235
-94
lines changed

11 files changed

+235
-94
lines changed

src/browser/cdp/selectivity/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { getCollectedTestplaneDependencies } from "./testplane-selectivity";
1010
import { getHashReader } from "./hash-reader";
1111
import type { Config } from "../../../config";
1212
import { MasterEvents } from "../../../events";
13+
import { selectivityShouldWrite } from "./modes";
1314

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

@@ -24,7 +25,7 @@ export const updateSelectivityHashes = async (config: Config): Promise<void> =>
2425
const browserConfig = config.forBrowser(browserId);
2526
const { enabled, testDependenciesPath, compression, disableSelectivityPatterns } = browserConfig.selectivity;
2627

27-
if (!enabled) {
28+
if (!selectivityShouldWrite(enabled)) {
2829
continue;
2930
}
3031

@@ -51,7 +52,7 @@ export const startSelectivity = async (browser: ExistingBrowser): Promise<StopSe
5152
const { enabled, compression, sourceRoot, testDependenciesPath, mapDependencyRelativePath } =
5253
browser.config.selectivity;
5354

54-
if (!enabled || !browser.publicAPI.isChromium) {
55+
if (!selectivityShouldWrite(enabled) || !browser.publicAPI.isChromium) {
5556
return () => Promise.resolve();
5657
}
5758

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { SelectivityMode, type SelectivityModeValue } from "../../../config/types";
2+
3+
export const selectivityIsDisabled = (mode: SelectivityModeValue): boolean => mode === SelectivityMode.Disabled;
4+
export const selectivityShouldWrite = (mode: SelectivityModeValue): boolean =>
5+
mode === SelectivityMode.Enabled || mode === SelectivityMode.WriteOnly;
6+
export const selectivityShouldRead = (mode: SelectivityModeValue): boolean =>
7+
mode === SelectivityMode.Enabled || mode === SelectivityMode.ReadOnly;

src/browser/cdp/selectivity/runner.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import type { Test, TestDepsContext, TestDepsData } from "../../../types";
99
import { MasterEvents } from "../../../events";
1010
import { getHashWriter } from "./hash-writer";
1111
import { getTestDependenciesReader } from "./test-dependencies-reader";
12+
import { selectivityShouldRead } from "./modes";
1213

1314
/** Called at the start of testplane run per each browser */
1415
const shouldDisableBrowserSelectivity = _.memoize(
1516
async (config: BrowserConfig, browserId: string): Promise<boolean> => {
16-
if (!config.selectivity.enabled) {
17+
if (!selectivityShouldRead(config.selectivity.enabled)) {
1718
return true;
1819
}
1920

@@ -60,8 +61,9 @@ const shouldDisableBrowserSelectivity = _.memoize(
6061
},
6162
config => {
6263
const { enabled, testDependenciesPath, compression, disableSelectivityPatterns } = config.selectivity;
63-
return enabled
64-
? enabled + "#" + testDependenciesPath + "#" + compression + "#" + disableSelectivityPatterns.join("#")
64+
65+
return selectivityShouldRead(enabled)
66+
? testDependenciesPath + "#" + compression + "#" + disableSelectivityPatterns.join("#")
6567
: "";
6668
},
6769
);
@@ -70,7 +72,7 @@ const shouldDisableTestBySelectivity = _.memoize(
7072
async (config: BrowserConfig, test: Test): Promise<boolean> => {
7173
const { enabled, testDependenciesPath, compression } = config.selectivity;
7274

73-
if (!enabled) {
75+
if (!selectivityShouldRead(enabled)) {
7476
return false;
7577
}
7678

@@ -99,7 +101,7 @@ const shouldDisableTestBySelectivity = _.memoize(
99101
(config, test) => {
100102
const { enabled, testDependenciesPath, compression } = config.selectivity;
101103

102-
return enabled ? enabled + "#" + testDependenciesPath + "#" + compression + "#" + test.id : "";
104+
return selectivityShouldRead(enabled) ? testDependenciesPath + "#" + compression + "#" + test.id : "";
103105
},
104106
);
105107

@@ -157,11 +159,11 @@ export class SelectivityRunner {
157159

158160
startTestCheckToRun(test: Test, browserId: string): void {
159161
const browserConfig = this._config.forBrowser(browserId);
160-
const isSelectivityEnabledForBrowser = browserConfig.selectivity.enabled;
162+
const shouldSelectivelySkipTests = selectivityShouldRead(browserConfig.selectivity.enabled);
161163

162164
// If selectivity is disabled for browser
163165
// If test is disabled on its own (e.g plugin testplane/chunks) we dont waste our time calculating the deps.
164-
if (!isSelectivityEnabledForBrowser || this._opts?.shouldDisableSelectivity || test.disabled) {
166+
if (!shouldSelectivelySkipTests || this._opts?.shouldDisableSelectivity || test.disabled) {
165167
this._testsToRun.push([test, browserId]);
166168
return;
167169
}
@@ -197,6 +199,10 @@ export class SelectivityRunner {
197199

198200
shouldDisableBrowserSelectivity.cache.clear?.();
199201
shouldDisableTestBySelectivity.cache.clear?.();
200-
getHashReader(this._config.selectivity.testDependenciesPath, this._config.selectivity.compression).clearCache();
202+
this._config.getBrowserIds().forEach(browserId => {
203+
const { selectivity } = this._config.forBrowser(browserId);
204+
205+
getHashReader(selectivity.testDependenciesPath, selectivity.compression).clearCache();
206+
});
201207
}
202208
}

src/config/types.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,16 +268,29 @@ type ReadinessProbeObj = {
268268
type ReadinessProbe = ReadinessProbeFn | ReadinessProbeObj;
269269

270270
export enum TimeTravelMode {
271-
// Record and save all test runs
271+
/** Record and save all test runs */
272272
On = "on",
273-
// Do not record any test runs
273+
/** Do not record any test runs */
274274
Off = "off",
275-
// Record and save all retries
275+
/** Record and save all retries */
276276
RetriesOnly = "retries-only",
277-
// Record all test runs, but save only last failed run
277+
/** Record all test runs, but save only last failed run */
278278
LastFailedRun = "last-failed-run",
279279
}
280280

281+
export const SelectivityMode = {
282+
/** @description Completely disables Testplane selectivity */
283+
Disabled: false,
284+
/** @description Skips running unchanged tests, does not update selectivity state after successful run */
285+
ReadOnly: "read-only",
286+
/** @description Runs unchanged tests, updates selectivity state after successful run */
287+
WriteOnly: "write-only",
288+
/** @description Skips running unchanged tests, updates selectivity state after successful run */
289+
Enabled: true,
290+
} as const;
291+
292+
export type SelectivityModeValue = (typeof SelectivityMode)[keyof typeof SelectivityMode];
293+
281294
export interface TimeTravelConfig {
282295
mode: TimeTravelMode;
283296
}
@@ -392,7 +405,7 @@ export interface CommonConfig {
392405
};
393406

394407
selectivity: {
395-
enabled: boolean;
408+
enabled: SelectivityModeValue;
396409
sourceRoot: string;
397410
testDependenciesPath: string;
398411
compression: SelectivityCompressionType;

src/config/utils.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import _ from "lodash";
2-
import { ConfigParsed } from "./types";
2+
import { ConfigParsed, SelectivityMode, SelectivityModeValue } from "./types";
33

44
type ValueType = "string" | "number" | "boolean" | "object" | "undefined" | "function";
55

@@ -100,16 +100,21 @@ export const addUserAgentToArgs = (config: ConfigParsed): ConfigParsed => {
100100
return config;
101101
};
102102

103-
export const extractSelectivityEnabledEnvVariable = (envPrefixes: string[] = []): { enabled?: boolean } => {
103+
export const extractSelectivityEnabledEnvVariable = (
104+
envPrefixes: string[] = [],
105+
): { enabled?: SelectivityModeValue } => {
104106
for (const envPrefix of envPrefixes) {
105107
const envName = envPrefix + "selectivity_enabled";
106108

107-
if (process.env[envName] === String(true)) {
108-
return { enabled: true };
109-
}
110-
111-
if (process.env[envName] === String(false)) {
112-
return { enabled: false };
109+
switch (process.env[envName]) {
110+
case String(SelectivityMode.Enabled):
111+
return { enabled: SelectivityMode.Enabled };
112+
case String(SelectivityMode.Disabled):
113+
return { enabled: SelectivityMode.Disabled };
114+
case String(SelectivityMode.ReadOnly):
115+
return { enabled: SelectivityMode.ReadOnly };
116+
case String(SelectivityMode.WriteOnly):
117+
return { enabled: SelectivityMode.WriteOnly };
113118
}
114119
}
115120

src/worker/runner/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const {
1313
} = require("../../browser/cdp/selectivity/testplane-selectivity");
1414
const ipc = require("../../utils/ipc");
1515
const { TEST_ASSIGNED_TO_WORKER } = require("../../constants/process-messages");
16+
const { selectivityShouldWrite } = require("../../browser/cdp/selectivity/modes");
1617

1718
module.exports = class Runner extends AsyncEmitter {
1819
static create(config) {
@@ -55,13 +56,14 @@ module.exports = class Runner extends AsyncEmitter {
5556
attempt,
5657
});
5758

58-
const selectivityEnabled = config.selectivity && config.selectivity.enabled && isRunInNodeJsEnv(this._config);
59+
const shouldRecordSelectivityDeps =
60+
config.selectivity && selectivityShouldWrite(config.selectivity.enabled) && isRunInNodeJsEnv(this._config);
5961

6062
const prepareParseAndRun = async () => {
6163
runner.prepareBrowser({ sessionId, sessionCaps, sessionOpts, state });
6264

6365
const readTestsFn = () => this._testParser.parse({ file, browserId });
64-
const tests = selectivityEnabled
66+
const tests = shouldRecordSelectivityDeps
6567
? await readTestFileWithTestplaneDependenciesCollecting(file, readTestsFn)
6668
: await readTestsFn();
6769

@@ -72,6 +74,8 @@ module.exports = class Runner extends AsyncEmitter {
7274
return runner.run();
7375
};
7476

75-
return selectivityEnabled ? runWithTestplaneDependenciesCollecting(prepareParseAndRun) : prepareParseAndRun();
77+
return shouldRecordSelectivityDeps
78+
? runWithTestplaneDependenciesCollecting(prepareParseAndRun)
79+
: prepareParseAndRun();
7680
}
7781
};

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ describe("CDP/Selectivity/HashReader", () => {
136136
const result = reader.patternHasChanged(pattern);
137137

138138
assert.isRejected(result, /Couldn't find files by disableSelectivityPattern/);
139-
assert.calledWith(hashProviderMock.calculateForPattern, pattern);
140139
});
141140
});
142141

0 commit comments

Comments
 (0)