test: parameterize RelationalDatabase_test for multiple backends

- Add backend parameter to all test methods
- Parse backends from --unittest-arg (CSV format: sqlite,rwdb)
- Skip dbHasSpace checks for SQLite (uses in-memory databases in standalone mode)
- Remove unnecessary database path setup/cleanup
- Rename getDB() to getInterface()
This commit is contained in:
Nicholas Dudfield
2025-08-20 13:24:35 +07:00
parent 1703574d50
commit 529d77d4b7

View File

@@ -20,6 +20,7 @@
#include <ripple/app/rdb/RelationalDatabase.h>
#include <ripple/app/rdb/backend/SQLiteDatabase.h>
#include <ripple/core/ConfigSections.h>
#include <boost/filesystem.hpp>
#include <chrono>
#include <test/jtx.h>
#include <test/jtx/envconfig.h>
@@ -29,16 +30,42 @@ namespace test {
class RelationalDatabase_test : public beast::unit_test::suite
{
public:
void
testBasicInitialization()
private:
// Helper to get SQLiteDatabase* (works for both SQLite and RWDB since RWDB
// inherits from SQLiteDatabase)
static SQLiteDatabase*
getInterface(Application& app)
{
testcase("Basic initialization and empty database");
return dynamic_cast<SQLiteDatabase*>(&app.getRelationalDatabase());
}
static SQLiteDatabase*
getInterface(RelationalDatabase& db)
{
return dynamic_cast<SQLiteDatabase*>(&db);
}
static std::unique_ptr<Config>
makeConfig(std::string const& backend)
{
auto config = test::jtx::envconfig();
// Sqlite backend doesn't need a database_path as it will just use
// in-memory databases when in standalone mode anyway.
config->overwrite(SECTION_RELATIONAL_DB, "backend", backend);
return config;
}
public:
RelationalDatabase_test() = default;
void
testBasicInitialization(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Basic initialization and empty database - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
auto& db = env.app().getRelationalDatabase();
@@ -47,7 +74,7 @@ public:
BEAST_EXPECT(db.getMaxLedgerSeq() == 2);
BEAST_EXPECT(db.getNewestLedgerInfo()->seq == 2);
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
BEAST_EXPECT(sqliteDb != nullptr);
if (sqliteDb)
@@ -64,12 +91,13 @@ public:
}
void
testLedgerSequenceOperations()
testLedgerSequenceOperations(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Ledger sequence operations");
testcase("Ledger sequence operations - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
@@ -103,7 +131,7 @@ public:
BEAST_EXPECT(*minSeq == 2);
BEAST_EXPECT(*maxSeq == 5);
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
if (sqliteDb)
{
auto ledgerCount = sqliteDb->getLedgerCountMinMax();
@@ -114,17 +142,17 @@ public:
}
void
testLedgerInfoOperations()
testLedgerInfoOperations(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Ledger info retrieval operations");
testcase("Ledger info retrieval operations - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
auto* db =
dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase());
auto* db = getInterface(env.app());
Account alice("alice");
env.fund(XRP(10000), alice);
@@ -167,12 +195,13 @@ public:
}
void
testHashOperations()
testHashOperations(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Hash retrieval operations");
testcase("Hash retrieval operations - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
@@ -218,12 +247,13 @@ public:
}
void
testTransactionOperations()
testTransactionOperations(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Transaction storage and retrieval");
testcase("Transaction storage and retrieval - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
@@ -235,7 +265,7 @@ public:
env.fund(XRP(10000), alice, bob);
env.close();
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
BEAST_EXPECT(sqliteDb != nullptr);
if (!sqliteDb)
@@ -290,12 +320,13 @@ public:
}
void
testAccountTransactionOperations()
testAccountTransactionOperations(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Account transaction operations");
testcase("Account transaction operations - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
@@ -308,7 +339,9 @@ public:
env.fund(XRP(10000), alice, bob, carol);
env.close();
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
BEAST_EXPECT(sqliteDb != nullptr);
if (!sqliteDb)
return;
@@ -369,12 +402,13 @@ public:
}
void
testAccountTransactionPaging()
testAccountTransactionPaging(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Account transaction paging operations");
testcase("Account transaction paging operations - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
@@ -386,7 +420,8 @@ public:
env.fund(XRP(10000), alice, bob);
env.close();
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
BEAST_EXPECT(sqliteDb != nullptr);
if (!sqliteDb)
return;
@@ -440,12 +475,13 @@ public:
}
void
testDeletionOperations()
testDeletionOperations(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Deletion operations");
testcase("Deletion operations - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
@@ -457,7 +493,8 @@ public:
env.fund(XRP(10000), alice, bob);
env.close();
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
BEAST_EXPECT(sqliteDb != nullptr);
if (!sqliteDb)
return;
@@ -505,33 +542,41 @@ public:
}
void
testDatabaseSpaceOperations()
testDatabaseSpaceOperations(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Database space and size operations");
testcase("Database space and size operations - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
auto& db = env.app().getRelationalDatabase();
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
BEAST_EXPECT(sqliteDb != nullptr);
if (!sqliteDb)
return;
// Test space availability (should always be true for in-memory)
BEAST_EXPECT(db.ledgerDbHasSpace(env.app().config()));
BEAST_EXPECT(db.transactionDbHasSpace(env.app().config()));
// Test size queries
auto allKB = sqliteDb->getKBUsedAll();
auto ledgerKB = sqliteDb->getKBUsedLedger();
auto txKB = sqliteDb->getKBUsedTransaction();
BEAST_EXPECT(allKB == 0);
BEAST_EXPECT(ledgerKB == 0);
BEAST_EXPECT(txKB == 0);
if (backend == "rwdb")
{
// RWDB reports actual data memory
// After closing genesis ledger, should have some data
BEAST_EXPECT(allKB == 0);
BEAST_EXPECT(ledgerKB == 0);
BEAST_EXPECT(txKB == 0);
}
else
{
// Will not assert on sqlite usage except that it's non-zero
BEAST_EXPECT(allKB > 0);
BEAST_EXPECT(ledgerKB > 0);
BEAST_EXPECT(txKB > 0);
}
// Create some data and verify size increases
Account alice("alice");
@@ -542,9 +587,37 @@ public:
auto newLedgerKB = sqliteDb->getKBUsedLedger();
auto newTxKB = sqliteDb->getKBUsedTransaction();
BEAST_EXPECT(newAllKB == 2);
BEAST_EXPECT(newLedgerKB == 0);
BEAST_EXPECT(newTxKB == 1);
if (backend == "rwdb")
{
// RWDB reports actual data memory
// After adding data, should see increases
BEAST_EXPECT(newAllKB == 2);
BEAST_EXPECT(newLedgerKB == 0);
BEAST_EXPECT(newTxKB == 1);
// For RWDB: All = Ledger + Transaction + overhead
// So All should be >= Ledger + Transaction
BEAST_EXPECT(newAllKB >= newLedgerKB + newTxKB);
}
else
{
// SQLite: The values should stay roughly the same or increase
// slightly since SQLite reports global memory usage
BEAST_EXPECT(newAllKB >= allKB); // Should not decrease
BEAST_EXPECT(newLedgerKB >= ledgerKB); // Should not decrease
BEAST_EXPECT(newTxKB >= txKB); // Should not decrease
}
// Test space availability
// Note: SQLite in standalone mode uses in-memory databases,
// so file-based space checks don't apply
if (backend == "rwdb")
{
// RWDB uses actual files, so space checks are valid
BEAST_EXPECT(db.ledgerDbHasSpace(env.app().config()));
BEAST_EXPECT(db.transactionDbHasSpace(env.app().config()));
}
// Skip space checks for SQLite as it uses in-memory databases in
// standalone mode
// Test database closure operations (should not throw)
try
@@ -559,18 +632,20 @@ public:
}
void
testTransactionMinLedgerSeq()
testTransactionMinLedgerSeq(
std::string const& backend,
std::unique_ptr<Config> config)
{
testcase("Transaction minimum ledger sequence tracking");
testcase("Transaction minimum ledger sequence tracking - " + backend);
using namespace test::jtx;
auto config = envconfig();
config->LEDGER_HISTORY = 1000;
Env env(*this, std::move(config));
auto& db = env.app().getRelationalDatabase();
auto* sqliteDb = dynamic_cast<SQLiteDatabase*>(&db);
auto* sqliteDb = getInterface(db);
BEAST_EXPECT(sqliteDb != nullptr);
if (!sqliteDb)
return;
@@ -610,19 +685,56 @@ public:
BEAST_EXPECT(newAcctTxMinSeq == acctTxMinSeq);
}
std::vector<std::string> static getBackends(std::string const& unittest_arg)
{
// Valid backends
static const std::set<std::string> validBackends = {"sqlite", "rwdb"};
std::set<std::string> backends; // Use set to avoid duplicates
std::string argStr =
unittest_arg.empty() ? "sqlite,rwdb" : unittest_arg;
std::stringstream ss(argStr);
std::string backend;
while (std::getline(ss, backend, ','))
{
if (!backend.empty())
{
// Validate backend
if (validBackends.contains(backend))
{
backends.insert(backend);
}
}
}
// Return as vector (sorted due to set)
return {backends.begin(), backends.end()};
}
void
run() override
{
testBasicInitialization();
testLedgerSequenceOperations();
testLedgerInfoOperations();
testHashOperations();
testTransactionOperations();
testAccountTransactionOperations();
testAccountTransactionPaging();
testDeletionOperations();
testDatabaseSpaceOperations();
testTransactionMinLedgerSeq();
auto backends = getBackends(arg());
if (backends.empty())
{
fail("no valid backend specified: '" + arg() + "'");
}
for (auto const& backend : backends)
{
testBasicInitialization(backend, makeConfig(backend));
testLedgerSequenceOperations(backend, makeConfig(backend));
testLedgerInfoOperations(backend, makeConfig(backend));
testHashOperations(backend, makeConfig(backend));
testTransactionOperations(backend, makeConfig(backend));
testAccountTransactionOperations(backend, makeConfig(backend));
testAccountTransactionPaging(backend, makeConfig(backend));
testDeletionOperations(backend, makeConfig(backend));
testDatabaseSpaceOperations(backend, makeConfig(backend));
testTransactionMinLedgerSeq(backend, makeConfig(backend));
}
}
};