diff --git a/e2e_projects/my_lib/src/my_lib/__init__.py b/e2e_projects/my_lib/src/my_lib/__init__.py index 16245d8f..95d6425d 100644 --- a/e2e_projects/my_lib/src/my_lib/__init__.py +++ b/e2e_projects/my_lib/src/my_lib/__init__.py @@ -234,3 +234,7 @@ def divide(a: int, b: int): ), ) raise Exception(f'Cannot divide if {b=} cannot be 0!!!') + +def write_into_file(path: str): + with open(path, 'w') as file: + file.write("Hello") diff --git a/e2e_projects/my_lib/tests/test_my_lib.py b/e2e_projects/my_lib/tests/test_my_lib.py index 9ff2767a..027aae60 100644 --- a/e2e_projects/my_lib/tests/test_my_lib.py +++ b/e2e_projects/my_lib/tests/test_my_lib.py @@ -172,3 +172,13 @@ def test_private_class_classmethod(): def test_divide(): with pytest.raises(Exception, match='.*cannot be 0!!!'): divide(1, 0) + +def test_tmp_dir_switch(tmpdir): + """Verify that mutmut works with the tmpdir fixture""" + # change to tmp directory + with tmpdir.as_cwd(): + write_into_file("foo.txt") + + with open("foo.txt", "r", encoding='utf-8') as file: + data = file.read() + assert "Hello" in data diff --git a/src/mutmut/__main__.py b/src/mutmut/__main__.py index 9ade02e8..f2fa8db3 100644 --- a/src/mutmut/__main__.py +++ b/src/mutmut/__main__.py @@ -121,7 +121,7 @@ def record_trampoline_hit(name: str, caller: str | None = None) -> None: assert not name.startswith("src."), "Failed trampoline hit. Module name starts with `src.`, which is invalid" - source_paths = [p.resolve(strict=True) for p in Config.get().source_paths] + mutated_source_paths = Config.get().resolved_mutated_source_paths if Config.get().max_stack_depth != -1: f = inspect.currentframe() @@ -132,7 +132,7 @@ def record_trampoline_hit(name: str, caller: str | None = None) -> None: if "pytest" in filename or "hammett" in filename or "unittest" in filename: break file_path = Path(filename).resolve(strict=True) - if any(path in file_path.parents for path in source_paths): + if any(path in file_path.parents for path in mutated_source_paths): # only include stack frames of user-code; exclude mutmut and 3rd library stack frames c -= 1 diff --git a/src/mutmut/configuration.py b/src/mutmut/configuration.py index ec9ef8b7..3324e7ba 100644 --- a/src/mutmut/configuration.py +++ b/src/mutmut/configuration.py @@ -103,6 +103,9 @@ def _load_config() -> Config: source_paths = [Path(path) for path in s("source_paths", [])] source_paths = source_paths or paths_to_mutate or [Path(path) for path in _guess_source_paths()] + # We resolve at startup, s.t. we are still in the current working directory (and no tests modified the directory) + resolved_mutated_source_paths = [Path.cwd().resolve(strict=True) / "mutants" / p for p in source_paths] + tests_dir = s("tests_dir", []) if tests_dir: warnings.warn( @@ -135,6 +138,7 @@ def _load_config() -> Config: debug=s("debug", False), mutate_only_covered_lines=s("mutate_only_covered_lines", False), source_paths=source_paths, + resolved_mutated_source_paths=resolved_mutated_source_paths, pytest_add_cli_args=s("pytest_add_cli_args", []), pytest_add_cli_args_test_selection=pytest_add_cli_args_test_selection, timeout_multiplier=s("timeout_multiplier", 15.0), @@ -164,6 +168,7 @@ class Config: max_stack_depth: int debug: bool source_paths: list[Path] + resolved_mutated_source_paths: list[Path] pytest_add_cli_args: list[str] pytest_add_cli_args_test_selection: list[str] mutate_only_covered_lines: bool diff --git a/tests/e2e/test_e2e_my_lib.py b/tests/e2e/test_e2e_my_lib.py index 5c0eddb3..ec5263a2 100644 --- a/tests/e2e/test_e2e_my_lib.py +++ b/tests/e2e/test_e2e_my_lib.py @@ -109,6 +109,16 @@ def test_my_lib_result_snapshot(): "my_lib.xǁ_PrivateClassǁget_question__mutmut_3": 0, "my_lib.xǁ_PrivateClassǁget_answer__mutmut_1": 0, "my_lib.x_divide__mutmut_1": 1, + "my_lib.x_write_into_file__mutmut_1": 1, + "my_lib.x_write_into_file__mutmut_2": 1, + "my_lib.x_write_into_file__mutmut_3": 1, + "my_lib.x_write_into_file__mutmut_4": 1, + "my_lib.x_write_into_file__mutmut_5": 1, + "my_lib.x_write_into_file__mutmut_6": 1, + "my_lib.x_write_into_file__mutmut_7": 1, + "my_lib.x_write_into_file__mutmut_8": 0, + "my_lib.x_write_into_file__mutmut_9": 1, + "my_lib.x_write_into_file__mutmut_10": 1, } } ) diff --git a/tests/mutation/test_mutation.py b/tests/mutation/test_mutation.py index 579a381b..e67786c0 100644 --- a/tests/mutation/test_mutation.py +++ b/tests/mutation/test_mutation.py @@ -1226,6 +1226,7 @@ def test_record_trampoline_hit_records_caller(monkeypatch): cfg = Mock(spec=Config) cfg.max_stack_depth = -1 cfg.source_paths = [] + cfg.resolved_mutated_source_paths = [] cfg.track_dependencies = True monkeypatch.setattr(Config, "get", lambda: cfg) @@ -1244,6 +1245,7 @@ def test_record_trampoline_hit_skips_caller_when_disabled(monkeypatch): cfg = Mock(spec=Config) cfg.max_stack_depth = -1 cfg.source_paths = [] + cfg.resolved_mutated_source_paths = [] cfg.track_dependencies = False monkeypatch.setattr(Config, "get", lambda: cfg) @@ -1318,6 +1320,7 @@ def _config_for_invalidation(**overrides): max_stack_depth=-1, debug=False, source_paths=[pathlib.Path("src")], + resolved_mutated_source_paths=[(pathlib.Path("mutants") / "src").absolute()], pytest_add_cli_args=[], pytest_add_cli_args_test_selection=[], mutate_only_covered_lines=False, diff --git a/tests/test_configuration.py b/tests/test_configuration.py index bb834783..8449fcbc 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -64,6 +64,7 @@ def _get_config(only_mutate: list[str], do_not_mutate: list[str]) -> Config: max_stack_depth=-1, debug=False, source_paths=[], + resolved_mutated_source_paths=[], pytest_add_cli_args=[], pytest_add_cli_args_test_selection=[], mutate_only_covered_lines=False,