The /test/ (/test/) directory contains integration tests that test bitcoind
and its utilities in their entirety. It does not contain unit tests, which
can be found in /src/test (/src/test), /src/wallet/test (/src/wallet/test),
etc.
There are currently two sets of tests in the /test/ (/test/) directory:
/test/functional) which test the functionality of
bitcoind and bitcoin-qt by interacting with them through the RPC and P2P
interfaces./test/util) which tests the bitcoin utilities, currently only
bitcoin-tx.The util tests are run as part of make check target. The functional
tests are run by the Teamcity continuous build process whenever a diff is
created or updated on Phabricator. Both sets of tests can also be run locally.
Build for your system first. Be sure to enable wallet, utils and daemon when you configure. Tests will not run otherwise.
The ZMQ functional test requires a python ZMQ library. To install it:
sudo apt-get install python3-zmqpip3 install pyzmqIndividual tests can be run through the test_runner harness, eg:
test/functional/test_runner.py example_test
You can run any combination (incl. duplicates) of tests by calling:
test/functional/test_runner.py <testname1> <testname2> <testname3> ...
Run the regression test suite with:
test/functional/test_runner.py
Run all possible tests with
test/functional/test_runner.py --extended
By default, the test_runner will run many tests in parallel. To specify
how many jobs to run, append --jobs=n
The individual tests and the test_runner harness have many command-line
options. Run test/functional/test_runner.py -h to see them all.
Don’t wait longer than you need to identify issues. Save yourself time while debugging:
Use –failfast when running many tests
Stop on first test failure:
test/functional/test_runner.py --failfast
Use –timeout-factor when debugging timeouts
Don’t wait for the default timeout (60 seconds) for every failure when debugging an issue you know about. Use –timeout-factor while you iterate on a solution:
test/functional/test_runner.py --timeout-factor=0.3 abc_rpc_isfinal
Use –repeat when debugging intermittent issues
Run a test multiple times in parallel to look for intermittent issues.
test/functional/test_runner.py --repeat 100 p2p_leak
The P2P and RPC ports used by the bitcoind nodes-under-test are chosen to make conflicts with other processes unlikely. However, if there is another bitcoind process running on the system (perhaps from a previous test which hasn’t successfully killed all its bitcoind nodes), then there may be a port conflict which will cause the test to fail. It is recommended that you run the tests on a system where no other bitcoind processes are running.
On linux, the test framework will warn if there is another bitcoind process running when the tests are started.
If there are zombie bitcoind processes after test failure, you can kill them by running the following commands. Note that these commands will kill all bitcoind processes running on the system, so should not be used if any non-test bitcoind processes are being run.
killall bitcoind
or
pkill -9 bitcoind
A pre-mined blockchain with 200 blocks is generated the first time a functional test is run and is stored in test/cache. This speeds up test startup times since new blockchains don’t need to be generated for each test. However, the cache may get into a bad state, in which case tests will fail. If this happens, remove the cache directory (and make sure bitcoind processes are stopped as above):
rm -rf test/cache
killall bitcoind
The tests contain logging at different levels (debug, info, warning, etc). By default:
test_framework.log and no logs are output to the console.test_framework.log and INFO
level and above are output to the console.test_framework.log and bitcoind debug.logs will all be dumped
to the console to help troubleshooting.These log files can be located under the test data directory (which is always printed in the first line of test output):
<test data directory>/test_framework.log<test data directory>/node<node number>/regtest/debug.log.The node number identifies the relevant test node, starting from node0, which
corresponds to its position in the nodes list of the specific test,
e.g. self.nodes[0].
To change the level of logs output to the console, use the -l command line
argument.
test_framework.log and bitcoind debug.logs can be combined into a single
aggregate log by running the combine_logs.py script. The output can be plain
text, colorized text or html. For example:
test/functional/combine_logs.py -c <test data directory> | less -r
will pipe the colorized logs from the test into less.
The last failed test data directory can also be accessed via the ./lastfailure symlink to avoid copy-pasting a new directory on each iterated test run.
Use --tracerpc to trace out all the RPC calls and responses to the console.
For some tests (eg any that use submitblock to submit a full block over RPC),
this can result in a lot of screen output.
By default, the test data directory will be deleted after a successful run.
Use --nocleanup to leave the test data directory intact. The test data
directory is never deleted after a failed test.
A python debugger can be attached to tests at any point. Just add the line:
import pdb; pdb.set_trace()
anywhere in the test. You will then be able to inspect variables, as well as call methods that interact with the bitcoind nodes-under-test.
If further introspection of the bitcoind instances themselves becomes
necessary, this can be accomplished by first setting a pdb breakpoint
at an appropriate location, running the test to that point, then using
gdb (or lldb on macOS) to attach to the process and debug.
For instance, to attach to self.node[1] during a run you can get
the pid of the node within pdb.
(pdb) self.node[1].process.pid
Alternatively, you can find the pid by inspecting the temp folder for the specific test you are running. The path to that folder is printed at the beginning of every test run:
2017-06-27 14:13:56.686000 TestFramework (INFO): Initializing test directory /tmp/user/1000/testo9vsdjo3
Use the path to find the pid file in the temp folder:
cat /tmp/user/1000/testo9vsdjo3/node1/regtest/bitcoind.pid
Then you can use the pid to start gdb:
gdb /home/example/bitcoind <pid>
Note: gdb attach step may require sudo. To get rid of this, you can run:
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
Often while debugging rpc calls from functional tests, the test might reach timeout before
process can return a response. Use --timeout-factor=0 to disable all rpc timeouts for that particular
functional test. Ex: test/functional/test_runner.py wallet_hd --timeout-factor=0.
An easy way to profile node performance during functional tests is provided
for Linux platforms using perf.
Perf will sample the running node and will generate profile data in the node’s
datadir. The profile data can then be presented using perf report or a graphical
tool like hotspot.
There are two ways of invoking perf: one is to use the --perf flag when
running tests, which will profile each node during the entire test run: perf
begins to profile when the node starts and ends when it shuts down. The other
way is the use the profile_with_perf context manager, e.g.
with node.profile_with_perf("send-big-msgs"):
# Perform activity on the node you're interested in profiling, e.g.:
for _ in range(10000):
node.p2p.send_message(some_large_message)
To see useful textual output, run
perf report -i /path/to/datadir/send-big-msgs.perf.data.xxxx --stdio | c++filt | less
Python will issue a DeprecationWarning when a deprecated feature is
encountered in a script. By default, this warning message is ignored and not
displayed to the user. This behavior can be changed by setting the environment
variable PYTHONWARNINGS as follow:
PYTHONWARNINGS=default::DeprecationWarning
The warning message will now be printed to the sys.stderr output.
Util tests can be run locally by running test/util/bitcoin-util-test.py.
Use the -v option for verbose output.
The file test/functional/example_test.py (/test/functional/example_test.py) is a heavily commented
example of a test case that uses both the RPC and P2P interfaces. If you are
writing your first test, copy that file and modify to fit your needs.
Running test/functional/test_runner.py with the --coverage argument tracks which RPCs are
called by the tests and prints a report of uncovered RPCs in the summary. This
can be used (along with the --extended argument) to find out which RPCs we
don’t have test cases for.
set_test_params(), add_options() and setup_xxxx() methods at the top of
the subclass, then locally-defined helper methods, then the run_test() method.<area>_test.py, where area can be one of the following:
feature for tests for full features that aren’t wallet/mining/mempool, eg feature_rbf.pyinterface for tests for other interfaces (REST, ZMQ, etc), eg interface_rest.pymempool for tests for mempool behaviour, eg mempool_reorg.pymining for tests for mining features, eg mining_prioritisetransaction.pyp2p for tests that explicitly test the p2p interface, eg p2p_disconnect_ban.pyrpc for tests for individual RPC methods or features, eg rpc_listtransactions.pytool for tests for tools, eg tool_wallet.pywallet for tests for wallet features, eg wallet_keypool.pyrpc_decodescript.py, not rpc_decode_script.pytest in the name, eg interface_zmq.py, not interface_zmq_test.pyself.log.info('Create enough transactions to fill a block').
Logs make the test code easier to read and the test logic easier
to debug.self.num_nodes to the minimum number of nodes necessary for the test.
Having additional unrequired nodes adds to the execution time of the test as
well as memory/CPU/disk requirements (which is important when running tests in
parallel).self.setup_clean_chain variable in set_test_params() to True to
initialize an empty blockchain and start from the Genesis block, rather than
load a premined blockchain from cache with the default value of False. The
cached data directories contain a 200-block pre-mined blockchain with the
spendable mining rewards being split between four nodes. Each node has 25
mature block subsidies (25x50=1250 BTC) in its wallet. Using them is much more
efficient than mining blocks in your test.CBlock and CTransaction
don’t allow new attributes to be added to their objects at runtime like
typical Python objects allow. This helps prevent unpredictable side effects
from typographical errors or usage of the objects outside of their intended
purpose.Test writers may find it helpful to refer to the definitions for the RPC and P2P messages. These can be found in the following source files:
/src/rpc/* for RPCs/src/wallet/rpc* for wallet RPCsProcessMessage() in /src/net_processing.cpp for parsing P2P messagesP2Ps can be used to test specific P2P protocol behavior.
p2p.py (/test/functional/test_framework/p2p.py) contains test framework
p2p objects and messages.py (/test/functional/test_framework/messages.py)
contains all the definitions for objects passed over the network (CBlock,
CTransaction, etc, along with the network-level wrappers for them,
msg_block, msg_tx, etc).
P2P tests have two threads. One thread handles all network communication with the bitcoind(s) being tested in a callback-based event loop; the other implements the test logic.
P2PConnection is the class used to connect to a bitcoind. P2PInterface
contains the higher level logic for processing P2P payloads and connecting to
the Bitcoin Core node application logic. For custom behaviour, subclass the
P2PInterface object and override the callback methods.
P2PConnections can be used as such:
p2p_conn = node.add_p2p_connection(P2PInterface())
p2p_conn.send_and_ping(msg)
They can also be referenced by indexing into a TestNode’s p2ps list, which
contains the list of test framework p2p objects connected to itself
(it does not include any TestNodes):
node.p2ps[0].sync_with_ping()
More examples can be found in p2p_unrequested_blocks.py (/test/functional/p2p_unrequested_blocks.py),
p2p_compactblocks.py (/test/functional/p2p_compactblocks.py).
The TestShell class exposes the BitcoinTestFramework
functionality to interactive Python3 environments and can be used to prototype
tests. This may be especially useful in a REPL environment with session logging
utilities, such as
IPython.
The logs of such interactive sessions can later be adapted into permanent test
cases.
The following are useful modules for test developers. They are located in
test/functional/test_framework/ (/test/functional/test_framework).
/test/functional/test_framework/authproxy.py)Taken from the python-bitcoinrpc repository.
/test/functional/test_framework/test_framework.py)Base class for functional tests.
/test/functional/test_framework/util.py)Generally useful functions.
/test/functional/test_framework/p2p.py)Test objects for interacting with a bitcoind node over the p2p interface.
/test/functional/test_framework/script.py)Utilities for manipulating transaction scripts (originally from python-bitcoinlib)
/test/functional/test_framework/key.py)Test-only secp256k1 elliptic curve implementation
/test/functional/test_framework/blocktools.py)Helper functions for creating blocks and transactions.