mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-07-12 13:35:09 +02:00
Merge bitcoin/bitcoin#30893: test: Introduce ensure_for helper
111465d72d
test: Remove unused attempts parameter from wait_until (Fabian Jahr)5468a23eb9
test: Add check_interval parameter to wait_until (Fabian Jahr)16c87d91fd
test: Introduce ensure_for helper (Fabian Jahr) Pull request description: A repeating pattern in the functional tests is that the test sleeps for a while to ensure that a certain condition is still true after some amount of time has elapsed. Most recently a new case of this was added in #30807. This PR here introduces an `ensure` helper to streamline this functionality. Some approach considerations: - It is possible to construct this by reusing `wait_until` and wrapping it in `try` internally. However, the logger output of the failing wait would still be printed which seems irritating. So I opted for simplified but similar internals to `wait_until`. - This implementation starts for a failure in the condition right away which has the nice side-effect that it might give feedback on a failure earlier than is currently the case. However, in some cases, it may be expected that the condition may still be false at the beginning and then turns true until time has run out, something that would work when the test sleeps without checking in a loop. I decided against this design (and even against adding it as an option) because such a test design seems like it would be racy either way. - I have also been going back and forth on naming. To me `ensure` works well but I am also not a native speaker, happy consider a different name if others don't think it's clear enough. ACKs for top commit: maflcko: re-ACK111465d72d
🍋 achow101: ACK111465d72d
tdb3: code review re ACK111465d72d
furszy: utACK111465d72d
Tree-SHA512: ce01a4f3531995375a6fbf01b27d51daa9d4c3d7cd10381be6e86ec5925d2965861000f7cb4796b8d40aabe3b64c4c27e2811270e4e3c9916689575b8ba4a2aa
This commit is contained in:
@ -585,13 +585,13 @@ class P2PInterface(P2PConnection):
|
||||
|
||||
# Connection helper methods
|
||||
|
||||
def wait_until(self, test_function_in, *, timeout=60, check_connected=True):
|
||||
def wait_until(self, test_function_in, *, timeout=60, check_connected=True, check_interval=0.05):
|
||||
def test_function():
|
||||
if check_connected:
|
||||
assert self.is_connected
|
||||
return test_function_in()
|
||||
|
||||
wait_until_helper_internal(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor)
|
||||
wait_until_helper_internal(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor, check_interval=check_interval)
|
||||
|
||||
def wait_for_connect(self, *, timeout=60):
|
||||
test_function = lambda: self.is_connected
|
||||
|
@ -787,8 +787,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
self.sync_blocks(nodes)
|
||||
self.sync_mempools(nodes)
|
||||
|
||||
def wait_until(self, test_function, timeout=60):
|
||||
return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor)
|
||||
def wait_until(self, test_function, timeout=60, check_interval=0.05):
|
||||
return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor, check_interval=check_interval)
|
||||
|
||||
# Private helper methods. These should not be accessed by the subclass test scripts.
|
||||
|
||||
|
@ -837,8 +837,8 @@ class TestNode():
|
||||
self.mocktime += seconds
|
||||
self.setmocktime(self.mocktime)
|
||||
|
||||
def wait_until(self, test_function, timeout=60):
|
||||
return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.timeout_factor)
|
||||
def wait_until(self, test_function, timeout=60, check_interval=0.05):
|
||||
return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.timeout_factor, check_interval=check_interval)
|
||||
|
||||
|
||||
class TestNodeCLIAttr:
|
||||
|
@ -268,7 +268,28 @@ def satoshi_round(amount: Union[int, float, str], *, rounding: str) -> Decimal:
|
||||
return Decimal(amount).quantize(SATOSHI_PRECISION, rounding=rounding)
|
||||
|
||||
|
||||
def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
|
||||
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, *, timeout=60, lock=None, timeout_factor=1.0, check_interval=0.05):
|
||||
"""Sleep until the predicate resolves to be True.
|
||||
|
||||
Warning: Note that this method is not recommended to be used in tests as it is
|
||||
@ -277,13 +298,10 @@ def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=floa
|
||||
properly scaled. Furthermore, `wait_until()` from `P2PInterface` class in
|
||||
`p2p.py` has a preset lock.
|
||||
"""
|
||||
if attempts == float('inf') and timeout == float('inf'):
|
||||
timeout = 60
|
||||
timeout = timeout * timeout_factor
|
||||
attempt = 0
|
||||
time_end = time.time() + timeout
|
||||
|
||||
while attempt < attempts and time.time() < time_end:
|
||||
while time.time() < time_end:
|
||||
if lock:
|
||||
with lock:
|
||||
if predicate():
|
||||
@ -291,17 +309,12 @@ def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=floa
|
||||
else:
|
||||
if predicate():
|
||||
return
|
||||
attempt += 1
|
||||
time.sleep(0.05)
|
||||
time.sleep(check_interval)
|
||||
|
||||
# Print the cause of the timeout
|
||||
predicate_source = "''''\n" + inspect.getsource(predicate) + "'''"
|
||||
logger.error("wait_until() failed. Predicate: {}".format(predicate_source))
|
||||
if attempt >= attempts:
|
||||
raise AssertionError("Predicate {} not true after {} attempts".format(predicate_source, attempts))
|
||||
elif time.time() >= time_end:
|
||||
raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout))
|
||||
raise RuntimeError('Unreachable')
|
||||
raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout))
|
||||
|
||||
|
||||
def sha256sum_file(filename):
|
||||
|
Reference in New Issue
Block a user