|
|
|
# pylint: disable=redefined-outer-name
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import tempfile
|
|
|
|
from pathlib import Path
|
|
|
|
from contextlib import contextmanager
|
|
|
|
from filelock import FileLock
|
|
|
|
from solc_select import solc_select
|
|
|
|
import pytest
|
|
|
|
from slither import Slither
|
|
|
|
|
|
|
|
|
|
|
|
def pytest_configure(config):
|
|
|
|
"""Create a temporary directory for the tests to use."""
|
|
|
|
if is_master():
|
|
|
|
config.stash["shared_directory"] = tempfile.mkdtemp()
|
|
|
|
|
|
|
|
|
|
|
|
def pytest_unconfigure(config):
|
|
|
|
"""Remove the temporary directory after the tests are done."""
|
|
|
|
if is_master():
|
|
|
|
shutil.rmtree(config.stash["shared_directory"])
|
|
|
|
|
|
|
|
|
|
|
|
def pytest_configure_node(node):
|
|
|
|
"""Configure each worker node with the shared directory."""
|
|
|
|
node.workerinput["shared_directory"] = node.config.stash["shared_directory"]
|
|
|
|
|
|
|
|
|
|
|
|
def is_master():
|
|
|
|
"""Returns True if the current process is the master process (which does not have a worker id)."""
|
|
|
|
return os.environ.get("PYTEST_XDIST_WORKER") is None
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def shared_directory(request):
|
|
|
|
"""Returns the shared directory for the current process."""
|
|
|
|
if is_master():
|
|
|
|
return request.config.stash["shared_directory"]
|
|
|
|
return request.config.workerinput["shared_directory"]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def solc_binary_path(shared_directory):
|
|
|
|
"""
|
|
|
|
Returns the path to the solc binary for the given version.
|
|
|
|
If the binary is not installed, it will be installed.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def inner(version):
|
|
|
|
lock = FileLock(f"{shared_directory}/{version}.lock", timeout=60)
|
|
|
|
with lock:
|
|
|
|
if not solc_select.artifact_path(version).exists():
|
|
|
|
print("Installing solc version", version)
|
|
|
|
solc_select.install_artifacts([version])
|
|
|
|
return solc_select.artifact_path(version).as_posix()
|
|
|
|
|
|
|
|
return inner
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def slither_from_solidity_source(solc_binary_path):
|
|
|
|
@contextmanager
|
|
|
|
def inner(source_code: str, solc_version: str = "0.8.19"):
|
|
|
|
"""Yields a Slither instance using source_code string and solc_version.
|
|
|
|
Creates a temporary file and compiles with solc_version.
|
|
|
|
"""
|
|
|
|
|
|
|
|
fname = ""
|
|
|
|
try:
|
|
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".sol", delete=False) as f:
|
|
|
|
fname = f.name
|
|
|
|
f.write(source_code)
|
|
|
|
solc_path = solc_binary_path(solc_version)
|
|
|
|
yield Slither(fname, solc=solc_path)
|
|
|
|
finally:
|
|
|
|
Path(fname).unlink()
|
|
|
|
|
|
|
|
return inner
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def slither_from_vyper_source():
|
|
|
|
@contextmanager
|
|
|
|
def inner(source_code: str):
|
|
|
|
"""Yields a Slither instance using source_code string.
|
|
|
|
Creates a temporary file and compiles with vyper.
|
|
|
|
"""
|
|
|
|
|
|
|
|
fname = ""
|
|
|
|
try:
|
|
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".vy", delete=False) as f:
|
|
|
|
fname = f.name
|
|
|
|
f.write(source_code)
|
|
|
|
yield Slither(fname)
|
|
|
|
finally:
|
|
|
|
Path(fname).unlink()
|
|
|
|
|
|
|
|
return inner
|