jest-test-lineage-reporter 2.1.3 ā 2.2.0
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/README.md +9 -1
- package/package.json +1 -1
- package/src/MutationTester.js +91 -34
- package/src/cli/index.js +1 -0
- package/src/cli/utils/config-loader.js +3 -0
package/README.md
CHANGED
|
@@ -90,16 +90,24 @@ jest-lineage test --no-performance --no-quality
|
|
|
90
90
|
Run mutation testing standalone (on existing lineage data).
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
|
-
# Basic mutation testing
|
|
93
|
+
# Basic mutation testing (serial)
|
|
94
94
|
jest-lineage mutate
|
|
95
95
|
|
|
96
96
|
# With custom threshold
|
|
97
97
|
jest-lineage mutate --threshold 90
|
|
98
98
|
|
|
99
|
+
# Parallel execution with 4 workers (faster!)
|
|
100
|
+
jest-lineage mutate --workers 4
|
|
101
|
+
|
|
102
|
+
# Auto-detect CPU cores and use optimal worker count
|
|
103
|
+
jest-lineage mutate --workers 0
|
|
104
|
+
|
|
99
105
|
# Debug mode (create mutation files without running tests)
|
|
100
106
|
jest-lineage mutate --debug --debug-dir ./mutations
|
|
101
107
|
```
|
|
102
108
|
|
|
109
|
+
**Performance Tip**: Use `--workers` to run mutations in parallel. Each worker processes a different file concurrently, significantly reducing execution time for projects with multiple files.
|
|
110
|
+
|
|
103
111
|
**Note**: Mutation results are saved to `.jest-lineage-mutation-results.json`. Run `jest-lineage report` after mutation testing to generate an HTML report with mutation data included.
|
|
104
112
|
|
|
105
113
|
#### `jest-lineage report`
|
package/package.json
CHANGED
package/src/MutationTester.js
CHANGED
|
@@ -219,42 +219,99 @@ class MutationTester {
|
|
|
219
219
|
fileResults: {},
|
|
220
220
|
};
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
|
|
222
|
+
// Determine worker count
|
|
223
|
+
const workers = this.config.workers || 1;
|
|
224
|
+
const shouldParallelize = workers > 1 || workers === 0;
|
|
225
|
+
|
|
226
|
+
if (shouldParallelize) {
|
|
227
|
+
// Parallel execution - process multiple files concurrently
|
|
228
|
+
const os = require('os');
|
|
229
|
+
const actualWorkers = workers === 0 ? Math.max(1, os.cpus().length - 1) : workers;
|
|
230
|
+
console.log(`\nā” Running mutations in parallel with ${actualWorkers} workers\n`);
|
|
231
|
+
|
|
232
|
+
const fileEntries = Object.entries(this.lineageData);
|
|
233
|
+
const filePromises = fileEntries.map(async ([filePath, lines], index) => {
|
|
234
|
+
console.log(
|
|
235
|
+
`\nš¬ [Worker ${(index % actualWorkers) + 1}] Testing mutations in ${filePath} (${index + 1}/${totalFiles})...`
|
|
236
|
+
);
|
|
224
237
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
const fileResults = await this.testFileLines(
|
|
239
|
+
filePath,
|
|
240
|
+
lines,
|
|
241
|
+
0, // Start from 0 for each file in parallel mode
|
|
242
|
+
totalMutationsCount
|
|
243
|
+
);
|
|
230
244
|
|
|
231
|
-
|
|
232
|
-
filePath
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
245
|
+
// Log file completion summary
|
|
246
|
+
const fileName = filePath.split("/").pop();
|
|
247
|
+
const fileScore =
|
|
248
|
+
fileResults.totalMutations > 0
|
|
249
|
+
? Math.round(
|
|
250
|
+
(fileResults.killedMutations / fileResults.totalMutations) * 100
|
|
251
|
+
)
|
|
252
|
+
: 0;
|
|
253
|
+
console.log(
|
|
254
|
+
`ā
[Worker ${(index % actualWorkers) + 1}] ${fileName}: ${fileResults.totalMutations} mutations, ${fileResults.killedMutations} killed, ${fileResults.survivedMutations} survived (${fileScore}% score)`
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return { filePath, fileResults };
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Process files with concurrency limit
|
|
261
|
+
const chunkSize = actualWorkers;
|
|
262
|
+
for (let i = 0; i < filePromises.length; i += chunkSize) {
|
|
263
|
+
const chunk = filePromises.slice(i, i + chunkSize);
|
|
264
|
+
const chunkResults = await Promise.all(chunk);
|
|
265
|
+
|
|
266
|
+
// Aggregate results
|
|
267
|
+
for (const { filePath, fileResults } of chunkResults) {
|
|
268
|
+
results.fileResults[filePath] = fileResults;
|
|
269
|
+
results.totalMutations += fileResults.totalMutations;
|
|
270
|
+
results.killedMutations += fileResults.killedMutations;
|
|
271
|
+
results.survivedMutations += fileResults.survivedMutations;
|
|
272
|
+
results.timeoutMutations += fileResults.timeoutMutations;
|
|
273
|
+
results.errorMutations += fileResults.errorMutations;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
// Serial execution - process one file at a time
|
|
278
|
+
let currentFileIndex = 0;
|
|
279
|
+
let currentMutationIndex = 0;
|
|
280
|
+
|
|
281
|
+
for (const [filePath, lines] of Object.entries(this.lineageData)) {
|
|
282
|
+
currentFileIndex++;
|
|
283
|
+
console.log(
|
|
284
|
+
`\nš¬ Testing mutations in ${filePath} (${currentFileIndex}/${totalFiles})...`
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const fileResults = await this.testFileLines(
|
|
288
|
+
filePath,
|
|
289
|
+
lines,
|
|
290
|
+
currentMutationIndex,
|
|
291
|
+
totalMutationsCount
|
|
292
|
+
);
|
|
293
|
+
results.fileResults[filePath] = fileResults;
|
|
294
|
+
|
|
295
|
+
results.totalMutations += fileResults.totalMutations;
|
|
296
|
+
results.killedMutations += fileResults.killedMutations;
|
|
297
|
+
results.survivedMutations += fileResults.survivedMutations;
|
|
298
|
+
results.timeoutMutations += fileResults.timeoutMutations;
|
|
299
|
+
results.errorMutations += fileResults.errorMutations;
|
|
300
|
+
|
|
301
|
+
currentMutationIndex += fileResults.totalMutations;
|
|
302
|
+
|
|
303
|
+
// Log file completion summary
|
|
304
|
+
const fileName = filePath.split("/").pop();
|
|
305
|
+
const fileScore =
|
|
306
|
+
fileResults.totalMutations > 0
|
|
307
|
+
? Math.round(
|
|
308
|
+
(fileResults.killedMutations / fileResults.totalMutations) * 100
|
|
309
|
+
)
|
|
310
|
+
: 0;
|
|
311
|
+
console.log(
|
|
312
|
+
`ā
${fileName}: ${fileResults.totalMutations} mutations, ${fileResults.killedMutations} killed, ${fileResults.survivedMutations} survived (${fileScore}% score)`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
258
315
|
}
|
|
259
316
|
|
|
260
317
|
// Calculate mutation score
|
package/src/cli/index.js
CHANGED
|
@@ -47,6 +47,7 @@ async function run(argv) {
|
|
|
47
47
|
.option('--data <path>', 'Path to lineage data file', '.jest-lineage-data.json')
|
|
48
48
|
.option('--threshold <number>', 'Mutation score threshold (%)', '80')
|
|
49
49
|
.option('--timeout <ms>', 'Timeout per mutation (ms)', '5000')
|
|
50
|
+
.option('--workers <number>', 'Number of parallel workers (1=serial, 0=auto)', '1')
|
|
50
51
|
.option('--debug', 'Create debug mutation files instead of running tests')
|
|
51
52
|
.option('--debug-dir <path>', 'Directory for debug files', './mutations-debug')
|
|
52
53
|
.option('--operators <list>', 'Comma-separated mutation operators to enable')
|
|
@@ -50,6 +50,9 @@ function mapCliOptionsToConfig(cliOptions) {
|
|
|
50
50
|
if (cliOptions.timeout !== undefined) {
|
|
51
51
|
config.mutationTimeout = parseInt(cliOptions.timeout);
|
|
52
52
|
}
|
|
53
|
+
if (cliOptions.workers !== undefined) {
|
|
54
|
+
config.workers = parseInt(cliOptions.workers);
|
|
55
|
+
}
|
|
53
56
|
if (cliOptions.debug === true) {
|
|
54
57
|
config.debugMutations = true;
|
|
55
58
|
}
|