From b23549f6e677a8e22953568704eac7ea0c2c1289 Mon Sep 17 00:00:00 2001
From: John Newbery <john@johnnewbery.com>
Date: Tue, 11 Jul 2017 13:01:44 -0400
Subject: [PATCH 1/2] [tests] add TestNodeCLI class for calling bitcoin-cli for
 a node

---
 test/functional/test_framework/test_node.py | 29 +++++++++++++++++++++
 test/functional/test_runner.py              |  1 +
 2 files changed, 30 insertions(+)

diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 66f89d43f..d3227f43c 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -4,8 +4,10 @@
 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
 """Class for bitcoind node under test"""
 
+import decimal
 import errno
 import http.client
+import json
 import logging
 import os
 import subprocess
@@ -45,6 +47,8 @@ class TestNode():
         self.extra_args = extra_args
         self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i]
 
+        self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir)
+
         self.running = False
         self.process = None
         self.rpc_connected = False
@@ -132,3 +136,28 @@ class TestNode():
             time.sleep(0.1)
         self.rpc = None
         self.rpc_connected = False
+
+class TestNodeCLI():
+    """Interface to bitcoin-cli for an individual node"""
+
+    def __init__(self, binary, datadir):
+        self.binary = binary
+        self.datadir = datadir
+
+    def __getattr__(self, command):
+        def dispatcher(*args, **kwargs):
+            return self.send_cli(command, *args, **kwargs)
+        return dispatcher
+
+    def send_cli(self, command, *args, **kwargs):
+        """Run bitcoin-cli command. Deserializes returned string as python object."""
+
+        pos_args = [str(arg) for arg in args]
+        named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()]
+        assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call"
+        p_args = [self.binary, "-datadir=" + self.datadir]
+        if named_args:
+            p_args += ["-named"]
+        p_args += [command] + pos_args + named_args
+        cli_output = subprocess.check_output(p_args, universal_newlines=True)
+        return json.loads(cli_output, parse_float=decimal.Decimal)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 93f180555..01236b607 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -279,6 +279,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
     #Set env vars
     if "BITCOIND" not in os.environ:
         os.environ["BITCOIND"] = build_dir + '/src/bitcoind' + exeext
+        os.environ["BITCOINCLI"] = build_dir + '/src/bitcoin-cli' + exeext
 
     tests_dir = src_dir + '/test/functional/'
 

From c6ec4358a797b7a11283238a0cf0b4531def9e92 Mon Sep 17 00:00:00 2001
From: John Newbery <john@johnnewbery.com>
Date: Tue, 11 Jul 2017 13:02:01 -0400
Subject: [PATCH 2/2] [tests] Add bitcoin_cli.py test script

---
 test/functional/bitcoin_cli.py | 26 ++++++++++++++++++++++++++
 test/functional/test_runner.py |  1 +
 2 files changed, 27 insertions(+)
 create mode 100755 test/functional/bitcoin_cli.py

diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py
new file mode 100755
index 000000000..103320209
--- /dev/null
+++ b/test/functional/bitcoin_cli.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test bitcoin-cli"""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class TestBitcoinCli(BitcoinTestFramework):
+
+    def __init__(self):
+        super().__init__()
+        self.setup_clean_chain = True
+        self.num_nodes = 1
+
+    def run_test(self):
+        """Main test logic"""
+
+        self.log.info("Compare responses from getinfo RPC and `bitcoin-cli getinfo`")
+        cli_get_info = self.nodes[0].cli.getinfo()
+        rpc_get_info = self.nodes[0].getinfo()
+
+        assert_equal(cli_get_info, rpc_get_info)
+
+if __name__ == '__main__':
+    TestBitcoinCli().main()
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 01236b607..fae4f66d7 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -81,6 +81,7 @@ BASE_SCRIPTS= [
     # vv Tests less than 30s vv
     'keypool-topup.py',
     'zmq_test.py',
+    'bitcoin_cli.py',
     'mempool_resurrect_test.py',
     'txn_doublespend.py --mineblock',
     'txn_clone.py',