Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

run tests in parallel locally with makefile #1808

Merged
merged 14 commits into from
Apr 17, 2023
Merged
2 changes: 0 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ jobs:
- name: Install dependencies
run: |
pip install ".[test]"
solc-select install 0.8.0
solc-select use 0.8.0

- name: Setup node
uses: actions/setup-node@v3
Expand Down
87 changes: 87 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
SHELL := /bin/bash

PY_MODULE := slither

ALL_PY_SRCS := $(shell find $(PY_MODULE) -name '*.py') \
$(shell find test -name '*.py')

# Optionally overriden by the user, if they're using a virtual environment manager.
VENV ?= env

# On Windows, venv scripts/shims are under `Scripts` instead of `bin`.
VENV_BIN := $(VENV)/bin
ifeq ($(OS),Windows_NT)
VENV_BIN := $(VENV)/Scripts
endif

# Optionally overridden by the user in the `release` target.
BUMP_ARGS :=

# Optionally overridden by the user in the `test` target.
TESTS :=

# Optionally overridden by the user/CI, to limit the installation to a specific
# subset of development dependencies.
SLITHER_EXTRA := dev

# If the user selects a specific test pattern to run, set `pytest` to fail fast
# and only run tests that match the pattern.
# Otherwise, run all tests and enable coverage assertions, since we expect
# complete test coverage.
ifneq ($(TESTS),)
TEST_ARGS := -x -k $(TESTS)
COV_ARGS :=
else
TEST_ARGS := -n auto
COV_ARGS := --cov-append # --fail-under 100
endif

.PHONY: all
all:
@echo "Run my targets individually!"

.PHONY: dev
dev: $(VENV)/pyvenv.cfg

.PHONY: run
run: $(VENV)/pyvenv.cfg
@. $(VENV_BIN)/activate && slither $(ARGS)

$(VENV)/pyvenv.cfg: pyproject.toml
# Create our Python 3 virtual environment
python3 -m venv env
$(VENV_BIN)/python -m pip install --upgrade pip
$(VENV_BIN)/python -m pip install -e .[$(SLITHER_EXTRA)]

.PHONY: lint
lint: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
black --check $(ALL_PY_SRCS) && \
pylint $(ALL_PY_SRCS)
# ruff $(ALL_PY_SRCS) && \
# mypy $(PY_MODULE) &&

.PHONY: reformat
reformat:
. $(VENV_BIN)/activate && \
black $(PY_MODULE)

.PHONY: test tests
test tests: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
pytest --cov=$(PY_MODULE) $(T) $(TEST_ARGS) && \
python -m coverage report -m $(COV_ARGS)

.PHONY: doc
doc: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
PDOC_ALLOW_EXEC=1 pdoc -o html slither '!slither.tools'

.PHONY: package
package: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
python3 -m build

.PHONY: edit
edit:
$(EDITOR) $(ALL_PY_SRCS)
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# "crytic-compile>=0.3.0",
"crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile",
"web3>=6.0.0",
"solc-select@git+https://github.com/crytic/solc-select.git@query-artifact-path#egg=solc-select",
],
extras_require={
"lint": [
Expand All @@ -31,6 +32,7 @@
"deepdiff",
"numpy",
"coverage[toml]",
"filelock",
],
"doc": [
"pdoc",
Expand Down
27 changes: 27 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pytest
from filelock import FileLock
from solc_select import solc_select

@pytest.fixture(scope="session")
def solc_versions_installed():
"""List of solc versions available in the test environment."""
return []

@pytest.fixture(scope="session", autouse=True)
def register_solc_versions_installed(solc_versions_installed):
solc_versions_installed.extend(solc_select.installed_versions())

@pytest.fixture(scope="session")
0xalpharush marked this conversation as resolved.
Show resolved Hide resolved
def use_solc_version(request, solc_versions_installed):
def _use_solc_version(version):
print(version)
if version not in solc_versions_installed:
print("Installing solc version", version)
solc_select.install_artifacts([version])
artifact_path = solc_select.artifact_path(version)
lock = FileLock(artifact_path)
try:
yield artifact_path
finally:
lock.release()
return _use_solc_version
12 changes: 6 additions & 6 deletions tests/e2e/compilation/test_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def test_node_modules() -> None:
_run_all_detectors(slither)


def test_contract_name_collisions() -> None:
solc_select.switch_global_version("0.8.0", always_install=True)
def test_contract_name_collision(use_solc_version) -> None:
solc_path = next(use_solc_version("0.8.0"))
standard_json = SolcStandardJson()
standard_json.add_source_file(
Path(TEST_DATA_DIR, "test_contract_name_collisions", "a.sol").as_posix()
Expand All @@ -34,13 +34,13 @@ def test_contract_name_collisions() -> None:
Path(TEST_DATA_DIR, "test_contract_name_collisions", "b.sol").as_posix()
)

compilation = CryticCompile(standard_json)
compilation = CryticCompile(standard_json, solc=solc_path)
slither = Slither(compilation)

_run_all_detectors(slither)


def test_cycle() -> None:
solc_select.switch_global_version("0.8.0", always_install=True)
slither = Slither(Path(TEST_DATA_DIR, "test_cyclic_import", "a.sol").as_posix())
def test_cycle(use_solc_version) -> None:
solc_path = next(use_solc_version("0.8.0"))
slither = Slither(Path(TEST_DATA_DIR, "test_cyclic_import", "a.sol").as_posix(), solc=solc_path)
_run_all_detectors(slither)
6 changes: 4 additions & 2 deletions tests/tools/read-storage/test_read_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ def deploy_contract(w3, ganache, contract_bin, contract_abi) -> Contract:

# pylint: disable=too-many-locals
@pytest.mark.usefixtures("web3", "ganache")
def test_read_storage(web3, ganache) -> None:
def test_read_storage(web3, ganache, use_solc_version) -> None:
solc_path = next(use_solc_version(version="0.8.10"))

assert web3.is_connected()
bin_path = Path(TEST_DATA_DIR, "StorageLayout.bin").as_posix()
abi_path = Path(TEST_DATA_DIR, "StorageLayout.abi").as_posix()
Expand All @@ -100,7 +102,7 @@ def test_read_storage(web3, ganache) -> None:
contract.functions.store().transact({"from": ganache.eth_address})
address = contract.address

sl = Slither(Path(TEST_DATA_DIR, "storage_layout-0.8.10.sol").as_posix())
sl = Slither(Path(TEST_DATA_DIR, "storage_layout-0.8.10.sol").as_posix(), solc=solc_path)
contracts = sl.contracts

srs = SlitherReadStorage(contracts, 100)
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/core/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" / "arithmetic_usage"


def test_arithmetic_usage() -> None:
solc_select.switch_global_version("0.8.15", always_install=True)
slither = Slither(Path(TEST_DATA_DIR, "test.sol").as_posix())
def test_arithmetic_usage(use_solc_version) -> None:
solc_path = next(use_solc_version("0.8.15"))
slither = Slither(Path(TEST_DATA_DIR, "test.sol").as_posix(), solc=solc_path)

assert {
f.source_mapping.content_hash for f in unchecked_arithemtic_usage(slither.contracts[0])
Expand Down
18 changes: 9 additions & 9 deletions tests/unit/core/test_code_comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
CUSTOM_COMMENTS_TEST_DATA_DIR = Path(TEST_DATA_DIR, "custom_comments")


def test_upgradeable_comments() -> None:
solc_select.switch_global_version("0.8.10", always_install=True)
slither = Slither(Path(CUSTOM_COMMENTS_TEST_DATA_DIR, "upgrade.sol").as_posix())
def test_upgradeable_comments(use_solc_version) -> None:
solc_path = next(use_solc_version("0.8.10"))
slither = Slither(Path(CUSTOM_COMMENTS_TEST_DATA_DIR, "upgrade.sol").as_posix(), solc=solc_path)
compilation_unit = slither.compilation_units[0]
proxy = compilation_unit.get_contract_from_name("Proxy")[0]

Expand All @@ -27,11 +27,11 @@ def test_upgradeable_comments() -> None:
assert v1.upgradeable_version == "version_1"


def test_contract_comments() -> None:
def test_contract_comments(use_solc_version) -> None:
comments = " @title Test Contract\n @dev Test comment"

solc_select.switch_global_version("0.8.10", always_install=True)
slither = Slither(Path(CUSTOM_COMMENTS_TEST_DATA_DIR, "contract_comment.sol").as_posix())
solc_path = next(use_solc_version("0.8.10"))
slither = Slither(Path(CUSTOM_COMMENTS_TEST_DATA_DIR, "contract_comment.sol").as_posix(), solc=solc_path)
compilation_unit = slither.compilation_units[0]
contract = compilation_unit.get_contract_from_name("A")[0]

Expand All @@ -40,19 +40,19 @@ def test_contract_comments() -> None:
# Old solc versions have a different parsing of comments
# the initial space (after *) is also not kept on every line
comments = "@title Test Contract\n@dev Test comment"
solc_select.switch_global_version("0.5.16", always_install=True)
slither = Slither(Path(CUSTOM_COMMENTS_TEST_DATA_DIR, "contract_comment.sol").as_posix())
solc_path = next(use_solc_version("0.5.16"))
slither = Slither(Path(CUSTOM_COMMENTS_TEST_DATA_DIR, "contract_comment.sol").as_posix(), solc=solc_path)
compilation_unit = slither.compilation_units[0]
contract = compilation_unit.get_contract_from_name("A")[0]

assert contract.comments == comments

# Test with legacy AST
comments = "@title Test Contract\n@dev Test comment"
solc_select.switch_global_version("0.5.16", always_install=True)
slither = Slither(
Path(CUSTOM_COMMENTS_TEST_DATA_DIR, "contract_comment.sol").as_posix(),
solc_force_legacy_json=True,
solc=solc_path,
)
compilation_unit = slither.compilation_units[0]
contract = compilation_unit.get_contract_from_name("A")[0]
Expand Down
15 changes: 9 additions & 6 deletions tests/unit/core/test_constant_folding.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
CONSTANT_FOLDING_TEST_ROOT = Path(TEST_DATA_DIR, "constant_folding")


def test_constant_folding_unary():
def test_constant_folding_unary(use_solc_version):
solc_path = next(use_solc_version("0.8.0"))
file = Path(CONSTANT_FOLDING_TEST_ROOT, "constant_folding_unary.sol").as_posix()
Slither(file)
Slither(file, solc=solc_path)


def test_constant_folding_rational():
s = Slither(Path(CONSTANT_FOLDING_TEST_ROOT, "constant_folding_rational.sol").as_posix())
def test_constant_folding_rational(use_solc_version):
solc_path = next(use_solc_version("0.8.0"))
s = Slither(Path(CONSTANT_FOLDING_TEST_ROOT, "constant_folding_rational.sol").as_posix(), solc=solc_path)
contract = s.get_contract_from_name("C")[0]

variable_a = contract.get_state_variable_from_name("a")
Expand Down Expand Up @@ -50,8 +52,9 @@ def test_constant_folding_rational():
assert str(ConstantFolding(variable_g.expression, "int64").result()) == "-7"


def test_constant_folding_binary_expressions():
sl = Slither(Path(CONSTANT_FOLDING_TEST_ROOT, "constant_folding_binop.sol").as_posix())
def test_constant_folding_binary_expressions(use_solc_version):
solc_path = next(use_solc_version("0.8.0"))
sl = Slither(Path(CONSTANT_FOLDING_TEST_ROOT, "constant_folding_binop.sol").as_posix(), solc=solc_path)
contract = sl.get_contract_from_name("BinOp")[0]

variable_a = contract.get_state_variable_from_name("a")
Expand Down
17 changes: 9 additions & 8 deletions tests/unit/core/test_contract_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,26 @@
CONTRACT_DECL_TEST_ROOT = Path(TEST_DATA_DIR, "contract_declaration")


def test_abstract_contract() -> None:
solc_select.switch_global_version("0.8.0", always_install=True)
slither = Slither(Path(CONTRACT_DECL_TEST_ROOT, "abstract.sol").as_posix())
def test_abstract_contract(use_solc_version) -> None:
solc_path = next(use_solc_version("0.8.0"))
slither = Slither(Path(CONTRACT_DECL_TEST_ROOT, "abstract.sol").as_posix(), solc=solc_path)
assert not slither.contracts[0].is_fully_implemented

solc_select.switch_global_version("0.5.0", always_install=True)
slither = Slither(Path(CONTRACT_DECL_TEST_ROOT, "implicit_abstract.sol").as_posix())
solc_path = next(use_solc_version("0.5.0"))
slither = Slither(Path(CONTRACT_DECL_TEST_ROOT, "implicit_abstract.sol").as_posix(), solc=solc_path)
assert not slither.contracts[0].is_fully_implemented

slither = Slither(
Path(CONTRACT_DECL_TEST_ROOT, "implicit_abstract.sol").as_posix(),
solc_force_legacy_json=True,
solc=solc_path
)
assert not slither.contracts[0].is_fully_implemented


def test_private_variable() -> None:
solc_select.switch_global_version("0.8.15", always_install=True)
slither = Slither(Path(CONTRACT_DECL_TEST_ROOT, "private_variable.sol").as_posix())
def test_private_variable(use_solc_version) -> None:
solc_path = next(use_solc_version("0.8.15"))
slither = Slither(Path(CONTRACT_DECL_TEST_ROOT, "private_variable.sol").as_posix(), solc=solc_path)
contract_c = slither.get_contract_from_name("C")[0]
f = contract_c.functions[0]
var_read = f.variables_read[0]
Expand Down
24 changes: 12 additions & 12 deletions tests/unit/core/test_function_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
FUNC_DELC_TEST_ROOT = Path(TEST_DATA_DIR, "function_declaration")


def test_functions():
def test_functions(use_solc_version):
# pylint: disable=too-many-statements
solc_select.switch_global_version("0.6.12", always_install=True)
solc_path = next(use_solc_version("0.6.12"))
file = Path(FUNC_DELC_TEST_ROOT, "test_function.sol").as_posix()
slither = Slither(file)
slither = Slither(file, solc=solc_path)
functions = slither.get_contract_from_name("TestFunction")[0].available_functions_as_dict()

f = functions["external_payable(uint256)"]
Expand Down Expand Up @@ -248,10 +248,10 @@ def test_functions():
assert f.return_type[0] == ElementaryType("bool")


def test_function_can_send_eth():
solc_select.switch_global_version("0.6.12", always_install=True)
def test_function_can_send_eth(use_solc_version):
solc_path = next(use_solc_version("0.6.12"))
file = Path(FUNC_DELC_TEST_ROOT, "test_function.sol").as_posix()
slither = Slither(file)
slither = Slither(file, solc=solc_path)
compilation_unit = slither.compilation_units[0]
functions = compilation_unit.get_contract_from_name("TestFunctionCanSendEth")[
0
Expand All @@ -273,10 +273,10 @@ def test_function_can_send_eth():
assert functions["highlevel_call_via_external()"].can_send_eth() is False


def test_reentrant():
solc_select.switch_global_version("0.8.10", always_install=True)
def test_reentrant(use_solc_version):
solc_path = next(use_solc_version("0.8.10"))
file = Path(FUNC_DELC_TEST_ROOT, "test_function_reentrant.sol").as_posix()
slither = Slither(file)
slither = Slither(file, solc=solc_path)
compilation_unit = slither.compilation_units[0]
functions = compilation_unit.get_contract_from_name("TestReentrant")[
0
Expand All @@ -290,10 +290,10 @@ def test_reentrant():
assert functions["internal_and_reentrant()"].is_reentrant


def test_public_variable() -> None:
solc_select.switch_global_version("0.6.12", always_install=True)
def test_public_variable(use_solc_version) -> None:
solc_path = next(use_solc_version("0.6.12"))
file = Path(FUNC_DELC_TEST_ROOT, "test_function.sol").as_posix()
slither = Slither(file)
slither = Slither(file, solc=solc_path)
contracts = slither.get_contract_from_name("TestFunction")
assert len(contracts) == 1
contract = contracts[0]
Expand Down
Loading