mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-10-11 03:53:22 +02:00
test: Add TestNode ipcbind option
With this change, tests can specify `self.extra_init = [{ipcbind: True}]` to start a node listening on an IPC socket, instead of needing to choose which node binary to invoke and what `self.extra_args=[["-ipcbind=..."]]` value to pass to it. The eliminates boilerplate code #30437 (interface_ipc_mining.py), #32297 (interface_ipc_cli.py), and #33201 (interface_ipc.py) previously needed in their test setup.
This commit is contained in:
committed by
Pieter Wuille
parent
3cceb60a71
commit
3cc9a06c8d
@@ -76,9 +76,9 @@ class Binaries:
|
|||||||
self.paths = paths
|
self.paths = paths
|
||||||
self.bin_dir = bin_dir
|
self.bin_dir = bin_dir
|
||||||
|
|
||||||
def node_argv(self):
|
def node_argv(self, **kwargs):
|
||||||
"Return argv array that should be used to invoke bitcoind"
|
"Return argv array that should be used to invoke bitcoind"
|
||||||
return self._argv("node", self.paths.bitcoind)
|
return self._argv("node", self.paths.bitcoind, **kwargs)
|
||||||
|
|
||||||
def rpc_argv(self):
|
def rpc_argv(self):
|
||||||
"Return argv array that should be used to invoke bitcoin-cli"
|
"Return argv array that should be used to invoke bitcoin-cli"
|
||||||
@@ -101,16 +101,19 @@ class Binaries:
|
|||||||
"Return argv array that should be used to invoke bitcoin-chainstate"
|
"Return argv array that should be used to invoke bitcoin-chainstate"
|
||||||
return self._argv("chainstate", self.paths.bitcoinchainstate)
|
return self._argv("chainstate", self.paths.bitcoinchainstate)
|
||||||
|
|
||||||
def _argv(self, command, bin_path):
|
def _argv(self, command, bin_path, need_ipc=False):
|
||||||
"""Return argv array that should be used to invoke the command. It
|
"""Return argv array that should be used to invoke the command. It
|
||||||
either uses the bitcoin wrapper executable (if BITCOIN_CMD is set), or
|
either uses the bitcoin wrapper executable (if BITCOIN_CMD is set or
|
||||||
the direct binary path (bitcoind, etc). When bin_dir is set (by tests
|
need_ipc is True), or the direct binary path (bitcoind, etc). When
|
||||||
calling binaries from previous releases) it always uses the direct
|
bin_dir is set (by tests calling binaries from previous releases) it
|
||||||
path."""
|
always uses the direct path."""
|
||||||
if self.bin_dir is not None:
|
if self.bin_dir is not None:
|
||||||
return [os.path.join(self.bin_dir, os.path.basename(bin_path))]
|
return [os.path.join(self.bin_dir, os.path.basename(bin_path))]
|
||||||
elif self.paths.bitcoin_cmd is not None:
|
elif self.paths.bitcoin_cmd is not None or need_ipc:
|
||||||
return self.paths.bitcoin_cmd + [command]
|
# If the current test needs IPC functionality, use the bitcoin
|
||||||
|
# wrapper binary and append -m so it calls multiprocess binaries.
|
||||||
|
bitcoin_cmd = self.paths.bitcoin_cmd or [self.paths.bitcoin_bin]
|
||||||
|
return bitcoin_cmd + (["-m"] if need_ipc else []) + [command]
|
||||||
else:
|
else:
|
||||||
return [bin_path]
|
return [bin_path]
|
||||||
|
|
||||||
@@ -158,6 +161,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
|||||||
self.noban_tx_relay: bool = False
|
self.noban_tx_relay: bool = False
|
||||||
self.nodes: list[TestNode] = []
|
self.nodes: list[TestNode] = []
|
||||||
self.extra_args = None
|
self.extra_args = None
|
||||||
|
self.extra_init = None
|
||||||
self.network_thread = None
|
self.network_thread = None
|
||||||
self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond
|
self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond
|
||||||
self.supports_cli = True
|
self.supports_cli = True
|
||||||
@@ -553,15 +557,15 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
|||||||
|
|
||||||
bin_dirs.append(bin_dir)
|
bin_dirs.append(bin_dir)
|
||||||
|
|
||||||
|
extra_init = [{}] * num_nodes if self.extra_init is None else self.extra_init # type: ignore[var-annotated]
|
||||||
|
assert_equal(len(extra_init), num_nodes)
|
||||||
assert_equal(len(extra_confs), num_nodes)
|
assert_equal(len(extra_confs), num_nodes)
|
||||||
assert_equal(len(extra_args), num_nodes)
|
assert_equal(len(extra_args), num_nodes)
|
||||||
assert_equal(len(versions), num_nodes)
|
assert_equal(len(versions), num_nodes)
|
||||||
assert_equal(len(bin_dirs), num_nodes)
|
assert_equal(len(bin_dirs), num_nodes)
|
||||||
for i in range(num_nodes):
|
for i in range(num_nodes):
|
||||||
args = list(extra_args[i])
|
args = list(extra_args[i])
|
||||||
test_node_i = TestNode(
|
init = dict(
|
||||||
i,
|
|
||||||
get_datadir_path(self.options.tmpdir, i),
|
|
||||||
chain=self.chain,
|
chain=self.chain,
|
||||||
rpchost=rpchost,
|
rpchost=rpchost,
|
||||||
timewait=self.rpc_timeout,
|
timewait=self.rpc_timeout,
|
||||||
@@ -578,6 +582,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
|||||||
v2transport=self.options.v2transport,
|
v2transport=self.options.v2transport,
|
||||||
uses_wallet=self.uses_wallet,
|
uses_wallet=self.uses_wallet,
|
||||||
)
|
)
|
||||||
|
init.update(extra_init[i])
|
||||||
|
test_node_i = TestNode(
|
||||||
|
i,
|
||||||
|
get_datadir_path(self.options.tmpdir, i),
|
||||||
|
**init)
|
||||||
self.nodes.append(test_node_i)
|
self.nodes.append(test_node_i)
|
||||||
if not test_node_i.version_is_at_least(170000):
|
if not test_node_i.version_is_at_least(170000):
|
||||||
# adjust conf for pre 17
|
# adjust conf for pre 17
|
||||||
|
@@ -11,6 +11,7 @@ from enum import Enum
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import pathlib
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -19,6 +20,7 @@ import time
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
import collections
|
import collections
|
||||||
import shlex
|
import shlex
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -52,6 +54,13 @@ CLI_MAX_ARG_SIZE = 131071 # many systems have a 128kb limit per arg (MAX_ARG_STR
|
|||||||
NULL_BLK_XOR_KEY = bytes([0] * NUM_XOR_BYTES)
|
NULL_BLK_XOR_KEY = bytes([0] * NUM_XOR_BYTES)
|
||||||
BITCOIN_PID_FILENAME_DEFAULT = "bitcoind.pid"
|
BITCOIN_PID_FILENAME_DEFAULT = "bitcoind.pid"
|
||||||
|
|
||||||
|
if sys.platform.startswith("linux"):
|
||||||
|
UNIX_PATH_MAX = 108 # includes the trailing NUL
|
||||||
|
elif sys.platform.startswith(("darwin", "freebsd", "netbsd", "openbsd")):
|
||||||
|
UNIX_PATH_MAX = 104
|
||||||
|
else: # safest portable value
|
||||||
|
UNIX_PATH_MAX = 92
|
||||||
|
|
||||||
|
|
||||||
class FailedToStartError(Exception):
|
class FailedToStartError(Exception):
|
||||||
"""Raised when a node fails to start correctly."""
|
"""Raised when a node fails to start correctly."""
|
||||||
@@ -77,7 +86,7 @@ class TestNode():
|
|||||||
To make things easier for the test writer, any unrecognised messages will
|
To make things easier for the test writer, any unrecognised messages will
|
||||||
be dispatched to the RPC connection."""
|
be dispatched to the RPC connection."""
|
||||||
|
|
||||||
def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, binaries, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, v2transport=False, uses_wallet=False):
|
def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, binaries, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, v2transport=False, uses_wallet=False, ipcbind=False):
|
||||||
"""
|
"""
|
||||||
Kwargs:
|
Kwargs:
|
||||||
start_perf (bool): If True, begin profiling the node with `perf` as soon as
|
start_perf (bool): If True, begin profiling the node with `perf` as soon as
|
||||||
@@ -109,7 +118,7 @@ class TestNode():
|
|||||||
# Configuration for logging is set as command-line args rather than in the bitcoin.conf file.
|
# Configuration for logging is set as command-line args rather than in the bitcoin.conf file.
|
||||||
# This means that starting a bitcoind using the temp dir to debug a failed test won't
|
# This means that starting a bitcoind using the temp dir to debug a failed test won't
|
||||||
# spam debug.log.
|
# spam debug.log.
|
||||||
self.args = self.binaries.node_argv() + [
|
self.args = self.binaries.node_argv(need_ipc=ipcbind) + [
|
||||||
f"-datadir={self.datadir_path}",
|
f"-datadir={self.datadir_path}",
|
||||||
"-logtimemicros",
|
"-logtimemicros",
|
||||||
"-debug",
|
"-debug",
|
||||||
@@ -121,6 +130,17 @@ class TestNode():
|
|||||||
if uses_wallet is not None and not uses_wallet:
|
if uses_wallet is not None and not uses_wallet:
|
||||||
self.args.append("-disablewallet")
|
self.args.append("-disablewallet")
|
||||||
|
|
||||||
|
self.ipc_tmp_dir = None
|
||||||
|
if ipcbind:
|
||||||
|
self.ipc_socket_path = self.chain_path / "node.sock"
|
||||||
|
if len(os.fsencode(self.ipc_socket_path)) < UNIX_PATH_MAX:
|
||||||
|
self.args.append("-ipcbind=unix")
|
||||||
|
else:
|
||||||
|
# Work around default CI path exceeding maximum socket path length.
|
||||||
|
self.ipc_tmp_dir = pathlib.Path(tempfile.mkdtemp(prefix="test-ipc-"))
|
||||||
|
self.ipc_socket_path = self.ipc_tmp_dir / "node.sock"
|
||||||
|
self.args.append(f"-ipcbind=unix:{self.ipc_socket_path}")
|
||||||
|
|
||||||
# Use valgrind, expect for previous release binaries
|
# Use valgrind, expect for previous release binaries
|
||||||
if use_valgrind and version is None:
|
if use_valgrind and version is None:
|
||||||
default_suppressions_file = Path(__file__).parents[3] / "contrib" / "valgrind.supp"
|
default_suppressions_file = Path(__file__).parents[3] / "contrib" / "valgrind.supp"
|
||||||
@@ -208,6 +228,9 @@ class TestNode():
|
|||||||
# this destructor is called.
|
# this destructor is called.
|
||||||
print(self._node_msg("Cleaning up leftover process"), file=sys.stderr)
|
print(self._node_msg("Cleaning up leftover process"), file=sys.stderr)
|
||||||
self.process.kill()
|
self.process.kill()
|
||||||
|
if self.ipc_tmp_dir:
|
||||||
|
print(self._node_msg(f"Cleaning up ipc directory {str(self.ipc_tmp_dir)!r}"))
|
||||||
|
shutil.rmtree(self.ipc_tmp_dir)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
"""Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
|
"""Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
|
||||||
|
Reference in New Issue
Block a user