Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion tests/e2e/test_cli_version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from click.testing import CliRunner

from mutmut import __version__
from mutmut.__main__ import cli
from click.testing import CliRunner


def test_cli_version():
result = CliRunner().invoke(cli, ["--version"])
Expand Down
11 changes: 6 additions & 5 deletions tests/e2e/test_e2e_result_snapshots.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from contextlib import contextmanager
import json
import os
from pathlib import Path
import shutil
from contextlib import contextmanager
from pathlib import Path
from typing import Any

import mutmut
from mutmut.__main__ import _run, walk_source_files, SourceFileMutationData, ensure_config_loaded
from mutmut.__main__ import SourceFileMutationData, _run, ensure_config_loaded, walk_source_files


@contextmanager
def change_cwd(path):
old_cwd = os.path.abspath(os.getcwd())
old_cwd = Path(Path.cwd()).resolve()
os.chdir(path)
try:
yield
Expand All @@ -25,7 +26,7 @@ def read_all_stats_for_project(project_path: Path) -> dict[str, dict]:

stats = {}
for p in walk_source_files():
if mutmut.config.should_ignore_for_mutation(p): # type: ignore
if mutmut.config.should_ignore_for_mutation(p): # type: ignore
continue
data = SourceFileMutationData(path=p)
data.load()
Expand Down
13 changes: 8 additions & 5 deletions tests/test_generation_error_handling.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
from pathlib import Path
from mutmut.__main__ import create_mutants, Config, InvalidGeneratedSyntaxException
import mutmut.__main__
import mutmut

import pytest
import os

import mutmut
import mutmut.__main__
from mutmut.__main__ import InvalidGeneratedSyntaxException, create_mutants

source_dir = Path(__file__).parent / 'data' / 'test_generation'
source_dir = source_dir.relative_to(os.getcwd())
source_dir = source_dir.relative_to(Path.cwd())


class MockConfig:
def should_ignore_for_mutation(self, path: Path) -> bool:
return False


def test_mutant_generation_raises_exception_on_invalid_syntax(monkeypatch):
mutmut._reset_globals()
mutmut.config = MockConfig()
Expand Down
37 changes: 22 additions & 15 deletions tests/test_mutation.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import os
from unittest.mock import Mock, patch
import pytest

import libcst as cst
import pytest

import mutmut
from mutmut.__main__ import (
CLASS_NAME_SEPARATOR,
CatchOutput,
MutmutProgrammaticFailException,
get_diff_for_mutant,
orig_function_and_class_names_from_key,
run_forced_fail_test,
Config,
MutmutProgrammaticFailException,
CatchOutput,
)
from mutmut.trampoline_templates import trampoline_impl, mangle_function_name
from mutmut.file_mutation import create_mutations, mutate_file_contents
from mutmut.trampoline_templates import mangle_function_name, trampoline_impl


def mutants_for_source(source: str) -> list[str]:
module, mutated_nodes = create_mutations(source)
mutants: list[str] = []
for m in mutated_nodes:
mutants.append(module.deep_replace(m.original_node, m.mutated_node).code) # type: ignore
mutants: list[str] = [module.deep_replace(m.original_node, m.mutated_node).code for m in mutated_nodes] # type: ignore

return mutants


def mutated_module(source: str) -> str:
mutated_code, _ = mutate_file_contents('', source)
return mutated_code
Expand Down Expand Up @@ -220,7 +220,6 @@ def mutated_module(source: str) -> str:
('foo.bar', []),
('for x in y: pass', []),
('def foo(a, *args, **kwargs): pass', []),
('import foo', []),
('isinstance(a, b)', []),
('len(a)', []),
('deepcopy(obj)', ['copy(obj)', 'deepcopy(None)']),
Expand Down Expand Up @@ -248,6 +247,7 @@ def foo() -> int:

assert not mutants


def test_do_not_mutate_specific_functions():
source = """
class A:
Expand All @@ -267,6 +267,7 @@ def __setattr__():

assert not mutants


def test_match_case():
source = """
match x:
Expand All @@ -285,6 +286,7 @@ def test_match_case():

assert sorted(mutants) == sorted(expected)


def test_mach_case_does_not_mutate_bitor():
source = """
def concat():
Expand Down Expand Up @@ -344,6 +346,7 @@ def test_pragma_no_mutate_and_no_cover():
mutants = mutants_for_source(source)
assert not mutants


def test_pragma_no_mutate_on_function_definition():
source = """
def foo(): # pragma: no mutate
Expand Down Expand Up @@ -438,7 +441,8 @@ def foo():


def test_orig_function_name_from_key():
assert orig_function_and_class_names_from_key(f'_{CLASS_NAME_SEPARATOR}Foo{CLASS_NAME_SEPARATOR}bar__mutmut_1') == ('bar', 'Foo')
assert orig_function_and_class_names_from_key(
f'_{CLASS_NAME_SEPARATOR}Foo{CLASS_NAME_SEPARATOR}bar__mutmut_1') == ('bar', 'Foo')
assert orig_function_and_class_names_from_key('x_bar__mutmut_1') == ('bar', None)


Expand Down Expand Up @@ -496,6 +500,7 @@ def foo():
assert mutated_source.split('\n')[0] == 'from __future__ import annotations'
assert mutated_source.count('from __future__') == 1


def test_from_future_with_docstring_still_first():
source = """
'''This documents the module'''
Expand Down Expand Up @@ -527,7 +532,7 @@ def test_run_forced_fail_test_with_failing_test(_start, _stop, _dump_output, cap
print(f"out: {out}")
print(f"err: {err}")
assert 'done' in out
assert os.environ['MUTANT_UNDER_TEST'] is ''
assert not os.environ['MUTANT_UNDER_TEST']


# Negate the effects of CatchOutput because it does not play nicely with capfd in GitHub Actions
Expand All @@ -540,9 +545,9 @@ def test_run_forced_fail_test_with_mutmut_programmatic_fail_exception(_start, _s

run_forced_fail_test(runner)

out, err = capfd.readouterr()
out, _ = capfd.readouterr()
assert 'done' in out
assert os.environ['MUTANT_UNDER_TEST'] is ''
assert not os.environ['MUTANT_UNDER_TEST']


# Negate the effects of CatchOutput because it does not play nicely with capfd in GitHub Actions
Expand All @@ -556,8 +561,8 @@ def test_run_forced_fail_test_with_all_tests_passing(_start, _stop, _dump_output
with pytest.raises(SystemExit) as error:
run_forced_fail_test(runner)

assert error.value.code is 1
out, err = capfd.readouterr()
assert error.value.code == 1
out, _ = capfd.readouterr()
assert 'FAILED: Unable to force test failures' in out


Expand All @@ -569,6 +574,7 @@ def _mocked_runner_run_forced_failed(return_value=None, side_effect=None):
)
return runner


def test_do_not_mutate_top_level_decorators():
# Modifying top-level decorators could influence all mutations
# because they are executed at import time
Expand All @@ -591,6 +597,7 @@ def x(self):

assert not mutants


# TODO: implement removal of inner decorators
@pytest.mark.skip
def test_decorated_inner_functions_mutation():
Expand Down
3 changes: 2 additions & 1 deletion tests/test_mutmut3.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from mutmut.trampoline_templates import trampoline_impl
from mutmut.file_mutation import mutate_file_contents
from mutmut.trampoline_templates import trampoline_impl


def mutated_module(source: str) -> str:
mutated_code, _ = mutate_file_contents('', source)
Expand Down