diff --git a/contrib/devtools/clang-format-diff.py b/contrib/devtools/clang-format-diff.py index f322b3a88049..98eee67f4300 100644 --- a/contrib/devtools/clang-format-diff.py +++ b/contrib/devtools/clang-format-diff.py @@ -106,7 +106,7 @@ def main(): filename = None lines_by_file = {} for line in sys.stdin: - match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) + match = re.search(r'^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) if match: filename = match.group(2) if filename is None: @@ -119,7 +119,7 @@ def main(): if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE): continue - match = re.search('^@@.*\+(\d+)(,(\d+))?', line) + match = re.search(r'^@@.*\+(\d+)(,(\d+))?', line) if match: start_line = int(match.group(1)) line_count = 1 diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py index 9d1a9097f686..99f554472301 100755 --- a/contrib/devtools/copyright_header.py +++ b/contrib/devtools/copyright_header.py @@ -74,7 +74,7 @@ def get_filenames_to_examine(base_directory): ################################################################################ -COPYRIGHT_WITH_C = 'Copyright \(c\)' +COPYRIGHT_WITH_C = r'Copyright \(c\)' COPYRIGHT_WITHOUT_C = 'Copyright' ANY_COPYRIGHT_STYLE = '(%s|%s)' % (COPYRIGHT_WITH_C, COPYRIGHT_WITHOUT_C) @@ -88,43 +88,42 @@ def get_filenames_to_examine(base_directory): ANY_COPYRIGHT_COMPILED = re.compile(ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE) def compile_copyright_regex(copyright_style, year_style, name): - return re.compile('%s %s,? %s' % (copyright_style, year_style, name)) + return re.compile('%s %s,? %s\n' % (copyright_style, year_style, name)) EXPECTED_HOLDER_NAMES = [ - "Satoshi Nakamoto\n", - "The Bitcoin Core developers\n", - "The Bitcoin Core developers \n", - "Bitcoin Core Developers\n", - "the Bitcoin Core developers\n", - "The Bitcoin developers\n", - "The LevelDB Authors\. All rights reserved\.\n", - "BitPay Inc\.\n", - "BitPay, Inc\.\n", - "University of Illinois at Urbana-Champaign\.\n", - "MarcoFalke\n", - "Pieter Wuille\n", - "Pieter Wuille +\*\n", - "Pieter Wuille, Gregory Maxwell +\*\n", - "Pieter Wuille, Andrew Poelstra +\*\n", - "Ian Miers, Christina Garman and Matthew Green\n", - "Andrew Poelstra +\*\n", - "Wladimir J. van der Laan\n", - "Jeff Garzik\n", - "Diederik Huys, Pieter Wuille +\*\n", - "Thomas Daede, Cory Fields +\*\n", - "Jan-Klaas Kollhof\n", - "Sam Rushing\n", - "ArtForz -- public domain half-a-node\n", - " Projet RNRT SAPHIR\n", - "The Zcash developers\n", - "The Dash developers\n", - "The Dash Developers\n", - "The Dash Core developers\n", - "The PIVX developers\n", - "The PPCoin developers\n", - "The NovaCoin Developers", - "The BlackCoin Developers\n", - "The Blackcoin More developers\n", + r"Satoshi Nakamoto", + r"The Bitcoin Core developers", + r"Bitcoin Core Developers", + r"the Bitcoin Core developers", + r"The Bitcoin developers", + r"The LevelDB Authors\. All rights reserved\.", + r"BitPay Inc\.", + r"BitPay, Inc\.", + r"University of Illinois at Urbana-Champaign\.", + r"MarcoFalke", + r"Pieter Wuille", + r"Pieter Wuille +\*", + r"Pieter Wuille, Gregory Maxwell +\*", + r"Pieter Wuille, Andrew Poelstra +\*", + r"Ian Miers, Christina Garman and Matthew Green", + r"Andrew Poelstra +\*", + r"Wladimir J. van der Laan", + r"Jeff Garzik", + r"Diederik Huys, Pieter Wuille +\*", + r"Thomas Daede, Cory Fields +\*", + r"Jan-Klaas Kollhof", + r"Sam Rushing", + r"ArtForz -- public domain half-a-node", + r" Projet RNRT SAPHIR", + r"The Zcash developers", + r"The Dash developers", + r"The Dash Developers", + r"The Dash Core developers", + r"The PIVX developers", + r"The PPCoin developers", + r"The NovaCoin Developers", + r"The BlackCoin Developers", + r"The Blackcoin More developers", ] DOMINANT_STYLE_COMPILED = {} @@ -354,7 +353,7 @@ def write_file_lines(filename, file_lines): # update header years execution ################################################################################ -COPYRIGHT = 'Copyright \(c\)' +COPYRIGHT = r'Copyright \(c\)' YEAR = "20[0-9][0-9]" YEAR_RANGE = '(%s)(-%s)?' % (YEAR, YEAR) HOLDER = 'The PIVX developers' diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index 11cb74ac4fd4..1bf36e224916 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -142,7 +142,7 @@ def read_libraries(filename): for line in stdout.splitlines(): tokens = line.split() if len(tokens)>2 and tokens[1] == '(NEEDED)': - match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) + match = re.match(r'^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) if match: libraries.append(match.group(1)) else: @@ -172,5 +172,3 @@ def read_libraries(filename): retval = 1 sys.exit(retval) - - diff --git a/src/init.cpp b/src/init.cpp index 729eb045e3c4..59b9b33cfcd5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1034,7 +1034,7 @@ bool AppInitParameterInteraction() // ********************************************************* Step 2: parameter interactions if (!fs::is_directory(GetBlocksDir())) { - return UIError(strprintf(_("Specified blocks directory \"%s\" does not exist.\n"), gArgs.GetArg("-blocksdir", "").c_str())); + return UIError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "").c_str())); } // Make sure enough file descriptors are available diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py index 724fc2778f6d..0de944136c72 100755 --- a/test/functional/combine_logs.py +++ b/test/functional/combine_logs.py @@ -11,6 +11,7 @@ import argparse from collections import defaultdict, namedtuple +import glob import heapq import itertools import os @@ -66,9 +67,17 @@ def read_logs(tmp_dir): Delegates to generator function get_log_events() to provide individual log events for each of the input log files.""" + # Find out what the folder is called that holds the debug.log file + chain = glob.glob("{}/node0/*/debug.log".format(tmp_dir)) + if chain: + chain = chain[0] # pick the first one if more than one chain was found (should never happen) + chain = re.search(r'node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name + else: + chain = 'regtest' # fallback to regtest (should only happen when none exists) + files = [("test", "%s/test_framework.log" % tmp_dir)] for i in itertools.count(): - logfile = "{}/node{}/regtest/debug.log".format(tmp_dir, i) + logfile = "{}/node{}/{}/debug.log".format(tmp_dir, i, chain) if not os.path.isfile(logfile): break files.append(("node%d" % i, logfile)) diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py index 676600a5f7ba..9fbd831f866b 100755 --- a/test/functional/feature_abortnode.py +++ b/test/functional/feature_abortnode.py @@ -2,12 +2,12 @@ # Copyright (c) 2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php. -"""Test bitcoind aborts if can't disconnect a block. +"""Test pivxd aborts if can't disconnect a block. - Start a single node and generate 3 blocks. - Delete the undo data. - Mine a fork that requires disconnecting the tip. -- Verify that bitcoind AbortNode's. +- Verify that pivxd AbortNode's. """ import os @@ -44,7 +44,7 @@ def run_test(self): self.log.info("Waiting for crash") wait_until(lambda: self.nodes[0].is_node_stopped(), timeout=60) self.log.info("Node crashed - now verifying restart fails") - self.assert_start_raises_init_error(0) + self.nodes[0].assert_start_raises_init_error() if __name__ == '__main__': AbortNodeTest().main() diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py index 30f5f482270d..90b968f40d68 100755 --- a/test/functional/feature_asmap.py +++ b/test/functional/feature_asmap.py @@ -76,7 +76,7 @@ def test_default_asmap_with_missing_file(self): self.log.info('Test pivxd -asmap with missing default map file') self.stop_node(0) msg = "Error: Could not find asmap file \"{}\"".format(self.default_asmap) - self.assert_start_raises_init_error(0, extra_args=['-asmap'], expected_msg=msg) + self.nodes[0].assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) def test_empty_asmap(self): self.log.info('Test pivxd -asmap with empty map file') @@ -84,7 +84,7 @@ def test_empty_asmap(self): with open(self.default_asmap, "w", encoding="utf-8") as f: f.write("") msg = "Error: Could not parse asmap file \"{}\"".format(self.default_asmap) - self.assert_start_raises_init_error(0, extra_args=['-asmap'], expected_msg=msg) + self.nodes[0].assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) os.remove(self.default_asmap) def run_test(self): diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py index cbd327e04b79..5cc5f3814cbd 100755 --- a/test/functional/feature_blocksdir.py +++ b/test/functional/feature_blocksdir.py @@ -22,10 +22,12 @@ def run_test(self): shutil.rmtree(self.nodes[0].datadir) initialize_datadir(self.options.tmpdir, 0) self.log.info("Starting with nonexistent blocksdir ...") - self.assert_start_raises_init_error(0, ["-blocksdir="+self.options.tmpdir+ "/blocksdir"], "Specified blocks director") - os.mkdir(self.options.tmpdir+ "/blocksdir") + blocksdir_path = os.path.join(self.options.tmpdir, 'blocksdir') + expected_err = 'Error: Specified blocks directory "{}" does not exist.'.format(blocksdir_path) + self.nodes[0].assert_start_raises_init_error(["-blocksdir=" + blocksdir_path], expected_err) + os.mkdir(blocksdir_path) self.log.info("Starting with existing blocksdir ...") - self.start_node(0, ["-blocksdir="+self.options.tmpdir+ "/blocksdir"]) + self.start_node(0, ["-blocksdir=" + blocksdir_path]) self.log.info("mining blocks..") self.nodes[0].generate(10) assert(os.path.isfile(os.path.join(self.options.tmpdir, "blocksdir", "regtest", "blocks", "blk00000.dat"))) diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index df0b890dba88..f828c2bd0142 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -26,7 +26,7 @@ def run_test(self): # Check that using -datadir argument on non-existent directory fails self.nodes[0].datadir = new_data_dir - self.assert_start_raises_init_error(0, ['-datadir='+new_data_dir], 'Error: Specified data directory "' + new_data_dir + '" does not exist.') + self.nodes[0].assert_start_raises_init_error(['-datadir=' + new_data_dir], 'Error: Specified data directory "' + new_data_dir + '" does not exist.') # Check that using non-existent datadir in conf file fails conf_file = os.path.join(default_data_dir, "pivx.conf") @@ -37,8 +37,7 @@ def run_test(self): f.write("datadir=" + new_data_dir + "\n") f.write(conf_file_contents) - # Temporarily disabled, because this test would access the user's home dir (~/.pivx) - #self.assert_start_raises_init_error(0, ['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') + self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') # Create the directory and ensure the config file now works os.mkdir(new_data_dir) diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py index 4a39e31125ec..4bbc930f440b 100755 --- a/test/functional/feature_logging.py +++ b/test/functional/feature_logging.py @@ -8,6 +8,7 @@ import os from test_framework.test_framework import PivxTestFramework +from test_framework.test_node import ErrorMatch class LoggingTest(PivxTestFramework): @@ -35,8 +36,8 @@ def run_test(self): invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo") invalidname = os.path.join("foo", "foo.log") self.stop_node(0) - self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % (invalidname)], - "Error: Could not open debug log file") + exp_stderr = r"Error: Could not open debug log file \S+$" + self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr, match=ErrorMatch.FULL_REGEX) assert not os.path.isfile(os.path.join(invdir, "foo.log")) self.log.info("Invalid relative filename throws") @@ -50,8 +51,7 @@ def run_test(self): self.stop_node(0) invdir = os.path.join(self.options.tmpdir, "foo") invalidname = os.path.join(invdir, "foo.log") - self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % invalidname], - "Error: Could not open debug log file") + self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % invalidname], exp_stderr, match=ErrorMatch.FULL_REGEX) assert not os.path.isfile(os.path.join(invdir, "foo.log")) self.log.info("Invalid absolute filename throws") diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index 0d0ecf605a0d..ecb48b08d428 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -79,7 +79,7 @@ def run_test(self): self.nodes[1].generate(51) self.sync_all() - # Give bitcoind 10 seconds to write the alert notification + # Give pivxd 10 seconds to write the alert notification wait_until(lambda: len(os.listdir(self.alertnotify_dir)), timeout=10) for notify_file in os.listdir(self.alertnotify_dir): diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py index 3d3c38ddacd5..c0a2c1da87ec 100755 --- a/test/functional/feature_proxy.py +++ b/test/functional/feature_proxy.py @@ -2,7 +2,7 @@ # Copyright (c) 2015-2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test bitcoind with different proxy configuration. +"""Test pivxd with different proxy configuration. Test plan: - Start pivxd's with different proxy configurations @@ -96,7 +96,7 @@ def node_test(self, node, proxies, auth, test_onion=True): node.addnode("15.61.23.23:1234", "onetry") cmd = proxies[0].queue.get() assert(isinstance(cmd, Socks5Command)) - # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6 + # Note: pivxd's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6 assert_equal(cmd.atyp, AddressType.DOMAINNAME) assert_equal(cmd.addr, b"15.61.23.23") assert_equal(cmd.port, 1234) @@ -110,7 +110,7 @@ def node_test(self, node, proxies, auth, test_onion=True): node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry") cmd = proxies[1].queue.get() assert(isinstance(cmd, Socks5Command)) - # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6 + # Note: pivxd's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6 assert_equal(cmd.atyp, AddressType.DOMAINNAME) assert_equal(cmd.addr, b"1233:3432:2434:2343:3234:2345:6546:4534") assert_equal(cmd.port, 5443) diff --git a/test/functional/feature_reindex.py b/test/functional/feature_reindex.py index a28bae1e0f6f..ab4e682d47ff 100755 --- a/test/functional/feature_reindex.py +++ b/test/functional/feature_reindex.py @@ -2,7 +2,7 @@ # Copyright (c) 2014-2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test running bitcoind with -reindex and -reindex-chainstate options. +"""Test running pivxd with -reindex and -reindex-chainstate options. - Start a single node and generate 3 blocks. - Stop the node and restart it with -reindex. Verify that the node has reindexed up to block 3. diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py index 567d14f828ac..e825bf7dcc30 100755 --- a/test/functional/feature_uacomment.py +++ b/test/functional/feature_uacomment.py @@ -4,7 +4,10 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the -uacomment option.""" +import re + from test_framework.test_framework import PivxTestFramework +from test_framework.test_node import ErrorMatch from test_framework.util import assert_equal class UacommentTest(PivxTestFramework): @@ -23,13 +26,14 @@ def run_test(self): self.log.info("test -uacomment max length") self.stop_node(0) - expected = "exceeds maximum length (256). Reduce the number or size of -uacomment." - self.assert_start_raises_init_error(0, ["-uacomment=" + 'a' * 256], expected) + expected = r"Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of -uacomment." + self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX) self.log.info("test -uacomment unsafe characters") for unsafe_char in ['/', ':', '(', ')']: - expected = "User Agent comment (" + unsafe_char + ") contains unsafe characters" - self.assert_start_raises_init_error(0, ["-uacomment=" + unsafe_char], expected) + expected = r"Error: User Agent comment \(" + re.escape(unsafe_char) + r"\) contains unsafe characters." + self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX) + if __name__ == '__main__': UacommentTest().main() diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index df13d026032e..7cfe8e3269df 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -4,7 +4,7 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php. """Test mempool persistence. -By default, bitcoind will dump mempool on shutdown and +By default, pivxd will dump mempool on shutdown and then reload it on startup. This can be overridden with the -persistmempool=false command line option. @@ -106,7 +106,7 @@ def run_test(self): assert self.nodes[0].getmempoolinfo()["loaded"] assert_equal(len(self.nodes[1].getrawmempool()), 5) - self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails") + self.log.debug("Prevent pivxd from writing mempool.dat to disk. Verify that `savemempool` fails") # to test the exception we are creating a tmp folder called mempool.dat.new # which is an implementation detail that could change and break this test mempooldotnew1 = mempooldat1 + '.new' diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py index f7eaaaa354ef..2d70ad30b7f5 100755 --- a/test/functional/p2p_leak.py +++ b/test/functional/p2p_leak.py @@ -10,7 +10,7 @@ This test connects to a node and sends it a few messages, trying to intice it into sending us something it shouldn't. -Also test that nodes that send unsupported service bits to bitcoind are disconnected +Also test that nodes that send unsupported service bits to pivxd are disconnected and don't receive a VERACK. Unsupported service bits are currently 1 << 5 and 1 << 7 (until August 1st 2018). """ @@ -64,7 +64,7 @@ def on_blocktxn(self, message): self.bad_message(message) # anyway, and eventually get disconnected. class CNodeNoVersionBan(CLazyNode): # send a bunch of veracks without sending a message. This should get us disconnected. - # NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes + # NOTE: implementation-specific check here. Remove if pivxd ban behavior changes def on_open(self): super().on_open() for i in range(banscore): diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py index d54f06a6337a..31ecdf8bbd8d 100755 --- a/test/functional/rpc_bind.py +++ b/test/functional/rpc_bind.py @@ -2,7 +2,7 @@ # Copyright (c) 2014-2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test running bitcoind with the -rpcbind and -rpcallowip options.""" +"""Test running pivxd with the -rpcbind and -rpcallowip options.""" import socket import sys diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index af151776c582..ad684d33b3b5 100644 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -188,7 +188,7 @@ def FromHex(obj, hex_string): def ToHex(obj): return bytes_to_hex_str(obj.serialize()) -# Objects that map to bitcoind objects, which can be serialized/deserialized +# Objects that map to pivxd objects, which can be serialized/deserialized class CAddress(): def __init__(self): @@ -1237,7 +1237,7 @@ def __init__(self, headers=None): self.headers = headers if headers is not None else [] def deserialize(self, f): - # comment in bitcoind indicates these should be deserialized as blocks + # comment in pivxd indicates these should be deserialized as blocks blocks = deser_vector(f, CBlock) for x in blocks: self.headers.append(CBlockHeader(x)) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index ceb4fa94562b..27b1532e45af 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -343,28 +343,6 @@ def restart_node(self, i, extra_args=None): self.stop_node(i) self.start_node(i, extra_args) - def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None, *args, **kwargs): - with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: - try: - self.start_node(i, extra_args, stderr=log_stderr, *args, **kwargs) - self.nodes[i].wait_for_rpc_connection() - self.stop_node(i) - except Exception as e: - assert 'pivxd exited' in str(e) # node must have shutdown - self.nodes[i].running = False - self.nodes[i].process = None - if expected_msg is not None: - log_stderr.seek(0) - stderr = log_stderr.read().decode('utf-8') - if expected_msg not in stderr: - raise AssertionError("Expected error \"" + expected_msg + "\" not found in:\n" + stderr) - else: - if expected_msg is None: - assert_msg = "pivxd should have exited with an error" - else: - assert_msg = "pivxd should have exited with expected error " + expected_msg - raise AssertionError(assert_msg) - def wait_for_node_exit(self, i, timeout): self.nodes[i].process.wait(timeout) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 7ac0b535eb12..c7a9ec05b18e 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -7,12 +7,14 @@ import contextlib import decimal import errno +from enum import Enum import http.client import json import logging import os import re import subprocess +import tempfile import time from .authproxy import JSONRPCException @@ -31,6 +33,12 @@ BITCOIND_PROC_WAIT_TIMEOUT = 600 +class ErrorMatch(Enum): + FULL_TEXT = 1 + FULL_REGEX = 2 + PARTIAL_REGEX = 3 + + class TestNode(): """A class for representing a pivxd node under test. @@ -83,7 +91,7 @@ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mo self.p2ps = [] def __del__(self): - # Ensure that we don't leave any bitcoind processes lying around after + # Ensure that we don't leave any pivxd processes lying around after # the test ends if self.process and self.cleanup_on_exit: # Should only happen on test failure @@ -107,7 +115,7 @@ def start(self, extra_args=None, stderr=None, *args, **kwargs): if stderr is None: stderr = self.stderr # Delete any existing cookie file -- if such a file exists (eg due to - # unclean shutdown), it will get overwritten anyway by bitcoind, and + # unclean shutdown), it will get overwritten anyway by pivxd, and # potentially interfere with our attempt to authenticate delete_cookie_file(self.datadir) self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs) @@ -159,7 +167,7 @@ def wait_for_rpc_connection(self): except JSONRPCException as e: # Initialization phase if e.error['code'] != -28: # RPC in warmup? raise # unknown JSON RPC exception - except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting + except ValueError as e: # cookie file not found and no rpcuser or rpcassword. pivxd still starting if "No RPC credentials" not in str(e): raise time.sleep(1.0 / poll_per_s) @@ -209,6 +217,44 @@ def is_node_stopped(self): def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): wait_until(self.is_node_stopped, timeout=timeout) + 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. + + extra_args: extra arguments to pass through to pivxd + expected_msg: regex that stderr should match when pivxd fails + + Will throw if pivxd starts without an error. + Will throw if an expected_msg is provided and it does not match pivxd's stdout.""" + with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: + try: + self.start(extra_args, stderr=log_stderr, *args, **kwargs) + self.wait_for_rpc_connection() + self.stop_node() + self.wait_until_stopped() + except Exception as e: + assert 'pivxd exited' in str(e) # node must have shutdown + self.running = False + self.process = None + # Check stderr for expected message + if expected_msg is not None: + log_stderr.seek(0) + stderr = log_stderr.read().decode('utf-8').strip() + if match == ErrorMatch.PARTIAL_REGEX: + if re.search(expected_msg, stderr, flags=re.MULTILINE) is None: + raise AssertionError('Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr)) + elif match == ErrorMatch.FULL_REGEX: + if re.fullmatch(expected_msg, stderr) is None: + raise AssertionError('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: + if expected_msg is None: + assert_msg = "pivxd should have exited with an error" + else: + assert_msg = "pivxd should have exited with expected error " + expected_msg + raise AssertionError(assert_msg) + @contextlib.contextmanager def assert_debug_log(self, expected_msgs): debug_log = os.path.join(self.datadir, 'regtest', 'debug.log') diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index d72f92583cfb..9dba4eff818b 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -297,7 +297,7 @@ def main(): enable_wallet = config["components"].getboolean("ENABLE_WALLET") enable_utils = config["components"].getboolean("ENABLE_UTILS") - enable_bitcoind = config["components"].getboolean("ENABLE_BITCOIND") + enable_pivxd = config["components"].getboolean("ENABLE_BITCOIND") if config["environment"]["EXEEXT"] == ".exe" and not args.force: # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 @@ -305,7 +305,7 @@ def main(): print("Tests currently disabled on Windows by default. Use --force option to enable") sys.exit(0) - if not (enable_wallet and enable_utils and enable_bitcoind): + if not (enable_wallet and enable_utils and enable_pivxd): print("No functional tests to run. Wallet, utils, and pivxd must all be enabled") print("Rerun `configure` with -enable-wallet, -with-utils and -with-daemon and rerun make") sys.exit(0) diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 0f3803c08e76..7fcc2282b123 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -13,7 +13,7 @@ from test_framework.test_framework import PivxTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error - +from test_framework.test_node import ErrorMatch class MultiWalletTest(PivxTestFramework): def set_test_params(self): @@ -65,42 +65,44 @@ def wallet_file(name): assert_equal(os.path.isfile(wallet_file(wallet_name)), True) # should not initialize if wallet path can't be created - self.assert_start_raises_init_error(0, ['-wallet=wallet.dat/bad'], 'Not a directory') + exp_stderr = "boost::filesystem::create_directory: (The system cannot find the path specified|Not a directory):" + self.nodes[0].assert_start_raises_init_error(['-wallet=wallet.dat/bad'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX) - self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist') - self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir()) - self.assert_start_raises_init_error(0, ['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir()) + self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist') + self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir()) + self.nodes[0].assert_start_raises_init_error(['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir()) # should not initialize if there are duplicate wallets - self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') + self.nodes[0].assert_start_raises_init_error(['-wallet=w1', '-wallet=w1'], 'Error: Error loading wallet w1. Duplicate -wallet filename specified.') # should not initialize if one wallet is a copy of another shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy')) - self.assert_start_raises_init_error(0, ['-wallet=w8', '-wallet=w8_copy'], 'duplicates fileid') + exp_stderr = r"CDB: Can't open database w8_copy \(duplicates fileid \w+ from w8\)" + self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX) # should not initialize if wallet file is a symlink os.symlink('w8', wallet_dir('w8_symlink')) - self.assert_start_raises_init_error(0, ['-wallet=w8_symlink'], 'Invalid -wallet path') + self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], r'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX) # should not initialize if the specified walletdir does not exist - self.assert_start_raises_init_error(0, ['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist') + self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist') # should not initialize if the specified walletdir is not a directory not_a_dir = wallet_dir('notadir') - open(not_a_dir, 'a', encoding="utf8").close() - self.assert_start_raises_init_error(0, ['-walletdir='+not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory') + open(not_a_dir, 'a').close() + self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory') self.log.info("Do not allow -zapwallettxes with multiwallet") - self.assert_start_raises_init_error(0, ['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") - self.assert_start_raises_init_error(0, ['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") - self.assert_start_raises_init_error(0, ['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") self.log.info("Do not allow -salvagewallet with multiwallet") - self.assert_start_raises_init_error(0, ['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") - self.assert_start_raises_init_error(0, ['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") self.log.info("Do not allow -upgradewallet with multiwallet") - self.assert_start_raises_init_error(0, ['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") - self.assert_start_raises_init_error(0, ['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") # if wallets/ doesn't exist, datadir should be the default wallet dir wallet_dir2 = data_dir('walletdir') @@ -120,8 +122,9 @@ def wallet_file(name): competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir') os.mkdir(competing_wallet_dir) - self.restart_node(0, ['-walletdir='+competing_wallet_dir]) - self.assert_start_raises_init_error(1, ['-walletdir='+competing_wallet_dir], 'Error initializing wallet database environment') + self.restart_node(0, ['-walletdir=' + competing_wallet_dir]) + exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\"!" + self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX) self.restart_node(0, extra_args)