Files
rippled/src/test/basics/Buffer_test.cpp
2026-07-02 18:30:59 +00:00

269 lines
8.3 KiB
C++

#include <xrpl/basics/Buffer.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/unit_test/suite.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>
#include <utility>
namespace xrpl::test {
struct Buffer_test : beast::unit_test::Suite
{
static bool
sane(Buffer const& b)
{
if (b.empty())
return b.data() == nullptr;
return b.data() != nullptr;
}
void
run() override
{
std::uint8_t const data[] = {0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23,
0x71, 0x6d, 0x2a, 0x18, 0xb4, 0x70, 0xcb, 0xf5,
0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c, 0xf0, 0x2c,
0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
Buffer const b0;
BEAST_EXPECT(sane(b0));
BEAST_EXPECT(b0.empty());
Buffer b1{0};
BEAST_EXPECT(sane(b1));
BEAST_EXPECT(b1.empty());
std::memcpy(b1.alloc(16), data, 16);
BEAST_EXPECT(sane(b1));
BEAST_EXPECT(!b1.empty());
BEAST_EXPECT(b1.size() == 16);
Buffer b2{b1.size()};
BEAST_EXPECT(sane(b2));
BEAST_EXPECT(!b2.empty());
BEAST_EXPECT(b2.size() == b1.size());
std::memcpy(b2.data(), data + 16, 16);
Buffer b3{data, sizeof(data)};
BEAST_EXPECT(sane(b3));
BEAST_EXPECT(!b3.empty());
BEAST_EXPECT(b3.size() == sizeof(data));
BEAST_EXPECT(std::memcmp(b3.data(), data, b3.size()) == 0);
// Check equality and inequality comparisons
BEAST_EXPECT(b0 == b0);
BEAST_EXPECT(b0 != b1);
BEAST_EXPECT(b1 == b1);
BEAST_EXPECT(b1 != b2);
BEAST_EXPECT(b2 != b3);
// Check copy constructors and copy assignments:
{
testcase("Copy Construction / Assignment");
Buffer x{b0};
BEAST_EXPECT(x == b0);
BEAST_EXPECT(sane(x));
Buffer y{b1};
BEAST_EXPECT(y == b1);
BEAST_EXPECT(sane(y));
x = b2;
BEAST_EXPECT(x == b2);
BEAST_EXPECT(sane(x));
x = y;
BEAST_EXPECT(x == y);
BEAST_EXPECT(sane(x));
y = b3;
BEAST_EXPECT(y == b3);
BEAST_EXPECT(sane(y));
x = b0;
BEAST_EXPECT(x == b0);
BEAST_EXPECT(sane(x));
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
#endif
x = x;
BEAST_EXPECT(x == b0);
BEAST_EXPECT(sane(x));
y = y;
BEAST_EXPECT(y == b3);
BEAST_EXPECT(sane(y));
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
// Check move constructor & move assignments:
{
testcase("Move Construction / Assignment");
static_assert(std::is_nothrow_move_constructible_v<Buffer>);
static_assert(std::is_nothrow_move_assignable_v<Buffer>);
{ // Move-construct from empty buf
Buffer x;
Buffer const y{std::move(x)};
BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(sane(y));
BEAST_EXPECT(y.empty());
BEAST_EXPECT(x == y); // NOLINT(bugprone-use-after-move)
}
{ // Move-construct from non-empty buf
Buffer x{b1};
Buffer const y{std::move(x)};
BEAST_EXPECT(sane(x)); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(x.empty()); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(sane(y));
BEAST_EXPECT(y == b1);
}
{ // Move assign empty buf to empty buf
Buffer x;
Buffer y;
x = std::move(y);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x.empty());
BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move)
}
{ // Move assign non-empty buf to empty buf
Buffer x;
Buffer y{b1};
x = std::move(y);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x == b1);
BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move)
}
{ // Move assign empty buf to non-empty buf
Buffer x{b1};
Buffer y;
x = std::move(y);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x.empty());
BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move)
}
{ // Move assign non-empty buf to non-empty buf
Buffer x{b1};
Buffer y{b2};
Buffer z{b3};
x = std::move(y);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(!x.empty());
BEAST_EXPECT(sane(y)); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(y.empty()); // NOLINT(bugprone-use-after-move)
x = std::move(z);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(!x.empty());
BEAST_EXPECT(sane(z)); // NOLINT(bugprone-use-after-move)
BEAST_EXPECT(z.empty()); // NOLINT(bugprone-use-after-move)
}
}
{
testcase("Slice Conversion / Construction / Assignment");
Buffer w{static_cast<Slice>(b0)};
BEAST_EXPECT(sane(w));
BEAST_EXPECT(w == b0);
Buffer x{static_cast<Slice>(b1)};
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x == b1);
Buffer y{static_cast<Slice>(b2)};
BEAST_EXPECT(sane(y));
BEAST_EXPECT(y == b2);
Buffer z{static_cast<Slice>(b3)};
BEAST_EXPECT(sane(z));
BEAST_EXPECT(z == b3);
// Assign empty slice to empty buffer
w = static_cast<Slice>(b0);
BEAST_EXPECT(sane(w));
BEAST_EXPECT(w == b0);
// Assign non-empty slice to empty buffer
w = static_cast<Slice>(b1);
BEAST_EXPECT(sane(w));
BEAST_EXPECT(w == b1);
// Assign non-empty slice to non-empty buffer
x = static_cast<Slice>(b2);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x == b2);
// Assign non-empty slice to non-empty buffer
y = static_cast<Slice>(z);
BEAST_EXPECT(sane(y));
BEAST_EXPECT(y == z);
// Assign empty slice to non-empty buffer:
z = static_cast<Slice>(b0);
BEAST_EXPECT(sane(z));
BEAST_EXPECT(z == b0);
}
{
testcase("Allocation, Deallocation and Clearing");
auto test = [this](Buffer const& b, std::size_t i) {
Buffer x{b};
// Try to allocate some number of bytes, possibly
// zero (which means clear) and sanity check
x(i);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x.size() == i);
BEAST_EXPECT((x.data() == nullptr) == (i == 0));
// Try to allocate some more data (always non-zero)
x(i + 1);
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x.size() == i + 1);
BEAST_EXPECT(x.data() != nullptr);
// Try to clear:
x.clear();
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x.empty());
BEAST_EXPECT(x.data() == nullptr);
// Try to clear again:
x.clear();
BEAST_EXPECT(sane(x));
BEAST_EXPECT(x.empty());
BEAST_EXPECT(x.data() == nullptr);
};
for (std::size_t i = 0; i < 16; ++i)
{
test(b0, i);
test(b1, i);
}
}
}
};
BEAST_DEFINE_TESTSUITE(Buffer, basics, xrpl);
} // namespace xrpl::test