@godzillaba/mutest 1.3.4 → 1.4.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/index.ts +18 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npx @godzillaba/mutest src/Counter.sol
|
|
|
10
10
|
|
|
11
11
|
Pass one or more Solidity files. Mutest will:
|
|
12
12
|
|
|
13
|
-
1. Create
|
|
13
|
+
1. Create parallel copies of your project (8 by default)
|
|
14
14
|
2. Generate mutants with Gambit (e.g. `++` -> `--`, assignments replaced)
|
|
15
15
|
3. Run `forge test` against each mutant across the worker copies
|
|
16
16
|
4. Report which mutants were killed (tests caught them) or survived (coverage gap)
|
|
@@ -27,6 +27,14 @@ npx @godzillaba/mutest
|
|
|
27
27
|
|
|
28
28
|
This reads `gambit_out/survivors.json` (or falls back to `gambit_out/gambit_results.json`) and re-runs the test suite against those mutants. Useful after improving your tests to check if previously surviving mutants are now caught.
|
|
29
29
|
|
|
30
|
+
### Options
|
|
31
|
+
|
|
32
|
+
`--workers <n>` / `-w <n>` — number of parallel workers (default: 8).
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npx @godzillaba/mutest --workers 4 src/Counter.sol
|
|
36
|
+
```
|
|
37
|
+
|
|
30
38
|
## Requirements
|
|
31
39
|
|
|
32
40
|
- [Foundry](https://getfoundry.sh/) (`forge`)
|
package/index.ts
CHANGED
|
@@ -28,15 +28,14 @@ async function setupWorkers(workerCount: number): Promise<string> {
|
|
|
28
28
|
return root;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async function runGambit(solFiles: string[]): Promise<Mutant[]> {
|
|
31
|
+
async function runGambit(solFiles: string[], concurrency: number): Promise<Mutant[]> {
|
|
32
32
|
await rm("gambit_out", { recursive: true, force: true });
|
|
33
33
|
const { stdout: remappingsRaw } = await execFile("forge", ["remappings"]);
|
|
34
|
-
const remappings = remappingsRaw.trim().split("\n").filter(Boolean);
|
|
34
|
+
const remappings = remappingsRaw.trim().replaceAll('/=', '=').split("\n").filter(Boolean);
|
|
35
35
|
const remapArgs = remappings.flatMap((r) => ["--solc_remappings", r]);
|
|
36
36
|
|
|
37
37
|
const results: Mutant[] = [];
|
|
38
38
|
const pending = [...solFiles];
|
|
39
|
-
const concurrency = 10;
|
|
40
39
|
|
|
41
40
|
async function worker() {
|
|
42
41
|
while (pending.length > 0) {
|
|
@@ -105,9 +104,22 @@ async function loadExistingMutants(): Promise<Mutant[]> {
|
|
|
105
104
|
return JSON.parse(raw);
|
|
106
105
|
}
|
|
107
106
|
|
|
107
|
+
function parseArgs() {
|
|
108
|
+
const args = process.argv.slice(2);
|
|
109
|
+
let workers = 8;
|
|
110
|
+
const solFiles: string[] = [];
|
|
111
|
+
for (let i = 0; i < args.length; i++) {
|
|
112
|
+
if (args[i] === "--workers" || args[i] === "-w") {
|
|
113
|
+
workers = parseInt(args[++i], 10);
|
|
114
|
+
} else {
|
|
115
|
+
solFiles.push(args[i]);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { solFiles, workers };
|
|
119
|
+
}
|
|
120
|
+
|
|
108
121
|
async function main() {
|
|
109
|
-
const solFiles =
|
|
110
|
-
const workerCount = 10;
|
|
122
|
+
const { solFiles, workers: workerCount } = parseArgs();
|
|
111
123
|
console.log(`Setting up ${workerCount} workers...`);
|
|
112
124
|
const tempDir = await setupWorkers(workerCount);
|
|
113
125
|
|
|
@@ -115,7 +127,7 @@ async function main() {
|
|
|
115
127
|
let mutants: Mutant[];
|
|
116
128
|
if (solFiles.length > 0) {
|
|
117
129
|
console.log(`Running gambit on ${solFiles.join(", ")}...`);
|
|
118
|
-
mutants = await runGambit(solFiles);
|
|
130
|
+
mutants = await runGambit(solFiles, workerCount);
|
|
119
131
|
} else {
|
|
120
132
|
console.log("No files specified, using existing gambit_out/...");
|
|
121
133
|
mutants = await loadExistingMutants();
|