/ 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          for node in self.nodes:
 39              assert_equal(node.args[:len(node_argv)], node_argv)
 40  
 41      def set_cmd_args(self, node, args):
 42          """Set up node so it will be started through bitcoin wrapper command with specified arguments."""
 43          # Manually construct the `bitcoin node` command, similar to Binaries::node_argv()
 44          bitcoin_cmd = node.binaries.valgrind_cmd + [node.binaries.paths.bitcoin_bin]
 45          node.args = bitcoin_cmd + args + ["node"] + self.node_options[node.index]
 46  
 47      def test_args(self, cmd_args, node_args, expect_exe=None, expect_error=None):
 48          node = self.nodes[0]
 49          self.set_cmd_args(node, cmd_args)
 50          extra_args = node_args + ["-version"]
 51          if expect_error is not None:
 52              node.assert_start_raises_init_error(expected_msg=expect_error, extra_args=extra_args)
 53          else:
 54              assert expect_exe
 55              node.start(extra_args=extra_args)
 56              ret, out, err = get_node_output(node)
 57              try:
 58                  assert_equal(get_exe_name(out), expect_exe.encode())
 59                  assert_equal(err, b"")
 60              except Exception as e:
 61                  raise RuntimeError(f"Unexpected output from {node.args + extra_args}: {out=!r} {err=!r} {ret=!r}") from e
 62  
 63      def run_test(self):
 64          node = self.nodes[0]
 65  
 66          self.log.info("Ensure bitcoin node command invokes bitcoind by default")
 67          self.test_args([], [], expect_exe="bitcoind")
 68  
 69          self.log.info("Ensure bitcoin -M invokes bitcoind")
 70          self.test_args(["-M"], [], expect_exe="bitcoind")
 71  
 72          self.log.info("Ensure bitcoin -M does not accept -ipcbind")
 73          self.test_args(["-M"], ["-ipcbind=unix"], expect_error='Error: Error parsing command line arguments: Invalid parameter -ipcbind=unix')
 74  
 75          if self.is_ipc_compiled():
 76              self.log.info("Ensure bitcoin -m invokes bitcoin-node")
 77              self.test_args(["-m"], [], expect_exe="bitcoin-node")
 78  
 79              self.log.info("Ensure bitcoin -m does accept -ipcbind")
 80              self.test_args(["-m"], ["-ipcbind=unix"], expect_exe="bitcoin-node")
 81  
 82              self.log.info("Ensure bitcoin accepts -ipcbind by default")
 83              self.test_args([], ["-ipcbind=unix"], expect_exe="bitcoin-node")
 84  
 85              self.log.info("Ensure bitcoin recognizes -ipcbind in config file")
 86              append_config(node.datadir_path, ["ipcbind=unix"])
 87              self.test_args([], [], expect_exe="bitcoin-node")
 88  
 89  
 90  def get_node_output(node):
 91      ret = node.process.wait(timeout=60)
 92      node.stdout.seek(0)
 93      node.stderr.seek(0)
 94      out = node.stdout.read()
 95      err = node.stderr.read()
 96      node.stdout.close()
 97      node.stderr.close()
 98  
 99      # Clean up TestNode state
100      node.running = False
101      node.process = None
102      node.rpc_connected = False
103      node.rpc = None
104  
105      return ret, out, err
106  
107  
108  def get_exe_name(version_str):
109      """Get exe name from last word of first line of version string."""
110      return re.match(rb".*?(\S+)\s*?(?:\n|$)", version_str.strip()).group(1)
111  
112  
113  if __name__ == '__main__':
114      ToolBitcoinTest(__file__).main()