From 1acfc0ab0664bec34d05fca8ee9e1b41b0f3ec2a Mon Sep 17 00:00:00 2001 From: Brad Swain Date: Fri, 25 Feb 2022 13:12:05 -0600 Subject: [PATCH 1/4] auto install missing solc versions --- tests/test_ast_parsing.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/test_ast_parsing.py b/tests/test_ast_parsing.py index 80235aa7b..e0707a924 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -85,6 +85,14 @@ def get_solc_versions() -> List[str]: return solc_versions +def install_solc_version(solc_version: str): + """ + install solc version using solc-select + :param solc_version: solc version to be installed, for example "0.8.7" + """ + subprocess.run(["solc-select", "install", solc_version], stderr=subprocess.PIPE, check=True) + + def get_tests(solc_versions) -> Dict[str, List[str]]: """ parse the list of testcases on disk @@ -119,7 +127,13 @@ def get_tests(solc_versions) -> Dict[str, List[str]]: else: for ver in vers: if ver not in solc_versions: - raise Exception("base version not found", test, ver) + try: + install_solc_version(ver) + solc_versions.append(ver) + except subprocess.CalledProcessError as e: + raise Exception( + "Failed to install solc version", test, ver, e.stderr.decode("utf-8") + ) from e return tests From 7ff981e602c901687dc8d65db9aa71f497408c2e Mon Sep 17 00:00:00 2001 From: Brad Swain Date: Fri, 25 Feb 2022 13:44:58 -0600 Subject: [PATCH 2/4] auto install solc versions in detector tests --- tests/solc.py | 30 ++++++++++++++++++++++++++++++ tests/test_ast_parsing.py | 34 ++++------------------------------ tests/test_detectors.py | 19 ++++++++++++++++++- 3 files changed, 52 insertions(+), 31 deletions(-) create mode 100644 tests/solc.py diff --git a/tests/solc.py b/tests/solc.py new file mode 100644 index 000000000..c6381f3ef --- /dev/null +++ b/tests/solc.py @@ -0,0 +1,30 @@ +import subprocess +from typing import List + + +def get_solc_versions() -> List[str]: + """ + get a list of all the supported versions of solidity, sorted from earliest to latest + :return: ascending list of versions, for example ["0.4.0", "0.4.1", ...] + """ + result = subprocess.run(["solc-select", "versions"], stdout=subprocess.PIPE, check=True) + solc_versions = result.stdout.decode("utf-8").split("\n") + + # there's an extra newline so just remove all empty strings + solc_versions = [version.split(" ")[0] for version in solc_versions if version != ""] + + solc_versions = sorted(solc_versions, key=lambda x: list(map(int, x.split(".")))) + return solc_versions + + +def install_solc_version(solc_version: str): + """ + install solc version using solc-select + :param solc_version: solc version to be installed, for example "0.8.7" + """ + try: + subprocess.run(["solc-select", "install", solc_version], stderr=subprocess.PIPE, check=True) + except subprocess.CalledProcessError as e: + raise Exception( + "Failed to install solc version", solc_version, e.stderr.decode("utf-8") + ) from e diff --git a/tests/test_ast_parsing.py b/tests/test_ast_parsing.py index e0707a924..8af5d6a65 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -15,6 +15,8 @@ from crytic_compile.utils.zip import load_from_zip from slither import Slither from slither.printers.guidance.echidna import Echidna +from .solc import get_solc_versions, install_solc_version + # these solc versions only support legacy ast format LEGACY_SOLC_VERS = [f"0.4.{v}" for v in range(12)] @@ -70,29 +72,6 @@ XFAIL = ( ) -def get_solc_versions() -> List[str]: - """ - get a list of all the supported versions of solidity, sorted from earliest to latest - :return: ascending list of versions, for example ["0.4.0", "0.4.1", ...] - """ - result = subprocess.run(["solc-select", "versions"], stdout=subprocess.PIPE, check=True) - solc_versions = result.stdout.decode("utf-8").split("\n") - - # there's an extra newline so just remove all empty strings - solc_versions = [version.split(" ")[0] for version in solc_versions if version != ""] - - solc_versions = sorted(solc_versions, key=lambda x: list(map(int, x.split(".")))) - return solc_versions - - -def install_solc_version(solc_version: str): - """ - install solc version using solc-select - :param solc_version: solc version to be installed, for example "0.8.7" - """ - subprocess.run(["solc-select", "install", solc_version], stderr=subprocess.PIPE, check=True) - - def get_tests(solc_versions) -> Dict[str, List[str]]: """ parse the list of testcases on disk @@ -127,13 +106,8 @@ def get_tests(solc_versions) -> Dict[str, List[str]]: else: for ver in vers: if ver not in solc_versions: - try: - install_solc_version(ver) - solc_versions.append(ver) - except subprocess.CalledProcessError as e: - raise Exception( - "Failed to install solc version", test, ver, e.stderr.decode("utf-8") - ) from e + install_solc_version(ver) + solc_versions.append(ver) return tests diff --git a/tests/test_detectors.py b/tests/test_detectors.py index 01d1d899a..a9c5c3d55 100644 --- a/tests/test_detectors.py +++ b/tests/test_detectors.py @@ -12,6 +12,8 @@ from slither import Slither from slither.detectors.abstract_detector import AbstractDetector from slither.detectors import all_detectors +from .solc import get_solc_versions, install_solc_version + class Test: # pylint: disable=too-few-public-methods def __init__( @@ -41,6 +43,7 @@ class Test: # pylint: disable=too-few-public-methods def set_solc(test_item: Test): # pylint: disable=too-many-lines + install_solc_version(test_item.solc_ver) # hacky hack hack to pick the solc version we want env = dict(os.environ) env["SOLC_VERSION"] = test_item.solc_ver @@ -52,7 +55,7 @@ def id_test(test_item: Test): return f"{test_item.detector}: {test_item.solc_ver}/{test_item.test_file}" -ALL_TESTS = [ +ALL_TEST_OBJECTS = [ Test( all_detectors.UninitializedFunctionPtrsConstructor, "uninitialized_function_ptr_constructor.sol", @@ -1200,6 +1203,20 @@ ALL_TESTS = [ "0.8.0", ), ] + + +def get_all_tests() -> List[Test]: + solc_versions = get_solc_versions() + for test in ALL_TEST_OBJECTS: + if test.solc_ver not in solc_versions: + install_solc_version(test.solc_ver) + solc_versions.append(test.solc_ver) + + return ALL_TEST_OBJECTS + + +ALL_TESTS = get_all_tests() + GENERIC_PATH = "/GENERIC_PATH" From ebab210806620732aed66095bb35e51cdca8cdf8 Mon Sep 17 00:00:00 2001 From: Brad Swain Date: Fri, 25 Feb 2022 13:55:28 -0600 Subject: [PATCH 3/4] remove redundant line --- tests/test_detectors.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_detectors.py b/tests/test_detectors.py index a9c5c3d55..dfad785f8 100644 --- a/tests/test_detectors.py +++ b/tests/test_detectors.py @@ -43,7 +43,6 @@ class Test: # pylint: disable=too-few-public-methods def set_solc(test_item: Test): # pylint: disable=too-many-lines - install_solc_version(test_item.solc_ver) # hacky hack hack to pick the solc version we want env = dict(os.environ) env["SOLC_VERSION"] = test_item.solc_ver From 783e527ce05a2fae882c7ae238fc4ecb378fd170 Mon Sep 17 00:00:00 2001 From: Brad Swain Date: Fri, 25 Feb 2022 16:22:48 -0600 Subject: [PATCH 4/4] use solc_select python api --- tests/solc.py | 30 ------------------------------ tests/test_ast_parsing.py | 19 +++++++++++-------- tests/test_detectors.py | 15 ++++++++------- 3 files changed, 19 insertions(+), 45 deletions(-) delete mode 100644 tests/solc.py diff --git a/tests/solc.py b/tests/solc.py deleted file mode 100644 index c6381f3ef..000000000 --- a/tests/solc.py +++ /dev/null @@ -1,30 +0,0 @@ -import subprocess -from typing import List - - -def get_solc_versions() -> List[str]: - """ - get a list of all the supported versions of solidity, sorted from earliest to latest - :return: ascending list of versions, for example ["0.4.0", "0.4.1", ...] - """ - result = subprocess.run(["solc-select", "versions"], stdout=subprocess.PIPE, check=True) - solc_versions = result.stdout.decode("utf-8").split("\n") - - # there's an extra newline so just remove all empty strings - solc_versions = [version.split(" ")[0] for version in solc_versions if version != ""] - - solc_versions = sorted(solc_versions, key=lambda x: list(map(int, x.split(".")))) - return solc_versions - - -def install_solc_version(solc_version: str): - """ - install solc version using solc-select - :param solc_version: solc version to be installed, for example "0.8.7" - """ - try: - subprocess.run(["solc-select", "install", solc_version], stderr=subprocess.PIPE, check=True) - except subprocess.CalledProcessError as e: - raise Exception( - "Failed to install solc version", solc_version, e.stderr.decode("utf-8") - ) from e diff --git a/tests/test_ast_parsing.py b/tests/test_ast_parsing.py index 8af5d6a65..d85058973 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -12,11 +12,12 @@ import pytest from crytic_compile import CryticCompile, save_to_zip from crytic_compile.utils.zip import load_from_zip +from solc_select.solc_select import install_artifacts as install_solc_versions +from solc_select.solc_select import installed_versions as get_installed_solc_versions + from slither import Slither from slither.printers.guidance.echidna import Echidna -from .solc import get_solc_versions, install_solc_version - # these solc versions only support legacy ast format LEGACY_SOLC_VERS = [f"0.4.{v}" for v in range(12)] @@ -72,10 +73,9 @@ XFAIL = ( ) -def get_tests(solc_versions) -> Dict[str, List[str]]: +def get_tests() -> Dict[str, List[str]]: """ parse the list of testcases on disk - :param solc_versions: the list of valid solidity versions :return: a dictionary of test id to list of base solidity versions supported """ slither_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -99,6 +99,8 @@ def get_tests(solc_versions) -> Dict[str, List[str]]: tests[key] = sorted(test, key=StrictVersion) # validate tests + solc_versions = get_installed_solc_versions() + missing_solc_versions = set() for test, vers in tests.items(): if len(vers) == 1: if vers[0] != "all": @@ -106,8 +108,9 @@ def get_tests(solc_versions) -> Dict[str, List[str]]: else: for ver in vers: if ver not in solc_versions: - install_solc_version(ver) - solc_versions.append(ver) + missing_solc_versions.add(ver) + if missing_solc_versions: + install_solc_versions(missing_solc_versions) return tests @@ -128,8 +131,8 @@ def get_all_test() -> List[Item]: generate a list of testcases by testing each test id with every solidity version for both legacy and compact ast :return: the testcases """ - solc_versions = get_solc_versions() - tests = get_tests(solc_versions) + tests = get_tests() + solc_versions = get_installed_solc_versions() ret = [] diff --git a/tests/test_detectors.py b/tests/test_detectors.py index dfad785f8..7b5fd993c 100644 --- a/tests/test_detectors.py +++ b/tests/test_detectors.py @@ -8,12 +8,13 @@ from typing import Type, Optional, List import pytest from deepdiff import DeepDiff # pip install deepdiff +from solc_select.solc_select import install_artifacts as install_solc_versions +from solc_select.solc_select import installed_versions as get_installed_solc_versions + from slither import Slither from slither.detectors.abstract_detector import AbstractDetector from slither.detectors import all_detectors -from .solc import get_solc_versions, install_solc_version - class Test: # pylint: disable=too-few-public-methods def __init__( @@ -1205,11 +1206,11 @@ ALL_TEST_OBJECTS = [ def get_all_tests() -> List[Test]: - solc_versions = get_solc_versions() - for test in ALL_TEST_OBJECTS: - if test.solc_ver not in solc_versions: - install_solc_version(test.solc_ver) - solc_versions.append(test.solc_ver) + installed_solcs = set(get_installed_solc_versions()) + required_solcs = {test.solc_ver for test in ALL_TEST_OBJECTS} + missing_solcs = list(required_solcs - installed_solcs) + if missing_solcs: + install_solc_versions(missing_solcs) return ALL_TEST_OBJECTS