Skip to content

Commit 9ea62a3

Browse files
author
MarcoFalke
committed
Merge #13049: [0.16] qa: Backports
41c29f6 qa: Fix python TypeError in script.py (MarcoFalke) 7460945 [qa] Delete cookie file before starting node (Suhas Daftuar) 0a76ed2 qa: Cache only chain and wallet for regtest datadir (MarcoFalke) 6c26df0 [qa] Ensure bitcoind processes are cleaned up when tests end (Suhas Daftuar) df38b13 [tests] Test starting bitcoind with -h and -version (John Newbery) 4bdb0ce [tests] Fix intermittent rpc_net.py failure. (John Newbery) 0e98f96 test: Use wait_until in tests where time was used for polling (Ben Woosley) 1286f3e test: Use wait_until to ensure ping goes out (Ben Woosley) cfebd40 [test] Round target fee to 8 decimals in assert_fee_amount (Karl-Johan Alm) Pull request description: Similar to #12967 this contains all relevant bugfixes and improvements to the functional test suite. I didn't include fixes to make the tests run on Windows, since that is still an ongoing effort and doesn't seem worth to backport. As all of these are clean cherry-picks, I suggest reviewers redo the cherry-picks to get the same branch and then run the extended test suite. Tree-SHA512: 70e1bc28d5572f93796f1ac4d97d77e8146869c15dcc1e3b65a730fa2641283050f769cefd9791d800c758e0a92f11fd55ed0797ccec87b897c7e701d0187f34
2 parents 845838c + 41c29f6 commit 9ea62a3

File tree

10 files changed

+121
-65
lines changed

10 files changed

+121
-65
lines changed

test/functional/feature_help.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2018 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Verify that starting bitcoin with -h works as expected."""
6+
import subprocess
7+
8+
from test_framework.test_framework import BitcoinTestFramework
9+
from test_framework.util import assert_equal
10+
11+
class HelpTest(BitcoinTestFramework):
12+
def set_test_params(self):
13+
self.setup_clean_chain = True
14+
self.num_nodes = 1
15+
16+
def setup_network(self):
17+
self.add_nodes(self.num_nodes)
18+
# Don't start the node
19+
20+
def run_test(self):
21+
self.log.info("Start bitcoin with -h for help text")
22+
self.nodes[0].start(extra_args=['-h'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
23+
# Node should exit immediately and output help to stdout.
24+
ret_code = self.nodes[0].process.wait(timeout=1)
25+
assert_equal(ret_code, 0)
26+
output = self.nodes[0].process.stdout.read()
27+
assert b'Options' in output
28+
self.log.info("Help text received: {} (...)".format(output[0:60]))
29+
self.nodes[0].running = False
30+
31+
self.log.info("Start bitcoin with -version for version information")
32+
self.nodes[0].start(extra_args=['-version'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
33+
# Node should exit immediately and output version to stdout.
34+
ret_code = self.nodes[0].process.wait(timeout=1)
35+
assert_equal(ret_code, 0)
36+
output = self.nodes[0].process.stdout.read()
37+
assert b'version' in output
38+
self.log.info("Version text received: {} (...)".format(output[0:60]))
39+
# Clean up TestNode state
40+
self.nodes[0].running = False
41+
self.nodes[0].process = None
42+
self.nodes[0].rpc_connected = False
43+
self.nodes[0].rpc = None
44+
45+
if __name__ == '__main__':
46+
HelpTest().main()

test/functional/feature_pruning.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from test_framework.test_framework import BitcoinTestFramework
1313
from test_framework.util import *
14-
import time
1514
import os
1615

1716
MIN_BLOCKS_TO_KEEP = 288
@@ -79,11 +78,8 @@ def test_height_min(self):
7978
for i in range(25):
8079
mine_large_block(self.nodes[0], self.utxo_cache_0)
8180

82-
waitstart = time.time()
83-
while os.path.isfile(self.prunedir+"blk00000.dat"):
84-
time.sleep(0.1)
85-
if time.time() - waitstart > 30:
86-
raise AssertionError("blk00000.dat not pruned when it should be")
81+
# Wait for blk00000.dat to be pruned
82+
wait_until(lambda: not os.path.isfile(self.prunedir+"blk00000.dat"), timeout=30)
8783

8884
self.log.info("Success")
8985
usage = calc_usage(self.prunedir)
@@ -218,11 +214,8 @@ def reorg_back(self):
218214
goalbestheight = first_reorg_height + 1
219215

220216
self.log.info("Verify node 2 reorged back to the main chain, some blocks of which it had to redownload")
221-
waitstart = time.time()
222-
while self.nodes[2].getblockcount() < goalbestheight:
223-
time.sleep(0.1)
224-
if time.time() - waitstart > 900:
225-
raise AssertionError("Node 2 didn't reorg to proper height")
217+
# Wait for Node 2 to reorg to proper height
218+
wait_until(lambda: self.nodes[2].getblockcount() >= goalbestheight, timeout=900)
226219
assert(self.nodes[2].getbestblockhash() == goalbesthash)
227220
# Verify we can now have the data for a block previously pruned
228221
assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight)

test/functional/feature_reindex.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
"""
1111

1212
from test_framework.test_framework import BitcoinTestFramework
13-
from test_framework.util import assert_equal
14-
import time
13+
from test_framework.util import wait_until
1514

1615
class ReindexTest(BitcoinTestFramework):
1716

@@ -25,9 +24,7 @@ def reindex(self, justchainstate=False):
2524
self.stop_nodes()
2625
extra_args = [["-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]]
2726
self.start_nodes(extra_args)
28-
while self.nodes[0].getblockcount() < blockcount:
29-
time.sleep(0.1)
30-
assert_equal(self.nodes[0].getblockcount(), blockcount)
27+
wait_until(lambda: self.nodes[0].getblockcount() == blockcount)
3128
self.log.info("Success")
3229

3330
def run_test(self):

test/functional/rpc_net.py

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
Tests correspond to code in rpc/net.cpp.
88
"""
99

10-
import time
11-
1210
from test_framework.test_framework import BitcoinTestFramework
1311
from test_framework.util import (
1412
assert_equal,
13+
assert_greater_than_or_equal,
1514
assert_raises_rpc_error,
1615
connect_nodes_bi,
1716
p2p_port,
17+
wait_until,
1818
)
1919

2020
class NetTest(BitcoinTestFramework):
@@ -34,40 +34,43 @@ def _test_connection_count(self):
3434
assert_equal(self.nodes[0].getconnectioncount(), 2)
3535

3636
def _test_getnettotals(self):
37-
# check that getnettotals totalbytesrecv and totalbytessent
38-
# are consistent with getpeerinfo
37+
# getnettotals totalbytesrecv and totalbytessent should be
38+
# consistent with getpeerinfo. Since the RPC calls are not atomic,
39+
# and messages might have been recvd or sent between RPC calls, call
40+
# getnettotals before and after and verify that the returned values
41+
# from getpeerinfo are bounded by those values.
42+
net_totals_before = self.nodes[0].getnettotals()
3943
peer_info = self.nodes[0].getpeerinfo()
44+
net_totals_after = self.nodes[0].getnettotals()
4045
assert_equal(len(peer_info), 2)
41-
net_totals = self.nodes[0].getnettotals()
42-
assert_equal(sum([peer['bytesrecv'] for peer in peer_info]),
43-
net_totals['totalbytesrecv'])
44-
assert_equal(sum([peer['bytessent'] for peer in peer_info]),
45-
net_totals['totalbytessent'])
46+
peers_recv = sum([peer['bytesrecv'] for peer in peer_info])
47+
peers_sent = sum([peer['bytessent'] for peer in peer_info])
48+
49+
assert_greater_than_or_equal(peers_recv, net_totals_before['totalbytesrecv'])
50+
assert_greater_than_or_equal(net_totals_after['totalbytesrecv'], peers_recv)
51+
assert_greater_than_or_equal(peers_sent, net_totals_before['totalbytessent'])
52+
assert_greater_than_or_equal(net_totals_after['totalbytessent'], peers_sent)
53+
4654
# test getnettotals and getpeerinfo by doing a ping
4755
# the bytes sent/received should change
4856
# note ping and pong are 32 bytes each
4957
self.nodes[0].ping()
50-
time.sleep(0.1)
58+
wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_after['totalbytessent'] + 32 * 2), timeout=1)
59+
wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_after['totalbytesrecv'] + 32 * 2), timeout=1)
60+
5161
peer_info_after_ping = self.nodes[0].getpeerinfo()
52-
net_totals_after_ping = self.nodes[0].getnettotals()
5362
for before, after in zip(peer_info, peer_info_after_ping):
54-
assert_equal(before['bytesrecv_per_msg']['pong'] + 32, after['bytesrecv_per_msg']['pong'])
55-
assert_equal(before['bytessent_per_msg']['ping'] + 32, after['bytessent_per_msg']['ping'])
56-
assert_equal(net_totals['totalbytesrecv'] + 32*2, net_totals_after_ping['totalbytesrecv'])
57-
assert_equal(net_totals['totalbytessent'] + 32*2, net_totals_after_ping['totalbytessent'])
63+
assert_greater_than_or_equal(after['bytesrecv_per_msg']['pong'], before['bytesrecv_per_msg']['pong'] + 32)
64+
assert_greater_than_or_equal(after['bytessent_per_msg']['ping'], before['bytessent_per_msg']['ping'] + 32)
5865

5966
def _test_getnetworkinginfo(self):
6067
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
6168
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
6269

6370
self.nodes[0].setnetworkactive(False)
6471
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False)
65-
timeout = 3
66-
while self.nodes[0].getnetworkinfo()['connections'] != 0:
67-
# Wait a bit for all sockets to close
68-
assert timeout > 0, 'not all connections closed in time'
69-
timeout -= 0.1
70-
time.sleep(0.1)
72+
# Wait a bit for all sockets to close
73+
wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0, timeout=3)
7174

7275
self.nodes[0].setnetworkactive(True)
7376
connect_nodes_bi(self.nodes, 0, 1)
@@ -84,8 +87,7 @@ def _test_getaddednodeinfo(self):
8487
assert_equal(len(added_nodes), 1)
8588
assert_equal(added_nodes[0]['addednode'], ip_port)
8689
# check that a non-existent node returns an error
87-
assert_raises_rpc_error(-24, "Node has not been added",
88-
self.nodes[0].getaddednodeinfo, '1.1.1.1')
90+
assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1')
8991

9092
def _test_getpeerinfo(self):
9193
peer_info = [x.getpeerinfo() for x in self.nodes]

test/functional/test_framework/script.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,11 +526,9 @@ def __iter__(self):
526526
yield CScriptOp(opcode)
527527

528528
def __repr__(self):
529-
# For Python3 compatibility add b before strings so testcases don't
530-
# need to change
531529
def _repr(o):
532530
if isinstance(o, bytes):
533-
return b"x('%s')" % hexlify(o).decode('ascii')
531+
return "x('%s')" % hexlify(o).decode('ascii')
534532
else:
535533
return repr(o)
536534

test/functional/test_framework/test_framework.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
check_json_precision,
2525
connect_nodes_bi,
2626
disconnect_nodes,
27+
get_datadir_path,
2728
initialize_datadir,
28-
log_filename,
2929
p2p_port,
3030
set_node_times,
3131
sync_blocks,
@@ -145,6 +145,8 @@ def main(self):
145145
if self.nodes:
146146
self.stop_nodes()
147147
else:
148+
for node in self.nodes:
149+
node.cleanup_on_exit = False
148150
self.log.info("Note: bitcoinds were not stopped and may still be running")
149151

150152
if not self.options.nocleanup and not self.options.noshutdown and success != TestStatus.FAILED:
@@ -372,7 +374,7 @@ def _initialize_chain(self):
372374
assert self.num_nodes <= MAX_NODES
373375
create_cache = False
374376
for i in range(MAX_NODES):
375-
if not os.path.isdir(os.path.join(self.options.cachedir, 'node' + str(i))):
377+
if not os.path.isdir(get_datadir_path(self.options.cachedir, i)):
376378
create_cache = True
377379
break
378380

@@ -381,8 +383,8 @@ def _initialize_chain(self):
381383

382384
# find and delete old cache directories if any exist
383385
for i in range(MAX_NODES):
384-
if os.path.isdir(os.path.join(self.options.cachedir, "node" + str(i))):
385-
shutil.rmtree(os.path.join(self.options.cachedir, "node" + str(i)))
386+
if os.path.isdir(get_datadir_path(self.options.cachedir, i)):
387+
shutil.rmtree(get_datadir_path(self.options.cachedir, i))
386388

387389
# Create cache directories, run bitcoinds:
388390
for i in range(MAX_NODES):
@@ -420,15 +422,18 @@ def _initialize_chain(self):
420422
self.stop_nodes()
421423
self.nodes = []
422424
self.disable_mocktime()
425+
426+
def cache_path(n, *paths):
427+
return os.path.join(get_datadir_path(self.options.cachedir, n), "regtest", *paths)
428+
423429
for i in range(MAX_NODES):
424-
os.remove(log_filename(self.options.cachedir, i, "debug.log"))
425-
os.remove(log_filename(self.options.cachedir, i, "wallets/db.log"))
426-
os.remove(log_filename(self.options.cachedir, i, "peers.dat"))
427-
os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat"))
430+
for entry in os.listdir(cache_path(i)):
431+
if entry not in ['wallets', 'chainstate', 'blocks']:
432+
os.remove(cache_path(i, entry))
428433

429434
for i in range(self.num_nodes):
430-
from_dir = os.path.join(self.options.cachedir, "node" + str(i))
431-
to_dir = os.path.join(self.options.tmpdir, "node" + str(i))
435+
from_dir = get_datadir_path(self.options.cachedir, i)
436+
to_dir = get_datadir_path(self.options.tmpdir, i)
432437
shutil.copytree(from_dir, to_dir)
433438
initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf
434439

test/functional/test_framework/test_node.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .authproxy import JSONRPCException
1818
from .util import (
1919
assert_equal,
20+
delete_cookie_file,
2021
get_rpc_proxy,
2122
rpc_url,
2223
wait_until,
@@ -70,9 +71,20 @@ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mo
7071
self.rpc = None
7172
self.url = None
7273
self.log = logging.getLogger('TestFramework.node%d' % i)
74+
self.cleanup_on_exit = True # Whether to kill the node when this object goes away
7375

7476
self.p2ps = []
7577

78+
def __del__(self):
79+
# Ensure that we don't leave any bitcoind processes lying around after
80+
# the test ends
81+
if self.process and self.cleanup_on_exit:
82+
# Should only happen on test failure
83+
# Avoid using logger, as that may have already been shutdown when
84+
# this destructor is called.
85+
print("Cleaning up leftover process")
86+
self.process.kill()
87+
7688
def __getattr__(self, name):
7789
"""Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
7890
if self.use_cli:
@@ -87,6 +99,10 @@ def start(self, extra_args=None, stderr=None, *args, **kwargs):
8799
extra_args = self.extra_args
88100
if stderr is None:
89101
stderr = self.stderr
102+
# Delete any existing cookie file -- if such a file exists (eg due to
103+
# unclean shutdown), it will get overwritten anyway by bitcoind, and
104+
# potentially interfere with our attempt to authenticate
105+
delete_cookie_file(self.datadir)
90106
self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs)
91107
self.running = True
92108
self.log.debug("bitcoind started, waiting for RPC to come up")

test/functional/test_framework/util.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
def assert_fee_amount(fee, tx_size, fee_per_kB):
2828
"""Assert the fee was in range"""
29-
target_fee = tx_size * fee_per_kB / 1000
29+
target_fee = round(tx_size * fee_per_kB / 1000, 8)
3030
if fee < target_fee:
3131
raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee)))
3232
# allow the wallet's estimation to be at most 2 bytes off
@@ -319,8 +319,11 @@ def get_auth_cookie(datadir):
319319
raise ValueError("No RPC credentials")
320320
return user, password
321321

322-
def log_filename(dirname, n_node, logname):
323-
return os.path.join(dirname, "node" + str(n_node), "regtest", logname)
322+
# If a cookie file exists in the given datadir, delete it.
323+
def delete_cookie_file(datadir):
324+
if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
325+
logger.debug("Deleting leftover cookie file")
326+
os.remove(os.path.join(datadir, "regtest", ".cookie"))
324327

325328
def get_bip9_status(node, key):
326329
info = node.getblockchaininfo()
@@ -334,20 +337,15 @@ def disconnect_nodes(from_connection, node_num):
334337
for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]:
335338
from_connection.disconnectnode(nodeid=peer_id)
336339

337-
for _ in range(50):
338-
if [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == []:
339-
break
340-
time.sleep(0.1)
341-
else:
342-
raise AssertionError("timed out waiting for disconnect")
340+
# wait to disconnect
341+
wait_until(lambda: [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5)
343342

344343
def connect_nodes(from_connection, node_num):
345344
ip_port = "127.0.0.1:" + str(p2p_port(node_num))
346345
from_connection.addnode(ip_port, "onetry")
347346
# poll until version handshake complete to avoid race conditions
348347
# with transaction relaying
349-
while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()):
350-
time.sleep(0.1)
348+
wait_until(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
351349

352350
def connect_nodes_bi(nodes, a, b):
353351
connect_nodes(nodes[a], b)

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
'feature_logging.py',
134134
'p2p_node_network_limited.py',
135135
'feature_config_args.py',
136+
'feature_help.py',
136137
# Don't append tests at the end to avoid merge conflicts
137138
# Put them in a random line within the section that fits their approximate run-time
138139
]

test/functional/wallet_basic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,9 +379,9 @@ def run_test(self):
379379
self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)])
380380
self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)])
381381
self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)])
382-
while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]:
382+
if m == '-reindex':
383383
# reindex will leave rpc warm up "early"; Wait for it to finish
384-
time.sleep(0.1)
384+
wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
385385
assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
386386

387387
# Exercise listsinceblock with the last two blocks

0 commit comments

Comments
 (0)