Bitcoin Core  26.1.0
P2P Digital Currency
db_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2022 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <boost/test/unit_test.hpp>
6 
8 #include <util/check.h>
9 #include <util/fs.h>
10 #include <util/translation.h>
11 #ifdef USE_BDB
12 #include <wallet/bdb.h>
13 #endif
14 #ifdef USE_SQLITE
15 #include <wallet/sqlite.h>
16 #endif
17 #include <wallet/test/util.h>
18 #include <wallet/walletutil.h> // for WALLET_FLAG_DESCRIPTORS
19 
20 #include <fstream>
21 #include <memory>
22 #include <string>
23 
24 inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)
25 {
26  Span key{kv.first}, value{kv.second};
27  os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""
28  << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\")";
29  return os;
30 }
31 
32 namespace wallet {
33 
34 static Span<const std::byte> StringBytes(std::string_view str)
35 {
36  return AsBytes<const char>({str.data(), str.size()});
37 }
38 
39 static SerializeData StringData(std::string_view str)
40 {
41  auto bytes = StringBytes(str);
42  return SerializeData{bytes.begin(), bytes.end()};
43 }
44 
46 {
47  std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
48  MockableData actual;
49  while (true) {
50  DataStream key, value;
51  DatabaseCursor::Status status = cursor->Next(key, value);
52  if (status == DatabaseCursor::Status::DONE) break;
55  actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);
56  }
57  BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
58 }
59 
60 BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
61 
62 static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
63 {
64  fs::path data_file = BDBDataFile(path);
65  database_filename = data_file.filename();
66  return GetBerkeleyEnv(data_file.parent_path(), false);
67 }
68 
69 BOOST_AUTO_TEST_CASE(getwalletenv_file)
70 {
71  fs::path test_name = "test_name.dat";
72  const fs::path datadir = m_args.GetDataDirNet();
73  fs::path file_path = datadir / test_name;
74  std::ofstream f{file_path};
75  f.close();
76 
77  fs::path filename;
78  std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
79  BOOST_CHECK_EQUAL(filename, test_name);
80  BOOST_CHECK_EQUAL(env->Directory(), datadir);
81 }
82 
83 BOOST_AUTO_TEST_CASE(getwalletenv_directory)
84 {
85  fs::path expected_name = "wallet.dat";
86  const fs::path datadir = m_args.GetDataDirNet();
87 
88  fs::path filename;
89  std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
90  BOOST_CHECK_EQUAL(filename, expected_name);
91  BOOST_CHECK_EQUAL(env->Directory(), datadir);
92 }
93 
94 BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
95 {
96  fs::path datadir = m_args.GetDataDirNet() / "1";
97  fs::path datadir_2 = m_args.GetDataDirNet() / "2";
98  fs::path filename;
99 
100  std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
101  std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
102  std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
103 
104  BOOST_CHECK(env_1 == env_2);
105  BOOST_CHECK(env_2 != env_3);
106 }
107 
108 BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
109 {
110  fs::path datadir = gArgs.GetDataDirNet() / "1";
111  fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
112  fs::path filename;
113 
114  std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
115  std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
116  env_1_a.reset();
117 
118  std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
119  std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
120 
121  BOOST_CHECK(env_1_a != env_1_b);
122  BOOST_CHECK(env_2_a == env_2_b);
123 }
124 
125 static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
126 {
127  std::vector<std::unique_ptr<WalletDatabase>> dbs;
128  DatabaseOptions options;
129  DatabaseStatus status;
131 #ifdef USE_BDB
132  dbs.emplace_back(MakeBerkeleyDatabase(path_root / "bdb", options, status, error));
133 #endif
134 #ifdef USE_SQLITE
135  dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
136 #endif
137  dbs.emplace_back(CreateMockableWalletDatabase());
138  return dbs;
139 }
140 
141 BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
142 {
143  // Test each supported db
144  for (const auto& database : TestDatabases(m_path_root)) {
145  std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
146 
147  // Write elements to it
148  std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
149  for (unsigned int i = 0; i < 10; i++) {
150  for (const auto& prefix : prefixes) {
151  BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
152  }
153  }
154 
155  // Now read all the items by prefix and verify that each element gets parsed correctly
156  for (const auto& prefix : prefixes) {
157  DataStream s_prefix;
158  s_prefix << prefix;
159  std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);
160  DataStream key;
161  DataStream value;
162  for (int i = 0; i < 10; i++) {
163  DatabaseCursor::Status status = cursor->Next(key, value);
165 
166  std::string key_back;
167  unsigned int i_back;
168  key >> key_back >> i_back;
169  BOOST_CHECK_EQUAL(key_back, prefix);
170 
171  unsigned int value_back;
172  value >> value_back;
173  BOOST_CHECK_EQUAL(value_back, i_back);
174  }
175 
176  // Let's now read it once more, it should return DONE
177  BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);
178  }
179  }
180 }
181 
182 // Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't
183 // covered in the higher level test above. The higher level test uses
184 // serialized strings which are prefixed with string length, so it doesn't test
185 // truly empty prefixes or prefixes that begin with \xff
186 BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
187 {
188  const MockableData::value_type
189  e{StringData(""), StringData("e")},
190  p{StringData("prefix"), StringData("p")},
191  ps{StringData("prefixsuffix"), StringData("ps")},
192  f{StringData("\xff"), StringData("f")},
193  fs{StringData("\xffsuffix"), StringData("fs")},
194  ff{StringData("\xff\xff"), StringData("ff")},
195  ffs{StringData("\xff\xffsuffix"), StringData("ffs")};
196  for (const auto& database : TestDatabases(m_path_root)) {
197  std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
198  for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
199  batch->Write(Span{k}, Span{v});
200  }
201  CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
202  CheckPrefix(*batch, StringBytes("prefix"), {p, ps});
203  CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});
204  CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});
205  }
206 }
207 
209 } // namespace wallet
CONSTEXPR_IF_NOT_DEBUG Span< C > first(std::size_t count) const noexcept
Definition: span.h:204
static std::shared_ptr< BerkeleyEnvironment > GetWalletEnv(const fs::path &path, fs::path &database_filename)
Definition: db_tests.cpp:62
Bilingual messages:
Definition: translation.h:18
static SerializeData StringData(std::string_view str)
Definition: db_tests.cpp:39
std::unique_ptr< SQLiteDatabase > MakeSQLiteDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: sqlite.cpp:637
std::map< SerializeData, SerializeData, std::less<> > MockableData
Definition: util.h:51
const char * prefix
Definition: rest.cpp:1004
virtual std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix)=0
bool(* handler)(const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:1005
std::shared_ptr< BerkeleyEnvironment > GetBerkeleyEnv(const fs::path &env_directory, bool use_shared_memory)
Get BerkeleyEnvironment given a directory path.
Definition: bdb.cpp:79
RAII class that provides access to a WalletDatabase.
Definition: db.h:45
Filesystem operations and types.
Basic testing setup.
Definition: setup_common.h:49
std::unique_ptr< BerkeleyDatabase > MakeBerkeleyDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Return object giving access to Berkeley database at specified path.
Definition: bdb.cpp:929
const_iterator end() const
Definition: streams.h:225
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:192
std::vector< std::byte, zero_after_free_allocator< std::byte > > SerializeData
Byte-vector that clears its contents before deletion.
Definition: zeroafterfree.h:49
BOOST_AUTO_TEST_SUITE_END()
static Span< const std::byte > StringBytes(std::string_view str)
Definition: db_tests.cpp:34
static void CheckPrefix(DatabaseBatch &batch, Span< const std::byte > prefix, MockableData expected)
Definition: db_tests.cpp:45
static std::vector< std::unique_ptr< WalletDatabase > > TestDatabases(const fs::path &path_root)
Definition: db_tests.cpp:125
ArgsManager gArgs
Definition: args.cpp:42
const_iterator begin() const
Definition: streams.h:223
DatabaseStatus
Definition: db.h:197
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase(MockableData records)
Definition: util.cpp:190
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
bool error(const char *fmt, const Args &... args)
Definition: logging.h:262
fs::path BDBDataFile(const fs::path &wallet_path)
Definition: db.cpp:64
A Span is an object that can refer to a contiguous sequence of objects.
Definition: solver.h:20
path filename() const
Definition: fs.h:67
BOOST_AUTO_TEST_CASE(bnb_search_test)
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
#define Assert(val)
Identity function.
Definition: check.h:73
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:231
#define BOOST_CHECK(expr)
Definition: object.cpp:17