@t09tanaka/test-gates 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +5 -0
- package/dist/stryker.cjs +4 -0
- package/dist/stryker.d.cts +9 -1
- package/dist/stryker.d.ts +9 -1
- package/dist/stryker.js +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- `createStrykerGatesConfig` sets Stryker's `maxTestRunnerReuse` to 50 unless the project passes its own (Stryker's default is 0: one test runner process for the whole run). In a project with mutants that never end, the runner became about three times slower from the first of them on, and mutants that are killed in seconds on their own timed out. Starting a new process every 50 mutants kept the speed: a full run of 781 mutants went from 14 to 18 minutes with stray timeouts to under 7 minutes with none.
|
|
8
|
+
- `timeoutMS` is no longer set by the helper; Stryker's default (5000) applies again. The 30000 of 0.3.1 was the wrong fix. It rested on the idea that a restarted runner makes the next mutant slow, when in fact a runner that is not restarted stays slow. With 30000 the same full run took 47 minutes and still failed, and mutants that exhaust the heap crashed the runner and were reported as `RuntimeError` instead of `Timeout`.
|
|
9
|
+
|
|
10
|
+
A project that set `timeoutMS` itself to work around stray timeouts can remove it.
|
|
11
|
+
|
|
12
|
+
## 0.3.1
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `createStrykerGatesConfig` sets Stryker's `timeoutMS` to 30000 unless the project passes its own (Stryker's default is 5000). With `maxTimeouts` at 0 since 0.3.0, a mutant that would be killed but ran out of time fails the run. That happened on every full run in two projects with mutants that really never end: after each of them Stryker starts a new test runner, and the next mutant timed out while it was starting. A full run takes longer by up to 25 seconds for each mutant that hangs without tripping Stryker's loop counter.
|
|
17
|
+
|
|
3
18
|
## 0.3.0
|
|
4
19
|
|
|
5
20
|
Three defaults move to the strict side. A project that passed with 0.2.0 can fail with 0.3.0 without any change of its own; each case below says what to do.
|
package/README.md
CHANGED
|
@@ -280,6 +280,10 @@ How an `expectedTimeouts` entry is judged:
|
|
|
280
280
|
|
|
281
281
|
When the run fails on a timeout, first run again with less load (`npm run test:gates:mutation -- --concurrency 1`). Add an entry only for a mutant that can never finish.
|
|
282
282
|
|
|
283
|
+
`createStrykerGatesConfig` sets Stryker's `maxTestRunnerReuse` to 50 (since 0.3.2; Stryker's own default is 0, one test runner process for the whole run). A mutant that would be killed can also time out for a reason that has nothing to do with the mutant: in a Jest project with three mutants that never end, the test runner handled about a third as many mutants per minute from the first of them on, for the rest of the run. Mutants that are killed in seconds on their own then timed out, a different set on each full run, and the run took 14 to 18 minutes. With a new process every 50 mutants the speed stayed the same to the end, nothing timed out that should not, and the run took under 7 minutes. Why the runner slows down was not found; the restart only cuts it short. Pass `maxTestRunnerReuse` to the helper to change it.
|
|
284
|
+
|
|
285
|
+
0.3.1 set `timeoutMS` to 30000 instead. That was the wrong fix and 0.3.2 takes it back: the longer limit let most slow mutants finish, but the run above took 47 minutes, and in another project mutants that exhaust the heap crashed the test runner before the limit and came back as `RuntimeError`, which is only counted, not failed. `timeoutMS` is Stryker's default (5000) again unless the project sets it.
|
|
286
|
+
|
|
283
287
|
### `test-gates selfcheck [--all | --first] [-- <gate command>]`
|
|
284
288
|
|
|
285
289
|
Negative control. For each gate, runs the gate with that gate's spec left out and requires the run to **fail on the coverage threshold of that very file**. A gate that stays green without its spec measures nothing; a run that fails for another reason (a config that does not load) proves nothing either.
|
|
@@ -460,6 +464,7 @@ Why these choices:
|
|
|
460
464
|
- Jest measures with istanbul (`babel`), because the v8 provider does not count the untaken side of an `if` without `else`.
|
|
461
465
|
- Jest fails on a `coverageThreshold` path key that matches no file, so a typo in `test-gates.json` cannot pass. Vitest does the opposite: a glob or path key in `thresholds` that matches nothing passes. The Vitest helper therefore narrows `coverage.include` to the gates and uses `perFile`.
|
|
462
466
|
- Stryker never fails on its own score (`break: null`). The verdict comes from `test-gates mutation`, which knows the allow list.
|
|
467
|
+
- The Stryker helper also sets defaults that can be overridden: `coverageAnalysis: 'perTest'`, `incremental` with its file under `reports/`, and `maxTestRunnerReuse: 50` (see [Timeouts](#timeouts)).
|
|
463
468
|
|
|
464
469
|
`loadGates(dir)` returns the validated manifest: `gates` (each with its resolved `spec`), `candidates` and the resolved `settings`. It throws when the manifest has any problem.
|
|
465
470
|
|
package/dist/stryker.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/stryker.ts
|
|
31
31
|
var stryker_exports = {};
|
|
32
32
|
__export(stryker_exports, {
|
|
33
|
+
DEFAULT_MAX_TEST_RUNNER_REUSE: () => DEFAULT_MAX_TEST_RUNNER_REUSE,
|
|
33
34
|
createStrykerGatesConfig: () => createStrykerGatesConfig
|
|
34
35
|
});
|
|
35
36
|
module.exports = __toCommonJS(stryker_exports);
|
|
@@ -601,6 +602,7 @@ function resolveRootDir(rootDir) {
|
|
|
601
602
|
|
|
602
603
|
// src/stryker.ts
|
|
603
604
|
var WEAKENING_OPTIONS = ["ignoreStatic", "ignorers"];
|
|
605
|
+
var DEFAULT_MAX_TEST_RUNNER_REUSE = 50;
|
|
604
606
|
function createStrykerGatesConfig(options = {}) {
|
|
605
607
|
const { rootDir: rootDirOption, ...overrides } = options;
|
|
606
608
|
for (const option of WEAKENING_OPTIONS) {
|
|
@@ -625,6 +627,7 @@ function createStrykerGatesConfig(options = {}) {
|
|
|
625
627
|
incremental: true,
|
|
626
628
|
incrementalFile: "reports/stryker-incremental.json",
|
|
627
629
|
tempDirName: ".stryker-tmp",
|
|
630
|
+
maxTestRunnerReuse: DEFAULT_MAX_TEST_RUNNER_REUSE,
|
|
628
631
|
clearTextReporter: { logTests: false, reportTests: false },
|
|
629
632
|
...overrides,
|
|
630
633
|
mutate: manifest.gates.map((gate) => gate.path),
|
|
@@ -635,5 +638,6 @@ function createStrykerGatesConfig(options = {}) {
|
|
|
635
638
|
}
|
|
636
639
|
// Annotate the CommonJS export names for ESM import in node:
|
|
637
640
|
0 && (module.exports = {
|
|
641
|
+
DEFAULT_MAX_TEST_RUNNER_REUSE,
|
|
638
642
|
createStrykerGatesConfig
|
|
639
643
|
});
|
package/dist/stryker.d.cts
CHANGED
|
@@ -4,6 +4,14 @@ interface StrykerGatesOptions {
|
|
|
4
4
|
/** Any other Stryker option: testRunner, jest / vitest, concurrency, ignorePatterns, … */
|
|
5
5
|
[option: string]: unknown;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Stryker's own default is 0: one test runner process for the whole run. Measured on a Jest
|
|
9
|
+
* project with three mutants that never end (`Hit limit reached`): from the first of them on
|
|
10
|
+
* the runner handled about a third as many mutants per minute for the rest of the run, and
|
|
11
|
+
* mutants that are killed in seconds on their own ran out of time. With a new process every 50
|
|
12
|
+
* mutants the speed stayed the same to the end.
|
|
13
|
+
*/
|
|
14
|
+
declare const DEFAULT_MAX_TEST_RUNNER_REUSE = 50;
|
|
7
15
|
/**
|
|
8
16
|
* Stryker config for the gates: mutate exactly the gates, never fail on the score
|
|
9
17
|
* (`thresholds.break: null`), and write the JSON report that `test-gates mutation` judges.
|
|
@@ -13,4 +21,4 @@ interface StrykerGatesOptions {
|
|
|
13
21
|
*/
|
|
14
22
|
declare function createStrykerGatesConfig(options?: StrykerGatesOptions): Record<string, unknown>;
|
|
15
23
|
|
|
16
|
-
export { type StrykerGatesOptions, createStrykerGatesConfig };
|
|
24
|
+
export { DEFAULT_MAX_TEST_RUNNER_REUSE, type StrykerGatesOptions, createStrykerGatesConfig };
|
package/dist/stryker.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ interface StrykerGatesOptions {
|
|
|
4
4
|
/** Any other Stryker option: testRunner, jest / vitest, concurrency, ignorePatterns, … */
|
|
5
5
|
[option: string]: unknown;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Stryker's own default is 0: one test runner process for the whole run. Measured on a Jest
|
|
9
|
+
* project with three mutants that never end (`Hit limit reached`): from the first of them on
|
|
10
|
+
* the runner handled about a third as many mutants per minute for the rest of the run, and
|
|
11
|
+
* mutants that are killed in seconds on their own ran out of time. With a new process every 50
|
|
12
|
+
* mutants the speed stayed the same to the end.
|
|
13
|
+
*/
|
|
14
|
+
declare const DEFAULT_MAX_TEST_RUNNER_REUSE = 50;
|
|
7
15
|
/**
|
|
8
16
|
* Stryker config for the gates: mutate exactly the gates, never fail on the score
|
|
9
17
|
* (`thresholds.break: null`), and write the JSON report that `test-gates mutation` judges.
|
|
@@ -13,4 +21,4 @@ interface StrykerGatesOptions {
|
|
|
13
21
|
*/
|
|
14
22
|
declare function createStrykerGatesConfig(options?: StrykerGatesOptions): Record<string, unknown>;
|
|
15
23
|
|
|
16
|
-
export { type StrykerGatesOptions, createStrykerGatesConfig };
|
|
24
|
+
export { DEFAULT_MAX_TEST_RUNNER_REUSE, type StrykerGatesOptions, createStrykerGatesConfig };
|
package/dist/stryker.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
// src/stryker.ts
|
|
9
9
|
var WEAKENING_OPTIONS = ["ignoreStatic", "ignorers"];
|
|
10
|
+
var DEFAULT_MAX_TEST_RUNNER_REUSE = 50;
|
|
10
11
|
function createStrykerGatesConfig(options = {}) {
|
|
11
12
|
const { rootDir: rootDirOption, ...overrides } = options;
|
|
12
13
|
for (const option of WEAKENING_OPTIONS) {
|
|
@@ -31,6 +32,7 @@ function createStrykerGatesConfig(options = {}) {
|
|
|
31
32
|
incremental: true,
|
|
32
33
|
incrementalFile: "reports/stryker-incremental.json",
|
|
33
34
|
tempDirName: ".stryker-tmp",
|
|
35
|
+
maxTestRunnerReuse: DEFAULT_MAX_TEST_RUNNER_REUSE,
|
|
34
36
|
clearTextReporter: { logTests: false, reportTests: false },
|
|
35
37
|
...overrides,
|
|
36
38
|
mutate: manifest.gates.map((gate) => gate.path),
|
|
@@ -40,5 +42,6 @@ function createStrykerGatesConfig(options = {}) {
|
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
export {
|
|
45
|
+
DEFAULT_MAX_TEST_RUNNER_REUSE,
|
|
43
46
|
createStrykerGatesConfig
|
|
44
47
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t09tanaka/test-gates",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Test gates for the code that must not be wrong: per-file 100% coverage as the floor, mutation testing with a per-mutant allow list as the real check.",
|
|
5
5
|
"author": "t09tanaka",
|
|
6
6
|
"license": "MIT",
|