Skip to content

Commit 16c87d9

Browse files
committed
test: Introduce ensure_for helper
1 parent e46bebb commit 16c87d9

File tree

7 files changed

+45
-29
lines changed

7 files changed

+45
-29
lines changed

test/functional/feature_assumeutxo.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
The assumeutxo value generated and used here is committed to in
1010
`CRegTestParams::m_assumeutxo_data` in `src/kernel/chainparams.cpp`.
1111
"""
12-
import time
1312
from shutil import rmtree
1413

1514
from dataclasses import dataclass
@@ -31,6 +30,7 @@
3130
assert_approx,
3231
assert_equal,
3332
assert_raises_rpc_error,
33+
ensure_for,
3434
sha256sum_file,
3535
try_rpc,
3636
)
@@ -305,8 +305,7 @@ def test_sync_from_assumeutxo_node(self, snapshot):
305305
# If it does request such blocks, the snapshot_node will ignore requests it cannot fulfill, causing the ibd_node
306306
# to stall. This stall could last for up to 10 min, ultimately resulting in an abrupt disconnection due to the
307307
# ibd_node's perceived unresponsiveness.
308-
time.sleep(3) # Sleep here because we can't detect when a node avoids requesting blocks from other peer.
309-
assert_equal(len(ibd_node.getpeerinfo()[0]['inflight']), 0)
308+
ensure_for(duration=3, f=lambda: len(ibd_node.getpeerinfo()[0]['inflight']) == 0)
310309

311310
# Now disconnect nodes and finish background chain sync
312311
self.disconnect_nodes(ibd_node.index, snapshot_node.index)

test/functional/feature_minchainwork.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919

2020
from test_framework.p2p import P2PInterface, msg_getheaders
2121
from test_framework.test_framework import BitcoinTestFramework
22-
from test_framework.util import assert_equal
22+
from test_framework.util import (
23+
assert_equal,
24+
ensure_for,
25+
)
2326

2427
# 2 hashes required per regtest block (with no difficulty adjustment)
2528
REGTEST_WORK_PER_BLOCK = 2
@@ -58,18 +61,14 @@ def run_test(self):
5861
hashes = self.generate(self.nodes[0], num_blocks_to_generate, sync_fun=self.no_op)
5962

6063
self.log.info(f"Node0 current chain work: {self.nodes[0].getblockheader(hashes[-1])['chainwork']}")
61-
62-
# Sleep a few seconds and verify that node2 didn't get any new blocks
63-
# or headers. We sleep, rather than sync_blocks(node0, node1) because
64-
# it's reasonable either way for node1 to get the blocks, or not get
65-
# them (since they're below node1's minchainwork).
66-
time.sleep(3)
67-
6864
self.log.info("Verifying node 2 has no more blocks than before")
6965
self.log.info(f"Blockcounts: {[n.getblockcount() for n in self.nodes]}")
7066
# Node2 shouldn't have any new headers yet, because node1 should not
7167
# have relayed anything.
72-
assert_equal(len(self.nodes[2].getchaintips()), 1)
68+
# We wait 3 seconds, rather than sync_blocks(node0, node1) because
69+
# it's reasonable either way for node1 to get the blocks, or not get
70+
# them (since they're below node1's minchainwork).
71+
ensure_for(duration=3, f=lambda: len(self.nodes[2].getchaintips()) == 1)
7372
assert_equal(self.nodes[2].getchaintips()[0]['height'], 0)
7473

7574
assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash()
@@ -81,8 +80,7 @@ def run_test(self):
8180
msg.locator.vHave = [int(self.nodes[2].getbestblockhash(), 16)]
8281
msg.hashstop = 0
8382
peer.send_and_ping(msg)
84-
time.sleep(5)
85-
assert "headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0
83+
ensure_for(duration=5, f=lambda: "headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0)
8684

8785
self.log.info("Generating one more block")
8886
self.generate(self.nodes[0], 1)

test/functional/interface_zmq.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import os
77
import struct
88
import tempfile
9-
from time import sleep
109
from io import BytesIO
1110

1211
from test_framework.address import (
@@ -27,6 +26,7 @@
2726
from test_framework.util import (
2827
assert_equal,
2928
assert_raises_rpc_error,
29+
ensure_for,
3030
p2p_port,
3131
)
3232
from test_framework.wallet import (
@@ -394,11 +394,10 @@ def test_sequence(self):
394394
block_count = self.nodes[0].getblockcount()
395395
best_hash = self.nodes[0].getbestblockhash()
396396
self.nodes[0].invalidateblock(best_hash)
397-
sleep(2) # Bit of room to make sure transaction things happened
398397

399398
# Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
400399
# of the time they were gathered.
401-
assert self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num
400+
ensure_for(duration=2, f=lambda: self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num)
402401

403402
assert_equal((best_hash, "D", None), seq.receive_sequence())
404403
assert_equal((rbf_txid, "A", seq_num), seq.receive_sequence())

test/functional/mempool_unbroadcast.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
"""Test that the mempool ensures transaction delivery by periodically sending
66
to peers until a GETDATA is received."""
77

8-
import time
9-
108
from test_framework.p2p import P2PTxInvStore
119
from test_framework.test_framework import BitcoinTestFramework
12-
from test_framework.util import assert_equal
10+
from test_framework.util import (
11+
assert_equal,
12+
ensure_for,
13+
)
1314
from test_framework.wallet import MiniWallet
1415

1516
MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds
@@ -83,8 +84,8 @@ def test_broadcast(self):
8384

8485
conn = node.add_p2p_connection(P2PTxInvStore())
8586
node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
86-
time.sleep(2) # allow sufficient time for possibility of broadcast
87-
assert_equal(len(conn.get_invs()), 0)
87+
# allow sufficient time for possibility of broadcast
88+
ensure_for(duration=2, f=lambda: len(conn.get_invs()) == 0)
8889

8990
self.disconnect_nodes(0, 1)
9091
node.disconnect_p2ps()

test/functional/p2p_segwit.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""Test segwit transactions and blocks on P2P network."""
66
from decimal import Decimal
77
import random
8-
import time
98

109
from test_framework.blocktools import (
1110
WITNESS_COMMITMENT_HEADER,
@@ -83,8 +82,9 @@
8382
from test_framework.test_framework import BitcoinTestFramework
8483
from test_framework.util import (
8584
assert_equal,
86-
softfork_active,
8785
assert_raises_rpc_error,
86+
ensure_for,
87+
softfork_active,
8888
)
8989
from test_framework.wallet import MiniWallet
9090
from test_framework.wallet_util import generate_keypair
@@ -184,8 +184,7 @@ def announce_tx_and_wait_for_getdata(self, tx, success=True, use_wtxid=False):
184184
else:
185185
self.wait_for_getdata([tx.sha256])
186186
else:
187-
time.sleep(5)
188-
assert not self.last_message.get("getdata")
187+
ensure_for(duration=5, f=lambda: not self.last_message.get("getdata"))
189188

190189
def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60):
191190
with p2p_lock:

test/functional/test_framework/util.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,27 @@ def satoshi_round(amount: Union[int, float, str], *, rounding: str) -> Decimal:
268268
return Decimal(amount).quantize(SATOSHI_PRECISION, rounding=rounding)
269269

270270

271+
def ensure_for(*, duration, f, check_interval=0.2):
272+
"""Check if the predicate keeps returning True for duration.
273+
274+
check_interval can be used to configure the wait time between checks.
275+
Setting check_interval to 0 will allow to have two checks: one in the
276+
beginning and one after duration.
277+
"""
278+
# If check_interval is 0 or negative or larger than duration, we fall back
279+
# to checking once in the beginning and once at the end of duration
280+
if check_interval <= 0 or check_interval > duration:
281+
check_interval = duration
282+
time_end = time.time() + duration
283+
predicate_source = "''''\n" + inspect.getsource(f) + "'''"
284+
while True:
285+
if not f():
286+
raise AssertionError(f"Predicate {predicate_source} became false within {duration} seconds")
287+
if time.time() > time_end:
288+
return
289+
time.sleep(check_interval)
290+
291+
271292
def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
272293
"""Sleep until the predicate resolves to be True.
273294

test/functional/wallet_multiwallet.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import platform
1313
import shutil
1414
import stat
15-
import time
1615

1716
from test_framework.authproxy import JSONRPCException
1817
from test_framework.blocktools import COINBASE_MATURITY
@@ -21,6 +20,7 @@
2120
from test_framework.util import (
2221
assert_equal,
2322
assert_raises_rpc_error,
23+
ensure_for,
2424
get_rpc_proxy,
2525
)
2626

@@ -373,8 +373,7 @@ def wallet_file(name):
373373
w2.encryptwallet('test')
374374
w2.walletpassphrase('test', 1)
375375
w2.unloadwallet()
376-
time.sleep(1.1)
377-
assert 'w2' not in self.nodes[0].listwallets()
376+
ensure_for(duration=1.1, f=lambda: 'w2' not in self.nodes[0].listwallets())
378377

379378
# Successfully unload all wallets
380379
for wallet_name in self.nodes[0].listwallets():

0 commit comments

Comments
 (0)