Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/test_node.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2017 The Bitcoin Core developers | # Copyright (c) 2017 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. | ||||
"""Class for bitcoind node under test""" | """Class for bitcoind node under test""" | ||||
import contextlib | import contextlib | ||||
import decimal | import decimal | ||||
from enum import Enum | |||||
import errno | import errno | ||||
import http.client | import http.client | ||||
import json | import json | ||||
import logging | import logging | ||||
import os | import os | ||||
import re | import re | ||||
import subprocess | import subprocess | ||||
import sys | import sys | ||||
Show All 17 Lines | |||||
BITCOIND_PROC_WAIT_TIMEOUT = 60 | BITCOIND_PROC_WAIT_TIMEOUT = 60 | ||||
class FailedToStartError(Exception): | class FailedToStartError(Exception): | ||||
"""Raised when a node fails to start correctly.""" | """Raised when a node fails to start correctly.""" | ||||
class ErrorMatch(Enum): | |||||
FULL_TEXT = 1 | |||||
FULL_REGEX = 2 | |||||
PARTIAL_REGEX = 3 | |||||
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 | ||||
▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | def assert_debug_log(self, expected_msgs): | ||||
dl.seek(prev_size) | dl.seek(prev_size) | ||||
log = dl.read() | log = dl.read() | ||||
print_log = " - " + "\n - ".join(log.splitlines()) | print_log = " - " + "\n - ".join(log.splitlines()) | ||||
for expected_msg in expected_msgs: | for expected_msg in expected_msgs: | ||||
if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None: | if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None: | ||||
self._raise_assertion_error( | self._raise_assertion_error( | ||||
'Expected message "{}" does not partially match log:\n\n{}\n\n'.format(expected_msg, print_log)) | 'Expected message "{}" does not partially match log:\n\n{}\n\n'.format(expected_msg, print_log)) | ||||
def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, partial_match=False, *args, **kwargs): | def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, match=ErrorMatch.FULL_TEXT, *args, **kwargs): | ||||
"""Attempt to start the node and expect it to raise an error. | """Attempt to start the node and expect it to raise an error. | ||||
extra_args: extra arguments to pass through to bitcoind | extra_args: extra arguments to pass through to bitcoind | ||||
expected_msg: regex that stderr should match when bitcoind fails | expected_msg: regex that stderr should match when bitcoind fails | ||||
Will throw if bitcoind starts without an error. | Will throw if bitcoind starts without an error. | ||||
Will throw if an expected_msg is provided and it does not match bitcoind's stdout.""" | Will throw if an expected_msg is provided and it does not match bitcoind's stdout.""" | ||||
with tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) as log_stderr, \ | with tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) as log_stderr, \ | ||||
tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) as log_stdout: | tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) as log_stdout: | ||||
try: | try: | ||||
self.start(extra_args, stdout=log_stdout, | self.start(extra_args, stdout=log_stdout, | ||||
stderr=log_stderr, *args, **kwargs) | stderr=log_stderr, *args, **kwargs) | ||||
self.wait_for_rpc_connection() | self.wait_for_rpc_connection() | ||||
self.stop_node() | self.stop_node() | ||||
self.wait_until_stopped() | self.wait_until_stopped() | ||||
except FailedToStartError as e: | except FailedToStartError as e: | ||||
self.log.debug('bitcoind failed to start: {}'.format(e)) | self.log.debug('bitcoind failed to start: {}'.format(e)) | ||||
self.running = False | self.running = False | ||||
self.process = None | self.process = None | ||||
# Check stderr for expected message | # Check stderr for expected message | ||||
if expected_msg is not None: | if expected_msg is not None: | ||||
log_stderr.seek(0) | log_stderr.seek(0) | ||||
stderr = log_stderr.read().decode('utf-8').strip() | stderr = log_stderr.read().decode('utf-8').strip() | ||||
if partial_match: | if match == ErrorMatch.PARTIAL_REGEX: | ||||
if re.search(expected_msg, stderr, flags=re.MULTILINE) is None: | if re.search(expected_msg, stderr, flags=re.MULTILINE) is None: | ||||
raise AssertionError( | raise AssertionError( | ||||
'Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr)) | 'Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr)) | ||||
else: | elif match == ErrorMatch.FULL_REGEX: | ||||
if re.fullmatch(expected_msg, stderr) is None: | if re.fullmatch(expected_msg, stderr) is None: | ||||
raise AssertionError( | raise AssertionError( | ||||
'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) | 'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) | ||||
elif match == ErrorMatch.FULL_TEXT: | |||||
if expected_msg != stderr: | |||||
raise AssertionError( | |||||
'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) | |||||
else: | else: | ||||
if expected_msg is None: | if expected_msg is None: | ||||
assert_msg = "bitcoind should have exited with an error" | assert_msg = "bitcoind should have exited with an error" | ||||
else: | else: | ||||
assert_msg = "bitcoind should have exited with expected error " + expected_msg | assert_msg = "bitcoind should have exited with expected error " + expected_msg | ||||
raise AssertionError(assert_msg) | raise AssertionError(assert_msg) | ||||
def node_encrypt_wallet(self, passphrase): | def node_encrypt_wallet(self, passphrase): | ||||
▲ Show 20 Lines • Show All 135 Lines • Show Last 20 Lines |