mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
py-fuzzer: fix minimization logic when --only-new-bugs
is passed (#17739)
This commit is contained in:
parent
5679bf00bc
commit
f31b1c695c
1 changed files with 45 additions and 34 deletions
|
@ -32,6 +32,7 @@ import concurrent.futures
|
||||||
import enum
|
import enum
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from collections.abc import Callable
|
||||||
from dataclasses import KW_ONLY, dataclass
|
from dataclasses import KW_ONLY, dataclass
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -50,14 +51,12 @@ ExitCode = NewType("ExitCode", int)
|
||||||
def redknot_contains_bug(code: str, *, red_knot_executable: Path) -> bool:
|
def redknot_contains_bug(code: str, *, red_knot_executable: Path) -> bool:
|
||||||
"""Return `True` if the code triggers a panic in type-checking code."""
|
"""Return `True` if the code triggers a panic in type-checking code."""
|
||||||
with tempfile.TemporaryDirectory() as tempdir:
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
Path(tempdir, "pyproject.toml").write_text('[project]\n\tname = "fuzz-input"')
|
input_file = Path(tempdir, "input.py")
|
||||||
Path(tempdir, "input.py").write_text(code)
|
input_file.write_text(code)
|
||||||
completed_process = subprocess.run(
|
completed_process = subprocess.run(
|
||||||
[red_knot_executable, "check", "--project", tempdir],
|
[red_knot_executable, "check", input_file], capture_output=True, text=True
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
)
|
||||||
return completed_process.returncode != 0 and completed_process.returncode != 1
|
return completed_process.returncode not in {0, 1, 2}
|
||||||
|
|
||||||
|
|
||||||
def ruff_contains_bug(code: str, *, ruff_executable: Path) -> bool:
|
def ruff_contains_bug(code: str, *, ruff_executable: Path) -> bool:
|
||||||
|
@ -150,39 +149,51 @@ class FuzzResult:
|
||||||
def fuzz_code(seed: Seed, args: ResolvedCliArgs) -> FuzzResult:
|
def fuzz_code(seed: Seed, args: ResolvedCliArgs) -> FuzzResult:
|
||||||
"""Return a `FuzzResult` instance describing the fuzzing result from this seed."""
|
"""Return a `FuzzResult` instance describing the fuzzing result from this seed."""
|
||||||
code = generate_random_code(seed)
|
code = generate_random_code(seed)
|
||||||
has_bug = (
|
bug_found = False
|
||||||
contains_new_bug(
|
minimizer_callback: Callable[[str], bool] | None = None
|
||||||
code,
|
|
||||||
|
if args.baseline_executable_path is None:
|
||||||
|
if contains_bug(
|
||||||
|
code, executable=args.executable, executable_path=args.test_executable_path
|
||||||
|
):
|
||||||
|
bug_found = True
|
||||||
|
minimizer_callback = partial(
|
||||||
|
contains_bug,
|
||||||
|
executable=args.executable,
|
||||||
|
executable_path=args.test_executable_path,
|
||||||
|
)
|
||||||
|
elif contains_new_bug(
|
||||||
|
code,
|
||||||
|
executable=args.executable,
|
||||||
|
test_executable_path=args.test_executable_path,
|
||||||
|
baseline_executable_path=args.baseline_executable_path,
|
||||||
|
):
|
||||||
|
bug_found = True
|
||||||
|
minimizer_callback = partial(
|
||||||
|
contains_new_bug,
|
||||||
executable=args.executable,
|
executable=args.executable,
|
||||||
test_executable_path=args.test_executable_path,
|
test_executable_path=args.test_executable_path,
|
||||||
baseline_executable_path=args.baseline_executable_path,
|
baseline_executable_path=args.baseline_executable_path,
|
||||||
)
|
)
|
||||||
if args.baseline_executable_path is not None
|
|
||||||
else contains_bug(
|
|
||||||
code, executable=args.executable, executable_path=args.test_executable_path
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if has_bug:
|
|
||||||
callback = partial(
|
|
||||||
contains_bug,
|
|
||||||
executable=args.executable,
|
|
||||||
executable_path=args.test_executable_path,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
maybe_bug = MinimizedSourceCode(minimize_repro(code, callback))
|
|
||||||
except CouldNotMinimize as e:
|
|
||||||
# This is to double-check that there isn't a bug in
|
|
||||||
# `pysource-minimize`/`pysource-codegen`.
|
|
||||||
# `pysource-minimize` *should* never produce code that's invalid syntax.
|
|
||||||
try:
|
|
||||||
ast.parse(code)
|
|
||||||
except SyntaxError:
|
|
||||||
raise e from None
|
|
||||||
else:
|
|
||||||
maybe_bug = MinimizedSourceCode(code)
|
|
||||||
|
|
||||||
else:
|
if not bug_found:
|
||||||
maybe_bug = None
|
return FuzzResult(seed, None, args.executable)
|
||||||
|
|
||||||
|
assert minimizer_callback is not None
|
||||||
|
|
||||||
|
try:
|
||||||
|
maybe_bug = MinimizedSourceCode(minimize_repro(code, minimizer_callback))
|
||||||
|
except CouldNotMinimize as e:
|
||||||
|
# This is to double-check that there isn't a bug in
|
||||||
|
# `pysource-minimize`/`pysource-codegen`.
|
||||||
|
# `pysource-minimize` *should* never produce code that's invalid syntax.
|
||||||
|
try:
|
||||||
|
ast.parse(code)
|
||||||
|
except SyntaxError:
|
||||||
|
raise e from None
|
||||||
|
else:
|
||||||
|
maybe_bug = MinimizedSourceCode(code)
|
||||||
|
|
||||||
return FuzzResult(seed, maybe_bug, args.executable)
|
return FuzzResult(seed, maybe_bug, args.executable)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue