Skip to content

Commit

Permalink
run tests in temporary directory
Browse files Browse the repository at this point in the history
  • Loading branch information
tuturu-tech committed Mar 28, 2024
1 parent f46fe5f commit 68c4d53
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 21 deletions.
3 changes: 0 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ reformat:
test tests: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
solc-select use 0.8.19 --always-install && \
cd tests/test_data && \
forge install && \
cd ../.. && \
pytest --ignore tests/test_data/lib $(T) $(TEST_ARGS)

.PHONY: package
Expand Down
72 changes: 71 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import subprocess
import re
import shutil
from typing import Any, Callable
import pytest

Expand Down Expand Up @@ -40,7 +41,7 @@ def medusa_generate_tests(self) -> None:
self.medusa_generator.create_poc()


@pytest.fixture(autouse=True) # type: ignore[misc]
@pytest.fixture() # type: ignore[misc]
def change_test_dir(request: Any, monkeypatch: Any) -> None:
"""Helper fixture to change the working directory"""
# Directory of the test file
Expand Down Expand Up @@ -103,6 +104,75 @@ def value_transfer() -> TestGenerator:
return TestGenerator(target, target_path, corpus_dir)


@pytest.fixture(scope="session") # type: ignore[misc]
def setup_foundry_temp_dir(tmp_path_factory):
"""Sets up a temporary directory for the tests that contain all the necessary Foundry files"""
# Create a temporary directory valid for the session
temp_dir = tmp_path_factory.mktemp("foundry_session")
original_dir: str = os.getcwd()

print("Installing Forge...")
subprocess.run(["forge", "init", "--no-git"], check=True, cwd=temp_dir)
subprocess.run(["forge", "install", "crytic/properties", "--no-git"], check=True, cwd=temp_dir)
subprocess.run(
["forge", "install", "transmissions11/solmate", "--no-git"], check=True, cwd=temp_dir
)
# Create remappings file
remappings = temp_dir / "remappings.txt"
with open(remappings, "w", encoding="utf-8") as outfile:
outfile.write(
"forge-std/=lib/forge-std/src/\nproperties/=lib/properties/contracts/\nsolmate/=lib/solmate/src/\nsrc/=src/"
)

# Delete unnecessary files
counter_path = temp_dir / "src" / "Counter.sol"
counter_path.unlink()
assert not counter_path.exists()

counter_test_path = temp_dir / "test" / "Counter.t.sol"
counter_test_path.unlink()
assert not counter_test_path.exists()

scripts_dir = temp_dir / "script"
shutil.rmtree(scripts_dir)
assert not scripts_dir.exists()

# Create the corpora directories in the temporary dir
echidna_corpora_dir = temp_dir / "echidna-corpora"
medusa_corpora_dir = temp_dir / "medusa-corpora"
echidna_corpora_dir.mkdir(exist_ok=True)
medusa_corpora_dir.mkdir(exist_ok=True)

# Copy all our contracts and corpora to the temporary directory
copy_directory_contents(
os.path.join(original_dir, "tests", "test_data", "echidna-corpora"),
temp_dir / "echidna-corpora",
)
copy_directory_contents(
os.path.join(original_dir, "tests", "test_data", "medusa-corpora"),
temp_dir / "medusa-corpora",
)
copy_directory_contents(
os.path.join(original_dir, "tests", "test_data", "src"), temp_dir / "src"
)

os.chdir(temp_dir)


def copy_directory_contents(src_dir, dest_dir) -> None:
"""
Copies the contents of src_dir into dest_dir. The dest_dir must already exist.
Directories under src_dir will be created under dest_dir and files will be copied.
"""
for item in os.listdir(src_dir):
s = os.path.join(src_dir, item)
d = os.path.join(dest_dir, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True) # For Python 3.8+, use dirs_exist_ok=True
else:
shutil.copy2(s, d)


def run_generation_command_test(
generate_tests: Callable, test_name: str, fuzzer: str, pattern: str
) -> None:
Expand Down
29 changes: 22 additions & 7 deletions tests/test_harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path
import copy
import subprocess
from pytest import TempPathFactory
from slither import Slither
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
Expand Down Expand Up @@ -32,7 +33,9 @@
}


def test_modifier_filtering() -> None:
def test_modifier_filtering(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
) -> None:
"""Test non-strict modifier filtering"""
filters = {
"strict": False,
Expand All @@ -51,7 +54,9 @@ def test_modifier_filtering() -> None:
)


def test_external_call_filtering() -> None:
def test_external_call_filtering(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
) -> None:
"""Test non-strict external call filtering"""
filters = {
"strict": False,
Expand All @@ -70,7 +75,9 @@ def test_external_call_filtering() -> None:
)


def test_payable_filtering() -> None:
def test_payable_filtering(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
) -> None:
"""Test non-strict payable call filtering"""
filters = {
"strict": False,
Expand All @@ -89,7 +96,9 @@ def test_payable_filtering() -> None:
)


def test_modifier_and_external_call_filtering() -> None:
def test_modifier_and_external_call_filtering(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
) -> None:
"""Test non-strict modifier and external call filtering"""
filters = {
"strict": False,
Expand All @@ -108,7 +117,9 @@ def test_modifier_and_external_call_filtering() -> None:
)


def test_strict_modifier_and_external_call_filtering() -> None:
def test_strict_modifier_and_external_call_filtering(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
) -> None:
"""Test strict modifier and external call filtering"""
filters = {
"strict": True,
Expand All @@ -127,7 +138,9 @@ def test_strict_modifier_and_external_call_filtering() -> None:
)


def test_multiple_external_calls_filtering() -> None:
def test_multiple_external_calls_filtering(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
) -> None:
"""Test multiple external calls filtering"""
filters = {
"strict": True,
Expand All @@ -146,7 +159,9 @@ def test_multiple_external_calls_filtering() -> None:
)


def test_strict_multiple_external_calls_filtering() -> None:
def test_strict_multiple_external_calls_filtering(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
) -> None:
"""Test strict multiple external calls filtering"""
filters = {
"strict": True,
Expand Down
27 changes: 22 additions & 5 deletions tests/test_types_echidna.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,57 @@
""" Tests for generating compilable test files from an Echidna corpus"""
from pathlib import Path
from pytest import TempPathFactory
from .conftest import TestGenerator, run_generation_command_test


TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"
PATTERN = r"(\d+)\s+failing tests,\s+(\d+)\s+tests succeeded"


def test_echidna_basic_types(basic_types: TestGenerator) -> None:
def test_echidna_basic_types(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
basic_types: TestGenerator,
) -> None:
"""Tests the BasicTypes contract with an Echidna corpus"""
run_generation_command_test(
basic_types.echidna_generate_tests, "BasicTypes", "Echidna", PATTERN
)


def test_echidna_fixed_array_types(fixed_size_arrays: TestGenerator) -> None:
def test_echidna_fixed_array_types(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
fixed_size_arrays: TestGenerator,
) -> None:
"""Tests the FixedArrays contract with an Echidna corpus"""
run_generation_command_test(
fixed_size_arrays.echidna_generate_tests, "FixedArrays", "Echidna", PATTERN
)


def test_echidna_dynamic_array_types(dynamic_arrays: TestGenerator) -> None:
def test_echidna_dynamic_array_types(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
dynamic_arrays: TestGenerator,
) -> None:
"""Tests the DynamicArrays contract with an Echidna corpus"""
run_generation_command_test(
dynamic_arrays.echidna_generate_tests, "DynamicArrays", "Echidna", PATTERN
)


def test_echidna_structs_and_enums(structs_and_enums: TestGenerator) -> None:
def test_echidna_structs_and_enums(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
structs_and_enums: TestGenerator,
) -> None:
"""Tests the TupleTypes contract with an Echidna corpus"""
run_generation_command_test(
structs_and_enums.echidna_generate_tests, "TupleTypes", "Echidna", PATTERN
)


def test_echidna_value_transfer(value_transfer: TestGenerator) -> None:
def test_echidna_value_transfer(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
value_transfer: TestGenerator,
) -> None:
"""Tests the ValueTransfer contract with an Echidna corpus"""
run_generation_command_test(
value_transfer.echidna_generate_tests, "ValueTransfer", "Echidna", PATTERN
Expand Down
27 changes: 22 additions & 5 deletions tests/test_types_medusa.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,57 @@
""" Tests for generating compilable test files from an Medusa corpus"""
from pathlib import Path
import pytest
from pytest import TempPathFactory

from .conftest import TestGenerator, run_generation_command_test

TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"
PATTERN = r"(\d+)\s+failing tests,\s+(\d+)\s+tests succeeded"


def test_medusa_basic_types(basic_types: TestGenerator) -> None:
def test_medusa_basic_types(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
basic_types: TestGenerator,
) -> None:
"""Tests the BasicTypes contract with a Medusa corpus"""
run_generation_command_test(basic_types.medusa_generate_tests, "BasicTypes", "Medusa", PATTERN)


def test_medusa_fixed_array_types(fixed_size_arrays: TestGenerator) -> None:
def test_medusa_fixed_array_types(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
fixed_size_arrays: TestGenerator,
) -> None:
"""Tests the FixedArrays contract with a Medusa corpus"""
run_generation_command_test(
fixed_size_arrays.medusa_generate_tests, "FixedArrays", "Medusa", PATTERN
)


def test_medusa_dynamic_array_types(dynamic_arrays: TestGenerator) -> None:
def test_medusa_dynamic_array_types(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
dynamic_arrays: TestGenerator,
) -> None:
"""Tests the DynamicArrays contract with a Medusa corpus"""
run_generation_command_test(
dynamic_arrays.medusa_generate_tests, "DynamicArrays", "Medusa", PATTERN
)


def test_medusa_structs_and_enums(structs_and_enums: TestGenerator) -> None:
def test_medusa_structs_and_enums(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
structs_and_enums: TestGenerator,
) -> None:
"""Tests the TupleTypes contract with a Medusa corpus"""
run_generation_command_test(
structs_and_enums.medusa_generate_tests, "TupleTypes", "Medusa", PATTERN
)


@pytest.mark.xfail(strict=True) # type: ignore[misc]
def test_medusa_value_transfer(value_transfer: TestGenerator) -> None:
def test_medusa_value_transfer(
setup_foundry_temp_dir: TempPathFactory, # pylint: disable=unused-argument
value_transfer: TestGenerator,
) -> None:
"""Tests the ValueTransfer contract with a Medusa corpus"""
run_generation_command_test(
value_transfer.medusa_generate_tests, "ValueTransfer", "Medusa", PATTERN
Expand Down

0 comments on commit 68c4d53

Please sign in to comment.