LCOV - code coverage report
Current view: top level - src/wallet/test - db_tests.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 97.3 % 219 213
Test Date: 2024-07-07 05:05:19 Functions: 96.4 % 28 27
Branches: 50.7 % 1380 699

             Branch data     Line data    Source code
       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 <config/bitcoin-config.h> // IWYU pragma: keep
       6                 :             : 
       7                 :             : #include <boost/test/unit_test.hpp>
       8                 :             : 
       9                 :             : #include <test/util/setup_common.h>
      10                 :             : #include <util/check.h>
      11                 :             : #include <util/fs.h>
      12                 :             : #include <util/translation.h>
      13                 :             : #ifdef USE_BDB
      14                 :             : #include <wallet/bdb.h>
      15                 :             : #endif
      16                 :             : #ifdef USE_SQLITE
      17                 :             : #include <wallet/sqlite.h>
      18                 :             : #endif
      19                 :             : #include <wallet/migrate.h>
      20                 :             : #include <wallet/test/util.h>
      21                 :             : #include <wallet/walletutil.h> // for WALLET_FLAG_DESCRIPTORS
      22                 :             : 
      23                 :             : #include <fstream>
      24                 :             : #include <memory>
      25                 :             : #include <string>
      26                 :             : 
      27                 :           0 : inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)
      28                 :             : {
      29                 :           0 :     Span key{kv.first}, value{kv.second};
      30                 :           0 :     os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""
      31                 :           0 :        << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\")";
      32                 :           0 :     return os;
      33                 :             : }
      34                 :             : 
      35                 :             : namespace wallet {
      36                 :             : 
      37                 :          30 : static Span<const std::byte> StringBytes(std::string_view str)
      38                 :             : {
      39                 :          30 :     return AsBytes<const char>({str.data(), str.size()});
      40                 :             : }
      41                 :             : 
      42                 :          14 : static SerializeData StringData(std::string_view str)
      43                 :             : {
      44                 :          14 :     auto bytes = StringBytes(str);
      45                 :          14 :     return SerializeData{bytes.begin(), bytes.end()};
      46                 :             : }
      47                 :             : 
      48                 :          16 : static void CheckPrefix(DatabaseBatch& batch, Span<const std::byte> prefix, MockableData expected)
      49                 :             : {
      50                 :          16 :     std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
      51                 :          16 :     MockableData actual;
      52                 :         136 :     while (true) {
      53         [ +  - ]:          76 :         DataStream key, value;
      54         [ +  - ]:          76 :         DatabaseCursor::Status status = cursor->Next(key, value);
      55         [ +  + ]:          76 :         if (status == DatabaseCursor::Status::DONE) break;
      56   [ +  -  +  -  :         120 :         BOOST_CHECK(status == DatabaseCursor::Status::MORE);
                   +  - ]
      57   [ +  -  +  -  :         120 :         BOOST_CHECK(
          +  -  +  -  +  
                      - ]
      58                 :             :             actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);
      59                 :          76 :     }
      60   [ +  -  +  -  :          32 :     BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
                   +  - ]
      61                 :          16 : }
      62                 :             : 
      63                 :             : BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
      64                 :             : 
      65                 :           9 : static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
      66                 :             : {
      67                 :           9 :     fs::path data_file = BDBDataFile(path);
      68         [ +  - ]:          18 :     database_filename = data_file.filename();
      69   [ +  -  +  - ]:          36 :     return GetBerkeleyEnv(data_file.parent_path(), false);
      70                 :           9 : }
      71                 :             : 
      72   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(getwalletenv_file)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
      73                 :             : {
      74                 :           1 :     fs::path test_name = "test_name.dat";
      75         [ +  - ]:           1 :     const fs::path datadir = m_args.GetDataDirNet();
      76   [ +  -  +  - ]:           2 :     fs::path file_path = datadir / test_name;
      77         [ +  - ]:           1 :     std::ofstream f{file_path};
      78         [ +  - ]:           1 :     f.close();
      79                 :             : 
      80                 :           1 :     fs::path filename;
      81         [ +  - ]:           1 :     std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
      82   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(filename, test_name);
      83   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(env->Directory(), datadir);
          +  -  +  -  +  
                      - ]
      84                 :           5 : }
      85                 :             : 
      86   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(getwalletenv_directory)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
      87                 :             : {
      88                 :           1 :     fs::path expected_name = "wallet.dat";
      89         [ +  - ]:           1 :     const fs::path datadir = m_args.GetDataDirNet();
      90                 :             : 
      91                 :           1 :     fs::path filename;
      92         [ +  - ]:           1 :     std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
      93   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(filename, expected_name);
      94   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(env->Directory(), datadir);
             +  -  +  - ]
      95                 :           4 : }
      96                 :             : 
      97   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
      98                 :             : {
      99         [ +  - ]:           2 :     fs::path datadir = m_args.GetDataDirNet() / "1";
     100   [ +  -  +  - ]:           2 :     fs::path datadir_2 = m_args.GetDataDirNet() / "2";
     101                 :           1 :     fs::path filename;
     102                 :             : 
     103         [ +  - ]:           1 :     std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
     104         [ +  - ]:           1 :     std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
     105         [ +  - ]:           1 :     std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
     106                 :             : 
     107   [ +  -  +  -  :           2 :     BOOST_CHECK(env_1 == env_2);
                   +  - ]
     108   [ +  -  +  -  :           2 :     BOOST_CHECK(env_2 != env_3);
                   +  - ]
     109   [ +  -  +  - ]:           6 : }
     110                 :             : 
     111   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     112                 :             : {
     113         [ +  - ]:           2 :     fs::path datadir = gArgs.GetDataDirNet() / "1";
     114   [ +  -  +  - ]:           2 :     fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
     115                 :           1 :     fs::path filename;
     116                 :             : 
     117         [ +  - ]:           1 :     std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
     118         [ +  - ]:           1 :     std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
     119                 :           1 :     env_1_a.reset();
     120                 :             : 
     121         [ +  - ]:           1 :     std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
     122         [ +  - ]:           1 :     std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
     123                 :             : 
     124   [ +  -  +  -  :           2 :     BOOST_CHECK(env_1_a != env_1_b);
                   +  - ]
     125   [ +  -  +  -  :           2 :     BOOST_CHECK(env_2_a == env_2_b);
                   +  - ]
     126   [ +  -  +  -  :           6 : }
                   -  + ]
     127                 :             : 
     128                 :           4 : static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
     129                 :             : {
     130                 :           4 :     std::vector<std::unique_ptr<WalletDatabase>> dbs;
     131         [ +  - ]:           4 :     DatabaseOptions options;
     132                 :           4 :     DatabaseStatus status;
     133         [ +  - ]:           4 :     bilingual_str error;
     134                 :             : #ifdef USE_BDB
     135   [ +  -  +  -  :          12 :     dbs.emplace_back(MakeBerkeleyDatabase(path_root / "bdb", options, status, error));
             +  -  +  - ]
     136                 :             :     // Needs BDB to make the DB to read
     137   [ +  -  +  -  :          16 :     dbs.emplace_back(std::make_unique<BerkeleyRODatabase>(BDBDataFile(path_root / "bdb"), /*open=*/false));
          +  -  +  -  +  
                      - ]
     138                 :             : #endif
     139                 :             : #ifdef USE_SQLITE
     140   [ +  -  +  -  :          12 :     dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
             +  -  +  - ]
     141                 :             : #endif
     142   [ +  -  +  - ]:           4 :     dbs.emplace_back(CreateMockableWalletDatabase());
     143                 :           4 :     return dbs;
     144                 :           4 : }
     145                 :             : 
     146   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     147                 :             : {
     148                 :             :     // Test each supported db
     149         [ +  + ]:           5 :     for (const auto& database : TestDatabases(m_path_root)) {
     150         [ +  - ]:           4 :         std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
     151                 :             : 
     152   [ +  -  +  - ]:           4 :         std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
     153   [ +  -  +  + ]:           4 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     154                 :             :             // For BerkeleyRO, open the file now. This must happen after BDB has written to the file
     155         [ +  - ]:           1 :             database->Open();
     156                 :             :         } else {
     157                 :             :             // Write elements to it if not berkeleyro
     158         [ +  + ]:          33 :             for (unsigned int i = 0; i < 10; i++) {
     159         [ +  + ]:         210 :                 for (const auto& prefix : prefixes) {
     160   [ +  -  +  -  :         360 :                     BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
             +  -  +  - ]
     161                 :             :                 }
     162                 :             :             }
     163                 :             :         }
     164                 :             : 
     165                 :             :         // Now read all the items by prefix and verify that each element gets parsed correctly
     166         [ +  + ]:          28 :         for (const auto& prefix : prefixes) {
     167         [ +  - ]:          24 :             DataStream s_prefix;
     168         [ +  - ]:          24 :             s_prefix << prefix;
     169         [ +  - ]:          24 :             std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);
     170                 :          24 :             DataStream key;
     171                 :          24 :             DataStream value;
     172         [ +  + ]:         264 :             for (int i = 0; i < 10; i++) {
     173         [ +  - ]:         240 :                 DatabaseCursor::Status status = cursor->Next(key, value);
     174   [ +  -  +  - ]:         240 :                 BOOST_CHECK_EQUAL(status, DatabaseCursor::Status::MORE);
     175                 :             : 
     176         [ +  - ]:         240 :                 std::string key_back;
     177                 :         240 :                 unsigned int i_back;
     178   [ +  -  +  - ]:         240 :                 key >> key_back >> i_back;
     179   [ +  -  +  - ]:         240 :                 BOOST_CHECK_EQUAL(key_back, prefix);
     180                 :             : 
     181                 :         240 :                 unsigned int value_back;
     182         [ +  - ]:         240 :                 value >> value_back;
     183   [ +  -  +  - ]:         240 :                 BOOST_CHECK_EQUAL(value_back, i_back);
     184                 :             :             }
     185                 :             : 
     186                 :             :             // Let's now read it once more, it should return DONE
     187   [ +  -  +  -  :          48 :             BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);
                   +  - ]
     188                 :          24 :         }
     189         [ +  - ]:           4 :         handler.reset();
     190         [ +  - ]:           4 :         database->Close();
     191                 :           5 :     }
     192                 :           1 : }
     193                 :             : 
     194                 :             : // Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't
     195                 :             : // covered in the higher level test above. The higher level test uses
     196                 :             : // serialized strings which are prefixed with string length, so it doesn't test
     197                 :             : // truly empty prefixes or prefixes that begin with \xff
     198   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     199                 :             : {
     200                 :           1 :     const MockableData::value_type
     201   [ +  -  +  - ]:           1 :         e{StringData(""), StringData("e")},
     202   [ +  -  +  -  :           1 :         p{StringData("prefix"), StringData("p")},
                   +  - ]
     203   [ +  -  +  -  :           1 :         ps{StringData("prefixsuffix"), StringData("ps")},
                   +  - ]
     204   [ +  -  +  -  :           1 :         f{StringData("\xff"), StringData("f")},
                   +  - ]
     205   [ +  -  +  -  :           1 :         fs{StringData("\xffsuffix"), StringData("fs")},
                   +  - ]
     206   [ +  -  +  -  :           1 :         ff{StringData("\xff\xff"), StringData("ff")},
                   +  - ]
     207   [ +  -  +  - ]:           1 :         ffs{StringData("\xff\xffsuffix"), StringData("ffs")};
     208   [ +  -  +  + ]:           5 :     for (const auto& database : TestDatabases(m_path_root)) {
     209         [ +  - ]:           4 :         std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
     210                 :             : 
     211   [ +  -  +  + ]:           4 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     212                 :             :             // For BerkeleyRO, open the file now. This must happen after BDB has written to the file
     213         [ +  - ]:           1 :             database->Open();
     214                 :             :         } else {
     215                 :             :             // Write elements to it if not berkeleyro
     216   [ +  -  +  -  :          66 :             for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
          +  -  +  -  +  
          -  +  -  +  -  
          -  +  +  +  -  
                      - ]
     217         [ -  + ]:          21 :                 batch->Write(Span{k}, Span{v});
     218   [ +  +  -  - ]:          24 :             }
     219                 :             :         }
     220                 :             : 
     221   [ +  -  +  -  :          36 :         CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
             +  +  -  - ]
     222   [ +  -  +  -  :          16 :         CheckPrefix(*batch, StringBytes("prefix"), {p, ps});
             +  +  -  - ]
     223   [ +  -  +  -  :          24 :         CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});
             +  +  -  - ]
     224   [ +  -  +  -  :          16 :         CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});
             +  +  -  - ]
     225         [ +  - ]:           4 :         batch.reset();
     226         [ +  - ]:           4 :         database->Close();
     227                 :           5 :     }
     228   [ +  -  +  -  :          23 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  -  -  -  -  
             -  -  -  - ]
     229                 :             : 
     230   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(db_availability_after_write_error)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     231                 :             : {
     232                 :             :     // Ensures the database remains accessible without deadlocking after a write error.
     233                 :             :     // To simulate the behavior, record overwrites are disallowed, and the test verifies
     234                 :             :     // that the database remains active after failing to store an existing record.
     235         [ +  + ]:           5 :     for (const auto& database : TestDatabases(m_path_root)) {
     236   [ +  -  +  + ]:           4 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     237                 :             :             // Skip this test if BerkeleyRO
     238                 :           1 :             continue;
     239                 :             :         }
     240                 :             :         // Write original record
     241         [ +  - ]:           3 :         std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
     242         [ +  - ]:           3 :         std::string key = "key";
     243         [ +  - ]:           3 :         std::string value = "value";
     244         [ +  - ]:           3 :         std::string value2 = "value_2";
     245   [ +  -  +  -  :           6 :         BOOST_CHECK(batch->Write(key, value));
             +  -  +  - ]
     246                 :             :         // Attempt to overwrite the record (expect failure)
     247   [ +  -  +  -  :           6 :         BOOST_CHECK(!batch->Write(key, value2, /*fOverwrite=*/false));
             +  -  +  - ]
     248                 :             :         // Successfully overwrite the record
     249   [ +  -  +  -  :           6 :         BOOST_CHECK(batch->Write(key, value2, /*fOverwrite=*/true));
             +  -  +  - ]
     250                 :             :         // Sanity-check; read and verify the overwritten value
     251         [ +  - ]:           3 :         std::string read_value;
     252   [ +  -  +  -  :           6 :         BOOST_CHECK(batch->Read(key, read_value));
             +  -  +  - ]
     253   [ +  -  +  - ]:           3 :         BOOST_CHECK_EQUAL(read_value, value2);
     254                 :           4 :     }
     255                 :           1 : }
     256                 :             : 
     257                 :             : // Verify 'ErasePrefix' functionality using db keys similar to the ones used by the wallet.
     258                 :             : // Keys are in the form of std::pair<TYPE, ENTRY_ID>
     259   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(erase_prefix)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     260                 :             : {
     261                 :           1 :     const std::string key = "key";
     262         [ +  - ]:           1 :     const std::string key2 = "key2";
     263         [ +  - ]:           1 :     const std::string value = "value";
     264         [ +  - ]:           1 :     const std::string value2 = "value_2";
     265   [ +  -  +  -  :          24 :     auto make_key = [](std::string type, std::string id) { return std::make_pair(type, id); };
          +  -  +  -  +  
          -  +  -  +  -  
                   +  - ]
     266                 :             : 
     267   [ +  -  +  + ]:           5 :     for (const auto& database : TestDatabases(m_path_root)) {
     268   [ +  -  +  + ]:           4 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     269                 :             :             // Skip this test if BerkeleyRO
     270                 :           1 :             continue;
     271                 :             :         }
     272         [ +  - ]:           3 :         std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
     273                 :             : 
     274                 :             :         // Write two entries with the same key type prefix, a third one with a different prefix
     275                 :             :         // and a fourth one with the type-id values inverted
     276   [ +  -  +  -  :          12 :         BOOST_CHECK(batch->Write(make_key(key, value), value));
          +  -  +  -  +  
                -  +  - ]
     277   [ +  -  +  -  :          12 :         BOOST_CHECK(batch->Write(make_key(key, value2), value2));
          +  -  +  -  +  
                -  +  - ]
     278   [ +  -  +  -  :          12 :         BOOST_CHECK(batch->Write(make_key(key2, value), value));
          +  -  +  -  +  
                -  +  - ]
     279   [ +  -  +  -  :          12 :         BOOST_CHECK(batch->Write(make_key(value, key), value));
          +  -  +  -  +  
                -  +  - ]
     280                 :             : 
     281                 :             :         // Erase the ones with the same prefix and verify result
     282   [ +  -  +  -  :           6 :         BOOST_CHECK(batch->TxnBegin());
             +  -  +  - ]
     283   [ +  -  +  -  :           6 :         BOOST_CHECK(batch->ErasePrefix(DataStream() << key));
          +  -  +  -  +  
                      - ]
     284   [ +  -  +  -  :           6 :         BOOST_CHECK(batch->TxnCommit());
             +  -  +  - ]
     285                 :             : 
     286   [ +  -  +  -  :          12 :         BOOST_CHECK(!batch->Exists(make_key(key, value)));
          +  -  +  -  +  
                -  +  - ]
     287   [ +  -  +  -  :          12 :         BOOST_CHECK(!batch->Exists(make_key(key, value2)));
          +  -  +  -  +  
                -  +  - ]
     288                 :             :         // Also verify that entries with a different prefix were not erased
     289   [ +  -  +  -  :          12 :         BOOST_CHECK(batch->Exists(make_key(key2, value)));
          +  -  +  -  +  
                -  +  - ]
     290   [ +  -  +  -  :          12 :         BOOST_CHECK(batch->Exists(make_key(value, key)));
          +  -  +  -  +  
                      - ]
     291                 :           4 :     }
     292                 :           1 : }
     293                 :             : 
     294                 :             : #ifdef USE_SQLITE
     295                 :             : 
     296                 :             : // Test-only statement execution error
     297                 :             : constexpr int TEST_SQLITE_ERROR = -999;
     298                 :             : 
     299                 :             : class DbExecBlocker : public SQliteExecHandler
     300                 :             : {
     301                 :             : private:
     302                 :             :     SQliteExecHandler m_base_exec;
     303                 :             :     std::set<std::string> m_blocked_statements;
     304                 :             : public:
     305         [ +  - ]:           1 :     DbExecBlocker(std::set<std::string> blocked_statements) : m_blocked_statements(blocked_statements) {}
     306                 :           1 :     int Exec(SQLiteDatabase& database, const std::string& statement) override {
     307         [ -  + ]:           1 :         if (m_blocked_statements.contains(statement)) return TEST_SQLITE_ERROR;
     308                 :           0 :         return m_base_exec.Exec(database, statement);
     309                 :             :     }
     310                 :             : };
     311                 :             : 
     312   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(txn_close_failure_dangling_txn)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     313                 :             : {
     314                 :             :     // Verifies that there is no active dangling, to-be-reversed db txn
     315                 :             :     // after the batch object that initiated it is destroyed.
     316         [ +  - ]:           1 :     DatabaseOptions options;
     317                 :           1 :     DatabaseStatus status;
     318         [ +  - ]:           1 :     bilingual_str error;
     319   [ +  -  +  -  :           3 :     std::unique_ptr<SQLiteDatabase> database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
                   +  - ]
     320                 :             : 
     321         [ +  - ]:           1 :     std::string key = "key";
     322         [ +  - ]:           1 :     std::string value = "value";
     323                 :             : 
     324         [ +  - ]:           1 :     std::unique_ptr<SQLiteBatch> batch = std::make_unique<SQLiteBatch>(*database);
     325   [ +  -  +  -  :           2 :     BOOST_CHECK(batch->TxnBegin());
             +  -  +  - ]
     326   [ +  -  +  -  :           2 :     BOOST_CHECK(batch->Write(key, value));
             +  -  +  - ]
     327                 :             :     // Set a handler to prevent txn abortion during destruction.
     328                 :             :     // Mimicking a db statement execution failure.
     329   [ +  -  +  -  :           2 :     batch->SetExecHandler(std::make_unique<DbExecBlocker>(std::set<std::string>{"ROLLBACK TRANSACTION"}));
          +  -  +  +  -  
                      - ]
     330                 :             :     // Destroy batch
     331         [ +  - ]:           1 :     batch.reset();
     332                 :             : 
     333                 :             :     // Ensure there is no dangling, to-be-reversed db txn
     334   [ +  -  +  -  :           2 :     BOOST_CHECK(!database->HasActiveTxn());
             +  -  +  - ]
     335                 :             : 
     336                 :             :     // And, just as a sanity check; verify that new batchs only write what they suppose to write
     337                 :             :     // and nothing else.
     338         [ +  - ]:           1 :     std::string key2 = "key2";
     339         [ +  - ]:           1 :     std::unique_ptr<SQLiteBatch> batch2 = std::make_unique<SQLiteBatch>(*database);
     340   [ +  -  +  -  :           2 :     BOOST_CHECK(batch2->Write(key2, value));
             +  -  +  - ]
     341                 :             :     // The first key must not exist
     342   [ +  -  +  -  :           2 :     BOOST_CHECK(!batch2->Exists(key));
                   +  - ]
     343         [ +  - ]:           3 : }
     344                 :             : 
     345   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(concurrent_txn_dont_interfere)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     346                 :             : {
     347                 :           1 :     std::string key = "key";
     348         [ +  - ]:           1 :     std::string value = "value";
     349         [ +  - ]:           1 :     std::string value2 = "value_2";
     350                 :             : 
     351         [ +  - ]:           1 :     DatabaseOptions options;
     352                 :           1 :     DatabaseStatus status;
     353         [ +  - ]:           1 :     bilingual_str error;
     354   [ +  -  +  -  :           3 :     const auto& database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
                   +  - ]
     355                 :             : 
     356   [ +  -  +  - ]:           1 :     std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
     357                 :             : 
     358                 :             :     // Verify concurrent db transactions does not interfere between each other.
     359                 :             :     // Start db txn, write key and check the key does exist within the db txn.
     360   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->TxnBegin());
             +  -  +  - ]
     361   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->Write(key, value));
             +  -  +  - ]
     362   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->Exists(key));
             +  -  +  - ]
     363                 :             : 
     364                 :             :     // But, the same key, does not exist in another handler
     365   [ +  -  +  - ]:           1 :     std::unique_ptr<DatabaseBatch> handler2 = Assert(database)->MakeBatch();
     366   [ +  -  +  -  :           2 :     BOOST_CHECK(handler2->Exists(key));
             +  -  +  - ]
     367                 :             : 
     368                 :             :     // Attempt to commit the handler txn calling the handler2 methods.
     369                 :             :     // Which, must not be possible.
     370   [ +  -  +  -  :           2 :     BOOST_CHECK(!handler2->TxnCommit());
             +  -  +  - ]
     371   [ +  -  +  -  :           2 :     BOOST_CHECK(!handler2->TxnAbort());
             +  -  +  - ]
     372                 :             : 
     373                 :             :     // Only the first handler can commit the changes.
     374   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->TxnCommit());
             +  -  +  - ]
     375                 :             :     // And, once commit is completed, handler2 can read the record
     376         [ +  - ]:           1 :     std::string read_value;
     377   [ +  -  +  -  :           2 :     BOOST_CHECK(handler2->Read(key, read_value));
             +  -  +  - ]
     378   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(read_value, value);
     379                 :             : 
     380                 :             :     // Also, once txn is committed, single write statements are re-enabled.
     381                 :             :     // Which means that handler2 can read the record changes directly.
     382   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->Write(key, value2, /*fOverwrite=*/true));
             +  -  +  - ]
     383   [ +  -  +  -  :           2 :     BOOST_CHECK(handler2->Read(key, read_value));
             +  -  +  - ]
     384   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(read_value, value2);
     385                 :           2 : }
     386                 :             : #endif // USE_SQLITE
     387                 :             : 
     388                 :             : BOOST_AUTO_TEST_SUITE_END()
     389                 :             : } // namespace wallet
        

Generated by: LCOV version 2.0-1