Skip to content

Commit ce46000

Browse files
committed
Merge bitcoin/bitcoin#32509: qa: feature_framework_startup_failures.py fixes & improvements (#30660 follow-up)
bf950c4 qa: Improve suppressed errors output (Hodlinator) 075352e qa: assert_raises_message() - search in str(e) (Hodlinator) bd8ebbc qa: Make --timeout-factor=0 result in a smaller factor (Hodlinator) d8f05e7 qa: Fix dormant bug caused by multiple --tmpdir (Hodlinator) Pull request description: * Handle multiple `--tmpdir` args properly. * Handle `--timeout-factor=0` properly (fixes #32506). * Improve readability of expected error message (`assert_raises_message()`). * Make suppressed error output less confusing (`wait_for_rpc_connection()`). ACKs for top commit: i-am-yuvi: re-ACK bf950c4 ismaelsadeeq: Code review and tested ACK bf950c4 achow101: ACK bf950c4 janb84: LGTM ACK bitcoin/bitcoin@bf950c4 Tree-SHA512: 8993f53e962231adef9cad92594dc6a8b8e979b1571d1381cd0fffa1929833b2163c68c03b6a6e29995006a3c3121822ec25ac7725e71ccdab8876ac24aae86c
2 parents d21612f + bf950c4 commit ce46000

File tree

4 files changed

+21
-23
lines changed

4 files changed

+21
-23
lines changed

test/functional/feature_framework_startup_failures.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,11 @@ def setup_network(self):
3636
# Launches a child test process that runs this same file, but instantiates
3737
# a child test. Verifies that it raises only the expected exception, once.
3838
def _verify_startup_failure(self, test, internal_args, expected_exception):
39-
# Inherit args from parent, only modifying tmpdir so children don't fail
40-
# as a cause of colliding with the parent dir.
41-
parent_args = sys.argv.copy()
39+
# Inherit sys.argv from parent, only overriding tmpdir to a subdirectory
40+
# so children don't fail due to colliding with the parent dir.
4241
assert self.options.tmpdir, "Framework should always set tmpdir."
43-
i, path = next(((i, m[1]) for i, arg in enumerate(parent_args) if (m := re.match(r'--tm(?:p(?:d(?:ir?)?)?)?=(.+)', arg))),
44-
(len(parent_args), self.options.tmpdir))
4542
subdir = md5(expected_exception.encode('utf-8')).hexdigest()[:8]
46-
parent_args[i:i + 1] = [f"--tmpdir={path}/{subdir}"]
47-
args = [sys.executable] + parent_args + [f"--internal_test={test.__name__}"] + internal_args
43+
args = [sys.executable] + sys.argv + [f"--tmpdir={self.options.tmpdir}/{subdir}", f"--internal_test={test.__name__}"] + internal_args
4844

4945
try:
5046
# The subprocess encoding argument gives different results for e.output
@@ -73,10 +69,10 @@ def run_test(self):
7369
self.log.info("Verifying _verify_startup_failure() functionality (self-check).")
7470
assert_raises_message(
7571
AssertionError,
76-
("Child test didn't contain (only) expected errors:\n" +
77-
linesep.join(["Found 0/1 tracebacks - expecting exactly one with no knock-on exceptions.",
78-
"Found 0/1 occurrences of the specific exception: NonExistentError",
79-
"Found 0/1 test failure output messages."])).encode("unicode_escape").decode("utf-8"),
72+
( "Child test didn't contain (only) expected errors:\n"
73+
f"Found 0/1 tracebacks - expecting exactly one with no knock-on exceptions.{linesep}"
74+
f"Found 0/1 occurrences of the specific exception: NonExistentError{linesep}"
75+
"Found 0/1 test failure output messages."),
8076
self._verify_startup_failure,
8177
TestSuccess, [],
8278
"NonExistentError",
@@ -93,7 +89,7 @@ def run_test(self):
9389
self.log.info("Verifying inability to connect to bitcoind's RPC interface due to wrong port results in one exception containing at least one OSError.")
9490
self._verify_startup_failure(
9591
TestWrongRpcPortStartupFailure, [f"--internal_node_start_duration={node_start_duration}"],
96-
r"AssertionError: \[node 0\] Unable to connect to bitcoind after \d+s \(ignored errors: {[^}]*'OSError \w+'?: \d+[^}]*}, latest error: \w+\([^)]+\)\)"
92+
r"AssertionError: \[node 0\] Unable to connect to bitcoind after \d+s \(ignored errors: {[^}]*'OSError \w+'?: \d+[^}]*}, latest: '[\w ]+'/\w+\([^)]+\)\)"
9793
)
9894

9995
self.log.info("Verifying startup failure due to invalid arg results in only one exception.")

test/functional/test_framework/test_framework.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def parse_args(self, test_file):
267267
parser.add_argument("-f", "--fff", help="a dummy argument to fool ipython", default="1")
268268
self.options = parser.parse_args()
269269
if self.options.timeout_factor == 0:
270-
self.options.timeout_factor = 99999
270+
self.options.timeout_factor = 999
271271
self.options.timeout_factor = self.options.timeout_factor or (4 if self.options.valgrind else 1)
272272
self.options.previous_releases_path = previous_releases_path
273273

test/functional/test_framework/test_node.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,13 @@ def wait_for_rpc_connection(self, *, wait_for_import=True):
262262
"""Sets up an RPC connection to the bitcoind process. Returns False if unable to connect."""
263263
# Poll at a rate of four times per second
264264
poll_per_s = 4
265+
265266
suppressed_errors = collections.defaultdict(int)
266-
latest_error = ""
267+
latest_error = None
268+
def suppress_error(category: str, e: Exception):
269+
suppressed_errors[category] += 1
270+
return (category, repr(e))
271+
267272
for _ in range(poll_per_s * self.rpc_timeout):
268273
if self.process.poll() is not None:
269274
# Attach abrupt shutdown error/s to the exception message
@@ -317,8 +322,7 @@ def wait_for_rpc_connection(self, *, wait_for_import=True):
317322
# -342 Service unavailable, could be starting up or shutting down
318323
if e.error['code'] not in [-28, -342]:
319324
raise # unknown JSON RPC exception
320-
suppressed_errors[f"JSONRPCException {e.error['code']}"] += 1
321-
latest_error = repr(e)
325+
latest_error = suppress_error(f"JSONRPCException {e.error['code']}", e)
322326
except OSError as e:
323327
error_num = e.errno
324328
# Work around issue where socket timeouts don't have errno set.
@@ -334,16 +338,14 @@ def wait_for_rpc_connection(self, *, wait_for_import=True):
334338
errno.ECONNREFUSED # Port not yet open?
335339
]:
336340
raise # unknown OS error
337-
suppressed_errors[f"OSError {errno.errorcode[error_num]}"] += 1
338-
latest_error = repr(e)
341+
latest_error = suppress_error(f"OSError {errno.errorcode[error_num]}", e)
339342
except ValueError as e:
340343
# Suppress if cookie file isn't generated yet and no rpcuser or rpcpassword; bitcoind may be starting.
341344
if "No RPC credentials" not in str(e):
342345
raise
343-
suppressed_errors["missing_credentials"] += 1
344-
latest_error = repr(e)
346+
latest_error = suppress_error("missing_credentials", e)
345347
time.sleep(1.0 / poll_per_s)
346-
self._raise_assertion_error(f"Unable to connect to bitcoind after {self.rpc_timeout}s (ignored errors: {str(dict(suppressed_errors))}, latest error: {latest_error})")
348+
self._raise_assertion_error(f"Unable to connect to bitcoind after {self.rpc_timeout}s (ignored errors: {dict(suppressed_errors)!s}{'' if latest_error is None else f', latest: {latest_error[0]!r}/{latest_error[1]}'})")
347349

348350
def wait_for_cookie_credentials(self):
349351
"""Ensures auth cookie credentials can be read, e.g. for testing CLI with -rpcwait before RPC connection is up."""

test/functional/test_framework/util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
102102
except JSONRPCException:
103103
raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
104104
except exc as e:
105-
if message is not None and message not in repr(e):
105+
if message is not None and message not in str(e):
106106
raise AssertionError("Expected substring not found in exception:\n"
107-
f"substring: '{message}'\nexception: {repr(e)}.")
107+
f"substring: '{message}'\nexception: {e!r}.")
108108
except Exception as e:
109109
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
110110
else:

0 commit comments

Comments
 (0)