forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor of BitcoinTestFramework main function. #26
Closed
jachiang
wants to merge
7
commits into
ChaincodeResidency:master
from
jachiang:master-testframework-main-refactor
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
8aa7738
Removal of network_event_loop when NetworkThread instance is closed.
jachiang 594364a
Refactored BitcoinTestFramework main method into setup and shutdown.
jachiang 3e55f72
Added removal of logging handlers.
jachiang 2c6fd51
Clear TestNode objects after shutdown.
jachiang 2aa26a6
Moved assert num_nodes is set into main().
jachiang e3455c7
Added TestWrapper class.
jachiang bf4fc41
Added documentation for test_wrapper submodule.
jachiang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
Test Wrapper for Interactive Environments | ||
========================================= | ||
|
||
This document describes the usage of the `TestWrapper` submodule in the `test_framework` module of the functional test framework. | ||
|
||
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: | ||
|
||
* Manage regtest bitcoind subprocesses. | ||
* Access RPC interfaces of these bitcoind instances. | ||
* Log events to functional test logging utility. | ||
|
||
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. | ||
|
||
## 1. Requirements | ||
|
||
* Python3 | ||
* `bitcoind` built in the same bitcoin repository as the TestWrapper. | ||
|
||
## 2. Importing TestWrapper from the Bitcoin Core repository | ||
|
||
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. | ||
|
||
``` | ||
>>> import sys | ||
>>> sys.path.insert(0, "/path/to/bitcoin/test/functional/test_framework") | ||
>>> from test_framework.test_wrapper import TestWrapper | ||
``` | ||
|
||
The following TestWrapper methods manage the lifetime of the underlying bitcoind processes and logging utilities. | ||
|
||
* `TestWrapper.setup()` | ||
* `TestWrapper.shutdown()` | ||
|
||
The TestWrapper inherits all BitcoinTestFramework members and methods, such as: | ||
* `TestWrapper.nodes[index].rpc_method()` | ||
* `TestWrapper.log.info("Custom log message")` | ||
|
||
The following sections demonstrate how to initialize, run and shutdown a TestWrapper object in an interactive Python3 environment. | ||
|
||
## 3. Initializing a TestWrapper object | ||
|
||
``` | ||
>>> test = TestWrapper() | ||
>>> test.setup("num_nodes"=2) | ||
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Initializing test directory /path/to/bitcoin_func_test_XXXXXXX | ||
``` | ||
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). | ||
|
||
**Note: Running multiple instances of TestWrapper is not allowed.** | ||
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. | ||
|
||
``` | ||
>>> test2 = TestWrapper() | ||
>>> test2.setup() | ||
TestWrapper is already running! | ||
``` | ||
|
||
## 4. Interacting with the TestWrapper | ||
|
||
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. | ||
|
||
During the time between the `setup` and `shutdown` calls, all `bitcoind` node processes and BitcoinTestFramework convenience methods can be accessed interactively. | ||
|
||
**Example: Mining a regtest chain** | ||
|
||
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. | ||
|
||
``` | ||
>>> test.nodes[0].getblockchaininfo()["blocks"] | ||
0 | ||
``` | ||
|
||
We now generate 101 regtest blocks, and send these to a wallet address owned by the first node. | ||
|
||
``` | ||
>>> address = test.nodes[0].getnewaddress() | ||
>>> test.nodes[0].generatetoaddress(101, address) | ||
['2b98dd0044aae6f1cca7f88a0acf366a4bfe053c7f7b00da3c0d115f03d67efb', ... | ||
``` | ||
Since the two nodes are each initialized to establish a connection to the other during `setup`, the second node will receive the newly mined blocks after they propagate. | ||
|
||
``` | ||
>>> test.nodes[1].getblockchaininfo()["blocks"] | ||
101 | ||
``` | ||
The block rewards of the first block are now spendable by the wallet of the first node. | ||
|
||
``` | ||
>>> test.nodes[0].getbalance() | ||
Decimal('50.00000000') | ||
``` | ||
|
||
We can also log custom events to the logger. | ||
|
||
``` | ||
>>> TestWrapper.log.info("Successfully mined regtest chain!") | ||
``` | ||
|
||
**Note: Please also consider the functional test [readme](../test/functional/README.md), which provides an overview of the test-framework**. Modules such as [key.py](../test/functional/test_framework/key.py), [script.py](../test/functional/test_framework/script.py) and [messages.py](../test/functional/test_framework/messages.py) are especially useful in constructing objects which can be passed to the bitcoind nodes managed by a running TestWrapper object. | ||
|
||
## 5. Shutting the TestWrapper down | ||
|
||
Shutting down the TestWrapper will safely tear down all running bitcoind instances and remove all temporary data and logging directories. | ||
|
||
``` | ||
>>> test.shutdown() | ||
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Stopping nodes | ||
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Cleaning up /path/to/bitcoin_func_test_XXXXXXX on exit | ||
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Tests successful | ||
``` | ||
To prevent the logs from being removed after a shutdown, simply set the `TestWrapper.options.nocleanup` member to `True`. | ||
``` | ||
>>> test.options.nocleanup = True | ||
>>> test.shutdown() | ||
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Stopping nodes | ||
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Not cleaning up dir /path/to/bitcoin_func_test_XXXXXXX on exit | ||
20XX-XX-24TXX:XX:XX.XXXXXXX TestFramework (INFO): Tests successful | ||
``` | ||
|
||
The following utility consolidates logs from the bitcoind nodes and the underlying BitcoinTestFramework: | ||
|
||
* `/path/to/bitcoin/test/functional/combine_logs.py '/path/to/bitcoin_func_test_XXXXXXX'` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright (c) 2014-2019 The Bitcoin Core developers | ||
# Distributed under the MIT software license, see the accompanying | ||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
"""Wrapper Class for BitcoinTestFramework. | ||
|
||
The TestWrapper class extends the BitcoinTestFramework | ||
rpc & daemon process management functionality to external | ||
python environments. | ||
|
||
It is a singleton class, which ensures that users only | ||
start a single TestWrapper at a time.""" | ||
|
||
import argparse | ||
from os import getpid | ||
from os.path import abspath, join | ||
|
||
from test_framework.test_framework import BitcoinTestFramework | ||
|
||
class TestWrapper: | ||
|
||
class __TestWrapper(BitcoinTestFramework): | ||
|
||
def set_test_params(self): | ||
pass | ||
|
||
def run_test(self): | ||
pass | ||
|
||
def setup(self, **kwargs): | ||
|
||
if self.running: | ||
print("TestWrapper is already running!") | ||
return | ||
|
||
self.setup_clean_chain = kwargs.get('setup_clean_chain',True) | ||
self.num_nodes = kwargs.get('num_nodes', 1) | ||
self.network_thread = kwargs.get('network_thread', None) | ||
self.rpc_timeout = kwargs.get('rpc_timeout', 60) | ||
self.supports_cli = kwargs.get('supports_cli', False) | ||
self.bind_to_localhost_only = kwargs.get('bind_to_localhost_only', True) | ||
|
||
self.options = argparse.Namespace | ||
self.options.nocleanup = kwargs.get('nocleanup', False) | ||
self.options.noshutdown = kwargs.get('noshutdown', False) | ||
self.options.cachedir = kwargs.get('cachedir', abspath(join(__file__ ,"../../../..") + "/test/cache")) | ||
self.options.tmpdir = kwargs.get('tmpdir', None) | ||
self.options.loglevel = kwargs.get('loglevel', 'INFO') | ||
self.options.trace_rpc = kwargs.get('trace_rpc', False) | ||
self.options.port_seed = kwargs.get('port_seed', getpid()) | ||
self.options.coveragedir = kwargs.get('coveragedir', None) | ||
self.options.configfile = kwargs.get('configfile', abspath(join(__file__ ,"../../../..") + "/test/config.ini")) | ||
self.options.pdbonfailure = kwargs.get('pdbonfailure', False) | ||
self.options.usecli = kwargs.get('usecli', False) | ||
self.options.perf = kwargs.get('perf', False) | ||
self.options.randomseed = kwargs.get('randomseed', None) | ||
|
||
self.options.bitcoind = kwargs.get('bitcoind', abspath(join(__file__ ,"../../../..") + "/src/bitcoind")) | ||
self.options.bitcoincli = kwargs.get('bitcoincli', None) | ||
|
||
super().setup() | ||
self.running = True | ||
|
||
def shutdown(self): | ||
if not self.running: | ||
print("TestWrapper is not running!") | ||
else: | ||
super().shutdown() | ||
self.running = False | ||
|
||
instance = None | ||
|
||
def __new__(cls): | ||
if not TestWrapper.instance: | ||
TestWrapper.instance = TestWrapper.__TestWrapper() | ||
TestWrapper.instance.running = False | ||
return TestWrapper.instance | ||
|
||
def __getattr__(self, name): | ||
return getattr(self.instance, name) | ||
|
||
def __setattr__(self, name, value): | ||
return setattr(self.instance, name, value) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these need to be flushed before being removed?