Static Analyzer for Solidity
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
slither/tests/tools/mutator/test_mutator.py

133 lines
3.8 KiB

import argparse
from contextlib import contextmanager
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
from unittest import mock
import pytest
from slither import Slither
from slither.tools.mutator.__main__ import _get_mutators, main
from slither.tools.mutator.utils.testing_generated_mutant import run_test_cmd
from slither.tools.mutator.utils.file_handling import get_sol_file_list, backup_source_file
TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"
foundry_available = shutil.which("forge") is not None
project_ready = Path(TEST_DATA_DIR, "test_source_unit/lib/forge-std").exists()
@contextmanager
def change_directory(new_dir):
original_dir = os.getcwd()
os.chdir(new_dir)
try:
yield
finally:
os.chdir(original_dir)
def test_get_mutators():
mutators = _get_mutators(None)
assert mutators
mutators = _get_mutators(["ASOR"])
assert len(mutators) == 1
assert mutators[0].NAME == "ASOR"
mutators = _get_mutators(["ASOR", "NotExisiting"])
assert len(mutators) == 1
@mock.patch(
"argparse.ArgumentParser.parse_args",
return_value=argparse.Namespace(
test_cmd="forge test",
test_dir=None,
ignore_dirs="lib,mutation_campaign",
output_dir=None,
timeout=None,
solc_remaps="forge-std=./lib/forge-std",
verbose=None,
very_verbose=None,
mutators_to_run=None,
comprehensive=None,
codebase=(TEST_DATA_DIR / "test_source_unit" / "src" / "Counter.sol").as_posix(),
contract_names="Counter",
),
)
@pytest.mark.skip(reason="Slow test")
def test_mutator(mock_args): # pylint: disable=unused-argument
with change_directory(TEST_DATA_DIR / "test_source_unit"):
main()
def test_backup_source_file():
file_path = (TEST_DATA_DIR / "test_source_unit" / "src" / "Counter.sol").as_posix()
sl = Slither(file_path)
with tempfile.TemporaryDirectory() as directory:
files_dict = backup_source_file(sl.source_code, Path(directory))
assert len(files_dict) == 1
assert Path(files_dict[file_path]).exists()
@pytest.mark.skipif(
not foundry_available or not project_ready, reason="requires Foundry and project setup"
)
def test_get_sol_file_list():
project_directory = TEST_DATA_DIR / "test_source_unit"
files = get_sol_file_list(project_directory, None)
assert len(files) == 46
files = get_sol_file_list(project_directory, ["lib"])
assert len(files) == 3
files = get_sol_file_list(project_directory, ["lib", "script"])
assert len(files) == 2
files = get_sol_file_list(project_directory / "src" / "Counter.sol", None)
assert len(files) == 1
(project_directory / "test.sol").mkdir()
files = get_sol_file_list(project_directory, None)
assert all("test.sol" not in file for file in files)
(project_directory / "test.sol").rmdir()
@pytest.mark.skipif(
not foundry_available or not project_ready, reason="requires Foundry and project setup"
)
def test_run_test(caplog):
with change_directory(TEST_DATA_DIR / "test_source_unit"):
result = run_test_cmd("forge test", timeout=None, target_file=None, verbose=True)
assert result
assert not caplog.records
# Failed command
result = run_test_cmd("forge non-test", timeout=None, target_file=None, verbose=True)
assert not result
assert caplog.records
def test_run_tests_timeout(caplog, monkeypatch):
def mock_run(*args, **kwargs):
raise subprocess.TimeoutExpired(cmd=args[0], timeout=kwargs.get("timeout"))
monkeypatch.setattr(subprocess, "run", mock_run)
with change_directory(TEST_DATA_DIR / "test_source_unit"):
result = run_test_cmd("forge test", timeout=1)
assert not result
assert "Tests took too long" in caplog.messages[0]