test: Introduce ensure_for helper

This commit is contained in:
Fabian Jahr 2024-09-12 14:00:08 +02:00
parent e46bebb444
commit 16c87d91fd
No known key found for this signature in database
GPG key ID: F13D1E9D890798CD
7 changed files with 45 additions and 29 deletions

View file

@ -9,7 +9,6 @@ to a hash that has been compiled into bitcoind.
The assumeutxo value generated and used here is committed to in The assumeutxo value generated and used here is committed to in
`CRegTestParams::m_assumeutxo_data` in `src/kernel/chainparams.cpp`. `CRegTestParams::m_assumeutxo_data` in `src/kernel/chainparams.cpp`.
""" """
import time
from shutil import rmtree from shutil import rmtree
from dataclasses import dataclass from dataclasses import dataclass
@ -31,6 +30,7 @@ from test_framework.util import (
assert_approx, assert_approx,
assert_equal, assert_equal,
assert_raises_rpc_error, assert_raises_rpc_error,
ensure_for,
sha256sum_file, sha256sum_file,
try_rpc, try_rpc,
) )
@ -305,8 +305,7 @@ class AssumeutxoTest(BitcoinTestFramework):
# If it does request such blocks, the snapshot_node will ignore requests it cannot fulfill, causing the ibd_node # If it does request such blocks, the snapshot_node will ignore requests it cannot fulfill, causing the ibd_node
# to stall. This stall could last for up to 10 min, ultimately resulting in an abrupt disconnection due to the # to stall. This stall could last for up to 10 min, ultimately resulting in an abrupt disconnection due to the
# ibd_node's perceived unresponsiveness. # ibd_node's perceived unresponsiveness.
time.sleep(3) # Sleep here because we can't detect when a node avoids requesting blocks from other peer. ensure_for(duration=3, f=lambda: len(ibd_node.getpeerinfo()[0]['inflight']) == 0)
assert_equal(len(ibd_node.getpeerinfo()[0]['inflight']), 0)
# Now disconnect nodes and finish background chain sync # Now disconnect nodes and finish background chain sync
self.disconnect_nodes(ibd_node.index, snapshot_node.index) self.disconnect_nodes(ibd_node.index, snapshot_node.index)

View file

@ -19,7 +19,10 @@ import time
from test_framework.p2p import P2PInterface, msg_getheaders from test_framework.p2p import P2PInterface, msg_getheaders
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal from test_framework.util import (
assert_equal,
ensure_for,
)
# 2 hashes required per regtest block (with no difficulty adjustment) # 2 hashes required per regtest block (with no difficulty adjustment)
REGTEST_WORK_PER_BLOCK = 2 REGTEST_WORK_PER_BLOCK = 2
@ -58,18 +61,14 @@ class MinimumChainWorkTest(BitcoinTestFramework):
hashes = self.generate(self.nodes[0], num_blocks_to_generate, sync_fun=self.no_op) hashes = self.generate(self.nodes[0], num_blocks_to_generate, sync_fun=self.no_op)
self.log.info(f"Node0 current chain work: {self.nodes[0].getblockheader(hashes[-1])['chainwork']}") self.log.info(f"Node0 current chain work: {self.nodes[0].getblockheader(hashes[-1])['chainwork']}")
# Sleep a few seconds and verify that node2 didn't get any new blocks
# or headers. We sleep, rather than sync_blocks(node0, node1) because
# it's reasonable either way for node1 to get the blocks, or not get
# them (since they're below node1's minchainwork).
time.sleep(3)
self.log.info("Verifying node 2 has no more blocks than before") self.log.info("Verifying node 2 has no more blocks than before")
self.log.info(f"Blockcounts: {[n.getblockcount() for n in self.nodes]}") self.log.info(f"Blockcounts: {[n.getblockcount() for n in self.nodes]}")
# Node2 shouldn't have any new headers yet, because node1 should not # Node2 shouldn't have any new headers yet, because node1 should not
# have relayed anything. # have relayed anything.
assert_equal(len(self.nodes[2].getchaintips()), 1) # We wait 3 seconds, rather than sync_blocks(node0, node1) because
# it's reasonable either way for node1 to get the blocks, or not get
# them (since they're below node1's minchainwork).
ensure_for(duration=3, f=lambda: len(self.nodes[2].getchaintips()) == 1)
assert_equal(self.nodes[2].getchaintips()[0]['height'], 0) assert_equal(self.nodes[2].getchaintips()[0]['height'], 0)
assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash() assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash()
@ -81,8 +80,7 @@ class MinimumChainWorkTest(BitcoinTestFramework):
msg.locator.vHave = [int(self.nodes[2].getbestblockhash(), 16)] msg.locator.vHave = [int(self.nodes[2].getbestblockhash(), 16)]
msg.hashstop = 0 msg.hashstop = 0
peer.send_and_ping(msg) peer.send_and_ping(msg)
time.sleep(5) ensure_for(duration=5, f=lambda: "headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0)
assert "headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0
self.log.info("Generating one more block") self.log.info("Generating one more block")
self.generate(self.nodes[0], 1) self.generate(self.nodes[0], 1)

View file

@ -6,7 +6,6 @@
import os import os
import struct import struct
import tempfile import tempfile
from time import sleep
from io import BytesIO from io import BytesIO
from test_framework.address import ( from test_framework.address import (
@ -27,6 +26,7 @@ from test_framework.messages import (
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
assert_raises_rpc_error, assert_raises_rpc_error,
ensure_for,
p2p_port, p2p_port,
) )
from test_framework.wallet import ( from test_framework.wallet import (
@ -394,11 +394,10 @@ class ZMQTest (BitcoinTestFramework):
block_count = self.nodes[0].getblockcount() block_count = self.nodes[0].getblockcount()
best_hash = self.nodes[0].getbestblockhash() best_hash = self.nodes[0].getbestblockhash()
self.nodes[0].invalidateblock(best_hash) self.nodes[0].invalidateblock(best_hash)
sleep(2) # Bit of room to make sure transaction things happened
# Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective # Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
# of the time they were gathered. # of the time they were gathered.
assert self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num ensure_for(duration=2, f=lambda: self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num)
assert_equal((best_hash, "D", None), seq.receive_sequence()) assert_equal((best_hash, "D", None), seq.receive_sequence())
assert_equal((rbf_txid, "A", seq_num), seq.receive_sequence()) assert_equal((rbf_txid, "A", seq_num), seq.receive_sequence())

View file

@ -5,11 +5,12 @@
"""Test that the mempool ensures transaction delivery by periodically sending """Test that the mempool ensures transaction delivery by periodically sending
to peers until a GETDATA is received.""" to peers until a GETDATA is received."""
import time
from test_framework.p2p import P2PTxInvStore from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal from test_framework.util import (
assert_equal,
ensure_for,
)
from test_framework.wallet import MiniWallet from test_framework.wallet import MiniWallet
MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds
@ -83,8 +84,8 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
conn = node.add_p2p_connection(P2PTxInvStore()) conn = node.add_p2p_connection(P2PTxInvStore())
node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY) node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
time.sleep(2) # allow sufficient time for possibility of broadcast # allow sufficient time for possibility of broadcast
assert_equal(len(conn.get_invs()), 0) ensure_for(duration=2, f=lambda: len(conn.get_invs()) == 0)
self.disconnect_nodes(0, 1) self.disconnect_nodes(0, 1)
node.disconnect_p2ps() node.disconnect_p2ps()

View file

@ -5,7 +5,6 @@
"""Test segwit transactions and blocks on P2P network.""" """Test segwit transactions and blocks on P2P network."""
from decimal import Decimal from decimal import Decimal
import random import random
import time
from test_framework.blocktools import ( from test_framework.blocktools import (
WITNESS_COMMITMENT_HEADER, WITNESS_COMMITMENT_HEADER,
@ -83,8 +82,9 @@ from test_framework.script_util import (
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
softfork_active,
assert_raises_rpc_error, assert_raises_rpc_error,
ensure_for,
softfork_active,
) )
from test_framework.wallet import MiniWallet from test_framework.wallet import MiniWallet
from test_framework.wallet_util import generate_keypair from test_framework.wallet_util import generate_keypair
@ -184,8 +184,7 @@ class TestP2PConn(P2PInterface):
else: else:
self.wait_for_getdata([tx.sha256]) self.wait_for_getdata([tx.sha256])
else: else:
time.sleep(5) ensure_for(duration=5, f=lambda: not self.last_message.get("getdata"))
assert not self.last_message.get("getdata")
def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60): def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60):
with p2p_lock: with p2p_lock:

View file

@ -268,6 +268,27 @@ def satoshi_round(amount: Union[int, float, str], *, rounding: str) -> Decimal:
return Decimal(amount).quantize(SATOSHI_PRECISION, rounding=rounding) return Decimal(amount).quantize(SATOSHI_PRECISION, rounding=rounding)
def ensure_for(*, duration, f, check_interval=0.2):
"""Check if the predicate keeps returning True for duration.
check_interval can be used to configure the wait time between checks.
Setting check_interval to 0 will allow to have two checks: one in the
beginning and one after duration.
"""
# If check_interval is 0 or negative or larger than duration, we fall back
# to checking once in the beginning and once at the end of duration
if check_interval <= 0 or check_interval > duration:
check_interval = duration
time_end = time.time() + duration
predicate_source = "''''\n" + inspect.getsource(f) + "'''"
while True:
if not f():
raise AssertionError(f"Predicate {predicate_source} became false within {duration} seconds")
if time.time() > time_end:
return
time.sleep(check_interval)
def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0): def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
"""Sleep until the predicate resolves to be True. """Sleep until the predicate resolves to be True.

View file

@ -12,7 +12,6 @@ import os
import platform import platform
import shutil import shutil
import stat import stat
import time
from test_framework.authproxy import JSONRPCException from test_framework.authproxy import JSONRPCException
from test_framework.blocktools import COINBASE_MATURITY from test_framework.blocktools import COINBASE_MATURITY
@ -21,6 +20,7 @@ from test_framework.test_node import ErrorMatch
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
assert_raises_rpc_error, assert_raises_rpc_error,
ensure_for,
get_rpc_proxy, get_rpc_proxy,
) )
@ -373,8 +373,7 @@ class MultiWalletTest(BitcoinTestFramework):
w2.encryptwallet('test') w2.encryptwallet('test')
w2.walletpassphrase('test', 1) w2.walletpassphrase('test', 1)
w2.unloadwallet() w2.unloadwallet()
time.sleep(1.1) ensure_for(duration=1.1, f=lambda: 'w2' not in self.nodes[0].listwallets())
assert 'w2' not in self.nodes[0].listwallets()
# Successfully unload all wallets # Successfully unload all wallets
for wallet_name in self.nodes[0].listwallets(): for wallet_name in self.nodes[0].listwallets():