cairo-mutate 0.1.0__tar.gz
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.
- cairo_mutate-0.1.0/LICENSE +21 -0
- cairo_mutate-0.1.0/PKG-INFO +347 -0
- cairo_mutate-0.1.0/README.md +316 -0
- cairo_mutate-0.1.0/cairo_mutate.egg-info/PKG-INFO +347 -0
- cairo_mutate-0.1.0/cairo_mutate.egg-info/SOURCES.txt +16 -0
- cairo_mutate-0.1.0/cairo_mutate.egg-info/dependency_links.txt +1 -0
- cairo_mutate-0.1.0/cairo_mutate.egg-info/entry_points.txt +2 -0
- cairo_mutate-0.1.0/cairo_mutate.egg-info/top_level.txt +2 -0
- cairo_mutate-0.1.0/mutate.py +247 -0
- cairo_mutate-0.1.0/mutators/__init__.py +40 -0
- cairo_mutate-0.1.0/mutators/as_flip.py +55 -0
- cairo_mutate-0.1.0/mutators/as_rem.py +41 -0
- cairo_mutate-0.1.0/mutators/common.py +286 -0
- cairo_mutate-0.1.0/mutators/op_ari.py +47 -0
- cairo_mutate-0.1.0/mutators/op_asg.py +44 -0
- cairo_mutate-0.1.0/mutators/op_eq.py +48 -0
- cairo_mutate-0.1.0/pyproject.toml +20 -0
- cairo_mutate-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sidarths
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cairo-mutate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mutation testing for Starknet/Cairo contracts
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 Sidarths
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
<!-- # `cairo-mutate`
|
|
33
|
+
> **Mutation-testing for Starknet contracts** -->
|
|
34
|
+
|
|
35
|
+
<h1 align="center"><b>cairo-mutate</b></h1>
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<b> Mutation testing for Starknet contracts</b>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
<div align="center">
|
|
43
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/logo.png"
|
|
44
|
+
alt="cairo-mutate"
|
|
45
|
+
style="width: 280px; border-radius: 10px;" />
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
**cairo-mutate** brings mutation testing to Starknet, giving developers a measurable signal of test quality.
|
|
51
|
+
|
|
52
|
+
Find weak tests, missing edge cases, and false confidence from high coverage.
|
|
53
|
+
|
|
54
|
+
Instead of asking:
|
|
55
|
+
|
|
56
|
+
> “Do the tests pass? What is the coverage?”
|
|
57
|
+
|
|
58
|
+
it asks:
|
|
59
|
+
|
|
60
|
+
> “If the contract were wrong, would the tests notice?”
|
|
61
|
+
|
|
62
|
+
The tool mutates contract code, reruns the test suite, and measures how many injected faults are actually caught.
|
|
63
|
+
|
|
64
|
+
That helps uncover:
|
|
65
|
+
- weak assertions
|
|
66
|
+
- missing edge cases
|
|
67
|
+
- broken invariants
|
|
68
|
+
- brittle state checks
|
|
69
|
+
- false confidence from high coverage
|
|
70
|
+
|
|
71
|
+
Even a test suite with 100% coverage can still miss important behavioral invariants. `cairo-mutate` helps reveal that gap by checking whether tests fail when those invariants are broken.
|
|
72
|
+
|
|
73
|
+
### Current Status (MVP)
|
|
74
|
+
A stable, string-based mutation engine with file-wise reporting, safe restore behavior, timeout handling, and a CLI that can run against any Starknet project root.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Quick Demo
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
cairo-mutate demo_staking_protocol -v
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Expected output:
|
|
84
|
+
|
|
85
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/demo-out.png"
|
|
86
|
+
alt="cairo-mutate"
|
|
87
|
+
style="width: 440px; border-radius: 10px;" />
|
|
88
|
+
|
|
89
|
+
> Screenshot shows part of the `-v` output for readability. Use `-vv` for the full mutation log.
|
|
90
|
+
|
|
91
|
+
## Requirements
|
|
92
|
+
|
|
93
|
+
- Python 3.10+
|
|
94
|
+
- `snforge`
|
|
95
|
+
- `scarb` `2.14.0` or newer
|
|
96
|
+
|
|
97
|
+
If your shell resolves the wrong `scarb`, use the asdf shim path:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
env PATH="$HOME/.asdf/shims:$PATH" snforge test
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Installation
|
|
104
|
+
|
|
105
|
+
### Install from PyPI
|
|
106
|
+
|
|
107
|
+
Create a virtual environment and install the published package:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python3 -m venv .venv
|
|
111
|
+
source .venv/bin/activate
|
|
112
|
+
|
|
113
|
+
pip install cairo-mutate
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Install from Source
|
|
117
|
+
|
|
118
|
+
Clone the repository and install it in editable mode:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
git clone https://github.com/<your-username>/cairo-mutate
|
|
122
|
+
cd cairo-mutate
|
|
123
|
+
|
|
124
|
+
# create venv
|
|
125
|
+
python3 -m venv .venv
|
|
126
|
+
source .venv/bin/activate
|
|
127
|
+
|
|
128
|
+
pip install -e .
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The CLI entrypoint is `cairo-mutate`.
|
|
132
|
+
|
|
133
|
+
When working from source, this is implemented via [`mutate.py`](./mutate.py).
|
|
134
|
+
|
|
135
|
+
## Use Cases
|
|
136
|
+
|
|
137
|
+
- Evaluate test strength before deployment
|
|
138
|
+
- Detect missing assertions and edge cases
|
|
139
|
+
- Improve confidence in contract invariants
|
|
140
|
+
- Demonstrate test quality in audits and grants
|
|
141
|
+
|
|
142
|
+
## Features
|
|
143
|
+
|
|
144
|
+
- Scans a Starknet project and mutates Cairo files under `src/`
|
|
145
|
+
- Applies one mutation at a time
|
|
146
|
+
- Runs a configurable test command, defaulting to `snforge test`
|
|
147
|
+
- Classifies each mutant as:
|
|
148
|
+
- `✔ Caught`
|
|
149
|
+
- `✘ Uncaught`
|
|
150
|
+
- `Compile Error`
|
|
151
|
+
- `Timeout`
|
|
152
|
+
- Restores original files automatically after each run
|
|
153
|
+
- Cleans up backups on exit, interrupt, or termination
|
|
154
|
+
- Prints file-wise mutation scores plus a final mutation score
|
|
155
|
+
- Supports quiet, summary, and full trace modes
|
|
156
|
+
|
|
157
|
+
Works on any Starknet project with `Scarb.toml`, `src/`, and `snforge` tests.
|
|
158
|
+
|
|
159
|
+
## Mutators
|
|
160
|
+
|
|
161
|
+
The current MVP uses the following mutators:
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
| Code | Abbr | Desc |
|
|
165
|
+
|--- |--- |---
|
|
166
|
+
| `AS-REM` | Assert removal | replaces an `assert` body with a no-op
|
|
167
|
+
| `AS-FLIP` | Assert condition flip | flips assertion comparisons, such as `== ↔ !=` and `> ↔ <` |
|
|
168
|
+
| `OP-EQ` | Equality operator mutation | flips equality and inequality operators outside `assert` expressions. |
|
|
169
|
+
| `OP-ARI` | Arithmetic operator mutation | flips arithmetic operators like `+ ↔ -` |
|
|
170
|
+
| `OP-ASG` | Assignment operator mutation | mutates assignment-style operations such as `+=` and `-=` into plain assignment behavior |
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
These mutators are intentionally string-based for V1. That keeps the tool fast and easy to understand while we build the Cairo-aware AST version later.
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
## Why Mutation Testing?
|
|
177
|
+
|
|
178
|
+
Passing tests ≠ correct behavior.
|
|
179
|
+
|
|
180
|
+
`cairo-mutate` measures whether your tests actually detect broken logic, permissions, and invariants.
|
|
181
|
+
|
|
182
|
+
## CLI
|
|
183
|
+
|
|
184
|
+
The intended entrypoint is `cairo-mutate`.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
cairo-mutate <target> [OPTIONS]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`<target>` is the Starknet project root that contains `Scarb.toml` and `src/`.
|
|
191
|
+
|
|
192
|
+
### Options
|
|
193
|
+
|
|
194
|
+
- `--test-cmd "snforge test"`
|
|
195
|
+
Custom test command to run after each mutant.
|
|
196
|
+
- `--file src/lib.cairo`
|
|
197
|
+
Mutate a single Cairo file relative to the project root.
|
|
198
|
+
- `--mutators as_rem,as_flip,op_eq`
|
|
199
|
+
Limit the run to specific mutators.
|
|
200
|
+
- `--timeout 20`
|
|
201
|
+
Timeout in seconds for each test command run.
|
|
202
|
+
- `--safe`
|
|
203
|
+
Run the project test command before mutation starts and again after restore.
|
|
204
|
+
- `-v`
|
|
205
|
+
Print mutator/file summaries.
|
|
206
|
+
- `-vv`
|
|
207
|
+
Print full line-by-line mutant logs.
|
|
208
|
+
- `--list-mutators`
|
|
209
|
+
Show the available mutators and exit.
|
|
210
|
+
|
|
211
|
+
### Verbosity modes
|
|
212
|
+
|
|
213
|
+
- No `-v`: report-only mode. Prints the final report and summary footer.
|
|
214
|
+
- `-v`: prints file start/finish markers, file counts, and per-mutator summaries.
|
|
215
|
+
- `-vv`: prints the full mutant log for each mutation, plus summaries and the final report.
|
|
216
|
+
|
|
217
|
+
Examples:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
cairo-mutate demo_staking_protocol --file src/lib.cairo --safe -v
|
|
221
|
+
cairo-mutate demo_staking_protocol --safe -v
|
|
222
|
+
cairo-mutate demo_staking_protocol --test-cmd "snforge test" --timeout 20 -vv
|
|
223
|
+
cairo-mutate demo_staking_protocol --mutators as_rem,as_flip,op_eq --safe
|
|
224
|
+
cairo-mutate --list-mutators
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Output Style
|
|
228
|
+
|
|
229
|
+
Example mutant line:
|
|
230
|
+
|
|
231
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/line.png"
|
|
232
|
+
alt="mutant-line"
|
|
233
|
+
style="width: 650px;" />
|
|
234
|
+
|
|
235
|
+
Example skipped summary:
|
|
236
|
+
|
|
237
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/skip.png"
|
|
238
|
+
alt="skip-summary"
|
|
239
|
+
style="width: 300px;" />
|
|
240
|
+
|
|
241
|
+
Example file-wise report:
|
|
242
|
+
|
|
243
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/report.png"
|
|
244
|
+
alt="report-summary"
|
|
245
|
+
style="width: 350px;" />
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
## Demo Project
|
|
249
|
+
|
|
250
|
+
The repository includes [`demo_staking_protocol/`](./demo_staking_protocol), a small Starknet `snforge` project used to demonstrate the tool.
|
|
251
|
+
|
|
252
|
+
It contains two contracts (`Vault`, `StakeVault`) to demonstrate mutation testing on permissions and time-based logic.
|
|
253
|
+
|
|
254
|
+
### Coverage vs Mutation
|
|
255
|
+
|
|
256
|
+
The demo project reports high test coverage:
|
|
257
|
+
|
|
258
|
+
- Line Coverage: **97%**
|
|
259
|
+
- Function Coverage: **100%**
|
|
260
|
+
|
|
261
|
+
But mutation testing reveals a different picture:
|
|
262
|
+
|
|
263
|
+
- Mutation Score: **65%**
|
|
264
|
+
|
|
265
|
+
This means many injected faults were **not detected by the test suite**, despite near-complete coverage.
|
|
266
|
+
|
|
267
|
+
> High coverage does not guarantee strong tests — mutation testing exposes that gap.
|
|
268
|
+
|
|
269
|
+
## Safety Behavior
|
|
270
|
+
|
|
271
|
+
`cairo-mutate` edits files in place, so it is designed to be safe by default:
|
|
272
|
+
|
|
273
|
+
- backs up each target file before mutation begins
|
|
274
|
+
- restores originals after each mutant
|
|
275
|
+
- restores on normal exit
|
|
276
|
+
- restores on `SIGINT` and `SIGTERM`
|
|
277
|
+
- removes backup files after the run
|
|
278
|
+
|
|
279
|
+
If a run is interrupted, the script restores the original source files before exiting.
|
|
280
|
+
|
|
281
|
+
### Safe Mode
|
|
282
|
+
|
|
283
|
+
Use `--safe` when you want an extra project-health check around the mutation pass.
|
|
284
|
+
|
|
285
|
+
With safe mode enabled, the tool:
|
|
286
|
+
|
|
287
|
+
- runs the test command before mutation starts
|
|
288
|
+
- aborts early if the project is already failing
|
|
289
|
+
- restores files after the mutation pass
|
|
290
|
+
- runs the test command again after restore
|
|
291
|
+
- fails the command if the restored project no longer passes
|
|
292
|
+
|
|
293
|
+
This is useful for CI, PR checks, and grant demos where you want to prove that the project was healthy before mutation and still healthy afterward.
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
## Where It Fits
|
|
297
|
+
|
|
298
|
+
`cairo-mutate` is designed to sit alongside existing Starknet tooling:
|
|
299
|
+
|
|
300
|
+
- After writing tests
|
|
301
|
+
- Before audits
|
|
302
|
+
- In CI pipeline for test quality checks
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
## Current Limitations
|
|
307
|
+
|
|
308
|
+
This is still an MVP, so a few limits are intentional:
|
|
309
|
+
|
|
310
|
+
- mutations are string-based, not AST-based
|
|
311
|
+
- the tool focuses on `src/` files
|
|
312
|
+
- the current engine is tuned for practicality and clarity, not full Cairo syntax coverage
|
|
313
|
+
|
|
314
|
+
Those limits are part of the plan, not a bug. The next major step is a Cairo-aware parser and AST-based mutation layer.
|
|
315
|
+
|
|
316
|
+
## Roadmap
|
|
317
|
+
|
|
318
|
+
### V1 (Current)
|
|
319
|
+
|
|
320
|
+
Stable string-based mutation engine with:
|
|
321
|
+
|
|
322
|
+
- modular mutator files
|
|
323
|
+
- configurable test command
|
|
324
|
+
- timeout support
|
|
325
|
+
- safe restore behavior
|
|
326
|
+
- file-wise reporting
|
|
327
|
+
- quiet / summary / verbose modes
|
|
328
|
+
|
|
329
|
+
### V2 (Next)
|
|
330
|
+
|
|
331
|
+
Cairo-aware AST mutation engine with:
|
|
332
|
+
|
|
333
|
+
- parser and source spans
|
|
334
|
+
- safer rewrites
|
|
335
|
+
- fewer false positives from string matching
|
|
336
|
+
- more precise comparison and arithmetic mutations
|
|
337
|
+
- cleaner expansion into static analysis and later symbolic reasoning
|
|
338
|
+
|
|
339
|
+
### Vision
|
|
340
|
+
|
|
341
|
+
A full mutation and analysis framework for Starknet contracts.
|
|
342
|
+
|
|
343
|
+
## Project Layout
|
|
344
|
+
|
|
345
|
+
- [`mutate.py`](./mutate.py) - root CLI orchestrator
|
|
346
|
+
- [`mutators/`](./mutators) - one file per mutator plus shared runtime helpers
|
|
347
|
+
- [`demo_staking_protocol/`](./demo_staking_protocol) - standalone Starknet demo project
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
<!-- # `cairo-mutate`
|
|
2
|
+
> **Mutation-testing for Starknet contracts** -->
|
|
3
|
+
|
|
4
|
+
<h1 align="center"><b>cairo-mutate</b></h1>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<b> Mutation testing for Starknet contracts</b>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
<div align="center">
|
|
12
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/logo.png"
|
|
13
|
+
alt="cairo-mutate"
|
|
14
|
+
style="width: 280px; border-radius: 10px;" />
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
**cairo-mutate** brings mutation testing to Starknet, giving developers a measurable signal of test quality.
|
|
20
|
+
|
|
21
|
+
Find weak tests, missing edge cases, and false confidence from high coverage.
|
|
22
|
+
|
|
23
|
+
Instead of asking:
|
|
24
|
+
|
|
25
|
+
> “Do the tests pass? What is the coverage?”
|
|
26
|
+
|
|
27
|
+
it asks:
|
|
28
|
+
|
|
29
|
+
> “If the contract were wrong, would the tests notice?”
|
|
30
|
+
|
|
31
|
+
The tool mutates contract code, reruns the test suite, and measures how many injected faults are actually caught.
|
|
32
|
+
|
|
33
|
+
That helps uncover:
|
|
34
|
+
- weak assertions
|
|
35
|
+
- missing edge cases
|
|
36
|
+
- broken invariants
|
|
37
|
+
- brittle state checks
|
|
38
|
+
- false confidence from high coverage
|
|
39
|
+
|
|
40
|
+
Even a test suite with 100% coverage can still miss important behavioral invariants. `cairo-mutate` helps reveal that gap by checking whether tests fail when those invariants are broken.
|
|
41
|
+
|
|
42
|
+
### Current Status (MVP)
|
|
43
|
+
A stable, string-based mutation engine with file-wise reporting, safe restore behavior, timeout handling, and a CLI that can run against any Starknet project root.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Quick Demo
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cairo-mutate demo_staking_protocol -v
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Expected output:
|
|
53
|
+
|
|
54
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/demo-out.png"
|
|
55
|
+
alt="cairo-mutate"
|
|
56
|
+
style="width: 440px; border-radius: 10px;" />
|
|
57
|
+
|
|
58
|
+
> Screenshot shows part of the `-v` output for readability. Use `-vv` for the full mutation log.
|
|
59
|
+
|
|
60
|
+
## Requirements
|
|
61
|
+
|
|
62
|
+
- Python 3.10+
|
|
63
|
+
- `snforge`
|
|
64
|
+
- `scarb` `2.14.0` or newer
|
|
65
|
+
|
|
66
|
+
If your shell resolves the wrong `scarb`, use the asdf shim path:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
env PATH="$HOME/.asdf/shims:$PATH" snforge test
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
### Install from PyPI
|
|
75
|
+
|
|
76
|
+
Create a virtual environment and install the published package:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
python3 -m venv .venv
|
|
80
|
+
source .venv/bin/activate
|
|
81
|
+
|
|
82
|
+
pip install cairo-mutate
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Install from Source
|
|
86
|
+
|
|
87
|
+
Clone the repository and install it in editable mode:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/<your-username>/cairo-mutate
|
|
91
|
+
cd cairo-mutate
|
|
92
|
+
|
|
93
|
+
# create venv
|
|
94
|
+
python3 -m venv .venv
|
|
95
|
+
source .venv/bin/activate
|
|
96
|
+
|
|
97
|
+
pip install -e .
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The CLI entrypoint is `cairo-mutate`.
|
|
101
|
+
|
|
102
|
+
When working from source, this is implemented via [`mutate.py`](./mutate.py).
|
|
103
|
+
|
|
104
|
+
## Use Cases
|
|
105
|
+
|
|
106
|
+
- Evaluate test strength before deployment
|
|
107
|
+
- Detect missing assertions and edge cases
|
|
108
|
+
- Improve confidence in contract invariants
|
|
109
|
+
- Demonstrate test quality in audits and grants
|
|
110
|
+
|
|
111
|
+
## Features
|
|
112
|
+
|
|
113
|
+
- Scans a Starknet project and mutates Cairo files under `src/`
|
|
114
|
+
- Applies one mutation at a time
|
|
115
|
+
- Runs a configurable test command, defaulting to `snforge test`
|
|
116
|
+
- Classifies each mutant as:
|
|
117
|
+
- `✔ Caught`
|
|
118
|
+
- `✘ Uncaught`
|
|
119
|
+
- `Compile Error`
|
|
120
|
+
- `Timeout`
|
|
121
|
+
- Restores original files automatically after each run
|
|
122
|
+
- Cleans up backups on exit, interrupt, or termination
|
|
123
|
+
- Prints file-wise mutation scores plus a final mutation score
|
|
124
|
+
- Supports quiet, summary, and full trace modes
|
|
125
|
+
|
|
126
|
+
Works on any Starknet project with `Scarb.toml`, `src/`, and `snforge` tests.
|
|
127
|
+
|
|
128
|
+
## Mutators
|
|
129
|
+
|
|
130
|
+
The current MVP uses the following mutators:
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
| Code | Abbr | Desc |
|
|
134
|
+
|--- |--- |---
|
|
135
|
+
| `AS-REM` | Assert removal | replaces an `assert` body with a no-op
|
|
136
|
+
| `AS-FLIP` | Assert condition flip | flips assertion comparisons, such as `== ↔ !=` and `> ↔ <` |
|
|
137
|
+
| `OP-EQ` | Equality operator mutation | flips equality and inequality operators outside `assert` expressions. |
|
|
138
|
+
| `OP-ARI` | Arithmetic operator mutation | flips arithmetic operators like `+ ↔ -` |
|
|
139
|
+
| `OP-ASG` | Assignment operator mutation | mutates assignment-style operations such as `+=` and `-=` into plain assignment behavior |
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
These mutators are intentionally string-based for V1. That keeps the tool fast and easy to understand while we build the Cairo-aware AST version later.
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
## Why Mutation Testing?
|
|
146
|
+
|
|
147
|
+
Passing tests ≠ correct behavior.
|
|
148
|
+
|
|
149
|
+
`cairo-mutate` measures whether your tests actually detect broken logic, permissions, and invariants.
|
|
150
|
+
|
|
151
|
+
## CLI
|
|
152
|
+
|
|
153
|
+
The intended entrypoint is `cairo-mutate`.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cairo-mutate <target> [OPTIONS]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`<target>` is the Starknet project root that contains `Scarb.toml` and `src/`.
|
|
160
|
+
|
|
161
|
+
### Options
|
|
162
|
+
|
|
163
|
+
- `--test-cmd "snforge test"`
|
|
164
|
+
Custom test command to run after each mutant.
|
|
165
|
+
- `--file src/lib.cairo`
|
|
166
|
+
Mutate a single Cairo file relative to the project root.
|
|
167
|
+
- `--mutators as_rem,as_flip,op_eq`
|
|
168
|
+
Limit the run to specific mutators.
|
|
169
|
+
- `--timeout 20`
|
|
170
|
+
Timeout in seconds for each test command run.
|
|
171
|
+
- `--safe`
|
|
172
|
+
Run the project test command before mutation starts and again after restore.
|
|
173
|
+
- `-v`
|
|
174
|
+
Print mutator/file summaries.
|
|
175
|
+
- `-vv`
|
|
176
|
+
Print full line-by-line mutant logs.
|
|
177
|
+
- `--list-mutators`
|
|
178
|
+
Show the available mutators and exit.
|
|
179
|
+
|
|
180
|
+
### Verbosity modes
|
|
181
|
+
|
|
182
|
+
- No `-v`: report-only mode. Prints the final report and summary footer.
|
|
183
|
+
- `-v`: prints file start/finish markers, file counts, and per-mutator summaries.
|
|
184
|
+
- `-vv`: prints the full mutant log for each mutation, plus summaries and the final report.
|
|
185
|
+
|
|
186
|
+
Examples:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
cairo-mutate demo_staking_protocol --file src/lib.cairo --safe -v
|
|
190
|
+
cairo-mutate demo_staking_protocol --safe -v
|
|
191
|
+
cairo-mutate demo_staking_protocol --test-cmd "snforge test" --timeout 20 -vv
|
|
192
|
+
cairo-mutate demo_staking_protocol --mutators as_rem,as_flip,op_eq --safe
|
|
193
|
+
cairo-mutate --list-mutators
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Output Style
|
|
197
|
+
|
|
198
|
+
Example mutant line:
|
|
199
|
+
|
|
200
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/line.png"
|
|
201
|
+
alt="mutant-line"
|
|
202
|
+
style="width: 650px;" />
|
|
203
|
+
|
|
204
|
+
Example skipped summary:
|
|
205
|
+
|
|
206
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/skip.png"
|
|
207
|
+
alt="skip-summary"
|
|
208
|
+
style="width: 300px;" />
|
|
209
|
+
|
|
210
|
+
Example file-wise report:
|
|
211
|
+
|
|
212
|
+
<img src="https://raw.githubusercontent.com/sidarths/cairo-mutate/main/assets/report.png"
|
|
213
|
+
alt="report-summary"
|
|
214
|
+
style="width: 350px;" />
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
## Demo Project
|
|
218
|
+
|
|
219
|
+
The repository includes [`demo_staking_protocol/`](./demo_staking_protocol), a small Starknet `snforge` project used to demonstrate the tool.
|
|
220
|
+
|
|
221
|
+
It contains two contracts (`Vault`, `StakeVault`) to demonstrate mutation testing on permissions and time-based logic.
|
|
222
|
+
|
|
223
|
+
### Coverage vs Mutation
|
|
224
|
+
|
|
225
|
+
The demo project reports high test coverage:
|
|
226
|
+
|
|
227
|
+
- Line Coverage: **97%**
|
|
228
|
+
- Function Coverage: **100%**
|
|
229
|
+
|
|
230
|
+
But mutation testing reveals a different picture:
|
|
231
|
+
|
|
232
|
+
- Mutation Score: **65%**
|
|
233
|
+
|
|
234
|
+
This means many injected faults were **not detected by the test suite**, despite near-complete coverage.
|
|
235
|
+
|
|
236
|
+
> High coverage does not guarantee strong tests — mutation testing exposes that gap.
|
|
237
|
+
|
|
238
|
+
## Safety Behavior
|
|
239
|
+
|
|
240
|
+
`cairo-mutate` edits files in place, so it is designed to be safe by default:
|
|
241
|
+
|
|
242
|
+
- backs up each target file before mutation begins
|
|
243
|
+
- restores originals after each mutant
|
|
244
|
+
- restores on normal exit
|
|
245
|
+
- restores on `SIGINT` and `SIGTERM`
|
|
246
|
+
- removes backup files after the run
|
|
247
|
+
|
|
248
|
+
If a run is interrupted, the script restores the original source files before exiting.
|
|
249
|
+
|
|
250
|
+
### Safe Mode
|
|
251
|
+
|
|
252
|
+
Use `--safe` when you want an extra project-health check around the mutation pass.
|
|
253
|
+
|
|
254
|
+
With safe mode enabled, the tool:
|
|
255
|
+
|
|
256
|
+
- runs the test command before mutation starts
|
|
257
|
+
- aborts early if the project is already failing
|
|
258
|
+
- restores files after the mutation pass
|
|
259
|
+
- runs the test command again after restore
|
|
260
|
+
- fails the command if the restored project no longer passes
|
|
261
|
+
|
|
262
|
+
This is useful for CI, PR checks, and grant demos where you want to prove that the project was healthy before mutation and still healthy afterward.
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
## Where It Fits
|
|
266
|
+
|
|
267
|
+
`cairo-mutate` is designed to sit alongside existing Starknet tooling:
|
|
268
|
+
|
|
269
|
+
- After writing tests
|
|
270
|
+
- Before audits
|
|
271
|
+
- In CI pipeline for test quality checks
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
## Current Limitations
|
|
276
|
+
|
|
277
|
+
This is still an MVP, so a few limits are intentional:
|
|
278
|
+
|
|
279
|
+
- mutations are string-based, not AST-based
|
|
280
|
+
- the tool focuses on `src/` files
|
|
281
|
+
- the current engine is tuned for practicality and clarity, not full Cairo syntax coverage
|
|
282
|
+
|
|
283
|
+
Those limits are part of the plan, not a bug. The next major step is a Cairo-aware parser and AST-based mutation layer.
|
|
284
|
+
|
|
285
|
+
## Roadmap
|
|
286
|
+
|
|
287
|
+
### V1 (Current)
|
|
288
|
+
|
|
289
|
+
Stable string-based mutation engine with:
|
|
290
|
+
|
|
291
|
+
- modular mutator files
|
|
292
|
+
- configurable test command
|
|
293
|
+
- timeout support
|
|
294
|
+
- safe restore behavior
|
|
295
|
+
- file-wise reporting
|
|
296
|
+
- quiet / summary / verbose modes
|
|
297
|
+
|
|
298
|
+
### V2 (Next)
|
|
299
|
+
|
|
300
|
+
Cairo-aware AST mutation engine with:
|
|
301
|
+
|
|
302
|
+
- parser and source spans
|
|
303
|
+
- safer rewrites
|
|
304
|
+
- fewer false positives from string matching
|
|
305
|
+
- more precise comparison and arithmetic mutations
|
|
306
|
+
- cleaner expansion into static analysis and later symbolic reasoning
|
|
307
|
+
|
|
308
|
+
### Vision
|
|
309
|
+
|
|
310
|
+
A full mutation and analysis framework for Starknet contracts.
|
|
311
|
+
|
|
312
|
+
## Project Layout
|
|
313
|
+
|
|
314
|
+
- [`mutate.py`](./mutate.py) - root CLI orchestrator
|
|
315
|
+
- [`mutators/`](./mutators) - one file per mutator plus shared runtime helpers
|
|
316
|
+
- [`demo_staking_protocol/`](./demo_staking_protocol) - standalone Starknet demo project
|