Skip to content

Commit 14a8c1a

Browse files
authored
Remove the boost test runner (#38)
* scripted-diff: Unroll `&&` conditions in tests Most test frameworks use a technique called expression decomposition, which captures the values of test expressions. The `&&` is `delete` when using value decomposition. ref: https://fekir.info/post/decomposing-an-expression/ -BEGIN VERIFY SCRIPT- set -eu MACRO_RE='BOOST_CHECK|BOOST_REQUIRE|BOOST_CHECK_MESSAGE|BOOST_REQUIRE_MESSAGE|BOOST_CHECK_NO_THROW|BOOST_REQUIRE_NO_THROW' FILES=$(git grep -lE "\b(${MACRO_RE})[[:space:]]*\(" -- \ ':(glob)src/test/**/*.cpp' ':(glob)src/test/**/*.h' \ ':(glob)src/test/*.cpp' ':(glob)src/test/*.h' \ ':(glob)src/ipc/test/**/*.cpp' ':(glob)src/ipc/test/**/*.h' \ ':(glob)src/ipc/test/*.cpp' ':(glob)src/ipc/test/*.h' 2>/dev/null || true) if [ -z "$FILES" ]; then echo "no matching files" exit 0 fi perl -i -0777 -pe ' use strict; use warnings; my $names = "BOOST_CHECK|BOOST_REQUIRE|BOOST_CHECK_MESSAGE|BOOST_REQUIRE_MESSAGE|BOOST_CHECK_NO_THROW|BOOST_REQUIRE_NO_THROW"; my $re = qr/\b($names)\s*\(/; my @DIGIT_SEP_PREV = (0) x 256; $DIGIT_SEP_PREV[ord($_)] = 1 for split //, "0123456789abcdefABCDEF" . chr(39); sub is_char_open { my ($s, $i) = @_; return 1 if $i == 0; return $DIGIT_SEP_PREV[ord(substr($$s, $i-1, 1))] ? 0 : 1; } sub close_paren { my ($s, $open) = @_; my ($depth, $i, $in_str, $in_char) = (0, $open, 0, 0); my $n = length($$s); while ($i < $n) { my $c = substr($$s, $i, 1); if ($in_str) { if ($c eq "\\") { $i += 2; next; } $in_str = 0 if $c eq q{"}; } elsif ($in_char) { if ($c eq "\\") { $i += 2; next; } $in_char = 0 if $c eq chr(39); } elsif ($c eq q{"}) { $in_str = 1; } elsif ($c eq chr(39) && is_char_open($s, $i)) { $in_char = 1; } elsif ($c =~ /[(\[{]/) { $depth++; } elsif ($c =~ /[)\]}]/) { $depth--; return $i if $depth == 0; } $i++; } return -1; } sub split_first_comma { my ($s) = @_; my ($depth, $i, $in_str, $in_char) = (0, 0, 0, 0); my $n = length($s); while ($i < $n) { my $c = substr($s, $i, 1); if ($in_str) { if ($c eq "\\") { $i += 2; next; } $in_str = 0 if $c eq q{"}; } elsif ($in_char) { if ($c eq "\\") { $i += 2; next; } $in_char = 0 if $c eq chr(39); } elsif ($c eq q{"}) { $in_str = 1; } elsif ($c eq chr(39) && is_char_open(\$s, $i)) { $in_char = 1; } elsif ($c =~ /[(\[{]/) { $depth++; } elsif ($c =~ /[)\]}]/) { $depth--; } elsif ($c eq "," && $depth == 0) { return (substr($s, 0, $i), substr($s, $i)); } $i++; } return ($s, ""); } sub top_level_and_positions { my ($e) = @_; my @ops; my ($depth, $i, $in_str, $in_char) = (0, 0, 0, 0); my $n = length($e); while ($i < $n) { my $c = substr($e, $i, 1); if ($in_str) { if ($c eq "\\") { $i += 2; next; } $in_str = 0 if $c eq q{"}; $i++; next; } elsif ($in_char) { if ($c eq "\\") { $i += 2; next; } $in_char = 0 if $c eq chr(39); $i++; next; } if ($c eq q{"}) { $in_str = 1; $i++; next; } if ($c eq chr(39) && is_char_open(\$e, $i)) { $in_char = 1; $i++; next; } if ($c =~ /[(\[{]/) { $depth++; $i++; next; } if ($c =~ /[)\]}]/) { $depth--; $i++; next; } if ($depth == 0 && $i + 1 < $n && substr($e, $i, 2) eq "&&") { push @ops, $i; $i += 2; next; } $i++; } return @ops; } my $text = $_; my $out = ""; my $cur = 0; while ($text =~ /$re/g) { my $macro = $1; my $m_start = $-[0]; my $p_open = $+[0] - 1; my $p_close = close_paren(\$text, $p_open); next if $p_close < 0; my $args = substr($text, $p_open + 1, $p_close - $p_open - 1); my ($expr_raw, $message) = split_first_comma($args); my $expr = $expr_raw; $expr =~ s/^\s+//; $expr =~ s/\s+$//; my @ats = top_level_and_positions($expr); next unless @ats; # Compose the replacement with original indentation. my $line_start = rindex(substr($text, 0, $m_start), "\n") + 1; my $indent = substr($text, $line_start, $m_start - $line_start); $indent =~ s/[^\s].*//s; my @Parts; my $last = 0; for my $p (@ats) { my $piece = substr($expr, $last, $p - $last); $piece =~ s/^\s+//; $piece =~ s/\s+$//; push @Parts, $piece; $last = $p + 2; } my $tail = substr($expr, $last); $tail =~ s/^\s+//; $tail =~ s/\s+$//; push @Parts, $tail; my $sep = ";\n" . $indent; my $replacement = join($sep, map { "$macro($_$message)" } @Parts); $out .= substr($text, $cur, $m_start - $cur); $out .= $replacement; $cur = $p_close + 1; # Resume the global match where the new content ends. pos($text) = $cur; } $out .= substr($text, $cur); $_ = $out; ' -- $FILES -END VERIFY SCRIPT- * scripted-diff: Wrap checks with `||` `||` is also `delete` when decomposing an expression. See previous commit message. ref: https://fekir.info/post/decomposing-an-expression/ -BEGIN VERIFY SCRIPT- set -eu MACRO_RE='BOOST_CHECK|BOOST_REQUIRE|BOOST_CHECK_MESSAGE|BOOST_REQUIRE_MESSAGE|BOOST_CHECK_NO_THROW|BOOST_REQUIRE_NO_THROW' FILES=$(git grep -lE "\b(${MACRO_RE})[[:space:]]*\(" -- \ ':(glob)src/test/**/*.cpp' ':(glob)src/test/**/*.h' \ ':(glob)src/test/*.cpp' ':(glob)src/test/*.h' \ ':(glob)src/ipc/test/**/*.cpp' ':(glob)src/ipc/test/**/*.h' \ ':(glob)src/ipc/test/*.cpp' ':(glob)src/ipc/test/*.h' 2>/dev/null || true) if [ -z "$FILES" ]; then echo "no matching files" exit 0 fi perl -i -0777 -pe ' use strict; use warnings; my $names = "BOOST_CHECK|BOOST_REQUIRE|BOOST_CHECK_MESSAGE|BOOST_REQUIRE_MESSAGE|BOOST_CHECK_NO_THROW|BOOST_REQUIRE_NO_THROW"; my $re = qr/\b($names)\s*\(/; my @DIGIT_SEP_PREV = (0) x 256; $DIGIT_SEP_PREV[ord($_)] = 1 for split //, "0123456789abcdefABCDEF" . chr(39); sub is_char_open { my ($s, $i) = @_; return 1 if $i == 0; return $DIGIT_SEP_PREV[ord(substr($$s, $i-1, 1))] ? 0 : 1; } sub close_paren { my ($s, $open) = @_; my ($depth, $i, $in_str, $in_char) = (0, $open, 0, 0); my $n = length($$s); while ($i < $n) { my $c = substr($$s, $i, 1); if ($in_str) { if ($c eq "\\") { $i += 2; next; } $in_str = 0 if $c eq q{"}; } elsif ($in_char) { if ($c eq "\\") { $i += 2; next; } $in_char = 0 if $c eq chr(39); } elsif ($c eq q{"}) { $in_str = 1; } elsif ($c eq chr(39) && is_char_open($s, $i)) { $in_char = 1; } elsif ($c =~ /[(\[{]/) { $depth++; } elsif ($c =~ /[)\]}]/) { $depth--; return $i if $depth == 0; } $i++; } return -1; } sub split_first_comma { my ($s) = @_; my ($depth, $i, $in_str, $in_char) = (0, 0, 0, 0); my $n = length($s); while ($i < $n) { my $c = substr($s, $i, 1); if ($in_str) { if ($c eq "\\") { $i += 2; next; } $in_str = 0 if $c eq q{"}; } elsif ($in_char) { if ($c eq "\\") { $i += 2; next; } $in_char = 0 if $c eq chr(39); } elsif ($c eq q{"}) { $in_str = 1; } elsif ($c eq chr(39) && is_char_open(\$s, $i)) { $in_char = 1; } elsif ($c =~ /[(\[{]/) { $depth++; } elsif ($c =~ /[)\]}]/) { $depth--; } elsif ($c eq "," && $depth == 0) { return (substr($s, 0, $i), substr($s, $i)); } $i++; } return ($s, ""); } sub has_top_level_or { my ($e) = @_; my ($depth, $i, $in_str, $in_char) = (0, 0, 0, 0); my $n = length($e); while ($i < $n) { my $c = substr($e, $i, 1); if ($in_str) { if ($c eq "\\") { $i += 2; next; } $in_str = 0 if $c eq q{"}; $i++; next; } elsif ($in_char) { if ($c eq "\\") { $i += 2; next; } $in_char = 0 if $c eq chr(39); $i++; next; } if ($c eq q{"}) { $in_str = 1; $i++; next; } if ($c eq chr(39) && is_char_open(\$e, $i)) { $in_char = 1; $i++; next; } if ($c =~ /[(\[{]/) { $depth++; $i++; next; } if ($c =~ /[)\]}]/) { $depth--; $i++; next; } if ($depth == 0 && $i + 1 < $n && substr($e, $i, 2) eq "||") { return 1; } $i++; } return 0; } my $text = $_; my $out = ""; my $cur = 0; while ($text =~ /$re/g) { my $macro = $1; my $m_start = $-[0]; my $p_open = $+[0] - 1; my $p_close = close_paren(\$text, $p_open); next if $p_close < 0; my $args = substr($text, $p_open + 1, $p_close - $p_open - 1); my ($expr_raw, $message) = split_first_comma($args); my $expr = $expr_raw; $expr =~ s/^\s+//; $expr =~ s/\s+$//; next unless has_top_level_or($expr); $out .= substr($text, $cur, $m_start - $cur); $out .= "$macro(($expr)$message)"; $cur = $p_close + 1; pos($text) = $cur; } $out .= substr($text, $cur); $_ = $out; ' -- $FILES -END VERIFY SCRIPT- * test: Add header-only test framework Adds src/test/util/framework.hpp as a lightweight Boost.Test replacement. This commit only introduces the header; build-system integration and call-site migration follow in subsequent commits. Includes: - `CHECK`, valid with any comparison operator, optional message - `REQUIRE`, valid with any comparison operator, optional message - `CHECK_EQUAL_RANGES`, better debugging for vectors - `THROW_*`, macros for checking throwing conditions - Info and warn messages * test: Integrate test framework Integrates src/test/util/framework.hpp into the build (CMake, main.cpp) and replaces the Boost.Test macros across the unit test suite via a scripted diff. * test: Enforce comparison of signed types at compile time * Remove remaining `BOOST` references
1 parent 6e7021e commit 14a8c1a

135 files changed

Lines changed: 9383 additions & 8634 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ci/test/00_setup_env.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ export TEST_RUNNER_TIMEOUT_FACTOR=${TEST_RUNNER_TIMEOUT_FACTOR:-40}
4444
export RUN_FUZZ_TESTS=${RUN_FUZZ_TESTS:-false}
4545

4646
# Randomize test order.
47-
# See https://www.boost.org/doc/libs/1_71_0/libs/test/doc/html/boost_test/utf_reference/rt_param_reference/random.html
48-
export BOOST_TEST_RANDOM=${BOOST_TEST_RANDOM:-1}
47+
export BITCOIN_TEST_RANDOM=${BITCOIN_TEST_RANDOM:-1}
4948
# See man 7 debconf
5049
export DEBIAN_FRONTEND=noninteractive
5150
export CCACHE_MAXSIZE=${CCACHE_MAXSIZE:-2G}

doc/developer-notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ code.
4949
naming style](#internal-interface-naming-style) for an exception to this
5050
convention.
5151

52-
- Test suite naming convention: The Boost test suite in file
52+
- Test suite naming convention: The test suite in file
5353
`src/test/foo_tests.cpp` should be named `foo_tests`. Test suite names
5454
must be unique.
5555

src/ipc/test/ipc_test.cpp

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
#include <kj/test.h>
2424
#include <stdexcept>
2525

26-
#include <boost/test/unit_test.hpp>
27-
26+
#include <test/util/framework.hpp>
2827
static_assert(ipc::capnp::messages::MAX_MONEY == MAX_MONEY);
2928
static_assert(ipc::capnp::messages::MAX_DOUBLE == std::numeric_limits<double>::max());
3029
static_assert(ipc::capnp::messages::DEFAULT_BLOCK_RESERVED_WEIGHT == DEFAULT_BLOCK_RESERVED_WEIGHT);
@@ -47,8 +46,8 @@ static std::string TempPath(std::string_view pattern)
4746
std::string temp{fs::PathToString(fs::path{fs::temp_directory_path()} / fs::PathFromString(std::string{pattern}))};
4847
temp.push_back('\0');
4948
int fd{mkstemp(temp.data())};
50-
BOOST_CHECK_GE(fd, 0);
51-
BOOST_CHECK_EQUAL(close(fd), 0);
49+
CHECK(fd >= 0);
50+
CHECK(close(fd) == 0);
5251
temp.resize(temp.size() - 1);
5352
fs::remove(fs::PathFromString(temp));
5453
return temp;
@@ -87,17 +86,17 @@ void IpcPipeTest()
8786
std::unique_ptr<mp::ProxyClient<gen::FooInterface>> foo{foo_promise.get_future().get()};
8887

8988
// Test: make sure arguments were sent and return value is received
90-
BOOST_CHECK_EQUAL(foo->add(1, 2), 3);
89+
CHECK(foo->add(1, 2) == 3);
9190

9291
COutPoint txout1{Txid::FromUint256(uint256{100}), 200};
9392
COutPoint txout2{foo->passOutPoint(txout1)};
94-
BOOST_CHECK(txout1 == txout2);
93+
CHECK((txout1 == txout2));
9594

9695
UniValue uni1{UniValue::VOBJ};
9796
uni1.pushKV("i", 1);
9897
uni1.pushKV("s", "two");
9998
UniValue uni2{foo->passUniValue(uni1)};
100-
BOOST_CHECK_EQUAL(uni1.write(), uni2.write());
99+
CHECK(uni1.write() == uni2.write());
101100

102101
CMutableTransaction mtx;
103102
mtx.version = 2;
@@ -106,15 +105,15 @@ void IpcPipeTest()
106105
mtx.vout.emplace_back(COIN, CScript());
107106
CTransactionRef tx1{MakeTransactionRef(mtx)};
108107
CTransactionRef tx2{foo->passTransaction(tx1)};
109-
BOOST_CHECK(*Assert(tx1) == *Assert(tx2));
108+
CHECK((*Assert(tx1) == *Assert(tx2)));
110109

111110
std::vector<char> vec1{'H', 'e', 'l', 'l', 'o'};
112111
std::vector<char> vec2{foo->passVectorChar(vec1)};
113-
BOOST_CHECK_EQUAL(std::string_view(vec1.begin(), vec1.end()), std::string_view(vec2.begin(), vec2.end()));
112+
CHECK(std::string_view(vec1.begin(), vec1.end()) == std::string_view(vec2.begin(), vec2.end()));
114113

115114
auto script1{CScript() << OP_11};
116115
auto script2{foo->passScript(script1)};
117-
BOOST_CHECK_EQUAL(HexStr(script1), HexStr(script2));
116+
CHECK(HexStr(script1) == HexStr(script2));
118117

119118
// Test cleanup: disconnect and join thread
120119
foo.reset();
@@ -125,7 +124,7 @@ void IpcPipeTest()
125124
void IpcSocketPairTest()
126125
{
127126
int fds[2];
128-
BOOST_CHECK_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, fds), 0);
127+
CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
129128
std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
130129
std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol()};
131130
std::promise<void> promise;
@@ -135,10 +134,10 @@ void IpcSocketPairTest()
135134
promise.get_future().wait();
136135
std::unique_ptr<interfaces::Init> remote_init{protocol->connect(fds[1], "test-connect")};
137136
std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
138-
BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
137+
CHECK(remote_echo->echo("echo test") == "echo test");
139138
remote_echo.reset();
140139
remote_init->stop();
141-
BOOST_CHECK(static_cast<TestInit*>(init.get())->stop_called.load());
140+
CHECK(static_cast<TestInit*>(init.get())->stop_called.load());
142141
remote_init.reset();
143142
thread.join();
144143
}
@@ -151,24 +150,24 @@ void IpcSocketTest(const fs::path& datadir)
151150
std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
152151

153152
std::string invalid_bind{"invalid:"};
154-
BOOST_CHECK_THROW(process->bind(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
155-
BOOST_CHECK_THROW(process->connect(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
153+
CHECK_THROWS_AS(process->bind(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
154+
CHECK_THROWS_AS(process->connect(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
156155

157156
auto bind_and_listen{[&](const std::string& bind_address) {
158157
std::string address{bind_address};
159158
int serve_fd = process->bind(datadir, "test_bitcoin", address);
160-
BOOST_CHECK_GE(serve_fd, 0);
161-
BOOST_CHECK_EQUAL(address, bind_address);
159+
CHECK(serve_fd >= 0);
160+
CHECK(address == bind_address);
162161
protocol->listen(serve_fd, "test-serve", *init);
163162
}};
164163

165164
auto connect_and_test{[&](const std::string& connect_address) {
166165
std::string address{connect_address};
167166
int connect_fd{process->connect(datadir, "test_bitcoin", address)};
168-
BOOST_CHECK_EQUAL(address, connect_address);
167+
CHECK(address == connect_address);
169168
std::unique_ptr<interfaces::Init> remote_init{protocol->connect(connect_fd, "test-connect")};
170169
std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
171-
BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
170+
CHECK(remote_echo->echo("echo test") == "echo test");
172171
}};
173172

174173
// Need to specify explicit socket addresses outside the data directory, because the data

src/ipc/test/ipc_tests.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,28 @@
77

88
#include <test/util/common.h>
99
#include <test/util/setup_common.h>
10-
#include <boost/test/unit_test.hpp>
11-
12-
BOOST_FIXTURE_TEST_SUITE(ipc_tests, BasicTestingSetup)
13-
BOOST_AUTO_TEST_CASE(ipc_tests)
10+
#include <test/util/framework.hpp>
11+
TEST_SUITE_BEGIN(ipc_tests)
12+
FIXTURE_TEST_CASE(ipc_tests, BasicTestingSetup)
1413
{
1514
IpcPipeTest();
1615
IpcSocketPairTest();
1716
IpcSocketTest(m_args.GetDataDirNet());
1817
}
1918

2019
// Test address parsing.
21-
BOOST_AUTO_TEST_CASE(parse_address_test)
20+
FIXTURE_TEST_CASE(parse_address_test, BasicTestingSetup)
2221
{
2322
std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
2423
fs::path datadir{"/var/empty/notexist"};
2524
auto check_notexist{[](const std::system_error& e) { return e.code() == std::errc::no_such_file_or_directory; }};
2625
auto check_address{[&](std::string address, std::string expect_address, std::string expect_error) {
2726
if (expect_error.empty()) {
28-
BOOST_CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::system_error, check_notexist);
27+
CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::system_error, check_notexist);
2928
} else {
30-
BOOST_CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::invalid_argument, HasReason(expect_error));
29+
CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::invalid_argument, HasReason(expect_error));
3130
}
32-
BOOST_CHECK_EQUAL(address, expect_address);
31+
CHECK(address == expect_address);
3332
}};
3433
check_address("unix", "unix:/var/empty/notexist/test_bitcoin.sock", "");
3534
check_address("unix:", "unix:/var/empty/notexist/test_bitcoin.sock", "");
@@ -40,4 +39,4 @@ BOOST_AUTO_TEST_CASE(parse_address_test)
4039
check_address("invalid", "invalid", "Unrecognized address 'invalid'");
4140
}
4241

43-
BOOST_AUTO_TEST_SUITE_END()
42+
TEST_SUITE_END()

src/test/CMakeLists.txt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,22 +160,20 @@ target_link_libraries(test_bitcoin
160160

161161
add_subdirectory(${PROJECT_SOURCE_DIR}/src/ipc/test ipc)
162162

163-
function(add_boost_test source_file)
163+
function(register_test_suite source_file)
164164
if(NOT EXISTS ${source_file})
165165
return()
166166
endif()
167167

168168
file(READ "${source_file}" source_file_content)
169169
string(REGEX
170-
MATCHALL "(BOOST_FIXTURE_TEST_SUITE|BOOST_AUTO_TEST_SUITE)\\(([A-Za-z0-9_]+)"
170+
MATCHALL "TEST_SUITE_BEGIN\\([A-Za-z_][A-Za-z0-9_]*"
171171
test_suite_macro "${source_file_content}"
172172
)
173-
list(TRANSFORM test_suite_macro
174-
REPLACE "(BOOST_FIXTURE_TEST_SUITE|BOOST_AUTO_TEST_SUITE)\\(" ""
175-
)
173+
list(TRANSFORM test_suite_macro REPLACE "^TEST_SUITE_BEGIN\\(" "")
176174
foreach(test_suite_name IN LISTS test_suite_macro)
177175
add_test(NAME ${test_suite_name}
178-
COMMAND test_bitcoin --run_test=${test_suite_name} --catch_system_error=no --log_level=test_suite -- -printtoconsole=1
176+
COMMAND test_bitcoin --run_test=${test_suite_name} -- -printtoconsole=1
179177
)
180178
set_property(TEST ${test_suite_name} PROPERTY
181179
SKIP_REGULAR_EXPRESSION
@@ -194,7 +192,7 @@ function(add_all_test_targets)
194192
if(result)
195193
cmake_path(APPEND test_source_dir ${test_source} OUTPUT_VARIABLE test_source)
196194
endif()
197-
add_boost_test(${test_source})
195+
register_test_suite(${test_source})
198196
endforeach()
199197
endfunction()
200198

src/test/README.md

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Unit tests
22

3-
The sources in this directory are unit test cases. Boost includes a
4-
unit testing framework, and since Bitcoin Core already uses Boost, it makes
5-
sense to simply use this framework rather than require developers to
6-
configure some other framework (we want as few impediments to creating
7-
unit tests as possible).
3+
The sources in this directory are unit test cases. Tests use a small
4+
header-only framework declared in `src/test/util/framework.hpp`, which
5+
provides registration macros (`TEST_CASE`, `FIXTURE_TEST_CASE`,
6+
`TEST_SUITE_BEGIN`/`TEST_SUITE_END`) and assertion macros (`CHECK`,
7+
`REQUIRE`, `CHECK_THROWS`, `CHECK_THROWS_AS`, `CHECK_EXCEPTION`,
8+
`CHECK_EQUAL_RANGES`, `TEST_MESSAGE`, `WARN_MESSAGE`).
89

910
The build system is set up to compile an executable called `test_bitcoin`
1011
that runs all of the unit tests. The main source file for the test library is found in
@@ -22,27 +23,26 @@ and tests weren't explicitly disabled.
2223
The unit tests can be run with `ctest --test-dir build`, which includes unit
2324
tests from subtrees.
2425

25-
Run `build/bin/test_bitcoin --list_content` for the full list of tests.
26+
Run `build/bin/test_bitcoin --list` for the full list of tests.
2627

2728
To run the unit tests manually, launch `build/bin/test_bitcoin`. To recompile
2829
after a test file was modified, run `cmake --build build` and then run the test again. If you
2930
modify a non-test file, use `cmake --build build --target test_bitcoin` to recompile only what's needed
3031
to run the unit tests.
3132

32-
To add more unit tests, add `BOOST_AUTO_TEST_CASE` functions to the existing
33-
.cpp files in the `test/` directory or add new .cpp files that
34-
implement new `BOOST_AUTO_TEST_SUITE` sections.
33+
To add more unit tests, add `TEST_CASE` (or `FIXTURE_TEST_CASE`) functions to
34+
the existing .cpp files in the `test/` directory, or add new .cpp files that
35+
declare a new suite with `TEST_SUITE_BEGIN("<name>")`.
3536

3637
### Running individual tests
3738

38-
The `test_bitcoin` runner accepts command line arguments from the Boost
39-
framework. To see the list of arguments that may be passed, run:
39+
To see the list of arguments that may be passed, run:
4040

4141
```
4242
build/bin/test_bitcoin --help
4343
```
4444

45-
For example, to run only the tests in the `getarg_tests` file, with full logging:
45+
For example, to run only the tests in the `getarg_tests` suite, with full logging:
4646

4747
```bash
4848
build/bin/test_bitcoin --log_level=all --run_test=getarg_tests
@@ -54,13 +54,14 @@ or
5454
build/bin/test_bitcoin -l all -t getarg_tests
5555
```
5656

57-
or to run only the doubledash test in `getarg_tests`
57+
or to run only the `doubledash` test in `getarg_tests`
5858

5959
```bash
60-
build/bin/test_bitcoin --run_test=getarg_tests/doubledash
60+
build/bin/test_bitcoin --run_test=getarg_tests::doubledash
6161
```
6262

6363
The `--log_level=` (or `-l`) argument controls the verbosity of the test output.
64+
Accepted values: `none`, `error` (default), `info`, `all`.
6465

6566
The `test_bitcoin` runner also accepts some of the command line arguments accepted by
6667
`bitcoind`. Use `--` to separate these sets of arguments:
@@ -126,8 +127,10 @@ additionally use the `--output-on-failure` option to display logs of the failed
126127
on failure. For running individual tests verbosely, refer to the section
127128
[above](#running-individual-tests).
128129

129-
To write to logs from unit tests you need to use specific message methods
130-
provided by Boost. The simplest is `BOOST_TEST_MESSAGE`.
130+
To write a diagnostic message from a unit test, use `TEST_MESSAGE(...)`
131+
(emitted at log level `info` or higher). `WARN_MESSAGE(cond, msg)` emits
132+
a warning when `cond` is false without failing the test — use `CHECK` /
133+
`REQUIRE` to fail.
131134

132135
For debugging you can launch the `test_bitcoin` executable with `gdb` or `lldb` and
133136
start debugging, just like you would with any other program:
@@ -146,13 +149,9 @@ Another tool that can be used to resolve segmentation faults is
146149
[valgrind](https://valgrind.org/).
147150

148151
If for whatever reason you want to produce a core dump file for this fault, you can do
149-
that as well. By default, the boost test runner will intercept system errors and not
150-
produce a core file. To bypass this, add `--catch_system_errors=no` to the
151-
`test_bitcoin` arguments and ensure that your ulimits are set properly (e.g. `ulimit -c
152-
unlimited`).
153-
154-
Running the tests and hitting a segmentation fault should now produce a file called `core`
155-
(on Linux platforms, the file name will likely depend on the contents of
152+
that as well. Ensure that your ulimits are set properly (e.g. `ulimit -c unlimited`),
153+
then running the tests and hitting a segmentation fault should produce a file called
154+
`core` (on Linux platforms, the file name will likely depend on the contents of
156155
`/proc/sys/kernel/core_pattern`).
157156

158157
You can then explore the core dump using

0 commit comments

Comments
 (0)