From 778d6f4b26f53fd5907440175c8921d76e82066e Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Wed, 22 Mar 2023 13:21:04 +0100 Subject: [PATCH] Migrate externalTests.sh to python MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil ƚliwak --- scripts/common/shell_command.py | 34 +++++++ test/externalTests.sh | 55 ----------- test/external_tests.py | 158 ++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 55 deletions(-) create mode 100644 scripts/common/shell_command.py delete mode 100755 test/externalTests.sh create mode 100755 test/external_tests.py diff --git a/scripts/common/shell_command.py b/scripts/common/shell_command.py new file mode 100644 index 000000000..e31389bb8 --- /dev/null +++ b/scripts/common/shell_command.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +import os +import subprocess + +def run_cmd(command: str, env: dict = None, logfile: str = None) -> int: + """ + Args: + command: command to run + logfile: log file name + env: dictionary holding key-value pairs for bash environment variables + Returns: + int: The exit status of the command. Exit status codes are: + 0 -> Success + 1-255 -> Failure + """ + if logfile is None: + logfile = os.devnull + if env is None: + env = os.environ.copy() + with open( + file=logfile, + mode='w', + encoding='utf8' + ) as log: + ret = subprocess.run( + command, + shell=True, + check=True, + executable='/bin/bash', + env=env, + stdout=log if not logfile else None, + stderr=None + ) + return ret.returncode diff --git a/test/externalTests.sh b/test/externalTests.sh deleted file mode 100755 index 4746e2a58..000000000 --- a/test/externalTests.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -#------------------------------------------------------------------------------ -# Bash script to run external Solidity tests. -# -# Argument: Path to soljson.js to test. -# -# Requires npm, networking access and git to download the tests. -# -# ------------------------------------------------------------------------------ -# This file is part of solidity. -# -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2016 solidity contributors. -#------------------------------------------------------------------------------ - -set -e - -source scripts/common.sh -source test/externalTests/common.sh - -REPO_ROOT=$(realpath "$(dirname "$0")/..") - -verify_input "$@" - -printTask "Running external tests..." - -"${REPO_ROOT}/test/externalTests/zeppelin.sh" "$@" -"${REPO_ROOT}/test/externalTests/gnosis.sh" "$@" -"${REPO_ROOT}/test/externalTests/colony.sh" "$@" -"${REPO_ROOT}/test/externalTests/ens.sh" "$@" -"${REPO_ROOT}/test/externalTests/trident.sh" "$@" -"${REPO_ROOT}/test/externalTests/euler.sh" "$@" -"${REPO_ROOT}/test/externalTests/yield-liquidator.sh" "$@" -"${REPO_ROOT}/test/externalTests/bleeps.sh" "$@" -"${REPO_ROOT}/test/externalTests/pool-together.sh" "$@" -"${REPO_ROOT}/test/externalTests/perpetual-pools.sh" "$@" -"${REPO_ROOT}/test/externalTests/uniswap.sh" "$@" -"${REPO_ROOT}/test/externalTests/prb-math.sh" "$@" -"${REPO_ROOT}/test/externalTests/elementfi.sh" "$@" -"${REPO_ROOT}/test/externalTests/brink.sh" "$@" -"${REPO_ROOT}/test/externalTests/chainlink.sh" "$@" -"${REPO_ROOT}/test/externalTests/gp2.sh" "$@" diff --git a/test/external_tests.py b/test/external_tests.py new file mode 100755 index 000000000..c230533a0 --- /dev/null +++ b/test/external_tests.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2023 solidity contributors. +# ------------------------------------------------------------------------------ + +from argparse import ArgumentParser, Namespace +import os +from pathlib import Path +import sys + +# Our scripts/ is not a proper Python package so we need to modify PYTHONPATH to import from it +# pragma pylint: disable=import-error,wrong-import-position +SCRIPTS_DIR = Path(__file__).parents[1] / "scripts" +sys.path.insert(0, str(SCRIPTS_DIR)) + +from common.shell_command import run_cmd + +EXTERNAL_TESTS_DIR = Path(__file__).parent / "externalTests" + + +class ExternalTestNotFound(Exception): + pass + + +def detect_external_tests() -> dict: + # TODO: Remove `file_path.stem != "common"` when we complete the migration + # of the external tests to python, since there will be no more + # common.sh script in the externalTests folder. + return { + file_path.stem: file_path + for file_path in Path(EXTERNAL_TESTS_DIR).iterdir() + if file_path.is_file() and file_path.suffix == ".sh" and file_path.stem != "common" + } + + +def display_available_external_tests(_): + print("Available external tests:") + print(*detect_external_tests().keys()) + + +def run_test_scripts(solc_binary_type: str, solc_binary_path: Path, tests: dict): + for test_name, test_script_path in tests.items(): + print(f"Running {test_name} external test...") + subprocess.run( + [test_script_path, solc_binary_type, solc_binary_path], + check=True + ) + + +def run_external_tests(args: dict): + solc_binary_type = args["solc_binary_type"] + solc_binary_path = args["solc_binary_path"] + + all_test_scripts = detect_external_tests() + if args["run_all"]: + run_test_scripts(solc_binary_type, solc_binary_path, all_test_scripts) + else: + selected_tests = args["selected_tests"] + if selected_tests: + unrecognized_tests = set(selected_tests) - set(all_test_scripts.keys()) + if unrecognized_tests != set(): + raise ExternalTestNotFound( + f"External test(s) not found: {', '.join(unrecognized_tests)}" + ) + run_test_scripts( + solc_binary_type, + solc_binary_path, + {k: all_test_scripts[k] for k in selected_tests}, + ) + raise ExternalTestNotFound( + "External test was not selected. Please use --run or --run-all option" + ) + + +def parse_commandline() -> Namespace: + script_description = "Script to run external Solidity tests." + + parser = ArgumentParser(description=script_description) + subparser = parser.add_subparsers() + list_command = subparser.add_parser( + "list", + help="List all available external tests.", + ) + list_command.set_defaults(cmd=display_available_external_tests) + + run_command = subparser.add_parser( + "test", + help="Run external tests.", + ) + run_command.set_defaults(cmd=run_external_tests) + + run_command.add_argument( + "--solc-binary-type", + dest="solc_binary_type", + type=str, + required=True, + choices=["native", "solcjs"], + help="Type of the solidity compiler binary to be used.", + ) + run_command.add_argument( + "--solc-binary-path", + dest="solc_binary_path", + type=Path, + required=True, + help="Path to the solidity compiler binary.", + ) + + running_mode = run_command.add_mutually_exclusive_group() + running_mode.add_argument( + "--run", + metavar="TEST_NAME", + dest="selected_tests", + nargs="+", + default=[], + help="List of one or more external tests to run (separated by sapce).", + ) + running_mode.add_argument( + "--run-all", + dest="run_all", + default=False, + action="store_true", + help="Run all available external tests.", + ) + + return parser.parse_args() + + +def main(): + try: + args = parse_commandline() + args.cmd(vars(args)) + return os.EX_OK + except ExternalTestNotFound as exception: + print(f"Error: {exception}", file=sys.stderr) + return os.EX_NOINPUT + except RuntimeError as exception: + print(f"Error: {exception}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + sys.exit(main())