feat: Mutation caching and transitive dependency tracking#509
Open
nicklafleur wants to merge 3 commits intoboxed:mainfrom
Open
feat: Mutation caching and transitive dependency tracking#509nicklafleur wants to merge 3 commits intoboxed:mainfrom
nicklafleur wants to merge 3 commits intoboxed:mainfrom
Conversation
This commit implements function-level hashing to skip re-testing unchanged mutants, along with fixes for mypy type errors and architectural improvements. A follow-up commit will implement transitive invalidation of mutants based on function call graphs and the new hashing mechanism. INCREMENTAL MUTATION TESTING - Add _compute_function_hashes() in file_mutation.py to generate SHA-256 hashes (truncated to 12 chars) for each mutated function's source code - Store hash_by_function_name in SourceFileMutationData for persistence - On subsequent runs, compare old vs new hashes to identify changed functions - Reset mutant results to None (needs re-testing) when function hash changes - Return changed_functions and current_hashes from create_mutants_for_file() MUTATION METADATA TRACKING - Add MutationMetadata dataclass with line_number, mutation_type, and description - Each Mutation now carries metadata about what changed and where - Add OPERATOR_TO_TYPE mapping to categorize mutations (number, string, boolean, etc.) - Add _determine_mutation_type() to disambiguate operator categories - Add _describe_mutation() for human-readable mutation descriptions - Serialize/deserialize metadata to JSON via to_dict()/from_dict() NAMING AND CONVENTIONS - Rename public functions to private (_create_mutations, _combine_mutations_to_source, etc.) - Rename mutation_operators to MUTATION_OPERATORS (constant naming convention) - Add explicit type annotations throughout (dict[str, MutationMetadata], etc.) NEW BENCHMARK PROJECT - Add e2e_projects/benchmark_1k/ with ~1000 mutants for testing - Includes modules: numbers, strings, booleans, operators, comparisons, arguments, returns, complex (recursion, higher-order functions) - Configurable delays via BENCHMARK_IMPORT_DELAY, BENCHMARK_CONFTEST_DELAY, BENCHMARK_TEST_DELAY environment variables
Introduce MutmutState class to more easily manage runtime state for dependency tracking (old_function_hashes, current_function_hashes, function_dependencies). Persist hashes and dependencies to mutmut-stats.json for incremental runs. Changes: - Add state.py with MutmutState dataclass and state() singleton accessor - Add core.py with MutmutCallStack (ContextVar-based) for async-safe tracking - Move record_trampoline_hit to core.py, now tracks caller->callee edges - Update trampoline to track call depth and record dependencies during stats - Extend load_stats/save_stats to persist function_hashes and dependencies - Add _cleanup_stale_stats and _invalidate_stale_dependency_edges functions - Add track_dependencies and dependency_tracking_depth config options - Update documentation describing the dependency tracking feature
This was referenced Apr 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds incremental mutation testing to mutmut by skipping mutants in unchanged code, with transitive invalidation via a runtime call graph. On re-runs, only mutants in functions whose source (or whose dependencies' source) changed are re-tested.
High-level
Commit Breakdown:
feat: add function hashing for incremental mutation testinghash_by_function_nameonSourceFileMutationData. On subsequent runs, compare old vs. new hashes and reset mutant results toNonefor changed functions only.MutationMetadata(line number, mutation type, human-readable description) carried on everyMutationand serialized to JSON, plus anOPERATOR_TO_TYPEmapping and helpers (_determine_mutation_type,_describe_mutation)._,MUTATION_OPERATORSconstant) and adds explicit type annotations.e2e_projects/benchmark_1k/project (1000 mutants across a broad range of mutation types) with configurable delays viaBENCHMARK_IMPORT_DELAY,BENCHMARK_CONFTEST_DELAY, andBENCHMARK_TEST_DELAY.refactor: relocate formatting utils__main__.py,file_mutation.py, andtrampoline_templates.pyintosrc/mutmut/utils/format_utils.py.feat: Add dependency tracking with function hash persistenceMutmutStatedataclass +state()singleton (state.py) consolidatingold_function_hashes,current_function_hashes, andfunction_dependenciesinstead of leaking module-level globals.core.pywithMutmutCallStack(backed byContextVarfor async/thread safety) and a relocatedrecord_trampoline_hitthat now records caller→callee edges during stats collection.load_stats/save_statsextended to persistfunction_hashesanddependenciesinmutmut-stats.json._cleanup_stale_statsand_invalidate_stale_dependency_edgesprune state for functions that no longer exist or whose hashes changed, and transitively invalidate callers of changed callees.track_dependenciesanddependency_tracking_depth.Known Issues