Changeset View
Changeset View
Standalone View
Standalone View
test/functional/feature_init.py
Show All 26 Lines | def run_test(self): | ||||
- test removing certain essential files to test startup error paths. | - test removing certain essential files to test startup error paths. | ||||
""" | """ | ||||
# TODO: skip Windows for now since it isn't clear how to SIGTERM. | # TODO: skip Windows for now since it isn't clear how to SIGTERM. | ||||
# | # | ||||
# Windows doesn't support `process.terminate()`. | # Windows doesn't support `process.terminate()`. | ||||
# and other approaches (like below) don't work: | # and other approaches (like below) don't work: | ||||
# | # | ||||
# os.kill(node.process.pid, signal.CTRL_C_EVENT) | # os.kill(node.process.pid, signal.CTRL_C_EVENT) | ||||
if os.name == 'nt': | if os.name == "nt": | ||||
raise SkipTest("can't SIGTERM on Windows") | raise SkipTest("can't SIGTERM on Windows") | ||||
self.stop_node(0) | self.stop_node(0) | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
def sigterm_node(): | def sigterm_node(): | ||||
node.process.terminate() | node.process.terminate() | ||||
node.process.wait() | node.process.wait() | ||||
def check_clean_start(): | def check_clean_start(): | ||||
"""Ensure that node restarts successfully after various interrupts.""" | """Ensure that node restarts successfully after various interrupts.""" | ||||
node.start() | node.start() | ||||
node.wait_for_rpc_connection() | node.wait_for_rpc_connection() | ||||
assert_equal(200, node.getblockcount()) | assert_equal(200, node.getblockcount()) | ||||
lines_to_terminate_after = [ | lines_to_terminate_after = [ | ||||
b'Validating signatures for all blocks', | b"Validating signatures for all blocks", | ||||
b'scheduler thread start', | b"scheduler thread start", | ||||
b'Starting HTTP server', | b"Starting HTTP server", | ||||
b'Loading P2P addresses', | b"Loading P2P addresses", | ||||
b'Loading banlist', | b"Loading banlist", | ||||
b'Loading block index', | b"Loading block index", | ||||
b'Switching active chainstate', | b"Switching active chainstate", | ||||
b'Checking all blk files are present', | b"Checking all blk files are present", | ||||
b'Loaded best chain:', | b"Loaded best chain:", | ||||
b'init message: Verifying blocks', | b"init message: Verifying blocks", | ||||
b'init message: Starting network threads', | b"init message: Starting network threads", | ||||
b'net thread start', | b"net thread start", | ||||
b'addcon thread start', | b"addcon thread start", | ||||
b'loadblk thread start', | b"loadblk thread start", | ||||
b'txindex thread start', | b"txindex thread start", | ||||
b'block filter index thread start', | b"block filter index thread start", | ||||
b'coinstatsindex thread start', | b"coinstatsindex thread start", | ||||
b'msghand thread start', | b"msghand thread start", | ||||
b'net thread start', | b"net thread start", | ||||
b'addcon thread start', | b"addcon thread start", | ||||
] | ] | ||||
if self.is_wallet_compiled(): | if self.is_wallet_compiled(): | ||||
lines_to_terminate_after.append(b'Verifying wallet') | lines_to_terminate_after.append(b"Verifying wallet") | ||||
for terminate_line in lines_to_terminate_after: | for terminate_line in lines_to_terminate_after: | ||||
self.log.info( | self.log.info(f"Starting node and will exit after line {terminate_line}") | ||||
f"Starting node and will exit after line {terminate_line}") | |||||
with node.wait_for_debug_log([terminate_line]): | with node.wait_for_debug_log([terminate_line]): | ||||
node.start( | node.start( | ||||
extra_args=[ | extra_args=[ | ||||
'-txindex=1', | "-txindex=1", | ||||
'-blockfilterindex=1', | "-blockfilterindex=1", | ||||
'-coinstatsindex=1', | "-coinstatsindex=1", | ||||
]) | ] | ||||
) | |||||
self.log.debug("Terminating node after terminate line was found") | self.log.debug("Terminating node after terminate line was found") | ||||
sigterm_node() | sigterm_node() | ||||
check_clean_start() | check_clean_start() | ||||
self.stop_node(0) | self.stop_node(0) | ||||
self.log.info( | self.log.info("Test startup errors after removing certain essential files") | ||||
"Test startup errors after removing certain essential files") | |||||
files_to_disturb = { | files_to_disturb = { | ||||
'blocks/index/*.ldb': 'Error opening block database.', | "blocks/index/*.ldb": "Error opening block database.", | ||||
'chainstate/*.ldb': 'Error opening block database.', | "chainstate/*.ldb": "Error opening block database.", | ||||
'blocks/blk*.dat': 'Error loading block database.', | "blocks/blk*.dat": "Error loading block database.", | ||||
} | } | ||||
for file_patt, err_fragment in files_to_disturb.items(): | for file_patt, err_fragment in files_to_disturb.items(): | ||||
target_files = list(node.chain_path.glob(file_patt)) | target_files = list(node.chain_path.glob(file_patt)) | ||||
for target_file in target_files: | for target_file in target_files: | ||||
self.log.info(f"Tweaking file to ensure failure {target_file}") | self.log.info(f"Tweaking file to ensure failure {target_file}") | ||||
bak_path = f"{target_file}.bak" | bak_path = f"{target_file}.bak" | ||||
target_file.rename(bak_path) | target_file.rename(bak_path) | ||||
# TODO: at some point, we should test perturbing the files instead | # TODO: at some point, we should test perturbing the files instead | ||||
# of removing them, e.g. | # of removing them, e.g. | ||||
# | # | ||||
# contents = target_file.read_bytes() | # contents = target_file.read_bytes() | ||||
# tweaked_contents = bytearray(contents) | # tweaked_contents = bytearray(contents) | ||||
# tweaked_contents[50:250] = b'1' * 200 | # tweaked_contents[50:250] = b'1' * 200 | ||||
# target_file.write_bytes(bytes(tweaked_contents)) | # target_file.write_bytes(bytes(tweaked_contents)) | ||||
# | # | ||||
# At the moment I can't get this to work (bitcoind loads | # At the moment I can't get this to work (bitcoind loads | ||||
# successfully?) so investigate doing this later. | # successfully?) so investigate doing this later. | ||||
node.assert_start_raises_init_error( | node.assert_start_raises_init_error( | ||||
extra_args=[ | extra_args=[ | ||||
'-txindex=1', | "-txindex=1", | ||||
'-blockfilterindex=1', | "-blockfilterindex=1", | ||||
'-coinstatsindex=1', | "-coinstatsindex=1", | ||||
], | ], | ||||
expected_msg=err_fragment, | expected_msg=err_fragment, | ||||
match=ErrorMatch.PARTIAL_REGEX, | match=ErrorMatch.PARTIAL_REGEX, | ||||
) | ) | ||||
for target_file in target_files: | for target_file in target_files: | ||||
bak_path = f"{target_file}.bak" | bak_path = f"{target_file}.bak" | ||||
self.log.debug( | self.log.debug(f"Restoring file from {bak_path} and restarting") | ||||
f"Restoring file from {bak_path} and restarting") | |||||
Path(bak_path).rename(target_file) | Path(bak_path).rename(target_file) | ||||
check_clean_start() | check_clean_start() | ||||
self.stop_node(0) | self.stop_node(0) | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
InitStressTest().main() | InitStressTest().main() |