Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/test_node.py
Show All 26 Lines | |||||
# For Python 3.4 compatibility | # For Python 3.4 compatibility | ||||
JSONDecodeError = getattr(json, "JSONDecodeError", ValueError) | JSONDecodeError = getattr(json, "JSONDecodeError", ValueError) | ||||
BITCOIND_PROC_WAIT_TIMEOUT = 60 | BITCOIND_PROC_WAIT_TIMEOUT = 60 | ||||
class TestNode(): | class TestNode(): | ||||
"""A class for representing a bitcoind node under test. | """A class for representing a bitcoind node under test. | ||||
This class contains: | This class contains: | ||||
- state about the node (whether it's running, etc) | - state about the node (whether it's running, etc) | ||||
- a Python subprocess.Popen object representing the running process | - a Python subprocess.Popen object representing the running process | ||||
- an RPC connection to the node | - an RPC connection to the node | ||||
- one or more P2P connections to the node | - one or more P2P connections to the node | ||||
Show All 22 Lines | def __init__(self, i, dirname, extra_args, host, rpc_port, p2p_port, timewait, binary, stderr, mocktime, coverage_dir, use_cli=False): | ||||
"Binary '{}' could not be found.\nTry setting it manually:\n\tBITCOIND=<path/to/bitcoind> {}".format(self.binary, sys.argv[0])) | "Binary '{}' could not be found.\nTry setting it manually:\n\tBITCOIND=<path/to/bitcoind> {}".format(self.binary, sys.argv[0])) | ||||
self.stderr = stderr | self.stderr = stderr | ||||
self.coverage_dir = coverage_dir | self.coverage_dir = coverage_dir | ||||
# Most callers will just need to add extra args to the default list | # Most callers will just need to add extra args to the default list | ||||
# below. | # below. | ||||
# For those callers that need more flexibity, they can access the | # For those callers that need more flexibity, they can access the | ||||
# default args using the provided facilities | # default args using the provided facilities | ||||
self.extra_args = extra_args | self.extra_args = extra_args | ||||
self.default_args = ["-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", | self.default_args = [ | ||||
"-datadir=" + | |||||
self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", | |||||
"-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=" + self.name] | "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=" + self.name] | ||||
cli_path = os.getenv("BITCOINCLI", "bitcoin-cli") | cli_path = os.getenv("BITCOINCLI", "bitcoin-cli") | ||||
if not os.path.isfile(cli_path): | if not os.path.isfile(cli_path): | ||||
raise FileNotFoundError( | raise FileNotFoundError( | ||||
"Binary '{}' could not be found.\nTry setting it manually:\n\tBITCOINCLI=<path/to/bitcoin-cli> {}".format(cli_path, sys.argv[0])) | "Binary '{}' could not be found.\nTry setting it manually:\n\tBITCOINCLI=<path/to/bitcoin-cli> {}".format(cli_path, sys.argv[0])) | ||||
self.cli = TestNodeCLI(cli_path, self.datadir) | self.cli = TestNodeCLI(cli_path, self.datadir) | ||||
self.use_cli = use_cli | self.use_cli = use_cli | ||||
self.running = False | self.running = False | ||||
self.process = None | self.process = None | ||||
self.rpc_connected = False | self.rpc_connected = False | ||||
self.rpc = None | self.rpc = None | ||||
self.url = None | self.url = None | ||||
self.relay_fee_cache = None | self.relay_fee_cache = None | ||||
self.log = logging.getLogger('TestFramework.node{}'.format(i)) | self.log = logging.getLogger('TestFramework.node{}'.format(i)) | ||||
self.cleanup_on_exit = True # Whether to kill the node when this object goes away | |||||
self.p2ps = [] | self.p2ps = [] | ||||
def __del__(self): | |||||
# Ensure that we don't leave any bitcoind processes lying around after | |||||
# the test ends | |||||
if self.process and self.cleanup_on_exit: | |||||
# Should only happen on test failure | |||||
# Avoid using logger, as that may have already been shutdown when | |||||
# this destructor is called. | |||||
print("Cleaning up leftover process") | |||||
self.process.kill() | |||||
def __getattr__(self, name): | def __getattr__(self, name): | ||||
"""Dispatches any unrecognised messages to the RPC connection or a CLI instance.""" | """Dispatches any unrecognised messages to the RPC connection or a CLI instance.""" | ||||
if self.use_cli: | if self.use_cli: | ||||
return getattr(self.cli, name) | return getattr(self.cli, name) | ||||
else: | else: | ||||
assert self.rpc is not None, "Error: RPC not initialized" | assert self.rpc is not None, "Error: RPC not initialized" | ||||
assert self.rpc_connected, "Error: No RPC connection" | assert self.rpc_connected, "Error: No RPC connection" | ||||
return getattr(self.rpc, name) | return getattr(self.rpc, name) | ||||
Show All 29 Lines | class TestNode(): | ||||
def wait_for_rpc_connection(self): | def wait_for_rpc_connection(self): | ||||
"""Sets up an RPC connection to the bitcoind process. Returns False if unable to connect.""" | """Sets up an RPC connection to the bitcoind process. Returns False if unable to connect.""" | ||||
# Poll at a rate of four times per second | # Poll at a rate of four times per second | ||||
poll_per_s = 4 | poll_per_s = 4 | ||||
for _ in range(poll_per_s * self.rpc_timeout): | for _ in range(poll_per_s * self.rpc_timeout): | ||||
assert self.process.poll( | assert self.process.poll( | ||||
) is None, "bitcoind exited with status {} during initialization".format(self.process.returncode) | ) is None, "bitcoind exited with status {} during initialization".format(self.process.returncode) | ||||
try: | try: | ||||
self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.host, self.rpc_port), | self.rpc = get_rpc_proxy( | ||||
rpc_url(self.datadir, self.host, self.rpc_port), | |||||
self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) | self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) | ||||
self.rpc.getblockcount() | self.rpc.getblockcount() | ||||
# If the call to getblockcount() succeeds then the RPC connection is up | # If the call to getblockcount() succeeds then the RPC | ||||
# connection is up | |||||
self.rpc_connected = True | self.rpc_connected = True | ||||
self.url = self.rpc.url | self.url = self.rpc.url | ||||
self.log.debug("RPC successfully started") | self.log.debug("RPC successfully started") | ||||
return | return | ||||
except IOError as e: | except IOError as e: | ||||
if e.errno != errno.ECONNREFUSED: # Port not yet open? | if e.errno != errno.ECONNREFUSED: # Port not yet open? | ||||
raise # unknown IO error | raise # unknown IO error | ||||
except JSONRPCException as e: # Initialization phase | except JSONRPCException as e: # Initialization phase | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | class TestNode(): | ||||
def disconnect_p2ps(self): | def disconnect_p2ps(self): | ||||
"""Close all p2p connections to the node.""" | """Close all p2p connections to the node.""" | ||||
for p in self.p2ps: | for p in self.p2ps: | ||||
p.peer_disconnect() | p.peer_disconnect() | ||||
del self.p2ps[:] | del self.p2ps[:] | ||||
class TestNodeCLIAttr: | class TestNodeCLIAttr: | ||||
def __init__(self, cli, command): | def __init__(self, cli, command): | ||||
self.cli = cli | self.cli = cli | ||||
self.command = command | self.command = command | ||||
def __call__(self, *args, **kwargs): | def __call__(self, *args, **kwargs): | ||||
return self.cli.send_cli(self.command, *args, **kwargs) | return self.cli.send_cli(self.command, *args, **kwargs) | ||||
def get_request(self, *args, **kwargs): | def get_request(self, *args, **kwargs): | ||||
return lambda: self(*args, **kwargs) | return lambda: self(*args, **kwargs) | ||||
class TestNodeCLI(): | class TestNodeCLI(): | ||||
"""Interface to bitcoin-cli for an individual node""" | """Interface to bitcoin-cli for an individual node""" | ||||
def __init__(self, binary, datadir): | def __init__(self, binary, datadir): | ||||
self.args = [] | self.args = [] | ||||
self.binary = binary | self.binary = binary | ||||
self.datadir = datadir | self.datadir = datadir | ||||
self.input = None | self.input = None | ||||
self.log = logging.getLogger('TestFramework.bitcoincli') | self.log = logging.getLogger('TestFramework.bitcoincli') | ||||
▲ Show 20 Lines • Show All 51 Lines • Show Last 20 Lines |