Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/test_node.py
Show First 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | class TestNode(): | ||||
- 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 | ||||
To make things easier for the test writer, any unrecognised messages will | To make things easier for the test writer, any unrecognised messages will | ||||
be dispatched to the RPC connection.""" | be dispatched to the RPC connection.""" | ||||
def __init__(self, i, datadir, *, chain, host, rpc_port, p2p_port, timewait, factor, bitcoind, bitcoin_cli, | def __init__(self, i, datadir, *, chain, host, rpc_port, p2p_port, timewait, timeout_factor, bitcoind, bitcoin_cli, | ||||
coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, emulator=None, start_perf=False, use_valgrind=False): | coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, emulator=None, start_perf=False, use_valgrind=False): | ||||
""" | """ | ||||
Kwargs: | Kwargs: | ||||
start_perf (bool): If True, begin profiling the node with `perf` as soon as | start_perf (bool): If True, begin profiling the node with `perf` as soon as | ||||
the node starts. | the node starts. | ||||
""" | """ | ||||
self.index = i | self.index = i | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | def __init__(self, i, datadir, *, chain, host, rpc_port, p2p_port, timewait, timeout_factor, bitcoind, bitcoin_cli, | ||||
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)) | ||||
# Whether to kill the node when this object goes away | # Whether to kill the node when this object goes away | ||||
self.cleanup_on_exit = True | self.cleanup_on_exit = True | ||||
# Cache perf subprocesses here by their data output filename. | # Cache perf subprocesses here by their data output filename. | ||||
self.perf_subprocesses = {} | self.perf_subprocesses = {} | ||||
self.p2ps = [] | self.p2ps = [] | ||||
self.factor = factor | self.timeout_factor = timeout_factor | ||||
AddressKeyPair = collections.namedtuple( | AddressKeyPair = collections.namedtuple( | ||||
'AddressKeyPair', ['address', 'key']) | 'AddressKeyPair', ['address', 'key']) | ||||
PRIV_KEYS = [ | PRIV_KEYS = [ | ||||
# address , privkey | # address , privkey | ||||
AddressKeyPair( | AddressKeyPair( | ||||
'mjTkW3DjgyZck4KbiRusZsqTgaYTxdSz6z', | 'mjTkW3DjgyZck4KbiRusZsqTgaYTxdSz6z', | ||||
'cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW'), | 'cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW'), | ||||
▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | def wait_for_rpc_connection(self): | ||||
# | # | ||||
# For example, the node will reject block messages from p2p | # For example, the node will reject block messages from p2p | ||||
# when it is still importing with the error "Unexpected | # when it is still importing with the error "Unexpected | ||||
# block message received" | # block message received" | ||||
# | # | ||||
# The wait is done here to make tests as robust as possible | # The wait is done here to make tests as robust as possible | ||||
# and prevent racy tests and intermittent failures as much | # and prevent racy tests and intermittent failures as much | ||||
# as possible. Some tests might not need this, but the | # as possible. Some tests might not need this, but the | ||||
# overhead is trivial, and the added gurantees are worth | # overhead is trivial, and the added guarantees are worth | ||||
# the minimal performance cost. | # the minimal performance cost. | ||||
self.log.debug("RPC successfully started") | self.log.debug("RPC successfully started") | ||||
if self.use_cli: | if self.use_cli: | ||||
return | return | ||||
self.rpc = rpc | self.rpc = rpc | ||||
self.rpc_connected = True | self.rpc_connected = True | ||||
self.url = self.rpc.url | self.url = self.rpc.url | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | def is_node_stopped(self): | ||||
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.log.debug("Node stopped") | self.log.debug("Node stopped") | ||||
return True | return True | ||||
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): | def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): | ||||
wait_until(self.is_node_stopped, timeout=timeout, factor=self.factor) | wait_until( | ||||
self.is_node_stopped, | |||||
timeout=timeout, | |||||
timeout_factor=self.timeout_factor) | |||||
@contextlib.contextmanager | @contextlib.contextmanager | ||||
def assert_debug_log(self, expected_msgs, unexpected_msgs=None, timeout=2): | def assert_debug_log(self, expected_msgs, unexpected_msgs=None, timeout=2): | ||||
"""Assert that some debug messages are present within some timeout. | """Assert that some debug messages are present within some timeout. | ||||
Unexpected debug messages may be optionally provided to fail a test | Unexpected debug messages may be optionally provided to fail a test | ||||
if they appear before expected messages. | if they appear before expected messages. | ||||
Note: expected_msgs must always be non-empty even if the goal is to check | Note: expected_msgs must always be non-empty even if the goal is to check | ||||
for unexpected_msgs. This provides a bounded scenario such that "we expect | for unexpected_msgs. This provides a bounded scenario such that "we expect | ||||
to reach some target resulting in expected_msgs without seeing unexpected_msgs. | to reach some target resulting in expected_msgs without seeing unexpected_msgs. | ||||
Otherwise, we are testing that something never happens, which is fundamentally | Otherwise, we are testing that something never happens, which is fundamentally | ||||
not robust test logic. | not robust test logic. | ||||
""" | """ | ||||
if not expected_msgs: | if not expected_msgs: | ||||
raise AssertionError("Expected debug messages is empty") | raise AssertionError("Expected debug messages is empty") | ||||
if unexpected_msgs is None: | if unexpected_msgs is None: | ||||
unexpected_msgs = [] | unexpected_msgs = [] | ||||
time_end = time.time() + timeout * self.factor | time_end = time.time() + timeout * self.timeout_factor | ||||
debug_log = os.path.join(self.datadir, self.chain, 'debug.log') | debug_log = os.path.join(self.datadir, self.chain, 'debug.log') | ||||
with open(debug_log, encoding='utf-8') as dl: | with open(debug_log, encoding='utf-8') as dl: | ||||
dl.seek(0, 2) | dl.seek(0, 2) | ||||
prev_size = dl.tell() | prev_size = dl.tell() | ||||
yield | yield | ||||
while True: | while True: | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, **kwargs): | ||||
This method adds the p2p connection to the self.p2ps list and also | This method adds the p2p connection to the self.p2ps list and also | ||||
returns the connection to the caller.""" | returns the connection to the caller.""" | ||||
if 'dstport' not in kwargs: | if 'dstport' not in kwargs: | ||||
kwargs['dstport'] = p2p_port(self.index) | kwargs['dstport'] = p2p_port(self.index) | ||||
if 'dstaddr' not in kwargs: | if 'dstaddr' not in kwargs: | ||||
kwargs['dstaddr'] = '127.0.0.1' | kwargs['dstaddr'] = '127.0.0.1' | ||||
p2p_conn.peer_connect(**kwargs, net=self.chain, factor=self.factor)() | p2p_conn.peer_connect( | ||||
**kwargs, | |||||
net=self.chain, | |||||
timeout_factor=self.timeout_factor)() | |||||
self.p2ps.append(p2p_conn) | self.p2ps.append(p2p_conn) | ||||
if wait_for_verack: | if wait_for_verack: | ||||
# Wait for the node to send us the version and verack | # Wait for the node to send us the version and verack | ||||
p2p_conn.wait_for_verack() | p2p_conn.wait_for_verack() | ||||
# At this point we have sent our version message and received the version and verack, however the full node | # At this point we have sent our version message and received the version and verack, however the full node | ||||
# has not yet received the verack from us (in reply to their version). So, the connection is not yet fully | # has not yet received the verack from us (in reply to their version). So, the connection is not yet fully | ||||
# established (fSuccessfullyConnected). | # established (fSuccessfullyConnected). | ||||
# | # | ||||
# This shouldn't lead to any issues when sending messages, since the verack will be in-flight before the | # This shouldn't lead to any issues when sending messages, since the verack will be in-flight before the | ||||
# message we send. However, it might lead to races where we are expecting to receive a message. E.g. a | # message we send. However, it might lead to races where we are expecting to receive a message. E.g. a | ||||
# transaction that will be added to the mempool as soon as we return here. | # transaction that will be added to the mempool as soon as we return here. | ||||
# | # | ||||
# So syncing here is redundant when we only want to send a message, but the cost is low (a few milliseconds) | # So syncing here is redundant when we only want to send a message, but the cost is low (a few milliseconds) | ||||
# in comparision to the upside of making tests less fragile and | # in comparison to the upside of making tests less fragile and | ||||
# unexpected intermittent errors less likely. | # unexpected intermittent errors less likely. | ||||
p2p_conn.sync_with_ping() | p2p_conn.sync_with_ping() | ||||
return p2p_conn | return p2p_conn | ||||
@property | @property | ||||
def p2p(self): | def p2p(self): | ||||
"""Return the first p2p connection | """Return the first p2p connection | ||||
▲ Show 20 Lines • Show All 203 Lines • Show Last 20 Lines |