Skip to content

Commit d27e2d8

Browse files
LarryRuanefurszy
andcommitted
test: test_bitcoin: allow -testdatadir=<datadir>
Specifying this argument overrides the path location for test_bitcoin; it becomes <datadir>/test_common_Bitcoin Core/<testname>/datadir. Also, this directory isn't removed after the test completes. This can make it easier for developers to study the results of a test (see the state of the data directory after the test runs), and also (for example) have an editor open on debug.log to monitor it across multiple test runs instead of having to re-open a different pathname each time. Example usage (note the "--" is needed): test_bitcoin --run_test=getarg_tests/boolarg -- \ -testdatadir=/somewhere/mydatadir This will create (if necessary) and use the data directory: /somewhere/mydatadir/test_common_Bitcoin Core/getarg_tests/boolarg/datadir Co-authored-by: furszy <[email protected]>
1 parent eefe4ba commit d27e2d8

File tree

8 files changed

+121
-14
lines changed

8 files changed

+121
-14
lines changed

src/bench/bench.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
2323

2424
const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS{};
2525

26+
const std::function<std::string()> G_TEST_GET_FULL_NAME{};
27+
2628
namespace {
2729

2830
void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& benchmarkResults, const fs::path& file, const char* tpl)

src/qt/main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ extern const std::function<std::string(const char*)> G_TRANSLATION_FUN = [](cons
1919
};
2020
UrlDecodeFn* const URL_DECODE = urlDecode;
2121

22+
const std::function<std::string()> G_TEST_GET_FULL_NAME{};
23+
2224
MAIN_FUNCTION
2325
{
2426
return GuiMain(argc, argv);

src/qt/test/test_main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
4848

4949
const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS{};
5050

51+
const std::function<std::string()> G_TEST_GET_FULL_NAME{};
52+
5153
// This is all you need to run all the tests
5254
int main(int argc, char* argv[])
5355
{

src/test/README.md

+43-7
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,61 @@ test_bitcoin --log_level=all --run_test=getarg_tests
4141
```
4242

4343
`log_level` controls the verbosity of the test framework, which logs when a
44-
test case is entered, for example. `test_bitcoin` also accepts the command
45-
line arguments accepted by `bitcoind`. Use `--` to separate both types of
46-
arguments:
44+
test case is entered, for example.
45+
46+
`test_bitcoin` also accepts some of the command line arguments accepted by
47+
`bitcoind`. Use `--` to separate these sets of arguments:
4748

4849
```bash
4950
test_bitcoin --log_level=all --run_test=getarg_tests -- -printtoconsole=1
5051
```
5152

52-
The `-printtoconsole=1` after the two dashes redirects the debug log, which
53-
would normally go to a file in the test datadir
54-
(`BasicTestingSetup::m_path_root`), to the standard terminal output.
53+
The `-printtoconsole=1` after the two dashes sends debug logging, which
54+
normally goes only to `debug.log` within the data directory, also to the
55+
standard terminal output.
5556

5657
... or to run just the doubledash test:
5758

5859
```bash
5960
test_bitcoin --run_test=getarg_tests/doubledash
6061
```
6162

62-
Run `test_bitcoin --help` for the full list.
63+
`test_bitcoin` creates a temporary working (data) directory with a randomly
64+
generated pathname within `test_common_Bitcoin Core/`, which in turn is within
65+
the system's temporary directory (see
66+
[`temp_directory_path`](https://en.cppreference.com/w/cpp/filesystem/temp_directory_path)).
67+
This data directory looks like a simplified form of the standard `bitcoind` data
68+
directory. Its content will vary depending on the test, but it will always
69+
have a `debug.log` file, for example.
70+
71+
The location of the temporary data directory can be specified with the
72+
`-testdatadir` option. This can make debugging easier. The directory
73+
path used is the argument path appended with
74+
`/test_common_Bitcoin Core/<test-name>/datadir`.
75+
The directory path is created if necessary.
76+
Specifying this argument also causes the data directory
77+
not to be removed after the last test. This is useful for looking at
78+
what the test wrote to `debug.log` after it completes, for example.
79+
(The directory is removed at the start of the next test run,
80+
so no leftover state is used.)
81+
82+
```bash
83+
$ test_bitcoin --run_test=getarg_tests/doubledash -- -testdatadir=/somewhere/mydatadir
84+
Test directory (will not be deleted): "/somewhere/mydatadir/test_common_Bitcoin Core/getarg_tests/doubledash/datadir
85+
Running 1 test case...
86+
87+
*** No errors detected
88+
$ ls -l '/somewhere/mydatadir/test_common_Bitcoin Core/getarg_tests/doubledash/datadir'
89+
total 8
90+
drwxrwxr-x 2 admin admin 4096 Nov 27 22:45 blocks
91+
-rw-rw-r-- 1 admin admin 1003 Nov 27 22:45 debug.log
92+
```
93+
94+
If you run an entire test suite, such as `--run_test=getarg_tests`, or all the test suites
95+
(by not specifying `--run_test`), a separate directory
96+
will be created for each individual test.
97+
98+
Run `test_bitcoin --help` for the full list of tests.
6399
64100
### Adding test cases
65101

src/test/fuzz/fuzz.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ __AFL_FUZZ_INIT();
3535

3636
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
3737

38+
const std::function<std::string()> G_TEST_GET_FULL_NAME{};
39+
3840
/**
3941
* A copy of the command line arguments that start with `--`.
4042
* First `LLVMFuzzerInitialize()` is called, which saves the arguments to `g_args`.

src/test/main.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,10 @@ const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS =
3939
}
4040
return args;
4141
};
42+
43+
/**
44+
* Retrieve the boost unit test name.
45+
*/
46+
const std::function<std::string()> G_TEST_GET_FULL_NAME = []() {
47+
return boost::unit_test::framework::current_test_case().full_name();
48+
};

src/test/util/setup_common.cpp

+57-6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <txmempool.h>
5050
#include <util/chaintype.h>
5151
#include <util/check.h>
52+
#include <util/fs_helpers.h>
5253
#include <util/rbf.h>
5354
#include <util/strencodings.h>
5455
#include <util/string.h>
@@ -97,9 +98,22 @@ struct NetworkSetup
9798
};
9899
static NetworkSetup g_networksetup_instance;
99100

101+
/** Register test-only arguments */
102+
static void SetupUnitTestArgs(ArgsManager& argsman)
103+
{
104+
argsman.AddArg("-testdatadir", strprintf("Custom data directory (default: %s<random_string>)", fs::PathToString(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / "")),
105+
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
106+
}
107+
108+
/** Test setup failure */
109+
static void ExitFailure(std::string_view str_err)
110+
{
111+
std::cerr << str_err << std::endl;
112+
exit(EXIT_FAILURE);
113+
}
114+
100115
BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args)
101-
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
102-
m_args{}
116+
: m_args{}
103117
{
104118
m_node.shutdown = &m_interrupt;
105119
m_node.args = &gArgs;
@@ -120,18 +134,49 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
120134
arguments = Cat(arguments, G_TEST_COMMAND_LINE_ARGUMENTS());
121135
}
122136
util::ThreadRename("test");
123-
fs::create_directories(m_path_root);
124-
m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
125-
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
126137
gArgs.ClearPathCache();
127138
{
128139
SetupServerArgs(*m_node.args);
140+
SetupUnitTestArgs(*m_node.args);
129141
std::string error;
130142
if (!m_node.args->ParseParameters(arguments.size(), arguments.data(), error)) {
131143
m_node.args->ClearArgs();
132144
throw std::runtime_error{error};
133145
}
134146
}
147+
148+
if (!m_node.args->IsArgSet("-testdatadir")) {
149+
// By default, the data directory has a random name
150+
const auto rand_str{g_insecure_rand_ctx_temp_path.rand256().ToString()};
151+
m_path_root = fs::temp_directory_path() / "test_common_" PACKAGE_NAME / rand_str;
152+
TryCreateDirectories(m_path_root);
153+
} else {
154+
// Custom data directory
155+
m_has_custom_datadir = true;
156+
fs::path root_dir{m_node.args->GetPathArg("-testdatadir")};
157+
if (root_dir.empty()) ExitFailure("-testdatadir argument is empty, please specify a path");
158+
159+
root_dir = fs::absolute(root_dir);
160+
const std::string test_path{G_TEST_GET_FULL_NAME ? G_TEST_GET_FULL_NAME() : ""};
161+
m_path_lock = root_dir / "test_common_" PACKAGE_NAME / fs::PathFromString(test_path);
162+
m_path_root = m_path_lock / "datadir";
163+
164+
// Try to obtain the lock; if unsuccessful don't disturb the existing test.
165+
TryCreateDirectories(m_path_lock);
166+
if (util::LockDirectory(m_path_lock, ".lock", /*probe_only=*/false) != util::LockResult::Success) {
167+
ExitFailure("Cannot obtain a lock on test data lock directory " + fs::PathToString(m_path_lock) + '\n' + "The test executable is probably already running.");
168+
}
169+
170+
// Always start with a fresh data directory; this doesn't delete the .lock file located one level above.
171+
fs::remove_all(m_path_root);
172+
if (!TryCreateDirectories(m_path_root)) ExitFailure("Cannot create test data directory");
173+
174+
// Print the test directory name if custom.
175+
std::cout << "Test directory (will not be deleted): " << m_path_root << std::endl;
176+
}
177+
m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
178+
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
179+
135180
SelectParams(chainType);
136181
SeedInsecureRand();
137182
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
@@ -159,7 +204,13 @@ BasicTestingSetup::~BasicTestingSetup()
159204
m_node.kernel.reset();
160205
SetMockTime(0s); // Reset mocktime for following tests
161206
LogInstance().DisconnectTestLogger();
162-
fs::remove_all(m_path_root);
207+
if (m_has_custom_datadir) {
208+
// Only remove the lock file, preserve the data directory.
209+
UnlockDirectory(m_path_lock, ".lock");
210+
fs::remove(m_path_lock / ".lock");
211+
} else {
212+
fs::remove_all(m_path_root);
213+
}
163214
gArgs.ClearArgs();
164215
}
165216

src/test/util/setup_common.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ extern const std::function<void(const std::string&)> G_TEST_LOG_FUN;
3232
/** Retrieve the command line arguments. */
3333
extern const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS;
3434

35+
/** Retrieve the unit test name. */
36+
extern const std::function<std::string()> G_TEST_GET_FULL_NAME;
37+
3538
// Enable BOOST_CHECK_EQUAL for enum class types
3639
namespace std {
3740
template <typename T>
@@ -53,7 +56,9 @@ struct BasicTestingSetup {
5356
explicit BasicTestingSetup(const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {});
5457
~BasicTestingSetup();
5558

56-
const fs::path m_path_root;
59+
fs::path m_path_root;
60+
fs::path m_path_lock;
61+
bool m_has_custom_datadir{false};
5762
ArgsManager m_args;
5863
};
5964

0 commit comments

Comments
 (0)