Skip to content

Commit f29ad87

Browse files
Add linter, formatter, and tests
Closes #18
1 parent 16404c4 commit f29ad87

23 files changed

+519
-370
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ source venv/bin/activate
1212
Next, you can run the fuzzer locally:
1313

1414
```
15-
pip install -r requirements.txt
15+
pip install -e .
1616
python src/zkregex_fuzzer/cli.py --help
1717
```
1818

@@ -23,6 +23,18 @@ pip install -e .
2323
zkregex-fuzzer --help
2424
```
2525

26+
## Linting and tests
27+
28+
```
29+
pip install -e '.[dev]'
30+
# Either run manually
31+
ruff check .
32+
ruff format .
33+
pytest
34+
# or through the script
35+
python scripts/lint_and_tests.py
36+
```
37+
2638
## Example Run
2739

2840
### Fuzzing

pyproject.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,26 @@ dependencies = [
2424
"exrex",
2525
]
2626

27+
[project.optional-dependencies]
28+
dev = [
29+
"ruff",
30+
"pytest",
31+
]
32+
33+
[tool.ruff]
34+
line-length = 88
35+
target-version = "py38"
36+
lint.select = ["E", "F", "W"]
37+
lint.extend-select = ["I"]
38+
lint.ignore = ["F401", "E501"]
39+
40+
[tool.ruff.format]
41+
quote-style = "double"
42+
indent-style = "space"
43+
2744
[project.scripts]
2845
zkregex-fuzzer = "zkregex_fuzzer.cli:main"
46+
47+
# Automatically find packages under `src/`
48+
[tool.setuptools.packages.find]
49+
where = ["src"]

requirements.txt

Lines changed: 0 additions & 3 deletions
This file was deleted.

scripts/lint_and_tests.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import subprocess
2+
import sys
3+
4+
5+
def run_command(command: str) -> int:
6+
"""Runs a shell command and returns the exit code."""
7+
print(f"Running: {command}")
8+
process = subprocess.run(command, shell=True)
9+
return process.returncode
10+
11+
12+
def run_linter() -> int:
13+
"""Runs Ruff to check for linting errors."""
14+
print("Running Ruff Linter...")
15+
return run_command("ruff check .")
16+
17+
18+
def run_formatter() -> int:
19+
"""Runs Black to check for formatting errors."""
20+
print("Running Black Formatter...")
21+
return run_command("ruff format .")
22+
23+
24+
def run_tests() -> int:
25+
"""Placeholder for future test execution."""
26+
print("Running Tests (Placeholder)...")
27+
return run_command("pytest")
28+
29+
30+
if __name__ == "__main__":
31+
lint_exit_code = run_linter()
32+
test_exit_code = run_tests()
33+
34+
if lint_exit_code != 0 or test_exit_code != 0:
35+
print("❌ Checks failed!")
36+
sys.exit(1) # Fail GitHub Action if linting or tests fail
37+
else:
38+
print("✅ All checks passed!")
39+
sys.exit(0)

src/zkregex_fuzzer/__init__.py

Whitespace-only changes.

src/zkregex_fuzzer/cli.py

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,48 @@
77
import random
88
import uuid
99
from pathlib import Path
10-
from zkregex_fuzzer.reproduce import reproduce
10+
11+
from zkregex_fuzzer.configs import GENERATORS, TARGETS, VALID_INPUT_GENERATORS
1112
from zkregex_fuzzer.fuzzer import fuzz_with_database, fuzz_with_grammar
1213
from zkregex_fuzzer.grammar import REGEX_GRAMMAR
13-
from zkregex_fuzzer.configs import TARGETS, VALID_INPUT_GENERATORS, GENERATORS
1414
from zkregex_fuzzer.harness import HarnessStatus
1515
from zkregex_fuzzer.logger import logger
16-
from zkregex_fuzzer.runner.circom import CircomSubprocess, SnarkjsSubprocess, ZkRegexSubprocess
16+
from zkregex_fuzzer.reproduce import reproduce
17+
from zkregex_fuzzer.runner.circom import (
18+
CircomSubprocess,
19+
SnarkjsSubprocess,
20+
ZkRegexSubprocess,
21+
)
22+
1723

1824
def fuzz_parser():
19-
parser = argparse.ArgumentParser(
20-
add_help=False
21-
)
25+
parser = argparse.ArgumentParser(add_help=False)
2226
parser.add_argument(
2327
"--regex-num",
2428
type=int,
2529
default=10,
26-
help="Number of regexes to generate (default: 10)."
30+
help="Number of regexes to generate (default: 10).",
2731
)
2832
parser.add_argument(
2933
"--inputs-num",
3034
type=int,
3135
default=10,
32-
help="Number of inputs to generate for each regex (default: 10)."
36+
help="Number of inputs to generate for each regex (default: 10).",
3337
)
3438
parser.add_argument(
3539
"--oracle",
3640
choices=["valid", "invalid"],
37-
help="Wherether the generated inputs should be valid or invalid wrt the regex."
41+
help="Wherether the generated inputs should be valid or invalid wrt the regex.",
3842
)
3943
parser.add_argument(
4044
"--target",
4145
choices=list(TARGETS.keys()),
42-
help=f"The target to fuzz (options: {list(TARGETS.keys())})."
46+
help=f"The target to fuzz (options: {list(TARGETS.keys())}).",
4347
)
4448
parser.add_argument(
4549
"--valid-input-generator",
4650
choices=list(VALID_INPUT_GENERATORS.keys()),
47-
help=f"The valid input generator to use for the fuzzer (options: {list(VALID_INPUT_GENERATORS.keys())})."
51+
help=f"The valid input generator to use for the fuzzer (options: {list(VALID_INPUT_GENERATORS.keys())}).",
4852
)
4953
parser.add_argument(
5054
"--save",
@@ -56,7 +60,7 @@ def fuzz_parser():
5660
"--save-output",
5761
type=str,
5862
default=os.getcwd(),
59-
help=f"The output path where the reproducible files will be stored (default: .)"
63+
help="The output path where the reproducible files will be stored (default: .)",
6064
)
6165
parser.add_argument(
6266
"--fuzzer",
@@ -68,56 +72,55 @@ def fuzz_parser():
6872
"--grammar-max-depth",
6973
type=int,
7074
default=5,
71-
help="Maximum depth of recursion in the grammar (default: 5)."
75+
help="Maximum depth of recursion in the grammar (default: 5).",
7276
)
7377
parser.add_argument(
7478
"--circom-max-input-size",
7579
type=int,
7680
default=600,
77-
help="Maximum size of the circuit input (default: 600)."
81+
help="Maximum size of the circuit input (default: 600).",
7882
)
7983
parser.add_argument(
8084
"--circom-library",
8185
nargs="*",
8286
type=str,
83-
help="Path to the circom library to be included"
87+
help="Path to the circom library to be included",
8488
)
8589

8690
parser.add_argument(
8791
"--circom-prove",
8892
action="store_true",
89-
help="Run the proving and verification step with SnarkJS."
93+
help="Run the proving and verification step with SnarkJS.",
9094
)
9195

9296
parser.add_argument(
9397
"--circom-ptau",
9498
type=str,
95-
help="Path to the ptau (powers-of-tau) file for the proving step"
99+
help="Path to the ptau (powers-of-tau) file for the proving step",
96100
)
97-
98101

99102
return parser
100103

104+
101105
def reproduce_parser():
102-
parser = argparse.ArgumentParser(
103-
add_help=False
104-
)
106+
parser = argparse.ArgumentParser(add_help=False)
105107

106108
parser.add_argument(
107109
"--path",
108110
nargs="+",
109111
type=str,
110112
help="Path to the target directory output that want to be reproduced (support wildcard pattern).",
111-
required=True
113+
required=True,
112114
)
113115

114116
return parser
115117

118+
116119
def do_fuzz(args):
117120
if args.oracle == "valid" and not args.valid_input_generator:
118121
print("Valid input generator is required for valid oracle.")
119122
exit(1)
120-
123+
121124
if args.target == "circom":
122125
try:
123126
circom_version = CircomSubprocess.get_installed_version()
@@ -150,7 +153,7 @@ def do_fuzz(args):
150153
if not args.circom_ptau:
151154
print("Path to ptau file is required for proving.")
152155
exit(1)
153-
156+
154157
ptau_path = Path(args.circom_ptau).resolve()
155158
if not ptau_path.exists():
156159
print(f"Path to ptau file {ptau_path} does not exist.")
@@ -171,7 +174,7 @@ def do_fuzz(args):
171174

172175
# set global seed
173176
seed = str(uuid.uuid4())
174-
kwargs['seed'] = seed
177+
kwargs["seed"] = seed
175178
random.seed(seed)
176179

177180
if args.fuzzer == "grammar":
@@ -182,52 +185,50 @@ def do_fuzz(args):
182185
regex_num=args.regex_num,
183186
inputs_num=args.inputs_num,
184187
max_depth=args.grammar_max_depth,
185-
kwargs=kwargs
188+
kwargs=kwargs,
186189
)
187190
elif args.fuzzer == "database":
188191
fuzz_with_database(
189192
target_implementation=args.target,
190193
oracle_params=(args.oracle == "valid", args.valid_input_generator),
191194
regex_num=args.regex_num,
192195
inputs_num=args.inputs_num,
193-
kwargs=kwargs
196+
kwargs=kwargs,
194197
)
195198

199+
196200
def do_reproduce(args):
197201
reproduce(args.path)
198202

203+
199204
def main():
200-
201205
parser = argparse.ArgumentParser()
202206
parser.add_argument(
203207
"--logger-level",
204208
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
205209
default="INFO",
206-
help="Set the logger level (default: INFO)."
210+
help="Set the logger level (default: INFO).",
207211
)
208212

209213
subparser = parser.add_subparsers(dest="subcommand")
210214
subparser.add_parser(
211-
"fuzz",
212-
help="Fuzz the target regex implementation.",
213-
parents=[fuzz_parser()]
215+
"fuzz", help="Fuzz the target regex implementation.", parents=[fuzz_parser()]
214216
)
215217
subparser.add_parser(
216218
"reproduce",
217219
help="Reproduce the bug that found by the fuzzer.",
218-
parents=[reproduce_parser()]
220+
parents=[reproduce_parser()],
219221
)
220222

221223
args = parser.parse_args()
222224

223-
224225
logger.setLevel(args.logger_level)
225226

226227
if args.subcommand == "fuzz":
227228
do_fuzz(args)
228229
elif args.subcommand == "reproduce":
229230
do_reproduce(args)
230-
231+
231232

232233
if __name__ == "__main__":
233234
main()

src/zkregex_fuzzer/configs.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
from zkregex_fuzzer.runner import CircomRunner, PythonReRunner
21
from zkregex_fuzzer.grammar import REGEX_GRAMMAR
3-
from zkregex_fuzzer.regexgen import GrammarRegexGenerator, DatabaseRegexGenerator
4-
from zkregex_fuzzer.vinpgen import GrammarBasedGenerator, RstrGenerator, ExrexGenerator
5-
2+
from zkregex_fuzzer.regexgen import DatabaseRegexGenerator, GrammarRegexGenerator
3+
from zkregex_fuzzer.runner import CircomRunner, PythonReRunner
4+
from zkregex_fuzzer.vinpgen import ExrexGenerator, GrammarBasedGenerator, RstrGenerator
65

76
TARGETS = {
87
"circom": CircomRunner,
@@ -22,4 +21,4 @@
2221
GENERATORS = {
2322
"grammar": GrammarRegexGenerator,
2423
"database": DatabaseRegexGenerator,
25-
}
24+
}

0 commit comments

Comments
 (0)