Skip to content

Commit 5f01275

Browse files
committed
Added documentation for test_wrapper submodule.
1 parent 6ada2e0 commit 5f01275

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

doc/test-wrapper.md

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
Test Wrapper for Interactive Environments
2+
=========================================
3+
4+
This document describes the usage of the `TestWrapper` submodule in the `test_framework` module of the functional test framework.
5+
6+
The TestWrapper submodule extends the `BitcoinTestFramework` functionality to external interactive environments for prototyping and educational purposes. Just like `BitcoinTestFramework`, the TestWrapper allows the user to:
7+
8+
* Manage regtest bitcoind subprocesses.
9+
* Access RPC interfaces of these bitcoind instances.
10+
* Log events to functional test logging utility.
11+
12+
The `TestWrapper` can be useful in interactive environments such as the Python3 command-line interpreter or [Jupyter](https://jupyter.org/) notebooks running a Python3 kernel, where is is necessary to extend the object lifetime of the underlying `BitcoinTestFramework` between user inputs.
13+
14+
## 1. Requirements
15+
16+
* Python3
17+
* `bitcoind` built in the same bitcoin repository as the TestWrapper.
18+
19+
## 2. Importing TestWrapper from the Bitcoin Core repository
20+
21+
We can import the TestWrapper by adding the path of the Bitcoin Core `test_framework` module to the beginning of the PATH variable, and then importing the `TestWrapper` class from the `test_wrapper` sub-package.
22+
23+
```
24+
>>> import sys
25+
>>> sys.path.insert(0, "/path/to/bitcoin/test/functional/test_framework")
26+
>>> from test_framework.test_wrapper import TestWrapper
27+
```
28+
<!-- >>> sys.path.insert(0, "/Users/jamesc/Dropbox/repos/bitcoin/test/functional/test_framework") -->
29+
30+
The following TestWrapper methods manage the lifetime of the underlying bitcoind processes and logging utilities.
31+
32+
* `TestWrapper.setup()`
33+
* `TestWrapper.shutdown()`
34+
35+
Exceptions which are raised can be forwarded to the TestWrapper for logging purposes.
36+
* `TestWrapper.handle_exception(e)`
37+
38+
The TestWrapper inherits all BitcoinTestFramework members and methods, such as:
39+
* `TestWrapper.nodes`
40+
* `TestWrapper.log.info("Custom log message")`
41+
* `TestWrapper.sync_all()`
42+
* `TestWrapper.success`
43+
* ...
44+
45+
The following sections demonstrate how to initialize, run and shutdown a TestWrapper object in an interactive Python3 environment.
46+
47+
## 3. Initializing a TestWrapper object
48+
49+
```
50+
>>> test = TestWrapper()
51+
>>> test.setup("num_nodes"=2)
52+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Initializing test directory /tmp/folders/../bitcoin_func_test_XXXXXXX
53+
```
54+
The TestWrapper supports all functional test parameters of the Bitcoin TestFramework class. The full set of argument keywords which can be used to initialize the TestWrapper can be found [here](../test/functional/test_framework/test_wrapper.py).
55+
56+
**Note: Running multiple instances of TestWrapper is not allowed.**
57+
This also ensures that logging remains consolidated in the same temporary folder. If you need more bitcoind nodes than set by default (1), simply increase the `num_nodes` parameter during setup.
58+
59+
```
60+
>>> test2 = TestWrapper()
61+
>>> test2.setup()
62+
TestWrapper is already running!
63+
```
64+
65+
## 4. Interacting with the TestWrapper
66+
67+
Unlike the BitcoinTestFramework class, the TestWrapper keeps the underlying Bitcoind subprocesses (nodes) and logging utilities running, until the user explicitly shuts down the TestWrapper object.
68+
69+
During the time between the `setup` and `shutdown` calls, all `bitcoind` node processes and BitcoinTestFramework convenience methods can be accessed interactively.
70+
71+
**Example: Mining a regtest chain**
72+
73+
By default, the TestWrapper nodes are initialized with a clean chain. This means that each node has at block height 0 after initialization of the TestWrapper.
74+
75+
```
76+
test.nodes[0].getblockchaininfo()["blocks"]
77+
>>> 0
78+
```
79+
80+
We now generate 101 regtest blocks, and send these to a wallet address owned by the first node.
81+
82+
```
83+
address = test.nodes[0].getnewaddress()
84+
test.nodes[0].generatetoaddress(101, address)
85+
>>> ['2b98dd0044aae6f1cca7f88a0acf366a4bfe053c7f7b00da3c0d115f03d67efb', ...
86+
```
87+
Since the nodes are initialized to connect to each other during `setup`, the second node will receive the newly mined blocks after they propagate.
88+
89+
```
90+
test.nodes[1].getblockchaininfo()["blocks"]
91+
>>> 101
92+
```
93+
The block rewards of the first block are now spendable by the wallet of the first node.
94+
95+
```
96+
>>> test.nodes[0].getbalance()
97+
Decimal('50.00000000')
98+
```
99+
100+
## 5. Safely shutting down the TestWrapper.
101+
102+
```
103+
>>> test.shutdown()
104+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Stopping nodes
105+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Cleaning up /var/folders/../bitcoin_func_test_XXXXXXX on exit
106+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Tests successful
107+
```
108+
To prevent the logs from being removed after a successful shutdown, simply set the `TestWrapper.options.nocleanup` member to `True`.
109+
```
110+
>>> test.options.nocleanup = True
111+
>>> test.shutdown()
112+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Stopping nodes
113+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Not cleaning up dir /tmp/folders/../bitcoin_func_test_XXXXXXX on exit
114+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Tests successful
115+
```
116+
117+
This allows consolidation of logs after shutting down the TestWrapper object by running:
118+
119+
* `/path/to/bitcoin/test/functional/combine_logs.py '/tmp/folders/../bitcoin_func_test_XXXXXXX'`
120+
121+
## 6. Handling exceptions with TestWrapper.
122+
123+
During the lifetime of the TestWrapper object, it is also possible to capture exceptions raised outside of the TestWrapper and forward them to the `TestWrapper.handle_exception()` method, so the exception type and traceback can be logged by the TestWrapper logging utility.
124+
125+
126+
**Note:** `BaseException` is the exception base class in Python and can be used to forward all Exception types to the TestWrapper.
127+
128+
```
129+
>>> try:
130+
... 3/0
131+
... except BaseException as e:
132+
... test.handle_exception(e)
133+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (ERROR): Unexpected exception caught during testing
134+
Traceback (most recent call last):
135+
File "<stdin>", line 2, in <module>
136+
ZeroDivisionError: division by zero
137+
```
138+
Passing an exception to TestWrapper will automatically set the success status to `FAILED`.
139+
```
140+
>>> test.success
141+
<TestStatus.FAILED: 2>
142+
```
143+
A `FAILED` status prevents the TestWrapper from removing the test folder upon shutdown.
144+
```
145+
>>> test.shutdown()
146+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Stopping nodes
147+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (WARNING): Not cleaning up dir /tmp/folders/../bitcoin_func_test_XXXXXXX
148+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (ERROR): Test failed. Test logging available at /tmp/folders/../bitcoin_func_test_XXXXXXX/test_framework.log
149+
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (ERROR): Hint: Call /path/to/bitcoin/test/functional/combine_logs.py '/tmp/folders/../bitcoin_func_test_XXXXXXX' to consolidate all logs
150+
```

0 commit comments

Comments
 (0)