sure, something like this, I think:
diff --git a/test/functional/feature_bind_port_discover.py b/test/functional/feature_bind_port_discover.py
index 86eca321e5e..778fce2fd23 100755
--- a/test/functional/feature_bind_port_discover.py
+++ b/test/functional/feature_bind_port_discover.py
@@ -72,24 +72,13 @@ class BindPortDiscoverTest(BitcoinTestFramework):
try:
self.start_nodes()
except FailedToStartError as e:
- for node in self.nodes:
- if node.running:
- if node.rpc_connected:
- node.stop_node(wait=node.rpc_timeout)
- else:
- node.process.kill()
- node.process.wait(timeout=node.rpc_timeout)
- node.process = None
- node.stdout.close()
- node.stderr.close()
- node.running = False
+ self.cleanup_partially_started_nodes()
if 'Unable to bind to ' in str(e):
raise SkipTest(
f'To run this test make sure that {ADDR1} and {ADDR2} '
'(routable addresses) are assigned to non-loopback '
'interfaces on this machine')
- else:
- raise e
+ raise
def run_test(self):
self.log.info(
diff --git a/test/functional/feature_bind_port_externalip.py b/test/functional/feature_bind_port_externalip.py
index a8e3abe851d..28250611e13 100755
--- a/test/functional/feature_bind_port_externalip.py
+++ b/test/functional/feature_bind_port_externalip.py
@@ -64,24 +64,13 @@ class BindPortExternalIPTest(BitcoinTestFramework):
try:
self.start_nodes()
except FailedToStartError as e:
- for node in self.nodes:
- if node.running:
- if node.rpc_connected:
- node.stop_node(wait=node.rpc_timeout)
- else:
- node.process.kill()
- node.process.wait(timeout=node.rpc_timeout)
- node.process = None
- node.stdout.close()
- node.stderr.close()
- node.running = False
+ self.cleanup_partially_started_nodes()
if 'Unable to bind to ' in str(e):
raise SkipTest(
f'To run this test make sure that {ADDR} '
'(routable address) is assigned to non-loopback '
'interface on this machine')
- else:
- raise e
+ raise
def run_test(self):
self.log.info("Test the proper port is used for -externalip=")
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index b809e68f4df..8cc7121f102 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -535,6 +535,27 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Wait for nodes to stop
node.wait_until_stopped()
+ def cleanup_partially_started_nodes(self):
+ """Tear down nodes left running after a failed start_nodes().
+
+ After start_nodes() raises (e.g. FailedToStartError), some nodes may be
+ RPC-connected, some may have a live process without RPC, and some may
+ already have exited. Stop the connected ones cleanly and force-kill the
+ rest so the framework's teardown can proceed.
+ """
+ for node in self.nodes:
+ if not node.running:
+ continue
+ if node.rpc_connected:
+ node.stop_node(wait=node.rpc_timeout)
+ else:
+ node.process.kill()
+ node.process.wait(timeout=node.rpc_timeout)
+ node.process = None
+ node.stdout.close()
+ node.stderr.close()
+ node.running = False
+
def restart_node(self, i, extra_args=None, clear_addrman=False, *, expected_stderr=''):
"""Stop and start a test node"""
self.stop_node(i, expected_stderr=expected_stderr)
I replaced raise e with raise, as in python3 this gets you a fuller traceback, but can drop that part if you don't think it's necessary