Skip to content

Commit fff72de

Browse files
author
MarcoFalke
committed
Merge #10171: [tests] Add node methods to test framework
4550049 Reorganize BitcoinTestFramework class (John Newbery) b7dd44c Add start and stop node methods to BitcoinTestFramework (John Newbery) b111324 move initialize_chain() and initialize_chain_clean() to be methods of BitcoinTestFramework (John Newbery) Tree-SHA512: 17e541aea8ca4c0d1189701499384e26239e2d5905de8adb0f042d3cf4c0bbed79fcaad61d563e1743bf4c62ad4915cebb4714783db839d9c53dfbbedcae6e9a
2 parents 750c5a5 + 4550049 commit fff72de

File tree

2 files changed

+169
-125
lines changed

2 files changed

+169
-125
lines changed

test/functional/test_framework/test_framework.py

Lines changed: 169 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,55 @@
88
import logging
99
import optparse
1010
import os
11-
import sys
1211
import shutil
12+
import subprocess
13+
import sys
1314
import tempfile
1415
import time
1516

1617
from .util import (
17-
initialize_chain,
18-
start_nodes,
18+
PortSeed,
19+
MAX_NODES,
20+
bitcoind_processes,
21+
check_json_precision,
1922
connect_nodes_bi,
23+
disable_mocktime,
2024
disconnect_nodes,
25+
enable_coverage,
26+
enable_mocktime,
27+
get_mocktime,
28+
get_rpc_proxy,
29+
initialize_datadir,
30+
log_filename,
31+
p2p_port,
32+
rpc_url,
33+
set_node_times,
34+
start_node,
35+
start_nodes,
36+
stop_node,
37+
stop_nodes,
2138
sync_blocks,
2239
sync_mempools,
23-
stop_nodes,
24-
stop_node,
25-
enable_coverage,
26-
check_json_precision,
27-
initialize_chain_clean,
28-
PortSeed,
40+
wait_for_bitcoind_start,
2941
)
3042
from .authproxy import JSONRPCException
3143

3244
class BitcoinTestFramework(object):
45+
"""Base class for a bitcoin test script.
46+
47+
Individual bitcoin test scripts should subclass this class and override the following methods:
48+
49+
- __init__()
50+
- add_options()
51+
- setup_chain()
52+
- setup_network()
53+
- run_test()
54+
55+
The main() method should not be overridden.
56+
57+
This class also contains various public and private helper methods."""
58+
59+
# Methods to override in subclass test scripts.
3360

3461
TEST_EXIT_PASSED = 0
3562
TEST_EXIT_FAILED = 1
@@ -40,27 +67,15 @@ def __init__(self):
4067
self.setup_clean_chain = False
4168
self.nodes = None
4269

43-
def run_test(self):
44-
raise NotImplementedError
45-
4670
def add_options(self, parser):
4771
pass
4872

4973
def setup_chain(self):
5074
self.log.info("Initializing test directory "+self.options.tmpdir)
5175
if self.setup_clean_chain:
52-
initialize_chain_clean(self.options.tmpdir, self.num_nodes)
76+
self._initialize_chain_clean(self.options.tmpdir, self.num_nodes)
5377
else:
54-
initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir)
55-
56-
def stop_node(self, num_node):
57-
stop_node(self.nodes[num_node], num_node)
58-
59-
def setup_nodes(self):
60-
extra_args = None
61-
if hasattr(self, "extra_args"):
62-
extra_args = self.extra_args
63-
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
78+
self._initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir)
6479

6580
def setup_network(self):
6681
self.setup_nodes()
@@ -72,27 +87,16 @@ def setup_network(self):
7287
connect_nodes_bi(self.nodes, i, i + 1)
7388
self.sync_all()
7489

75-
def split_network(self):
76-
"""
77-
Split the network of four nodes into nodes 0/1 and 2/3.
78-
"""
79-
disconnect_nodes(self.nodes[1], 2)
80-
disconnect_nodes(self.nodes[2], 1)
81-
self.sync_all([self.nodes[:2], self.nodes[2:]])
82-
83-
def sync_all(self, node_groups=None):
84-
if not node_groups:
85-
node_groups = [self.nodes]
90+
def setup_nodes(self):
91+
extra_args = None
92+
if hasattr(self, "extra_args"):
93+
extra_args = self.extra_args
94+
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
8695

87-
[sync_blocks(group) for group in node_groups]
88-
[sync_mempools(group) for group in node_groups]
96+
def run_test(self):
97+
raise NotImplementedError
8998

90-
def join_network(self):
91-
"""
92-
Join the (previously split) network halves together.
93-
"""
94-
connect_nodes_bi(self.nodes, 1, 2)
95-
self.sync_all()
99+
# Main function. This should not be overridden by the subclass test scripts.
96100

97101
def main(self):
98102

@@ -156,7 +160,7 @@ def main(self):
156160

157161
if not self.options.noshutdown:
158162
self.log.info("Stopping nodes")
159-
stop_nodes(self.nodes)
163+
self.stop_nodes()
160164
else:
161165
self.log.info("Note: bitcoinds were not stopped and may still be running")
162166

@@ -190,6 +194,45 @@ def main(self):
190194
logging.shutdown()
191195
sys.exit(self.TEST_EXIT_FAILED)
192196

197+
# Public helper methods. These can be accessed by the subclass test scripts.
198+
199+
def start_node(self, i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None):
200+
return start_node(i, dirname, extra_args, rpchost, timewait, binary, stderr)
201+
202+
def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
203+
return start_nodes(num_nodes, dirname, extra_args, rpchost, timewait, binary)
204+
205+
def stop_node(self, num_node):
206+
stop_node(self.nodes[num_node], num_node)
207+
208+
def stop_nodes(self):
209+
stop_nodes(self.nodes)
210+
211+
def split_network(self):
212+
"""
213+
Split the network of four nodes into nodes 0/1 and 2/3.
214+
"""
215+
disconnect_nodes(self.nodes[1], 2)
216+
disconnect_nodes(self.nodes[2], 1)
217+
self.sync_all([self.nodes[:2], self.nodes[2:]])
218+
219+
def join_network(self):
220+
"""
221+
Join the (previously split) network halves together.
222+
"""
223+
connect_nodes_bi(self.nodes, 1, 2)
224+
self.sync_all()
225+
226+
def sync_all(self, node_groups=None):
227+
if not node_groups:
228+
node_groups = [self.nodes]
229+
230+
for group in node_groups:
231+
sync_blocks(group)
232+
sync_mempools(group)
233+
234+
# Private helper methods. These should not be accessed by the subclass test scripts.
235+
193236
def _start_logging(self):
194237
# Add logger and logging handlers
195238
self.log = logging.getLogger('TestFramework')
@@ -218,6 +261,88 @@ def _start_logging(self):
218261
rpc_handler.setLevel(logging.DEBUG)
219262
rpc_logger.addHandler(rpc_handler)
220263

264+
def _initialize_chain(self, test_dir, num_nodes, cachedir):
265+
"""Initialize a pre-mined blockchain for use by the test.
266+
267+
Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
268+
Afterward, create num_nodes copies from the cache."""
269+
270+
assert num_nodes <= MAX_NODES
271+
create_cache = False
272+
for i in range(MAX_NODES):
273+
if not os.path.isdir(os.path.join(cachedir, 'node' + str(i))):
274+
create_cache = True
275+
break
276+
277+
if create_cache:
278+
self.log.debug("Creating data directories from cached datadir")
279+
280+
# find and delete old cache directories if any exist
281+
for i in range(MAX_NODES):
282+
if os.path.isdir(os.path.join(cachedir, "node" + str(i))):
283+
shutil.rmtree(os.path.join(cachedir, "node" + str(i)))
284+
285+
# Create cache directories, run bitcoinds:
286+
for i in range(MAX_NODES):
287+
datadir = initialize_datadir(cachedir, i)
288+
args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"]
289+
if i > 0:
290+
args.append("-connect=127.0.0.1:" + str(p2p_port(0)))
291+
bitcoind_processes[i] = subprocess.Popen(args)
292+
self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
293+
wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i)
294+
self.log.debug("initialize_chain: RPC successfully started")
295+
296+
self.nodes = []
297+
for i in range(MAX_NODES):
298+
try:
299+
self.nodes.append(get_rpc_proxy(rpc_url(i), i))
300+
except:
301+
self.log.exception("Error connecting to node %d" % i)
302+
sys.exit(1)
303+
304+
# Create a 200-block-long chain; each of the 4 first nodes
305+
# gets 25 mature blocks and 25 immature.
306+
# Note: To preserve compatibility with older versions of
307+
# initialize_chain, only 4 nodes will generate coins.
308+
#
309+
# blocks are created with timestamps 10 minutes apart
310+
# starting from 2010 minutes in the past
311+
enable_mocktime()
312+
block_time = get_mocktime() - (201 * 10 * 60)
313+
for i in range(2):
314+
for peer in range(4):
315+
for j in range(25):
316+
set_node_times(self.nodes, block_time)
317+
self.nodes[peer].generate(1)
318+
block_time += 10 * 60
319+
# Must sync before next peer starts generating blocks
320+
sync_blocks(self.nodes)
321+
322+
# Shut them down, and clean up cache directories:
323+
self.stop_nodes()
324+
self.nodes = []
325+
disable_mocktime()
326+
for i in range(MAX_NODES):
327+
os.remove(log_filename(cachedir, i, "debug.log"))
328+
os.remove(log_filename(cachedir, i, "db.log"))
329+
os.remove(log_filename(cachedir, i, "peers.dat"))
330+
os.remove(log_filename(cachedir, i, "fee_estimates.dat"))
331+
332+
for i in range(num_nodes):
333+
from_dir = os.path.join(cachedir, "node" + str(i))
334+
to_dir = os.path.join(test_dir, "node" + str(i))
335+
shutil.copytree(from_dir, to_dir)
336+
initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf
337+
338+
def _initialize_chain_clean(self, test_dir, num_nodes):
339+
"""Initialize empty blockchain for use by the test.
340+
341+
Create an empty blockchain and num_nodes wallets.
342+
Useful if a test case wants complete control over initialization."""
343+
for i in range(num_nodes):
344+
initialize_datadir(test_dir, i)
345+
221346
# Test framework for doing p2p comparison testing, which sets up some bitcoind
222347
# binaries:
223348
# 1 binary: test binary
@@ -240,7 +365,7 @@ def add_options(self, parser):
240365
help="bitcoind binary to use for reference nodes (if any)")
241366

242367
def setup_network(self):
243-
self.nodes = start_nodes(
368+
self.nodes = self.start_nodes(
244369
self.num_nodes, self.options.tmpdir,
245370
extra_args=[['-whitelist=127.0.0.1']] * self.num_nodes,
246371
binary=[self.options.testbinary] +

test/functional/test_framework/util.py

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -226,87 +226,6 @@ def wait_for_bitcoind_start(process, url, i):
226226
raise # unknown JSON RPC exception
227227
time.sleep(0.25)
228228

229-
def initialize_chain(test_dir, num_nodes, cachedir):
230-
"""
231-
Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
232-
Afterward, create num_nodes copies from the cache
233-
"""
234-
235-
assert num_nodes <= MAX_NODES
236-
create_cache = False
237-
for i in range(MAX_NODES):
238-
if not os.path.isdir(os.path.join(cachedir, 'node'+str(i))):
239-
create_cache = True
240-
break
241-
242-
if create_cache:
243-
logger.debug("Creating data directories from cached datadir")
244-
245-
#find and delete old cache directories if any exist
246-
for i in range(MAX_NODES):
247-
if os.path.isdir(os.path.join(cachedir,"node"+str(i))):
248-
shutil.rmtree(os.path.join(cachedir,"node"+str(i)))
249-
250-
# Create cache directories, run bitcoinds:
251-
for i in range(MAX_NODES):
252-
datadir=initialize_datadir(cachedir, i)
253-
args = [ os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ]
254-
if i > 0:
255-
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
256-
bitcoind_processes[i] = subprocess.Popen(args)
257-
logger.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
258-
wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i)
259-
logger.debug("initialize_chain: RPC successfully started")
260-
261-
rpcs = []
262-
for i in range(MAX_NODES):
263-
try:
264-
rpcs.append(get_rpc_proxy(rpc_url(i), i))
265-
except:
266-
sys.stderr.write("Error connecting to "+url+"\n")
267-
sys.exit(1)
268-
269-
# Create a 200-block-long chain; each of the 4 first nodes
270-
# gets 25 mature blocks and 25 immature.
271-
# Note: To preserve compatibility with older versions of
272-
# initialize_chain, only 4 nodes will generate coins.
273-
#
274-
# blocks are created with timestamps 10 minutes apart
275-
# starting from 2010 minutes in the past
276-
enable_mocktime()
277-
block_time = get_mocktime() - (201 * 10 * 60)
278-
for i in range(2):
279-
for peer in range(4):
280-
for j in range(25):
281-
set_node_times(rpcs, block_time)
282-
rpcs[peer].generate(1)
283-
block_time += 10*60
284-
# Must sync before next peer starts generating blocks
285-
sync_blocks(rpcs)
286-
287-
# Shut them down, and clean up cache directories:
288-
stop_nodes(rpcs)
289-
disable_mocktime()
290-
for i in range(MAX_NODES):
291-
os.remove(log_filename(cachedir, i, "debug.log"))
292-
os.remove(log_filename(cachedir, i, "db.log"))
293-
os.remove(log_filename(cachedir, i, "peers.dat"))
294-
os.remove(log_filename(cachedir, i, "fee_estimates.dat"))
295-
296-
for i in range(num_nodes):
297-
from_dir = os.path.join(cachedir, "node"+str(i))
298-
to_dir = os.path.join(test_dir, "node"+str(i))
299-
shutil.copytree(from_dir, to_dir)
300-
initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf
301-
302-
def initialize_chain_clean(test_dir, num_nodes):
303-
"""
304-
Create an empty blockchain and num_nodes wallets.
305-
Useful if a test case wants complete control over initialization.
306-
"""
307-
for i in range(num_nodes):
308-
datadir=initialize_datadir(test_dir, i)
309-
310229

311230
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None):
312231
"""

0 commit comments

Comments
 (0)