@godzillaba/mutest 1.3.5 → 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.
Files changed (3) hide show
  1. package/README.md +9 -1
  2. package/index.ts +17 -5
  3. 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 10 parallel copies of your project
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,7 +28,7 @@ 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
34
  const remappings = remappingsRaw.trim().replaceAll('/=', '=').split("\n").filter(Boolean);
@@ -36,7 +36,6 @@ async function runGambit(solFiles: string[]): Promise<Mutant[]> {
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 = process.argv.slice(2);
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@godzillaba/mutest",
3
- "version": "1.3.5",
3
+ "version": "1.4.0",
4
4
  "bin": {
5
5
  "mutest": "./index.ts"
6
6
  },