|
| 1 | +Test Shell for Interactive Environments |
| 2 | +========================================= |
| 3 | + |
| 4 | +This document describes how to use the `TestShell` submodule in the functional |
| 5 | +test suite. |
| 6 | + |
| 7 | +The `TestShell` submodule extends the `BitcoinTestFramework` functionality to |
| 8 | +external interactive environments for prototyping and educational purposes. Just |
| 9 | +like `BitcoinTestFramework`, the `TestShell` allows the user to: |
| 10 | + |
| 11 | +* Manage regtest bitcoind subprocesses. |
| 12 | +* Access RPC interfaces of the underlying bitcoind instances. |
| 13 | +* Log events to the functional test logging utility. |
| 14 | + |
| 15 | +The `TestShell` can be useful in interactive environments where it is necessary |
| 16 | +to extend the object lifetime of the underlying `BitcoinTestFramework` between |
| 17 | +user inputs. Such environments include the Python3 command line interpreter or |
| 18 | +[Jupyter](https://jupyter.org/) notebooks running a Python3 kernel. |
| 19 | + |
| 20 | +## 1. Requirements |
| 21 | + |
| 22 | +* Python3 |
| 23 | +* `bitcoind` built in the same repository as the `TestShell`. |
| 24 | + |
| 25 | +## 2. Importing `TestShell` from the Bitcoin Core repository |
| 26 | + |
| 27 | +We can import the `TestShell` by adding the path of the Bitcoin Core |
| 28 | +`test_framework` module to the beginning of the PATH variable, and then |
| 29 | +importing the `TestShell` class from the `test_shell` sub-package. |
| 30 | + |
| 31 | +``` |
| 32 | +>>> import sys |
| 33 | +>>> sys.path.insert(0, "/path/to/bitcoin/test/functional") |
| 34 | +>>> from test_framework.test_shell import `TestShell` |
| 35 | +``` |
| 36 | + |
| 37 | +The following `TestShell` methods manage the lifetime of the underlying bitcoind |
| 38 | +processes and logging utilities. |
| 39 | + |
| 40 | +* `TestShell.setup()` |
| 41 | +* `TestShell.shutdown()` |
| 42 | + |
| 43 | +The `TestShell` inherits all `BitcoinTestFramework` members and methods, such |
| 44 | +as: |
| 45 | +* `TestShell.nodes[index].rpc_method()` |
| 46 | +* `TestShell.log.info("Custom log message")` |
| 47 | + |
| 48 | +The following sections demonstrate how to initialize, run, and shut down a |
| 49 | +`TestShell` object. |
| 50 | + |
| 51 | +## 3. Initializing a `TestShell` object |
| 52 | + |
| 53 | +``` |
| 54 | +>>> test = TestShell() |
| 55 | +>>> test.setup(num_nodes=2, setup_clean_chain=True) |
| 56 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework (INFO): Initializing test directory /path/to/bitcoin_func_test_XXXXXXX |
| 57 | +``` |
| 58 | +The `TestShell` forwards all functional test parameters of the parent |
| 59 | +`BitcoinTestFramework` object. The full set of argument keywords which can be |
| 60 | +used to initialize the `TestShell` can be found in [section |
| 61 | +#6](#custom-testshell-parameters) of this document. |
| 62 | + |
| 63 | +**Note: Running multiple instances of `TestShell` is not allowed.** Running a |
| 64 | +single process also ensures that logging remains consolidated in the same |
| 65 | +temporary folder. If you need more bitcoind nodes than set by default (1), |
| 66 | +simply increase the `num_nodes` parameter during setup. |
| 67 | + |
| 68 | +``` |
| 69 | +>>> test2 = TestShell() |
| 70 | +>>> test2.setup() |
| 71 | +TestShell is already running! |
| 72 | +``` |
| 73 | + |
| 74 | +## 4. Interacting with the `TestShell` |
| 75 | + |
| 76 | +Unlike the `BitcoinTestFramework` class, the `TestShell` keeps the underlying |
| 77 | +Bitcoind subprocesses (nodes) and logging utilities running until the user |
| 78 | +explicitly shuts down the `TestShell` object. |
| 79 | + |
| 80 | +During the time between the `setup` and `shutdown` calls, all `bitcoind` node |
| 81 | +processes and `BitcoinTestFramework` convenience methods can be accessed |
| 82 | +interactively. |
| 83 | + |
| 84 | +**Example: Mining a regtest chain** |
| 85 | + |
| 86 | +By default, the `TestShell` nodes are initialized with a clean chain. This means |
| 87 | +that each node of the `TestShell` is initialized with a block height of 0. |
| 88 | + |
| 89 | +``` |
| 90 | +>>> test.nodes[0].getblockchaininfo()["blocks"] |
| 91 | +0 |
| 92 | +``` |
| 93 | + |
| 94 | +We now let the first node generate 101 regtest blocks, and direct the coinbase |
| 95 | +rewards to a wallet address owned by the mining node. |
| 96 | + |
| 97 | +``` |
| 98 | +>>> address = test.nodes[0].getnewaddress() |
| 99 | +>>> test.nodes[0].generatetoaddress(101, address) |
| 100 | +['2b98dd0044aae6f1cca7f88a0acf366a4bfe053c7f7b00da3c0d115f03d67efb', ... |
| 101 | +``` |
| 102 | +Since the two nodes are both initialized by default to establish an outbound |
| 103 | +connection to each other during `setup`, the second node's chain will include |
| 104 | +the mined blocks as soon as they propagate. |
| 105 | + |
| 106 | +``` |
| 107 | +>>> test.nodes[1].getblockchaininfo()["blocks"] |
| 108 | +101 |
| 109 | +``` |
| 110 | +The block rewards from the first block are now spendable by the wallet of the |
| 111 | +first node. |
| 112 | + |
| 113 | +``` |
| 114 | +>>> test.nodes[0].getbalance() |
| 115 | +Decimal('50.00000000') |
| 116 | +``` |
| 117 | + |
| 118 | +We can also log custom events to the logger. |
| 119 | + |
| 120 | +``` |
| 121 | +>>> test.nodes[0].log.info("Successfully mined regtest chain!") |
| 122 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework.node0 (INFO): Successfully mined regtest chain! |
| 123 | +``` |
| 124 | + |
| 125 | +**Note: Please also consider the functional test |
| 126 | +[readme](../test/functional/README.md), which provides an overview of the |
| 127 | +test-framework**. Modules such as |
| 128 | +[key.py](../test/functional/test_framework/key.py), |
| 129 | +[script.py](../test/functional/test_framework/script.py) and |
| 130 | +[messages.py](../test/functional/test_framework/messages.py) are particularly |
| 131 | +useful in constructing objects which can be passed to the bitcoind nodes managed |
| 132 | +by a running `TestShell` object. |
| 133 | + |
| 134 | +## 5. Shutting the `TestShell` down |
| 135 | + |
| 136 | +Shutting down the `TestShell` will safely tear down all running bitcoind |
| 137 | +instances and remove all temporary data and logging directories. |
| 138 | + |
| 139 | +``` |
| 140 | +>>> test.shutdown() |
| 141 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework (INFO): Stopping nodes |
| 142 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework (INFO): Cleaning up /path/to/bitcoin_func_test_XXXXXXX on exit |
| 143 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework (INFO): Tests successful |
| 144 | +``` |
| 145 | +To prevent the logs from being removed after a shutdown, simply set the |
| 146 | +`TestShell.options.nocleanup` member to `True`. |
| 147 | +``` |
| 148 | +>>> test.options.nocleanup = True |
| 149 | +>>> test.shutdown() |
| 150 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework (INFO): Stopping nodes |
| 151 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework (INFO): Not cleaning up dir /path/to/bitcoin_func_test_XXXXXXX on exit |
| 152 | +20XX-XX-XXTXX:XX:XX.XXXXXXX TestFramework (INFO): Tests successful |
| 153 | +``` |
| 154 | + |
| 155 | +The following utility consolidates logs from the bitcoind nodes and the |
| 156 | +underlying `BitcoinTestFramework`: |
| 157 | + |
| 158 | +* `/path/to/bitcoin/test/functional/combine_logs.py |
| 159 | + '/path/to/bitcoin_func_test_XXXXXXX'` |
| 160 | + |
| 161 | +## 6. Custom `TestShell` parameters |
| 162 | + |
| 163 | +The `TestShell` object initializes with the default settings inherited from the |
| 164 | +`BitcoinTestFramework` class. The user can override these in |
| 165 | +`TestShell.setup(key=value)`. |
| 166 | + |
| 167 | +**Note:** `TestShell.reset()` will reset test parameters to default values and |
| 168 | +can be called after the TestShell is shut down. |
| 169 | + |
| 170 | +| Test parameter key | Default Value | Description | |
| 171 | +|---|---|---| |
| 172 | +| `bind_to_localhost_only` | `True` | Binds bitcoind RPC services to `127.0.0.1` if set to `True`.| |
| 173 | +| `cachedir` | `"/path/to/bitcoin/test/cache"` | Sets the bitcoind datadir directory. | |
| 174 | +| `chain` | `"regtest"` | Sets the chain-type for the underlying test bitcoind processes. | |
| 175 | +| `configfile` | `"/path/to/bitcoin/test/config.ini"` | Sets the location of the test framework config file. | |
| 176 | +| `coveragedir` | `None` | Records bitcoind RPC test coverage into this directory if set. | |
| 177 | +| `loglevel` | `INFO` | Logs events at this level and higher. Can be set to `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL`. | |
| 178 | +| `nocleanup` | `False` | Cleans up temporary test directory if set to `True` during `shutdown`. | |
| 179 | +| `noshutdown` | `False` | Does not stop bitcoind instances after `shutdown` if set to `True`. | |
| 180 | +| `num_nodes` | `1` | Sets the number of initialized bitcoind processes. | |
| 181 | +| `perf` | False | Profiles running nodes with `perf` for the duration of the test if set to `True`. | |
| 182 | +| `rpc_timeout` | `60` | Sets the RPC server timeout for the underlying bitcoind processes. | |
| 183 | +| `setup_clean_chain` | `False` | Initializes an empty blockchain by default. A 199-block-long chain is initialized if set to `True`. | |
| 184 | +| `randomseed` | Random Integer | `TestShell.options.randomseed` is a member of `TestShell` which can be accessed during a test to seed a random generator. User can override default with a constant value for reproducible test runs. | |
| 185 | +| `supports_cli` | `False` | Whether the bitcoin-cli utility is compiled and available for the test. | |
| 186 | +| `tmpdir` | `"/var/folders/.../"` | Sets directory for test logs. Will be deleted upon a successful test run unless `nocleanup` is set to `True` | |
| 187 | +| `trace_rpc` | `False` | Logs all RPC calls if set to `True`. | |
| 188 | +| `usecli` | `False` | Uses the bitcoin-cli interface for all bitcoind commands instead of directly calling the RPC server. Requires `supports_cli`. | |
0 commit comments