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
`CRegTestParams::m_assumeutxo_data` in `src/kernel/chainparams.cpp`.
"""
import time
from shutil import rmtree
from dataclasses import dataclass
@ -31,6 +30,7 @@ from test_framework.util import (
assert_approx,
assert_equal,
assert_raises_rpc_error,
ensure_for,
sha256sum_file,
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
# 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.
time.sleep(3) # Sleep here because we can't detect when a node avoids requesting blocks from other peer.
assert_equal(len(ibd_node.getpeerinfo()[0]['inflight']), 0)
ensure_for(duration=3, f=lambda: len(ibd_node.getpeerinfo()[0]['inflight']) == 0)
# Now disconnect nodes and finish background chain sync
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.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)
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)
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(f"Blockcounts: {[n.getblockcount() for n in self.nodes]}")
# Node2 shouldn't have any new headers yet, because node1 should not
# 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 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.hashstop = 0
peer.send_and_ping(msg)
time.sleep(5)
assert "headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0
ensure_for(duration=5, f=lambda: "headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0)
self.log.info("Generating one more block")
self.generate(self.nodes[0], 1)

View file

@ -6,7 +6,6 @@
import os
import struct
import tempfile
from time import sleep
from io import BytesIO
from test_framework.address import (
@ -27,6 +26,7 @@ from test_framework.messages import (
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
ensure_for,
p2p_port,
)
from test_framework.wallet import (
@ -394,11 +394,10 @@ class ZMQTest (BitcoinTestFramework):
block_count = self.nodes[0].getblockcount()
best_hash = self.nodes[0].getbestblockhash()
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
# 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((rbf_txid, "A", seq_num), seq.receive_sequence())

View file

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

View file

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

View file

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