/ test / functional / tool_bitcoin.py
tool_bitcoin.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) The Bitcoin Core developers
  3  # Distributed under the MIT software license, see the accompanying
  4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5  """Test the bitcoin wrapper tool."""
  6  from test_framework.test_framework import (
  7      BitcoinTestFramework,
  8      SkipTest,
  9  )
 10  from test_framework.util import (
 11      append_config,
 12      assert_equal,
 13  )
 14  
 15  import platform
 16  import re
 17  
 18  
 19  class ToolBitcoinTest(BitcoinTestFramework):
 20      def set_test_params(self):
 21          self.setup_clean_chain = True
 22          self.num_nodes = 1
 23  
 24      def skip_test_if_missing_module(self):
 25          # Skip test on windows because currently when `bitcoin node -version` is
 26          # run on windows, python doesn't capture output from the child
 27          # `bitcoind` and `bitcoin-node` process started with _wexecvp, and
 28          # stdout/stderr are always empty. See
 29          # https://github.com/bitcoin/bitcoin/pull/33229#issuecomment-3265524908
 30          if platform.system() == "Windows":
 31              raise SkipTest("Test does not currently work on windows")
 32  
 33      def setup_network(self):
 34          """Set up nodes normally, but save a copy of their arguments before starting them."""
 35          self.add_nodes(self.num_nodes, self.extra_args)
 36          node_argv = self.get_binaries().node_argv()
 37          self.node_options = [node.args[len(node_argv):] for node in self.nodes]
 38          assert all(node.args[:len(node_argv)] == node_argv for node in self.nodes)
 39  
 40      def set_cmd_args(self, node, args):
 41          """Set up node so it will be started through bitcoin wrapper command with specified arguments."""
 42          # Manually construct the `bitcoin node` command, similar to Binaries::node_argv()
 43          bitcoin_cmd = node.binaries.valgrind_cmd + [node.binaries.paths.bitcoin_bin]
 44          node.args = bitcoin_cmd + args + ["node"] + self.node_options[node.index]
 45  
 46      def test_args(self, cmd_args, node_args, expect_exe=None, expect_error=None):
 47          node = self.nodes[0]
 48          self.set_cmd_args(node, cmd_args)
 49          extra_args = node_args + ["-version"]
 50          if expect_error is not None:
 51              node.assert_start_raises_init_error(expected_msg=expect_error, extra_args=extra_args)
 52          else:
 53              assert expect_exe
 54              node.start(extra_args=extra_args)
 55              ret, out, err = get_node_output(node)
 56              try:
 57                  assert_equal(get_exe_name(out), expect_exe.encode())
 58                  assert_equal(err, b"")
 59              except Exception as e:
 60                  raise RuntimeError(f"Unexpected output from {node.args + extra_args}: {out=!r} {err=!r} {ret=!r}") from e
 61  
 62      def run_test(self):
 63          node = self.nodes[0]
 64  
 65          self.log.info("Ensure bitcoin node command invokes bitcoind by default")
 66          self.test_args([], [], expect_exe="bitcoind")
 67  
 68          self.log.info("Ensure bitcoin -M invokes bitcoind")
 69          self.test_args(["-M"], [], expect_exe="bitcoind")
 70  
 71          self.log.info("Ensure bitcoin -M does not accept -ipcbind")
 72          self.test_args(["-M"], ["-ipcbind=unix"], expect_error='Error: Error parsing command line arguments: Invalid parameter -ipcbind=unix')
 73  
 74          if self.is_ipc_compiled():
 75              self.log.info("Ensure bitcoin -m invokes bitcoin-node")
 76              self.test_args(["-m"], [], expect_exe="bitcoin-node")
 77  
 78              self.log.info("Ensure bitcoin -m does accept -ipcbind")
 79              self.test_args(["-m"], ["-ipcbind=unix"], expect_exe="bitcoin-node")
 80  
 81              self.log.info("Ensure bitcoin accepts -ipcbind by default")
 82              self.test_args([], ["-ipcbind=unix"], expect_exe="bitcoin-node")
 83  
 84              self.log.info("Ensure bitcoin recognizes -ipcbind in config file")
 85              append_config(node.datadir_path, ["ipcbind=unix"])
 86              self.test_args([], [], expect_exe="bitcoin-node")
 87  
 88  
 89  def get_node_output(node):
 90      ret = node.process.wait(timeout=60)
 91      node.stdout.seek(0)
 92      node.stderr.seek(0)
 93      out = node.stdout.read()
 94      err = node.stderr.read()
 95      node.stdout.close()
 96      node.stderr.close()
 97  
 98      # Clean up TestNode state
 99      node.running = False
100      node.process = None
101      node.rpc_connected = False
102      node.rpc = None
103  
104      return ret, out, err
105  
106  
107  def get_exe_name(version_str):
108      """Get exe name from last word of first line of version string."""
109      return re.match(rb".*?(\S+)\s*?(?:\n|$)", version_str.strip()).group(1)
110  
111  
112  if __name__ == '__main__':
113      ToolBitcoinTest(__file__).main()