mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-07 20:28:12 +00:00
Various small improvements to the fuzz-parser
script (#11186)
This commit is contained in:
parent
3474e37836
commit
113e259e6d
3 changed files with 41 additions and 21 deletions
|
@ -11,8 +11,9 @@ Example invocations of the script:
|
|||
but only reporting bugs that are new on your branch:
|
||||
`python scripts/fuzz-parser/fuzz.py 0-10 --new-bugs-only`
|
||||
- Run the fuzzer concurrently on 10,000 different Python source-code files,
|
||||
and only print a summary at the end:
|
||||
`python scripts/fuzz-parser/fuzz.py 1-10000 --quiet
|
||||
using a random selection of seeds, and only print a summary at the end
|
||||
(the `shuf` command is Unix-specific):
|
||||
`python scripts/fuzz-parser/fuzz.py $(shuf -i 0-1000000 -n 10000) --quiet
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
@ -27,6 +28,7 @@ from typing import NewType
|
|||
|
||||
from pysource_codegen import generate as generate_random_code
|
||||
from pysource_minimize import minimize as minimize_repro
|
||||
from rich_argparse import RawDescriptionRichHelpFormatter
|
||||
from termcolor import colored
|
||||
|
||||
MinimizedSourceCode = NewType("MinimizedSourceCode", str)
|
||||
|
@ -67,19 +69,20 @@ class FuzzResult:
|
|||
# required to trigger the bug. If not, it will be `None`.
|
||||
maybe_bug: MinimizedSourceCode | None
|
||||
|
||||
def print_description(self) -> None:
|
||||
def print_description(self, index: int, num_seeds: int) -> None:
|
||||
"""Describe the results of fuzzing the parser with this seed."""
|
||||
progress = f"[{index}/{num_seeds}]"
|
||||
msg = (
|
||||
colored(f"Ran fuzzer on seed {self.seed}", "red")
|
||||
if self.maybe_bug
|
||||
else colored(f"Ran fuzzer successfully on seed {self.seed}", "green")
|
||||
)
|
||||
print(f"{msg:<55} {progress:>15}", flush=True)
|
||||
if self.maybe_bug:
|
||||
print(colored(f"Ran fuzzer on seed {self.seed}", "red"))
|
||||
print(colored("The following code triggers a bug:", "red"))
|
||||
print()
|
||||
print(self.maybe_bug)
|
||||
print(flush=True)
|
||||
else:
|
||||
print(
|
||||
colored(f"Ran fuzzer successfully on seed {self.seed}", "green"),
|
||||
flush=True,
|
||||
)
|
||||
|
||||
|
||||
def fuzz_code(
|
||||
|
@ -110,9 +113,10 @@ def fuzz_code(
|
|||
|
||||
|
||||
def run_fuzzer_concurrently(args: ResolvedCliArgs) -> list[FuzzResult]:
|
||||
num_seeds = len(args.seeds)
|
||||
print(
|
||||
f"Concurrently running the fuzzer on "
|
||||
f"{len(args.seeds)} randomly generated source-code files..."
|
||||
f"{num_seeds} randomly generated source-code files..."
|
||||
)
|
||||
bugs: list[FuzzResult] = []
|
||||
with concurrent.futures.ProcessPoolExecutor() as executor:
|
||||
|
@ -127,10 +131,12 @@ def run_fuzzer_concurrently(args: ResolvedCliArgs) -> list[FuzzResult]:
|
|||
for seed in args.seeds
|
||||
]
|
||||
try:
|
||||
for future in concurrent.futures.as_completed(fuzz_result_futures):
|
||||
for i, future in enumerate(
|
||||
concurrent.futures.as_completed(fuzz_result_futures), start=1
|
||||
):
|
||||
fuzz_result = future.result()
|
||||
if not args.quiet:
|
||||
fuzz_result.print_description()
|
||||
fuzz_result.print_description(i, num_seeds)
|
||||
if fuzz_result.maybe_bug:
|
||||
bugs.append(fuzz_result)
|
||||
except KeyboardInterrupt:
|
||||
|
@ -142,12 +148,13 @@ def run_fuzzer_concurrently(args: ResolvedCliArgs) -> list[FuzzResult]:
|
|||
|
||||
|
||||
def run_fuzzer_sequentially(args: ResolvedCliArgs) -> list[FuzzResult]:
|
||||
num_seeds = len(args.seeds)
|
||||
print(
|
||||
f"Sequentially running the fuzzer on "
|
||||
f"{len(args.seeds)} randomly generated source-code files..."
|
||||
f"{num_seeds} randomly generated source-code files..."
|
||||
)
|
||||
bugs: list[FuzzResult] = []
|
||||
for seed in args.seeds:
|
||||
for i, seed in enumerate(args.seeds, start=1):
|
||||
fuzz_result = fuzz_code(
|
||||
seed,
|
||||
test_executable=args.test_executable,
|
||||
|
@ -155,7 +162,7 @@ def run_fuzzer_sequentially(args: ResolvedCliArgs) -> list[FuzzResult]:
|
|||
only_new_bugs=args.only_new_bugs,
|
||||
)
|
||||
if not args.quiet:
|
||||
fuzz_result.print_description()
|
||||
fuzz_result.print_description(i, num_seeds)
|
||||
if fuzz_result.maybe_bug:
|
||||
bugs.append(fuzz_result)
|
||||
return bugs
|
||||
|
@ -212,7 +219,7 @@ class ResolvedCliArgs:
|
|||
def parse_args() -> ResolvedCliArgs:
|
||||
"""Parse command-line arguments"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
description=__doc__, formatter_class=RawDescriptionRichHelpFormatter
|
||||
)
|
||||
parser.add_argument(
|
||||
"seeds",
|
||||
|
@ -293,7 +300,16 @@ def parse_args() -> ResolvedCliArgs:
|
|||
print(
|
||||
"Running `cargo build --release` since no test executable was specified..."
|
||||
)
|
||||
subprocess.run(["cargo", "build", "--release"], check=True, capture_output=True)
|
||||
try:
|
||||
subprocess.run(
|
||||
["cargo", "build", "--release", "--color", "always"],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e.stderr)
|
||||
raise
|
||||
args.test_executable = os.path.join("target", "release", "ruff")
|
||||
assert os.path.exists(args.test_executable)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue