diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69c119d7371..2d4985c9974 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -260,6 +260,7 @@ jobs: env: BITCOIND: '${{ github.workspace }}\build\bin\Release\bitcoind.exe' BITCOINCLI: '${{ github.workspace }}\build\bin\Release\bitcoin-cli.exe' + BITCOINTX: '${{ github.workspace }}\build\bin\Release\bitcoin-tx.exe' BITCOINUTIL: '${{ github.workspace }}\build\bin\Release\bitcoin-util.exe' BITCOINWALLET: '${{ github.workspace }}\build\bin\Release\bitcoin-wallet.exe' TEST_RUNNER_EXTRA: ${{ github.event_name != 'pull_request' && '--extended' || '' }} @@ -389,9 +390,6 @@ jobs: (Get-Content "test/config.ini") -replace '(?<=^SRCDIR=).*', '${{ github.workspace }}' -replace '(?<=^BUILDDIR=).*', '${{ github.workspace }}' -replace '(?<=^RPCAUTH=).*', '${{ github.workspace }}/share/rpcauth/rpcauth.py' | Set-Content "test/config.ini" Get-Content "test/config.ini" - - name: Run util tests - run: py -3 test/util/test_runner.py - - name: Run rpcauth test run: py -3 test/util/rpcauth-test.py diff --git a/CMakeLists.txt b/CMakeLists.txt index f7241020815..18d23142a10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -594,7 +594,7 @@ if(Python3_EXECUTABLE) set(PYTHON_COMMAND ${Python3_EXECUTABLE}) else() list(APPEND configure_warnings - "Minimum required Python not found. Utils and rpcauth tests are disabled." + "Minimum required Python not found. Rpcauth tests are disabled." ) endif() diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 27913298001..46104593c85 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -2,12 +2,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://opensource.org/license/mit/. -if(TARGET bitcoin-util AND TARGET bitcoin-tx AND PYTHON_COMMAND) - add_test(NAME util_test_runner - COMMAND ${CMAKE_COMMAND} -E env BITCOINUTIL=$ BITCOINTX=$ ${PYTHON_COMMAND} ${PROJECT_BINARY_DIR}/test/util/test_runner.py - ) -endif() - if(PYTHON_COMMAND) add_test(NAME util_rpcauth_test COMMAND ${PYTHON_COMMAND} ${PROJECT_BINARY_DIR}/test/util/rpcauth-test.py diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7de03407354..b7fde825604 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,7 @@ function(create_test_config) set_configure_variable(ENABLE_WALLET ENABLE_WALLET) set_configure_variable(BUILD_CLI BUILD_BITCOIN_CLI) + set_configure_variable(BUILD_TX BUILD_BITCOIN_TX) set_configure_variable(BUILD_UTIL BUILD_BITCOIN_UTIL) set_configure_variable(BUILD_UTIL_CHAINSTATE BUILD_BITCOIN_CHAINSTATE) set_configure_variable(BUILD_WALLET_TOOL BUILD_BITCOIN_WALLET) @@ -36,7 +37,7 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/fuzz) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/util) file(GLOB_RECURSE functional_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} functional/*) -foreach(script ${functional_tests} fuzz/test_runner.py util/rpcauth-test.py util/test_runner.py) +foreach(script ${functional_tests} fuzz/test_runner.py util/rpcauth-test.py) if(CMAKE_HOST_WIN32) set(symlink) else() diff --git a/test/README.md b/test/README.md index 74adc644b83..843777da06e 100644 --- a/test/README.md +++ b/test/README.md @@ -10,10 +10,9 @@ This directory contains the following sets of tests: - [functional](/test/functional) which test the functionality of bitcoind and bitcoin-qt by interacting with them through the RPC and P2P interfaces. -- [util](/test/util) which tests the utilities (bitcoin-util, bitcoin-tx, ...). - [lint](/test/lint/) which perform various static analysis checks. -The util tests are run as part of `ctest` invocation. The fuzz tests, functional +The fuzz tests, functional tests and lint scripts can be run as explained in the sections below. # Running tests locally @@ -321,11 +320,6 @@ perf report -i /path/to/datadir/send-big-msgs.perf.data.xxxx --stdio | c++filt | For ways to generate more granular profiles, see the README in [test/functional](/test/functional). -### Util tests - -Util tests can be run locally by running `build/test/util/test_runner.py`. -Use the `-v` option for verbose output. - ### Lint tests See the README in [test/lint](/test/lint). diff --git a/test/config.ini.in b/test/config.ini.in index 9a297cb3391..6865fa8a47d 100644 --- a/test/config.ini.in +++ b/test/config.ini.in @@ -17,6 +17,7 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py # Which components are enabled. These are commented out by cmake if they were disabled during configuration. @ENABLE_WALLET_TRUE@ENABLE_WALLET=true @BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true +@BUILD_BITCOIN_TX_TRUE@BUILD_BITCOIN_TX=true @BUILD_BITCOIN_UTIL_TRUE@ENABLE_BITCOIN_UTIL=true @BUILD_BITCOIN_CHAINSTATE_TRUE@ENABLE_BITCOIN_CHAINSTATE=true @BUILD_BITCOIN_WALLET_TRUE@ENABLE_WALLET_TOOL=true diff --git a/test/util/data/bitcoin-util-test.json b/test/functional/data/util/bitcoin-util-test.json similarity index 100% rename from test/util/data/bitcoin-util-test.json rename to test/functional/data/util/bitcoin-util-test.json diff --git a/test/util/data/blanktxv1.hex b/test/functional/data/util/blanktxv1.hex similarity index 100% rename from test/util/data/blanktxv1.hex rename to test/functional/data/util/blanktxv1.hex diff --git a/test/util/data/blanktxv1.json b/test/functional/data/util/blanktxv1.json similarity index 100% rename from test/util/data/blanktxv1.json rename to test/functional/data/util/blanktxv1.json diff --git a/test/util/data/blanktxv2.hex b/test/functional/data/util/blanktxv2.hex similarity index 100% rename from test/util/data/blanktxv2.hex rename to test/functional/data/util/blanktxv2.hex diff --git a/test/util/data/blanktxv2.json b/test/functional/data/util/blanktxv2.json similarity index 100% rename from test/util/data/blanktxv2.json rename to test/functional/data/util/blanktxv2.json diff --git a/test/util/data/tt-delin1-out.hex b/test/functional/data/util/tt-delin1-out.hex similarity index 100% rename from test/util/data/tt-delin1-out.hex rename to test/functional/data/util/tt-delin1-out.hex diff --git a/test/util/data/tt-delin1-out.json b/test/functional/data/util/tt-delin1-out.json similarity index 100% rename from test/util/data/tt-delin1-out.json rename to test/functional/data/util/tt-delin1-out.json diff --git a/test/util/data/tt-delout1-out.hex b/test/functional/data/util/tt-delout1-out.hex similarity index 100% rename from test/util/data/tt-delout1-out.hex rename to test/functional/data/util/tt-delout1-out.hex diff --git a/test/util/data/tt-delout1-out.json b/test/functional/data/util/tt-delout1-out.json similarity index 100% rename from test/util/data/tt-delout1-out.json rename to test/functional/data/util/tt-delout1-out.json diff --git a/test/util/data/tt-locktime317000-out.hex b/test/functional/data/util/tt-locktime317000-out.hex similarity index 100% rename from test/util/data/tt-locktime317000-out.hex rename to test/functional/data/util/tt-locktime317000-out.hex diff --git a/test/util/data/tt-locktime317000-out.json b/test/functional/data/util/tt-locktime317000-out.json similarity index 100% rename from test/util/data/tt-locktime317000-out.json rename to test/functional/data/util/tt-locktime317000-out.json diff --git a/test/util/data/tx394b54bb.hex b/test/functional/data/util/tx394b54bb.hex similarity index 100% rename from test/util/data/tx394b54bb.hex rename to test/functional/data/util/tx394b54bb.hex diff --git a/test/util/data/txcreate1.hex b/test/functional/data/util/txcreate1.hex similarity index 100% rename from test/util/data/txcreate1.hex rename to test/functional/data/util/txcreate1.hex diff --git a/test/util/data/txcreate1.json b/test/functional/data/util/txcreate1.json similarity index 100% rename from test/util/data/txcreate1.json rename to test/functional/data/util/txcreate1.json diff --git a/test/util/data/txcreate2.hex b/test/functional/data/util/txcreate2.hex similarity index 100% rename from test/util/data/txcreate2.hex rename to test/functional/data/util/txcreate2.hex diff --git a/test/util/data/txcreate2.json b/test/functional/data/util/txcreate2.json similarity index 100% rename from test/util/data/txcreate2.json rename to test/functional/data/util/txcreate2.json diff --git a/test/util/data/txcreatedata1.hex b/test/functional/data/util/txcreatedata1.hex similarity index 100% rename from test/util/data/txcreatedata1.hex rename to test/functional/data/util/txcreatedata1.hex diff --git a/test/util/data/txcreatedata1.json b/test/functional/data/util/txcreatedata1.json similarity index 100% rename from test/util/data/txcreatedata1.json rename to test/functional/data/util/txcreatedata1.json diff --git a/test/util/data/txcreatedata2.hex b/test/functional/data/util/txcreatedata2.hex similarity index 100% rename from test/util/data/txcreatedata2.hex rename to test/functional/data/util/txcreatedata2.hex diff --git a/test/util/data/txcreatedata2.json b/test/functional/data/util/txcreatedata2.json similarity index 100% rename from test/util/data/txcreatedata2.json rename to test/functional/data/util/txcreatedata2.json diff --git a/test/util/data/txcreatedata_seq0.hex b/test/functional/data/util/txcreatedata_seq0.hex similarity index 100% rename from test/util/data/txcreatedata_seq0.hex rename to test/functional/data/util/txcreatedata_seq0.hex diff --git a/test/util/data/txcreatedata_seq0.json b/test/functional/data/util/txcreatedata_seq0.json similarity index 100% rename from test/util/data/txcreatedata_seq0.json rename to test/functional/data/util/txcreatedata_seq0.json diff --git a/test/util/data/txcreatedata_seq1.hex b/test/functional/data/util/txcreatedata_seq1.hex similarity index 100% rename from test/util/data/txcreatedata_seq1.hex rename to test/functional/data/util/txcreatedata_seq1.hex diff --git a/test/util/data/txcreatedata_seq1.json b/test/functional/data/util/txcreatedata_seq1.json similarity index 100% rename from test/util/data/txcreatedata_seq1.json rename to test/functional/data/util/txcreatedata_seq1.json diff --git a/test/util/data/txcreatemultisig1.hex b/test/functional/data/util/txcreatemultisig1.hex similarity index 100% rename from test/util/data/txcreatemultisig1.hex rename to test/functional/data/util/txcreatemultisig1.hex diff --git a/test/util/data/txcreatemultisig1.json b/test/functional/data/util/txcreatemultisig1.json similarity index 100% rename from test/util/data/txcreatemultisig1.json rename to test/functional/data/util/txcreatemultisig1.json diff --git a/test/util/data/txcreatemultisig2.hex b/test/functional/data/util/txcreatemultisig2.hex similarity index 100% rename from test/util/data/txcreatemultisig2.hex rename to test/functional/data/util/txcreatemultisig2.hex diff --git a/test/util/data/txcreatemultisig2.json b/test/functional/data/util/txcreatemultisig2.json similarity index 100% rename from test/util/data/txcreatemultisig2.json rename to test/functional/data/util/txcreatemultisig2.json diff --git a/test/util/data/txcreatemultisig3.hex b/test/functional/data/util/txcreatemultisig3.hex similarity index 100% rename from test/util/data/txcreatemultisig3.hex rename to test/functional/data/util/txcreatemultisig3.hex diff --git a/test/util/data/txcreatemultisig3.json b/test/functional/data/util/txcreatemultisig3.json similarity index 100% rename from test/util/data/txcreatemultisig3.json rename to test/functional/data/util/txcreatemultisig3.json diff --git a/test/util/data/txcreatemultisig4.hex b/test/functional/data/util/txcreatemultisig4.hex similarity index 100% rename from test/util/data/txcreatemultisig4.hex rename to test/functional/data/util/txcreatemultisig4.hex diff --git a/test/util/data/txcreatemultisig4.json b/test/functional/data/util/txcreatemultisig4.json similarity index 100% rename from test/util/data/txcreatemultisig4.json rename to test/functional/data/util/txcreatemultisig4.json diff --git a/test/util/data/txcreatemultisig5.json b/test/functional/data/util/txcreatemultisig5.json similarity index 100% rename from test/util/data/txcreatemultisig5.json rename to test/functional/data/util/txcreatemultisig5.json diff --git a/test/util/data/txcreateoutpubkey1.hex b/test/functional/data/util/txcreateoutpubkey1.hex similarity index 100% rename from test/util/data/txcreateoutpubkey1.hex rename to test/functional/data/util/txcreateoutpubkey1.hex diff --git a/test/util/data/txcreateoutpubkey1.json b/test/functional/data/util/txcreateoutpubkey1.json similarity index 100% rename from test/util/data/txcreateoutpubkey1.json rename to test/functional/data/util/txcreateoutpubkey1.json diff --git a/test/util/data/txcreateoutpubkey2.hex b/test/functional/data/util/txcreateoutpubkey2.hex similarity index 100% rename from test/util/data/txcreateoutpubkey2.hex rename to test/functional/data/util/txcreateoutpubkey2.hex diff --git a/test/util/data/txcreateoutpubkey2.json b/test/functional/data/util/txcreateoutpubkey2.json similarity index 100% rename from test/util/data/txcreateoutpubkey2.json rename to test/functional/data/util/txcreateoutpubkey2.json diff --git a/test/util/data/txcreateoutpubkey3.hex b/test/functional/data/util/txcreateoutpubkey3.hex similarity index 100% rename from test/util/data/txcreateoutpubkey3.hex rename to test/functional/data/util/txcreateoutpubkey3.hex diff --git a/test/util/data/txcreateoutpubkey3.json b/test/functional/data/util/txcreateoutpubkey3.json similarity index 100% rename from test/util/data/txcreateoutpubkey3.json rename to test/functional/data/util/txcreateoutpubkey3.json diff --git a/test/util/data/txcreatescript1.hex b/test/functional/data/util/txcreatescript1.hex similarity index 100% rename from test/util/data/txcreatescript1.hex rename to test/functional/data/util/txcreatescript1.hex diff --git a/test/util/data/txcreatescript1.json b/test/functional/data/util/txcreatescript1.json similarity index 100% rename from test/util/data/txcreatescript1.json rename to test/functional/data/util/txcreatescript1.json diff --git a/test/util/data/txcreatescript2.hex b/test/functional/data/util/txcreatescript2.hex similarity index 100% rename from test/util/data/txcreatescript2.hex rename to test/functional/data/util/txcreatescript2.hex diff --git a/test/util/data/txcreatescript2.json b/test/functional/data/util/txcreatescript2.json similarity index 100% rename from test/util/data/txcreatescript2.json rename to test/functional/data/util/txcreatescript2.json diff --git a/test/util/data/txcreatescript3.hex b/test/functional/data/util/txcreatescript3.hex similarity index 100% rename from test/util/data/txcreatescript3.hex rename to test/functional/data/util/txcreatescript3.hex diff --git a/test/util/data/txcreatescript3.json b/test/functional/data/util/txcreatescript3.json similarity index 100% rename from test/util/data/txcreatescript3.json rename to test/functional/data/util/txcreatescript3.json diff --git a/test/util/data/txcreatescript4.hex b/test/functional/data/util/txcreatescript4.hex similarity index 100% rename from test/util/data/txcreatescript4.hex rename to test/functional/data/util/txcreatescript4.hex diff --git a/test/util/data/txcreatescript4.json b/test/functional/data/util/txcreatescript4.json similarity index 100% rename from test/util/data/txcreatescript4.json rename to test/functional/data/util/txcreatescript4.json diff --git a/test/util/data/txcreatescript5.hex b/test/functional/data/util/txcreatescript5.hex similarity index 100% rename from test/util/data/txcreatescript5.hex rename to test/functional/data/util/txcreatescript5.hex diff --git a/test/util/data/txcreatescript6.hex b/test/functional/data/util/txcreatescript6.hex similarity index 100% rename from test/util/data/txcreatescript6.hex rename to test/functional/data/util/txcreatescript6.hex diff --git a/test/util/data/txcreatesignsegwit1.hex b/test/functional/data/util/txcreatesignsegwit1.hex similarity index 100% rename from test/util/data/txcreatesignsegwit1.hex rename to test/functional/data/util/txcreatesignsegwit1.hex diff --git a/test/util/data/txcreatesignv1.hex b/test/functional/data/util/txcreatesignv1.hex similarity index 100% rename from test/util/data/txcreatesignv1.hex rename to test/functional/data/util/txcreatesignv1.hex diff --git a/test/util/data/txcreatesignv1.json b/test/functional/data/util/txcreatesignv1.json similarity index 100% rename from test/util/data/txcreatesignv1.json rename to test/functional/data/util/txcreatesignv1.json diff --git a/test/util/data/txcreatesignv2.hex b/test/functional/data/util/txcreatesignv2.hex similarity index 100% rename from test/util/data/txcreatesignv2.hex rename to test/functional/data/util/txcreatesignv2.hex diff --git a/test/util/data/txreplace1.hex b/test/functional/data/util/txreplace1.hex similarity index 100% rename from test/util/data/txreplace1.hex rename to test/functional/data/util/txreplace1.hex diff --git a/test/util/data/txreplacenoinputs.hex b/test/functional/data/util/txreplacenoinputs.hex similarity index 100% rename from test/util/data/txreplacenoinputs.hex rename to test/functional/data/util/txreplacenoinputs.hex diff --git a/test/util/data/txreplaceomittedn.hex b/test/functional/data/util/txreplaceomittedn.hex similarity index 100% rename from test/util/data/txreplaceomittedn.hex rename to test/functional/data/util/txreplaceomittedn.hex diff --git a/test/util/data/txreplacesingleinput.hex b/test/functional/data/util/txreplacesingleinput.hex similarity index 100% rename from test/util/data/txreplacesingleinput.hex rename to test/functional/data/util/txreplacesingleinput.hex diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py index 75507877d54..9e475bbe853 100755 --- a/test/functional/rpc_users.py +++ b/test/functional/rpc_users.py @@ -17,7 +17,6 @@ import urllib.parse import subprocess from random import SystemRandom import string -import configparser import sys from typing import Optional @@ -47,9 +46,7 @@ class HTTPBasicsTest(BitcoinTestFramework): self.rpcuser = "rpcuser💻" self.rpcpassword = "rpcpassword🔑" - config = configparser.ConfigParser() - config.read_file(open(self.options.configfile)) - gen_rpcauth = config['environment']['RPCAUTH'] + gen_rpcauth = self.config["environment"]["RPCAUTH"] # Generate RPCAUTH with specified password self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 6ae2cd07185..1014a608ab9 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -84,6 +84,10 @@ class Binaries: # Add -nonamed because "bitcoin rpc" enables -named by default, but bitcoin-cli doesn't return self._argv("rpc", self.paths.bitcoincli) + ["-nonamed"] + def tx_argv(self): + "Return argv array that should be used to invoke bitcoin-tx" + return self._argv("tx", self.paths.bitcointx) + def util_argv(self): "Return argv array that should be used to invoke bitcoin-util" return self._argv("util", self.paths.bitcoinutil) @@ -272,9 +276,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.options.timeout_factor = self.options.timeout_factor or (4 if self.options.valgrind else 1) self.options.previous_releases_path = previous_releases_path - config = configparser.ConfigParser() - config.read_file(open(self.options.configfile)) - self.config = config + self.config = configparser.ConfigParser() + self.config.read_file(open(self.options.configfile)) self.binary_paths = self.get_binary_paths() if self.options.v1transport: self.options.v2transport=False @@ -286,19 +289,20 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): paths = types.SimpleNamespace() binaries = { - "bitcoind": ("bitcoind", "BITCOIND"), - "bitcoin-cli": ("bitcoincli", "BITCOINCLI"), - "bitcoin-util": ("bitcoinutil", "BITCOINUTIL"), - "bitcoin-chainstate": ("bitcoinchainstate", "BITCOINCHAINSTATE"), - "bitcoin-wallet": ("bitcoinwallet", "BITCOINWALLET"), + "bitcoind": "BITCOIND", + "bitcoin-cli": "BITCOINCLI", + "bitcoin-util": "BITCOINUTIL", + "bitcoin-tx": "BITCOINTX", + "bitcoin-chainstate": "BITCOINCHAINSTATE", + "bitcoin-wallet": "BITCOINWALLET", } - for binary, [attribute_name, env_variable_name] in binaries.items(): + for binary, env_variable_name in binaries.items(): default_filename = os.path.join( self.config["environment"]["BUILDDIR"], "bin", binary + self.config["environment"]["EXEEXT"], ) - setattr(paths, attribute_name, os.getenv(env_variable_name, default=default_filename)) + setattr(paths, env_variable_name.lower(), os.getenv(env_variable_name, default=default_filename)) # BITCOIN_CMD environment variable can be specified to invoke bitcoin # wrapper binary instead of other executables. paths.bitcoin_cmd = shlex.split(os.getenv("BITCOIN_CMD", "")) or None @@ -314,10 +318,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.options.cachedir = os.path.abspath(self.options.cachedir) - config = self.config - os.environ['PATH'] = os.pathsep.join([ - os.path.join(config['environment']['BUILDDIR'], 'bin'), + os.path.join(self.config["environment"]["BUILDDIR"], "bin"), os.environ['PATH'] ]) @@ -1000,6 +1002,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): if not self.is_wallet_tool_compiled(): raise SkipTest("bitcoin-wallet has not been compiled") + def skip_if_no_bitcoin_tx(self): + """Skip the running test if bitcoin-tx has not been compiled.""" + if not self.is_bitcoin_tx_compiled(): + raise SkipTest("bitcoin-tx has not been compiled") + def skip_if_no_bitcoin_util(self): """Skip the running test if bitcoin-util has not been compiled.""" if not self.is_bitcoin_util_compiled(): @@ -1054,6 +1061,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): """Checks whether bitcoin-wallet was compiled.""" return self.config["components"].getboolean("ENABLE_WALLET_TOOL") + def is_bitcoin_tx_compiled(self): + """Checks whether bitcoin-tx was compiled.""" + return self.config["components"].getboolean("BUILD_BITCOIN_TX") + def is_bitcoin_util_compiled(self): """Checks whether bitcoin-util was compiled.""" return self.config["components"].getboolean("ENABLE_BITCOIN_UTIL") diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 4764f6eab0f..75a7cca8fc9 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -170,6 +170,7 @@ BASE_SCRIPTS = [ 'wallet_txn_doublespend.py --mineblock', 'tool_bitcoin_chainstate.py', 'tool_wallet.py', + 'tool_utils.py', 'tool_signet_miner.py', 'wallet_txn_clone.py', 'wallet_txn_clone.py --segwit', diff --git a/test/functional/tool_utils.py b/test/functional/tool_utils.py new file mode 100755 index 00000000000..6dfe27c3466 --- /dev/null +++ b/test/functional/tool_utils.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# Copyright 2014 BitPay Inc. +# Copyright 2016-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/license/mit. +"""Exercise the utils via json-defined tests.""" + +from test_framework.test_framework import BitcoinTestFramework + +import difflib +import json +import os +import subprocess +from pathlib import Path + + +class ToolUtils(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 0 # No node/datadir needed + + def setup_network(self): + pass + + def skip_test_if_missing_module(self): + self.skip_if_no_bitcoin_tx() + self.skip_if_no_bitcoin_util() + + def run_test(self): + self.testcase_dir = Path(self.config["environment"]["SRCDIR"]) / "test" / "functional" / "data" / "util" + self.bins = self.get_binaries() + with open(self.testcase_dir / "bitcoin-util-test.json", encoding="utf8") as f: + input_data = json.loads(f.read()) + + for i, test_obj in enumerate(input_data): + self.log.debug(f"Running [{i}]: " + test_obj["description"]) + self.test_one(test_obj) + + def test_one(self, testObj): + """Runs a single test, comparing output and RC to expected output and RC. + + Raises an error if input can't be read, executable fails, or output/RC + are not as expected. Error is caught by bctester() and reported. + """ + # Get the exec names and arguments + if testObj["exec"] == "./bitcoin-util": + execrun = self.bins.util_argv() + testObj["args"] + elif testObj["exec"] == "./bitcoin-tx": + execrun = self.bins.tx_argv() + testObj["args"] + + # Read the input data (if there is any) + inputData = None + if "input" in testObj: + with open(self.testcase_dir / testObj["input"], encoding="utf8") as f: + inputData = f.read() + + # Read the expected output data (if there is any) + outputFn = None + outputData = None + outputType = None + if "output_cmp" in testObj: + outputFn = testObj['output_cmp'] + outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare) + with open(self.testcase_dir / outputFn, encoding="utf8") as f: + outputData = f.read() + if not outputData: + raise Exception(f"Output data missing for {outputFn}") + if not outputType: + raise Exception(f"Output file {outputFn} does not have a file extension") + + # Run the test + res = subprocess.run(execrun, capture_output=True, text=True, input=inputData) + + if outputData: + data_mismatch, formatting_mismatch = False, False + # Parse command output and expected output + try: + a_parsed = parse_output(res.stdout, outputType) + except Exception as e: + self.log.error(f"Error parsing command output as {outputType}: '{str(e)}'; res: {str(res)}") + raise + try: + b_parsed = parse_output(outputData, outputType) + except Exception as e: + self.log.error('Error parsing expected output %s as %s: %s' % (outputFn, outputType, e)) + raise + # Compare data + if a_parsed != b_parsed: + self.log.error(f"Output data mismatch for {outputFn} (format {outputType}); res: {str(res)}") + data_mismatch = True + # Compare formatting + if res.stdout != outputData: + error_message = f"Output formatting mismatch for {outputFn}:\nres: {str(res)}\n" + error_message += "".join(difflib.context_diff(outputData.splitlines(True), + res.stdout.splitlines(True), + fromfile=outputFn, + tofile="returned")) + self.log.error(error_message) + formatting_mismatch = True + + assert not data_mismatch and not formatting_mismatch + + # Compare the return code to the expected return code + wantRC = 0 + if "return_code" in testObj: + wantRC = testObj['return_code'] + if res.returncode != wantRC: + raise Exception(f"Return code mismatch for {outputFn}; res: {str(res)}") + + if "error_txt" in testObj: + want_error = testObj["error_txt"] + # A partial match instead of an exact match makes writing tests easier + # and should be sufficient. + if want_error not in res.stderr: + raise Exception(f"Error mismatch:\nExpected: {want_error}\nReceived: {res.stderr.rstrip()}\nres: {str(res)}") + else: + if res.stderr: + raise Exception(f"Unexpected error received: {res.stderr.rstrip()}\nres: {str(res)}") + + +def parse_output(a, fmt): + """Parse the output according to specified format. + + Raise an error if the output can't be parsed.""" + if fmt == 'json': # json: compare parsed data + return json.loads(a) + elif fmt == 'hex': # hex: parse and compare binary data + return bytes.fromhex(a.strip()) + else: + raise NotImplementedError("Don't know how to compare %s" % fmt) + + +if __name__ == "__main__": + ToolUtils(__file__).main() diff --git a/test/util/test_runner.py b/test/util/test_runner.py deleted file mode 100755 index 11400b32ba3..00000000000 --- a/test/util/test_runner.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2014 BitPay Inc. -# Copyright 2016-present The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test framework for bitcoin utils. - -Runs automatically during `ctest --test-dir build/`. - -Can also be run manually.""" - -import argparse -import configparser -import difflib -import json -import logging -import os -import pprint -import subprocess -import sys - -def main(): - config = configparser.ConfigParser() - config.optionxform = str - with open(os.path.join(os.path.dirname(__file__), "../config.ini"), encoding="utf8") as f: - config.read_file(f) - env_conf = dict(config.items('environment')) - - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('-v', '--verbose', action='store_true') - args = parser.parse_args() - verbose = args.verbose - - if verbose: - level = logging.DEBUG - else: - level = logging.ERROR - formatter = '%(asctime)s - %(levelname)s - %(message)s' - # Add the format/level to the logger - logging.basicConfig(format=formatter, level=level) - - bctester(os.path.join(env_conf["SRCDIR"], "test", "util", "data"), "bitcoin-util-test.json", env_conf) - -def bctester(testDir, input_basename, buildenv): - """ Loads and parses the input file, runs all tests and reports results""" - input_filename = os.path.join(testDir, input_basename) - with open(input_filename, encoding="utf8") as f: - raw_data = f.read() - input_data = json.loads(raw_data) - - failed_testcases = [] - - for testObj in input_data: - try: - bctest(testDir, testObj, buildenv) - logging.info("PASSED: " + testObj["description"]) - except Exception: - logging.info("FAILED: " + testObj["description"]) - failed_testcases.append(testObj["description"]) - - if failed_testcases: - error_message = "FAILED_TESTCASES:\n" - error_message += pprint.pformat(failed_testcases, width=400) - logging.error(error_message) - sys.exit(1) - else: - sys.exit(0) - -def bctest(testDir, testObj, buildenv): - """Runs a single test, comparing output and RC to expected output and RC. - - Raises an error if input can't be read, executable fails, or output/RC - are not as expected. Error is caught by bctester() and reported. - """ - # Get the exec names and arguments - execprog = os.path.join(buildenv["BUILDDIR"], "bin", testObj["exec"] + buildenv["EXEEXT"]) - if testObj["exec"] == "./bitcoin-util": - execprog = os.getenv("BITCOINUTIL", default=execprog) - elif testObj["exec"] == "./bitcoin-tx": - execprog = os.getenv("BITCOINTX", default=execprog) - - execargs = testObj['args'] - execrun = [execprog] + execargs - - # Read the input data (if there is any) - inputData = None - if "input" in testObj: - filename = os.path.join(testDir, testObj["input"]) - with open(filename, encoding="utf8") as f: - inputData = f.read() - - # Read the expected output data (if there is any) - outputFn = None - outputData = None - outputType = None - if "output_cmp" in testObj: - outputFn = testObj['output_cmp'] - outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare) - try: - with open(os.path.join(testDir, outputFn), encoding="utf8") as f: - outputData = f.read() - except Exception: - logging.error("Output file " + outputFn + " cannot be opened") - raise - if not outputData: - logging.error("Output data missing for " + outputFn) - raise Exception - if not outputType: - logging.error("Output file %s does not have a file extension" % outputFn) - raise Exception - - # Run the test - try: - res = subprocess.run(execrun, capture_output=True, text=True, input=inputData) - except OSError: - logging.error("OSError, Failed to execute " + execprog) - raise - - if outputData: - data_mismatch, formatting_mismatch = False, False - # Parse command output and expected output - try: - a_parsed = parse_output(res.stdout, outputType) - except Exception as e: - logging.error(f"Error parsing command output as {outputType}: '{str(e)}'; res: {str(res)}") - raise - try: - b_parsed = parse_output(outputData, outputType) - except Exception as e: - logging.error('Error parsing expected output %s as %s: %s' % (outputFn, outputType, e)) - raise - # Compare data - if a_parsed != b_parsed: - logging.error(f"Output data mismatch for {outputFn} (format {outputType}); res: {str(res)}") - data_mismatch = True - # Compare formatting - if res.stdout != outputData: - error_message = f"Output formatting mismatch for {outputFn}:\nres: {str(res)}\n" - error_message += "".join(difflib.context_diff(outputData.splitlines(True), - res.stdout.splitlines(True), - fromfile=outputFn, - tofile="returned")) - logging.error(error_message) - formatting_mismatch = True - - assert not data_mismatch and not formatting_mismatch - - # Compare the return code to the expected return code - wantRC = 0 - if "return_code" in testObj: - wantRC = testObj['return_code'] - if res.returncode != wantRC: - logging.error(f"Return code mismatch for {outputFn}; res: {str(res)}") - raise Exception - - if "error_txt" in testObj: - want_error = testObj["error_txt"] - # A partial match instead of an exact match makes writing tests easier - # and should be sufficient. - if want_error not in res.stderr: - logging.error(f"Error mismatch:\nExpected: {want_error}\nReceived: {res.stderr.rstrip()}\nres: {str(res)}") - raise Exception - else: - if res.stderr: - logging.error(f"Unexpected error received: {res.stderr.rstrip()}\nres: {str(res)}") - raise Exception - - -def parse_output(a, fmt): - """Parse the output according to specified format. - - Raise an error if the output can't be parsed.""" - if fmt == 'json': # json: compare parsed data - return json.loads(a) - elif fmt == 'hex': # hex: parse and compare binary data - return bytes.fromhex(a.strip()) - else: - raise NotImplementedError("Don't know how to compare %s" % fmt) - -if __name__ == '__main__': - main()