Reliability is critical for a fuzzer. If the fuzzer crashes, it cannot find bugs. If the coverage parser is inaccurate, the evolutionary search becomes random noise. Therefore, lafleur maintains a rigorous test suite covering everything from low-level AST mutations to high-level orchestration logic.
This document guides you through running the tests, understanding the test architecture, and writing new tests for your contributions. For development workflow including quality checks, see 06. Developer Getting Started and CONTRIBUTING.md.
The test suite is built using Python's standard unittest framework. Test configuration lives in pyproject.toml, which sets testpaths = ["tests"] and excludes directories like cpython-src, .git, build, and venv from test discovery.
Run the entire suite from the project root:
python -m unittest discover testsRun a specific test file:
python -m unittest tests/mutators/test_engine.pyRun a specific test case:
python -m unittest tests.mutators.test_engine.TestASTMutator.test_mutate_with_seedRun with coverage reporting:
coverage run -m unittest discover tests
coverage reportTests are only one part of the quality check pipeline. Before submitting a PR, also run ruff format --check ., ruff check ., and mypy lafleur/ as described in CONTRIBUTING.md.
The tests are organized to mirror the source code structure, with two main subdirectories (tests/mutators/ and tests/orchestrator/) plus standalone test files in tests/ for core modules and tooling.
This is the largest part of the suite. It verifies that the "Hands" of the fuzzer produce valid, deterministic, and "evil" Python code. See tests/mutators/README.md for coverage goals and MUTATOR_TEST_PLAN.md for detailed implementation status.
test_engine.py: Tests theASTMutatororchestration and theSlicingMutatoroptimization.test_generic.py: Tests standard mutations likeOperatorSwapperorBoundaryValuesMutator.test_scenarios_types.py: Tests type system attack mutators (MRO manipulation, descriptors).test_scenarios_control.py: Tests control flow mutators (recursion depth attacks, exception storms).test_scenarios_data.py: Tests data structure mutators (builtin overrides, comprehension mutations).test_scenarios_runtime.py: Tests runtime manipulation mutators (frame injection, weakref abuse).test_utils.py: Tests utility transformers (instrumentation, normalization).- Key Pattern: These tests heavily use
ast.parse()to verify that mutated code is syntactically valid, andunittest.mock.patchonrandomto ensure mutations are deterministic. Each mutator file intests/mutators/corresponds 1:1 with a source module inlafleur/mutators/.
These tests verify the "Brain" of the fuzzer — the evolutionary loop, scoring logic, and execution pipeline.
test_execution.py: Mockssubprocess.runto verify child script assembly, timeout handling, and crash detection byExecutionManager.test_coverage.py: TestsScoringManager.find_new_coverage— global vs. relative discovery detection, empty coverage handling.test_scoring.py: Verifies theInterestingnessScorermath: richness bonus, density penalty, minimum score thresholds.test_jit_scoring.py: Tests JIT vitals scoring factors: zombie bonus (+50), tachycardia bonus, chain depth bonus (+10), stub bonus (+5), and stacked multi-factor scoring.test_jit_differential.py: Tests parent-relative density scoring — the delta and absolute tachycardia paths, minimum thresholds, and the condition where children must exceed their parent's instability.test_jit_clamping.py: Tests dynamic density clamping math (min(parent × 5, child)) and tachycardia decay (× 0.95).test_crash_detection.py: TestsArtifactManagercrash detection and artifact saving — signal-based crashes, keyword matching, session bundle creation, log compression.test_main_loop.py: Testsexecute_mutation_and_analysis_cycle, deepening session sterility limits, run summary formatting, and the main CLI entry point.test_integration.py: End-to-end tests with real file I/O and temporary directories. Covers full mutation cycles, state persistence across orchestrator restarts, corpus bootstrapping, and timeout artifact saving.test_sniper_targeting.py: Tests that the harness function name is excluded fromwatched_keyspassed to the Sniper strategy.
These files test standalone components and the coverage pipeline.
test_coverage.py: Tests the "Eyes" of the fuzzer. Verifies regex patterns (UOP_REGEX,HARNESS_MARKER_REGEX,RARE_EVENT_REGEX), the state machine's attribution of edges toTRACING/OPTIMIZED/EXECUTINGstates, spurious UOP filtering, side exit tracking, multi-harness parsing, andCoverageManagerinteger ID mapping.test_learning.py: Tests the adaptive learning engine — score decay over attempts, weight floor enforcement, grace period behavior, and score persistence.test_corpus_manager.py: TestsCorpusSchedulerheuristics and corpus pruning logic — subsumption detection, dry-run safety, and metadata mutation guards.
These files test the post-run analysis tools and CLI entry points.
test_analysis.py: TestsCrashFingerprinter— ASAN report parsing (heap-use-after-free, stack-buffer-overflow), assertion matching, signal detection, Python panic recognition, and fallback fingerprinting for unrecognized crash types.test_report.py: Testslafleur-reportgeneration — formatting helpers, JIT/ASAN status detection from build flags, duration calculation from metadata and stats.test_campaign.py: Testslafleur-campaign—CampaignAggregatorinstance loading, crash deduplication across instances, registry enrichment, HTML report generation, and handling of many-instance campaigns.test_triage.py: Testslafleur-triage—CrashRegistrydatabase operations, instance discovery, crash import from campaign directories, interactive triage actions (set status, record issue), list/filter/show commands, and export/import.test_triage_display.py: Tests triage display formatting and presentation logic.test_state_tool.py: Tests state file inspection — JSON conversion, set-to-list serialization for JSON compatibility, and old-to-new format migration.test_cli_entry_points.py: Testsmain()functions forcoverage.py,driver.py, andminimize.py— argument parsing, error handling for missing files, and end-to-end execution.
test_driver_internals.py: Tests driver internals —walk_code_objectsrecursion and cycle detection,snapshot_executor_statebaseline capture, mock executor pointer construction for controlled introspection testing.test_bloom_integration.py: Tests Bloom filter probing — verifies thatscan_watched_variablesis triggered when exit density is high and correctly identifies watched globals through the mock bloom filter.
When contributing code to lafleur, you are expected to add corresponding tests. For a full walkthrough of adding a new mutator with tests, see 07. Extending Lafleur.
Fuzzing involves randomness, but tests must be deterministic.
- Never rely on actual
random.random(). - Always use
unittest.mock.patchto force the RNG to return known values.
@patch("random.random", return_value=0.05)
def test_my_mutation(self, mock_random):
# ... logic that expects the mutation to trigger ...If you write a new mutator, you must include a test case that parses the output.
def test_produces_valid_code(self):
# ... apply mutation ...
try:
ast.parse(mutated_code)
except SyntaxError:
self.fail("Mutator produced invalid Python syntax")Do not interact with the real filesystem or run real subprocesses unless you are writing an integration test (like those in test_integration.py).
- Use
tempfile.TemporaryDirectory()when you need real files, and always clean up intearDown. - Mock
subprocess.runto test execution logic without spawning processes. - Use
unittest.mock.patchon module-level constants (likeCORPUS_DIR) to redirect file operations to temporary directories.
- Happy Path: Does the mutator inject the code when the probability is met?
- Safety Net: Does the mutator avoid crashing when passed invalid input, an empty function, or a file with no loops?
Each mutator or component should have tests that verify (from tests/mutators/README.md):
- Basic functionality — the transformation is applied correctly.
- Probabilistic behavior — respects random probability thresholds.
- Valid output — produces parseable Python code.
- Edge cases — empty bodies, missing variables, nested structures.
- Output quality — generated code matches expected patterns.