Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/test_framework.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2014-2016 The Bitcoin Core developers | # Copyright (c) 2014-2016 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Base class for RPC testing.""" | """Base class for RPC testing.""" | ||||
import argparse | import argparse | ||||
from collections import deque | from collections import deque | ||||
import configparser | |||||
from enum import Enum | from enum import Enum | ||||
import logging | import logging | ||||
import os | import os | ||||
import pdb | import pdb | ||||
import shutil | import shutil | ||||
import sys | import sys | ||||
import tempfile | import tempfile | ||||
import time | import time | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | class BitcoinTestFramework(): | ||||
def main(self): | def main(self): | ||||
"""Main function. This should not be overridden by the subclass test scripts.""" | """Main function. This should not be overridden by the subclass test scripts.""" | ||||
parser = argparse.ArgumentParser(usage="%(prog)s [options]") | parser = argparse.ArgumentParser(usage="%(prog)s [options]") | ||||
parser.add_argument("--nocleanup", dest="nocleanup", default=False, action="store_true", | parser.add_argument("--nocleanup", dest="nocleanup", default=False, action="store_true", | ||||
help="Leave bitcoinds and test.* datadir on exit or error") | help="Leave bitcoinds and test.* datadir on exit or error") | ||||
parser.add_argument("--noshutdown", dest="noshutdown", default=False, action="store_true", | parser.add_argument("--noshutdown", dest="noshutdown", default=False, action="store_true", | ||||
help="Don't stop bitcoinds after the test execution") | help="Don't stop bitcoinds after the test execution") | ||||
parser.add_argument("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../../src"), | parser.add_argument("--srcdir", dest="srcdir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../../src"), | ||||
help="Source directory containing bitcoind/bitcoin-cli (default: %(default)s)") | help="Source directory containing bitcoind/bitcoin-cli (default: %(default)s)") | ||||
parser.add_argument("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"), | parser.add_argument("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"), | ||||
help="Directory for caching pregenerated datadirs") | help="Directory for caching pregenerated datadirs (default: %default)") | ||||
parser.add_argument("--tmpdir", dest="tmpdir", | parser.add_argument("--tmpdir", dest="tmpdir", | ||||
help="Root directory for datadirs") | help="Root directory for datadirs") | ||||
parser.add_argument("-l", "--loglevel", dest="loglevel", default="INFO", | parser.add_argument("-l", "--loglevel", dest="loglevel", default="INFO", | ||||
help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console. Note that logs at all levels are always written to the test_framework.log file in the temporary test directory.") | help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console. Note that logs at all levels are always written to the test_framework.log file in the temporary test directory.") | ||||
parser.add_argument("--tracerpc", dest="trace_rpc", default=False, action="store_true", | parser.add_argument("--tracerpc", dest="trace_rpc", default=False, action="store_true", | ||||
help="Print out all RPC calls as they are made") | help="Print out all RPC calls as they are made") | ||||
parser.add_argument("--portseed", dest="port_seed", default=os.getpid(), type=int, | parser.add_argument("--portseed", dest="port_seed", default=os.getpid(), type=int, | ||||
help="The seed to use for assigning port numbers (default: current process id)") | help="The seed to use for assigning port numbers (default: current process id)") | ||||
parser.add_argument("--coveragedir", dest="coveragedir", | parser.add_argument("--coveragedir", dest="coveragedir", | ||||
help="Write tested RPC commands into this directory") | help="Write tested RPC commands into this directory") | ||||
parser.add_argument("--configfile", dest="configfile", | parser.add_argument("--configfile", dest="configfile", default=os.path.abspath(os.path.dirname(os.path.realpath( | ||||
help="Location of the test framework config file") | __file__)) + "/../../config.ini"), help="Location of the test framework config file (default: %default)") | ||||
parser.add_argument("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true", | parser.add_argument("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true", | ||||
help="Attach a python debugger if test fails") | help="Attach a python debugger if test fails") | ||||
parser.add_argument("--usecli", dest="usecli", default=False, action="store_true", | parser.add_argument("--usecli", dest="usecli", default=False, action="store_true", | ||||
help="use bitcoin-cli instead of RPC for all commands") | help="use bitcoin-cli instead of RPC for all commands") | ||||
parser.add_argument("--with-gravitonactivation", dest="gravitonactivation", default=False, action="store_true", | parser.add_argument("--with-gravitonactivation", dest="gravitonactivation", default=False, action="store_true", | ||||
help="Activate graviton update on timestamp {}".format(TIMESTAMP_IN_THE_PAST)) | help="Activate graviton update on timestamp {}".format(TIMESTAMP_IN_THE_PAST)) | ||||
self.add_options(parser) | self.add_options(parser) | ||||
self.options = parser.parse_args() | self.options = parser.parse_args() | ||||
self.set_test_params() | self.set_test_params() | ||||
assert hasattr( | assert hasattr( | ||||
self, "num_nodes"), "Test must set self.num_nodes in set_test_params()" | self, "num_nodes"), "Test must set self.num_nodes in set_test_params()" | ||||
PortSeed.n = self.options.port_seed | PortSeed.n = self.options.port_seed | ||||
os.environ['PATH'] = self.options.srcdir + ":" + \ | os.environ['PATH'] = self.options.srcdir + ":" + \ | ||||
self.options.srcdir + "/qt:" + os.environ['PATH'] | self.options.srcdir + "/qt:" + os.environ['PATH'] | ||||
check_json_precision() | check_json_precision() | ||||
self.options.cachedir = os.path.abspath(self.options.cachedir) | self.options.cachedir = os.path.abspath(self.options.cachedir) | ||||
config = configparser.ConfigParser() | |||||
config.read_file(open(self.options.configfile)) | |||||
self.options.bitcoind = os.getenv( | |||||
"BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/bitcoind' + config["environment"]["EXEEXT"]) | |||||
self.options.bitcoincli = os.getenv( | |||||
"BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/bitcoin-cli' + config["environment"]["EXEEXT"]) | |||||
# Set up temp directory and start logging | # Set up temp directory and start logging | ||||
if self.options.tmpdir: | if self.options.tmpdir: | ||||
self.options.tmpdir = os.path.abspath(self.options.tmpdir) | self.options.tmpdir = os.path.abspath(self.options.tmpdir) | ||||
os.makedirs(self.options.tmpdir, exist_ok=False) | os.makedirs(self.options.tmpdir, exist_ok=False) | ||||
else: | else: | ||||
self.options.tmpdir = tempfile.mkdtemp(prefix="test") | self.options.tmpdir = tempfile.mkdtemp(prefix="test") | ||||
self._start_logging() | self._start_logging() | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | def add_nodes(self, num_nodes, extra_args=None, rpchost=None, timewait=None, binary=None): | ||||
"""Instantiate TestNode objects""" | """Instantiate TestNode objects""" | ||||
if self.bind_to_localhost_only: | if self.bind_to_localhost_only: | ||||
extra_confs = [["bind=127.0.0.1"]] * num_nodes | extra_confs = [["bind=127.0.0.1"]] * num_nodes | ||||
else: | else: | ||||
extra_confs = [[]] * num_nodes | extra_confs = [[]] * num_nodes | ||||
if extra_args is None: | if extra_args is None: | ||||
extra_args = [[]] * num_nodes | extra_args = [[]] * num_nodes | ||||
if binary is None: | if binary is None: | ||||
binary = [None] * num_nodes | binary = [self.options.bitcoind] * num_nodes | ||||
assert_equal(len(extra_confs), num_nodes) | assert_equal(len(extra_confs), num_nodes) | ||||
assert_equal(len(extra_args), num_nodes) | assert_equal(len(extra_args), num_nodes) | ||||
assert_equal(len(binary), num_nodes) | assert_equal(len(binary), num_nodes) | ||||
for i in range(num_nodes): | for i in range(num_nodes): | ||||
self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), host=rpchost, rpc_port=rpc_port(i), p2p_port=p2p_port(i), timewait=timewait, | self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), host=rpchost, rpc_port=rpc_port(i), p2p_port=p2p_port(i), timewait=timewait, | ||||
binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli)) | bitcoind=binary[i], bitcoin_cli=self.options.bitcoincli, stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli)) | ||||
if self.options.gravitonactivation: | if self.options.gravitonactivation: | ||||
self.nodes[i].extend_default_args( | self.nodes[i].extend_default_args( | ||||
["-gravitonactivationtime={}".format(TIMESTAMP_IN_THE_PAST)]) | ["-gravitonactivationtime={}".format(TIMESTAMP_IN_THE_PAST)]) | ||||
def start_node(self, i, *args, **kwargs): | def start_node(self, i, *args, **kwargs): | ||||
"""Start a bitcoind""" | """Start a bitcoind""" | ||||
node = self.nodes[i] | node = self.nodes[i] | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | def _initialize_chain(self): | ||||
for i in range(MAX_NODES): | for i in range(MAX_NODES): | ||||
if os.path.isdir(get_datadir_path(self.options.cachedir, i)): | if os.path.isdir(get_datadir_path(self.options.cachedir, i)): | ||||
shutil.rmtree(get_datadir_path(self.options.cachedir, i)) | shutil.rmtree(get_datadir_path(self.options.cachedir, i)) | ||||
# Create cache directories, run bitcoinds: | # Create cache directories, run bitcoinds: | ||||
for i in range(MAX_NODES): | for i in range(MAX_NODES): | ||||
datadir = initialize_datadir(self.options.cachedir, i) | datadir = initialize_datadir(self.options.cachedir, i) | ||||
self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], host=None, rpc_port=rpc_port( | self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], host=None, rpc_port=rpc_port( | ||||
i), p2p_port=p2p_port(i), timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) | i), p2p_port=p2p_port(i), timewait=None, bitcoind=self.options.bitcoind, bitcoin_cli=self.options.bitcoincli, stderr=None, mocktime=self.mocktime, coverage_dir=None)) | ||||
self.nodes[i].clear_default_args() | self.nodes[i].clear_default_args() | ||||
self.nodes[i].extend_default_args([ | self.nodes[i].extend_default_args([ | ||||
"-server", "-keypool=1", "-datadir=" + datadir, | "-server", "-keypool=1", "-datadir=" + datadir, | ||||
"-discover=0"]) | "-discover=0"]) | ||||
if i > 0: | if i > 0: | ||||
self.nodes[i].extend_default_args( | self.nodes[i].extend_default_args( | ||||
["-connect=127.0.0.1:" + str(p2p_port(0))]) | ["-connect=127.0.0.1:" + str(p2p_port(0))]) | ||||
if self.options.gravitonactivation: | if self.options.gravitonactivation: | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | class ComparisonTestFramework(BitcoinTestFramework): | ||||
- n>2 binaries: 1 test binary, n-1 ref binaries""" | - n>2 binaries: 1 test binary, n-1 ref binaries""" | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
def add_options(self, parser): | def add_options(self, parser): | ||||
parser.add_argument("--testbinary", dest="testbinary", | parser.add_argument("--testbinary", dest="testbinary", | ||||
default=os.getenv("BITCOIND", "bitcoind"), | |||||
help="bitcoind binary to test") | help="bitcoind binary to test") | ||||
parser.add_argument("--refbinary", dest="refbinary", | parser.add_argument("--refbinary", dest="refbinary", | ||||
default=os.getenv("BITCOIND", "bitcoind"), | |||||
help="bitcoind binary to use for reference nodes (if any)") | help="bitcoind binary to use for reference nodes (if any)") | ||||
def setup_network(self): | def setup_network(self): | ||||
extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes | extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes | ||||
if not self.options.testbinary: | |||||
self.options.testbinary = self.options.bitcoind | |||||
if not self.options.refbinary: | |||||
self.options.refbinary = self.options.bitcoind | |||||
if hasattr(self, "extra_args"): | if hasattr(self, "extra_args"): | ||||
extra_args = self.extra_args | extra_args = self.extra_args | ||||
self.add_nodes(self.num_nodes, extra_args, | self.add_nodes(self.num_nodes, extra_args, | ||||
binary=[self.options.testbinary] + | binary=[self.options.testbinary] + | ||||
[self.options.refbinary] * (self.num_nodes - 1)) | [self.options.refbinary] * (self.num_nodes - 1)) | ||||
self.start_nodes() | self.start_nodes() | ||||
class SkipTest(Exception): | class SkipTest(Exception): | ||||
"""This exception is raised to skip a test""" | """This exception is raised to skip a test""" | ||||
def __init__(self, message): | def __init__(self, message): | ||||
self.message = message | self.message = message |