Files
rippled/src/test/nodestore/TestBase.h
2025-11-12 08:23:45 -05:00

215 lines
5.3 KiB
C++

#ifndef XRPL_NODESTORE_BASE_H_INCLUDED
#define XRPL_NODESTORE_BASE_H_INCLUDED
#include <xrpl/basics/StringUtilities.h>
#include <xrpl/basics/random.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/beast/utility/rngfill.h>
#include <xrpl/beast/xor_shift_engine.h>
#include <xrpl/nodestore/Backend.h>
#include <xrpl/nodestore/Database.h>
#include <xrpl/nodestore/Types.h>
#include <boost/algorithm/string.hpp>
#include <iomanip>
namespace ripple {
namespace NodeStore {
/** Binary function that satisfies the strict-weak-ordering requirement.
This compares the hashes of both objects and returns true if
the first hash is considered to go before the second.
@see std::sort
*/
struct LessThan
{
bool
operator()(
std::shared_ptr<NodeObject> const& lhs,
std::shared_ptr<NodeObject> const& rhs) const noexcept
{
return lhs->getHash() < rhs->getHash();
}
};
/** Returns `true` if objects are identical. */
inline bool
isSame(
std::shared_ptr<NodeObject> const& lhs,
std::shared_ptr<NodeObject> const& rhs)
{
return (lhs->getType() == rhs->getType()) &&
(lhs->getHash() == rhs->getHash()) &&
(lhs->getData() == rhs->getData());
}
// Some common code for the unit tests
//
class TestBase : public beast::unit_test::suite
{
public:
// Tunable parameters
//
static std::size_t const minPayloadBytes = 1;
static std::size_t const maxPayloadBytes = 2000;
static int const numObjectsToTest = 2000;
public:
// Create a predictable batch of objects
static Batch
createPredictableBatch(int numObjects, std::uint64_t seed)
{
Batch batch;
batch.reserve(numObjects);
beast::xor_shift_engine rng(seed);
for (int i = 0; i < numObjects; ++i)
{
NodeObjectType const type = [&] {
switch (rand_int(rng, 3))
{
case 0:
return hotLEDGER;
case 1:
return hotACCOUNT_NODE;
case 2:
return hotTRANSACTION_NODE;
case 3:
return hotUNKNOWN;
}
// will never happen, but make static analysis tool happy.
return hotUNKNOWN;
}();
uint256 hash;
beast::rngfill(hash.begin(), hash.size(), rng);
Blob blob(rand_int(rng, minPayloadBytes, maxPayloadBytes));
beast::rngfill(blob.data(), blob.size(), rng);
batch.push_back(
NodeObject::createObject(type, std::move(blob), hash));
}
return batch;
}
// Compare two batches for equality
static bool
areBatchesEqual(Batch const& lhs, Batch const& rhs)
{
bool result = true;
if (lhs.size() == rhs.size())
{
for (int i = 0; i < lhs.size(); ++i)
{
if (!isSame(lhs[i], rhs[i]))
{
result = false;
break;
}
}
}
else
{
result = false;
}
return result;
}
// Store a batch in a backend
void
storeBatch(Backend& backend, Batch const& batch)
{
for (int i = 0; i < batch.size(); ++i)
{
backend.store(batch[i]);
}
}
// Get a copy of a batch in a backend
void
fetchCopyOfBatch(Backend& backend, Batch* pCopy, Batch const& batch)
{
pCopy->clear();
pCopy->reserve(batch.size());
for (int i = 0; i < batch.size(); ++i)
{
std::shared_ptr<NodeObject> object;
Status const status =
backend.fetch(batch[i]->getHash().cbegin(), &object);
BEAST_EXPECT(status == ok);
if (status == ok)
{
BEAST_EXPECT(object != nullptr);
pCopy->push_back(object);
}
}
}
void
fetchMissing(Backend& backend, Batch const& batch)
{
for (int i = 0; i < batch.size(); ++i)
{
std::shared_ptr<NodeObject> object;
Status const status =
backend.fetch(batch[i]->getHash().cbegin(), &object);
BEAST_EXPECT(status == notFound);
}
}
// Store all objects in a batch
static void
storeBatch(Database& db, Batch const& batch)
{
for (int i = 0; i < batch.size(); ++i)
{
std::shared_ptr<NodeObject> const object(batch[i]);
Blob data(object->getData());
db.store(
object->getType(),
std::move(data),
object->getHash(),
db.earliestLedgerSeq());
}
}
// Fetch all the hashes in one batch, into another batch.
static void
fetchCopyOfBatch(Database& db, Batch* pCopy, Batch const& batch)
{
pCopy->clear();
pCopy->reserve(batch.size());
for (int i = 0; i < batch.size(); ++i)
{
std::shared_ptr<NodeObject> object =
db.fetchNodeObject(batch[i]->getHash(), 0);
if (object != nullptr)
pCopy->push_back(object);
}
}
};
} // namespace NodeStore
} // namespace ripple
#endif