@godzillaba/mutest 1.2.2 → 1.3.3
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 +8 -6
- package/index.ts +19 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,16 +15,18 @@ Pass one or more Solidity files. Mutest will:
|
|
|
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)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Surviving mutants are written to `gambit_out/survivors.json`.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
[SURVIVED] #3 AssignmentMutation src/Counter.sol
|
|
20
|
+
### Re-testing survivors
|
|
21
|
+
|
|
22
|
+
Run without arguments to re-test only the survivors from a previous run:
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
```sh
|
|
25
|
+
npx @godzillaba/mutest
|
|
26
26
|
```
|
|
27
27
|
|
|
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
|
+
|
|
28
30
|
## Requirements
|
|
29
31
|
|
|
30
32
|
- [Foundry](https://getfoundry.sh/) (`forge`)
|
package/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { execFile as execFileCb } from "child_process";
|
|
3
|
-
import { readFile, cp, rm } from "fs/promises";
|
|
3
|
+
import { readFile, writeFile, cp, rm } from "fs/promises";
|
|
4
4
|
import { promisify } from "util";
|
|
5
5
|
|
|
6
6
|
const execFile = promisify(execFileCb);
|
|
@@ -65,8 +65,8 @@ async function processMutants(
|
|
|
65
65
|
for (let i = 0; i < mutants.length; i++)
|
|
66
66
|
queues[i % workerCount].push(mutants[i]);
|
|
67
67
|
|
|
68
|
-
let
|
|
69
|
-
|
|
68
|
+
let done = 0;
|
|
69
|
+
const survivors: Mutant[] = [];
|
|
70
70
|
|
|
71
71
|
const workers = queues.map(async (queue, workerIdx) => {
|
|
72
72
|
const workerDir = `${tempDir}/worker-${workerIdx}`;
|
|
@@ -74,26 +74,29 @@ async function processMutants(
|
|
|
74
74
|
await cp(`gambit_out/${mutant.name}`, `${workerDir}/${mutant.original}`);
|
|
75
75
|
try {
|
|
76
76
|
await execFile("forge", ["test", "--optimize", "false", "--root", workerDir]);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} catch {
|
|
82
|
-
killed++;
|
|
83
|
-
console.log(
|
|
84
|
-
`[KILLED] #${mutant.id} ${mutant.description} ${mutant.original}`,
|
|
85
|
-
);
|
|
86
|
-
}
|
|
77
|
+
survivors.push(mutant);
|
|
78
|
+
} catch {}
|
|
79
|
+
done++;
|
|
80
|
+
console.log(`${done}/${mutants.length} tested`);
|
|
87
81
|
}
|
|
88
82
|
});
|
|
89
83
|
|
|
90
84
|
await Promise.all(workers);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
85
|
+
const killed = mutants.length - survivors.length;
|
|
86
|
+
console.log(`\n\n${killed} killed, ${survivors.length} survived`);
|
|
87
|
+
await writeFile("gambit_out/survivors.json", JSON.stringify(survivors, null, 2) + "\n");
|
|
88
|
+
console.log(`Wrote survivors.json`);
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
async function loadExistingMutants(): Promise<Mutant[]> {
|
|
92
|
+
try {
|
|
93
|
+
const raw = await readFile("gambit_out/survivors.json", "utf-8");
|
|
94
|
+
const survivors: Mutant[] = JSON.parse(raw);
|
|
95
|
+
if (survivors.length > 0) {
|
|
96
|
+
console.log(`Found survivors.json, re-testing ${survivors.length} survivors...`);
|
|
97
|
+
return survivors;
|
|
98
|
+
}
|
|
99
|
+
} catch {}
|
|
97
100
|
const raw = await readFile("gambit_out/gambit_results.json", "utf-8");
|
|
98
101
|
return JSON.parse(raw);
|
|
99
102
|
}
|