chore: Gtest migration followups second pass (#7888)

This commit is contained in:
Alex Kremer
2026-08-11 12:38:40 +00:00
committed by GitHub
parent 639943123c
commit 0a572833ea
9 changed files with 446 additions and 352 deletions

View File

@@ -365,6 +365,7 @@ words:
- xchain
- ximinez
- XMACRO
- xored
- xrpkuwait
- xrpl
- xrpld

View File

@@ -44,6 +44,7 @@ setup_target_for_coverage_gcovr(
EXCLUDE
"src/test"
"src/tests"
"src/benchmarks"
"include/xrpl/beast/test"
"include/xrpl/beast/unit_test"
"${CMAKE_BINARY_DIR}/pb-xrpl.libpb"

View File

@@ -3,6 +3,7 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <memory>
@@ -156,6 +157,19 @@ public:
}
/** @} */
/**
* Set every byte in the buffer to the given value.
*
* The size is unchanged, and this is a no-op on an empty buffer.
*
* @param value the byte to write to every position.
*/
void
fill(std::uint8_t value) noexcept
{
std::fill_n(p_.get(), size_, value);
}
/**
* Reset the buffer.
* All memory is deallocated. The resulting size is 0.

View File

@@ -41,10 +41,11 @@ struct RunState
release()
{
harness.reset();
Batch{}.swap(present);
Batch{}.swap(recent);
std::vector<uint256>{}.swap(missing);
std::vector<std::size_t>{}.swap(shuffle);
present = Batch{};
recent = Batch{};
missing = std::vector<uint256>{};
shuffle = std::vector<std::size_t>{};
avgPayload = 0;
}
};
@@ -239,9 +240,13 @@ registerWorkload(BackendConfig const& bc, Workload const& w)
if (!w.pinToPool)
{
auto rs = std::make_shared<RunState>();
auto* b = benchmark::RegisterBenchmark(name, makeRunner(w, cfg, rs));
b->RangeMultiplier(10)->Range(kPoolSizes.front(), kPoolSizes.back());
b->Threads(1)->Threads(4)->Threads(8)->UseRealTime();
benchmark::RegisterBenchmark(name, makeRunner(w, cfg, rs))
->RangeMultiplier(10)
->Range(kPoolSizes.front(), kPoolSizes.back())
->Threads(1)
->Threads(4)
->Threads(8)
->UseRealTime();
return;
}

View File

@@ -297,12 +297,11 @@ struct BackendConfig
inline std::vector<BackendConfig> const&
backendConfigs()
{
// Use factory settings for each DB
static std::vector<BackendConfig> const kConfigs = {
{.name = "nudb", .config = "type=nudb"},
#if XRPL_ROCKSDB_AVAILABLE
{.name = "rocksdb",
.config = "type=rocksdb,open_files=2000,filter_bits=12,cache_mb=256,"
"file_size_mb=8,file_size_mult=2"},
{.name = "rocksdb", .config = "type=rocksdb"},
#endif
};
return kConfigs;

View File

@@ -4,6 +4,7 @@
#include <gtest/gtest.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
@@ -12,8 +13,18 @@
namespace xrpl::test {
static_assert(std::is_nothrow_move_constructible_v<Buffer>);
static_assert(std::is_nothrow_move_assignable_v<Buffer>);
struct BufferTest : public ::testing::Test
{
static constexpr auto kRandomData = std::to_array<std::uint8_t>(
{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});
static constexpr std::size_t kHalf = kRandomData.size() / 2;
static bool
sane(Buffer const& b)
{
@@ -22,239 +33,321 @@ struct BufferTest : public ::testing::Test
return b.data() != nullptr;
}
/**
* Check the state Buffer documents for a moved-from buffer: "the other buffer is reset", i.e.
* empty and sane.
*
* Zeroing the size is not incidental tidiness. Moving the member unique_ptr nulls the data
* pointer whether Buffer wants it or not, so a moved-from buffer that kept its old size would
* lie about itself everywhere: alloc() would take its `n == size_` early-out and hand back a
* null pointer while still reporting the old size, fill() would run std::fill_n over a null
* pointer, and the Slice conversion would publish {nullptr, oldSize} to callers. A moved-from
* Buffer has to be a usable empty Buffer rather than a landmine, which is why the tests below
* assert this state instead of treating a moved-from buffer as untouchable.
*/
static void
checkEmptyAfterMove(Buffer const& buf)
{
EXPECT_TRUE(sane(buf));
EXPECT_TRUE(buf.empty());
}
Buffer const emptyBuffer;
Buffer const firstHalf{kRandomData.data(), kHalf};
Buffer const secondHalf{kRandomData.data() + kHalf, kHalf};
Buffer const whole{kRandomData.data(), kRandomData.size()};
};
TEST_F(BufferTest, buffer)
TEST_F(BufferTest, default_constructed_is_empty)
{
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 b;
Buffer const b0;
EXPECT_TRUE(sane(b0));
EXPECT_TRUE(b0.empty());
EXPECT_TRUE(sane(b));
EXPECT_TRUE(b.empty());
EXPECT_EQ(b.data(), nullptr);
}
Buffer b1{0};
EXPECT_TRUE(sane(b1));
EXPECT_TRUE(b1.empty());
std::memcpy(b1.alloc(16), data, 16);
EXPECT_TRUE(sane(b1));
EXPECT_FALSE(b1.empty());
EXPECT_EQ(b1.size(), 16);
TEST_F(BufferTest, zero_sized_construction_is_empty)
{
Buffer const b{0};
Buffer b2{b1.size()};
EXPECT_TRUE(sane(b2));
EXPECT_FALSE(b2.empty());
EXPECT_EQ(b2.size(), b1.size());
std::memcpy(b2.data(), data + 16, 16);
EXPECT_TRUE(sane(b));
EXPECT_TRUE(b.empty());
}
Buffer b3{data, sizeof(data)};
EXPECT_TRUE(sane(b3));
EXPECT_FALSE(b3.empty());
EXPECT_EQ(b3.size(), sizeof(data));
EXPECT_EQ(std::memcmp(b3.data(), data, b3.size()), 0);
TEST_F(BufferTest, alloc_grows_an_empty_buffer)
{
Buffer b{0};
std::memcpy(b.alloc(kHalf), kRandomData.data(), kHalf);
// Check equality and inequality comparisons.
// For code readability, we want to use general
// EXPECT_TRUE instead of specific EXPECT_EQ etc.
EXPECT_TRUE(b0 == b0);
EXPECT_TRUE(b0 != b1);
EXPECT_TRUE(b1 == b1);
EXPECT_TRUE(b1 != b2);
EXPECT_TRUE(b2 != b3);
EXPECT_TRUE(sane(b));
EXPECT_FALSE(b.empty());
EXPECT_EQ(b.size(), kHalf);
EXPECT_EQ(b, firstHalf);
}
// Check copy constructors and copy assignments:
{
Buffer x{b0};
EXPECT_EQ(x, b0);
EXPECT_TRUE(sane(x));
Buffer y{b1};
EXPECT_EQ(y, b1);
EXPECT_TRUE(sane(y));
x = b2;
EXPECT_EQ(x, b2);
EXPECT_TRUE(sane(x));
x = y;
EXPECT_EQ(x, y);
EXPECT_TRUE(sane(x));
y = b3;
EXPECT_EQ(y, b3);
EXPECT_TRUE(sane(y));
x = b0;
EXPECT_EQ(x, b0);
EXPECT_TRUE(sane(x));
TEST_F(BufferTest, sized_construction_reserves_without_filling)
{
Buffer b{kHalf};
EXPECT_TRUE(sane(b));
EXPECT_FALSE(b.empty());
EXPECT_EQ(b.size(), kHalf);
std::memcpy(b.data(), kRandomData.data() + kHalf, kHalf);
EXPECT_EQ(b, secondHalf);
}
TEST_F(BufferTest, construction_copies_raw_memory)
{
Buffer const b{kRandomData.data(), kRandomData.size()};
EXPECT_TRUE(sane(b));
EXPECT_FALSE(b.empty());
EXPECT_EQ(b.size(), kRandomData.size());
EXPECT_EQ(std::memcmp(b.data(), kRandomData.data(), b.size()), 0);
}
TEST_F(BufferTest, equality_compares_contents)
{
// Uses EXPECT_TRUE rather than EXPECT_EQ/EXPECT_NE because the operators are what is under test
// here.
EXPECT_TRUE(emptyBuffer == emptyBuffer);
EXPECT_TRUE(firstHalf == firstHalf);
EXPECT_TRUE(emptyBuffer != firstHalf);
EXPECT_TRUE(firstHalf != secondHalf);
EXPECT_TRUE(secondHalf != whole);
}
TEST_F(BufferTest, copy_construction)
{
Buffer const fromEmpty{emptyBuffer};
EXPECT_TRUE(sane(fromEmpty));
EXPECT_EQ(fromEmpty, emptyBuffer);
Buffer const fromNonEmpty{firstHalf};
EXPECT_TRUE(sane(fromNonEmpty));
EXPECT_EQ(fromNonEmpty, firstHalf);
}
TEST_F(BufferTest, copy_assignment)
{
Buffer b{emptyBuffer};
// empty <- non-empty
b = secondHalf;
EXPECT_TRUE(sane(b));
EXPECT_EQ(b, secondHalf);
// non-empty <- non-empty of a different size
b = whole;
EXPECT_TRUE(sane(b));
EXPECT_EQ(b, whole);
// non-empty <- empty
b = emptyBuffer;
EXPECT_TRUE(sane(b));
EXPECT_EQ(b, emptyBuffer);
}
TEST_F(BufferTest, self_assignment_preserves_contents)
{
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
#endif
x = x;
EXPECT_EQ(x, b0);
EXPECT_TRUE(sane(x));
y = y;
EXPECT_EQ(y, b3);
EXPECT_TRUE(sane(y));
Buffer emptyCopy{emptyBuffer};
emptyCopy = emptyCopy;
EXPECT_TRUE(sane(emptyCopy));
EXPECT_EQ(emptyCopy, emptyBuffer);
Buffer wholeCopy{whole};
wholeCopy = wholeCopy;
EXPECT_TRUE(sane(wholeCopy));
EXPECT_EQ(wholeCopy, whole);
#ifdef __clang__
#pragma clang diagnostic pop
#endif
}
}
// Check move constructor & move assignments:
TEST_F(BufferTest, move_construct_from_empty)
{
Buffer source;
Buffer const moved{std::move(source)};
checkEmptyAfterMove(source); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(sane(moved));
EXPECT_TRUE(moved.empty());
}
TEST_F(BufferTest, move_construct_from_non_empty)
{
Buffer source{firstHalf};
Buffer const moved{std::move(source)};
checkEmptyAfterMove(source); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(sane(moved));
EXPECT_EQ(moved, firstHalf);
}
TEST_F(BufferTest, move_assign_empty_to_empty)
{
Buffer target;
Buffer source;
target = std::move(source);
EXPECT_TRUE(sane(target));
EXPECT_TRUE(target.empty());
checkEmptyAfterMove(source); // NOLINT(bugprone-use-after-move)
}
TEST_F(BufferTest, move_assign_non_empty_to_empty)
{
Buffer target;
Buffer source{firstHalf};
target = std::move(source);
EXPECT_TRUE(sane(target));
EXPECT_EQ(target, firstHalf);
checkEmptyAfterMove(source); // NOLINT(bugprone-use-after-move)
}
TEST_F(BufferTest, move_assign_empty_to_non_empty)
{
Buffer target{firstHalf};
Buffer source;
target = std::move(source);
EXPECT_TRUE(sane(target));
EXPECT_TRUE(target.empty());
checkEmptyAfterMove(source); // NOLINT(bugprone-use-after-move)
}
TEST_F(BufferTest, move_assign_non_empty_to_non_empty)
{
Buffer target{firstHalf};
Buffer sameSize{secondHalf};
Buffer largerSize{whole};
target = std::move(sameSize);
EXPECT_TRUE(sane(target));
EXPECT_EQ(target, secondHalf);
checkEmptyAfterMove(sameSize); // NOLINT(bugprone-use-after-move)
target = std::move(largerSize);
EXPECT_TRUE(sane(target));
EXPECT_EQ(target, whole);
checkEmptyAfterMove(largerSize); // NOLINT(bugprone-use-after-move)
}
TEST_F(BufferTest, construction_from_slice)
{
Buffer const fromEmpty{static_cast<Slice>(emptyBuffer)};
EXPECT_TRUE(sane(fromEmpty));
EXPECT_EQ(fromEmpty, emptyBuffer);
Buffer const fromNonEmpty{static_cast<Slice>(whole)};
EXPECT_TRUE(sane(fromNonEmpty));
EXPECT_EQ(fromNonEmpty, whole);
}
TEST_F(BufferTest, assignment_from_slice)
{
Buffer b;
// empty <- empty slice
b = static_cast<Slice>(emptyBuffer);
EXPECT_TRUE(sane(b));
EXPECT_EQ(b, emptyBuffer);
// empty <- non-empty slice
b = static_cast<Slice>(firstHalf);
EXPECT_TRUE(sane(b));
EXPECT_EQ(b, firstHalf);
// non-empty <- non-empty slice
b = static_cast<Slice>(secondHalf);
EXPECT_TRUE(sane(b));
EXPECT_EQ(b, secondHalf);
// non-empty <- empty slice
b = static_cast<Slice>(emptyBuffer);
EXPECT_TRUE(sane(b));
EXPECT_EQ(b, emptyBuffer);
}
TEST_F(BufferTest, resize_allocates_and_clear_releases)
{
auto check = [](Buffer const& original, std::size_t size) {
SCOPED_TRACE(::testing::Message() << "size: " << size);
Buffer b{original};
// Resizing to zero is equivalent to clearing.
b(size);
EXPECT_TRUE(sane(b));
EXPECT_EQ(b.size(), size);
EXPECT_EQ(b.data() == nullptr, size == 0);
b(size + 1);
EXPECT_TRUE(sane(b));
EXPECT_EQ(b.size(), size + 1);
EXPECT_NE(b.data(), nullptr);
b.clear();
EXPECT_TRUE(sane(b));
EXPECT_TRUE(b.empty());
EXPECT_EQ(b.data(), nullptr);
// clear() is idempotent.
b.clear();
EXPECT_TRUE(sane(b));
EXPECT_TRUE(b.empty());
EXPECT_EQ(b.data(), nullptr);
};
for (auto size = 0uz; size < kHalf; ++size)
{
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)};
EXPECT_TRUE(sane(x)); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(x.empty()); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(sane(y));
EXPECT_TRUE(y.empty());
EXPECT_EQ(x, y); // NOLINT(bugprone-use-after-move)
}
{ // Move-construct from non-empty buf
Buffer x{b1};
Buffer const y{std::move(x)};
EXPECT_TRUE(sane(x)); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(x.empty()); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(sane(y));
EXPECT_EQ(y, b1);
}
{ // Move assign empty buf to empty buf
Buffer x;
Buffer y;
x = std::move(y);
EXPECT_TRUE(sane(x));
EXPECT_TRUE(x.empty());
EXPECT_TRUE(sane(y)); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(y.empty()); // NOLINT(bugprone-use-after-move)
}
{ // Move assign non-empty buf to empty buf
Buffer x;
Buffer y{b1};
x = std::move(y);
EXPECT_TRUE(sane(x));
EXPECT_EQ(x, b1);
EXPECT_TRUE(sane(y)); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(y.empty()); // NOLINT(bugprone-use-after-move)
}
{ // Move assign empty buf to non-empty buf
Buffer x{b1};
Buffer y;
x = std::move(y);
EXPECT_TRUE(sane(x));
EXPECT_TRUE(x.empty());
EXPECT_TRUE(sane(y)); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(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);
EXPECT_TRUE(sane(x));
EXPECT_FALSE(x.empty());
EXPECT_TRUE(sane(y)); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(y.empty()); // NOLINT(bugprone-use-after-move)
x = std::move(z);
EXPECT_TRUE(sane(x));
EXPECT_FALSE(x.empty());
EXPECT_TRUE(sane(z)); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(z.empty()); // NOLINT(bugprone-use-after-move)
}
}
{
Buffer w{static_cast<Slice>(b0)};
EXPECT_TRUE(sane(w));
EXPECT_EQ(w, b0);
Buffer x{static_cast<Slice>(b1)};
EXPECT_TRUE(sane(x));
EXPECT_EQ(x, b1);
Buffer y{static_cast<Slice>(b2)};
EXPECT_TRUE(sane(y));
EXPECT_EQ(y, b2);
Buffer z{static_cast<Slice>(b3)};
EXPECT_TRUE(sane(z));
EXPECT_EQ(z, b3);
// Assign empty slice to empty buffer
w = static_cast<Slice>(b0);
EXPECT_TRUE(sane(w));
EXPECT_EQ(w, b0);
// Assign non-empty slice to empty buffer
w = static_cast<Slice>(b1);
EXPECT_TRUE(sane(w));
EXPECT_EQ(w, b1);
// Assign non-empty slice to non-empty buffer
x = static_cast<Slice>(b2);
EXPECT_TRUE(sane(x));
EXPECT_EQ(x, b2);
// Assign non-empty slice to non-empty buffer
y = static_cast<Slice>(z);
EXPECT_TRUE(sane(y));
EXPECT_EQ(y, z);
// Assign empty slice to non-empty buffer:
z = static_cast<Slice>(b0);
EXPECT_TRUE(sane(z));
EXPECT_EQ(z, b0);
}
{
auto test = [](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);
EXPECT_TRUE(sane(x));
EXPECT_EQ(x.size(), i);
EXPECT_EQ((x.data() == nullptr), (i == 0));
// Try to allocate some more data (always non-zero)
x(i + 1);
EXPECT_TRUE(sane(x));
EXPECT_EQ(x.size(), i + 1);
EXPECT_NE(x.data(), nullptr);
// Try to clear:
x.clear();
EXPECT_TRUE(sane(x));
EXPECT_TRUE(x.empty());
EXPECT_EQ(x.data(), nullptr);
// Try to clear again:
x.clear();
EXPECT_TRUE(sane(x));
EXPECT_TRUE(x.empty());
EXPECT_EQ(x.data(), nullptr);
};
for (std::size_t i = 0; i < 16; ++i)
{
test(b0, i);
test(b1, i);
}
check(emptyBuffer, size);
check(firstHalf, size);
}
}
TEST_F(BufferTest, fill_sets_every_byte)
{
Buffer b{4};
b.fill(0xab);
EXPECT_EQ(b.size(), 4);
for (auto const byte : Slice{b})
EXPECT_EQ(byte, 0xab);
}
TEST_F(BufferTest, fill_overwrites_and_keeps_size)
{
Buffer b{4};
b.fill(0xab);
b.fill(0x00);
EXPECT_EQ(b.size(), 4);
for (auto const byte : Slice{b})
EXPECT_EQ(byte, 0x00);
}
TEST_F(BufferTest, fill_on_empty_buffer_is_a_noop)
{
Buffer empty;
empty.fill(0xff);
EXPECT_TRUE(empty.empty());
EXPECT_EQ(empty.data(), nullptr);
}
} // namespace xrpl::test

View File

@@ -92,6 +92,7 @@ public:
static constexpr std::size_t kMaxStates = 128;
static std::array<std::atomic<TrackedState>, kMaxStates> state;
static std::atomic<std::size_t> nextId;
static TrackedState
getState(std::size_t id)
{
@@ -100,13 +101,12 @@ public:
return state[id].load(std::memory_order_acquire);
}
static void
resetStates(bool resetCallback)
{
for (std::size_t i = 0; i < kMaxStates; ++i)
{
state[i].store(TrackedState::Uninitialized, std::memory_order_release);
}
nextId.store(0, std::memory_order_release);
if (resetCallback)
TIBase::tracingCallback = [](TrackedState, std::optional<TrackedState>) {};
@@ -120,6 +120,7 @@ public:
{
TIBase::resetStates(resetCallback);
}
~ResetStatesGuard()
{
TIBase::resetStates(resetCallback);
@@ -130,6 +131,7 @@ public:
{
state[id].store(TrackedState::Alive, std::memory_order_relaxed);
}
~TIBase() override
{
using enum TrackedState;
@@ -218,9 +220,7 @@ TEST(IntrusiveSharedTest, basics)
EXPECT_EQ(TIBase::getState(id), Alive);
EXPECT_EQ(b->useCount(), 1);
for (auto i = 0uz; i < 10; ++i)
{
strong.push_back(b);
}
b.reset();
EXPECT_EQ(TIBase::getState(id), Alive);
strong.resize(strong.size() - 1);
@@ -244,8 +244,7 @@ TEST(IntrusiveSharedTest, basics)
EXPECT_EQ(TIBase::getState(id), PartiallyDeleted);
while (!weak.empty())
{
weak.resize(weak.size() - 1);
if (!weak.empty())
if (weak.resize(weak.size() - 1); !weak.empty())
{
EXPECT_EQ(TIBase::getState(id), PartiallyDeleted);
}

View File

@@ -6,6 +6,7 @@
#include <boost/endian/detail/order.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <array>
@@ -205,125 +206,119 @@ TEST_F(BaseUintTest, base_uint)
Blob const raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
EXPECT_EQ(BaseUInt96::kBytes, raw.size());
BaseUInt96 u = BaseUInt96::fromRaw(raw);
uset.insert(u);
EXPECT_EQ(raw.size(), u.size());
EXPECT_EQ(to_string(u), "0102030405060708090A0B0C");
EXPECT_EQ(toShortString(u), "01020304...");
EXPECT_EQ(*u.data(), 1);
EXPECT_EQ(u.signum(), 1);
EXPECT_FALSE(!u);
EXPECT_FALSE(u.isZero());
EXPECT_TRUE(u.isNonZero());
unsigned char t = 0;
for (auto& d : u)
{
EXPECT_EQ(d, ++t);
}
BaseUInt96 ascending = BaseUInt96::fromRaw(raw);
uset.insert(ascending);
EXPECT_EQ(raw.size(), ascending.size());
EXPECT_EQ(to_string(ascending), "0102030405060708090A0B0C");
EXPECT_EQ(toShortString(ascending), "01020304...");
EXPECT_EQ(*ascending.data(), 1);
EXPECT_EQ(ascending.signum(), 1);
EXPECT_FALSE(!ascending);
EXPECT_FALSE(ascending.isZero());
EXPECT_TRUE(ascending.isNonZero());
unsigned char expectedByte = 0;
for (auto& byte : ascending)
EXPECT_EQ(byte, ++expectedByte);
// Test hash_append by "hashing" with a no-op hasher (h)
// Test hash_append by "hashing" with a no-op hasher (hasher)
// and then extracting the bytes that were written during hashing
// back into another base_uint (w) for comparison with the original
Nonhash<96> h{};
hash_append(h, u);
BaseUInt96 const w =
BaseUInt96::fromRaw(std::vector<std::uint8_t>(h.data.begin(), h.data.end()));
EXPECT_EQ(w, u);
// back into another base_uint (rehashed) for comparison with the original
Nonhash<96> hasher{};
hash_append(hasher, ascending);
BaseUInt96 const rehashed =
BaseUInt96::fromRaw(std::vector<std::uint8_t>(hasher.data.begin(), hasher.data.end()));
EXPECT_EQ(rehashed, ascending);
BaseUInt96 v{~u};
uset.insert(v);
EXPECT_EQ(to_string(v), "FEFDFCFBFAF9F8F7F6F5F4F3");
EXPECT_EQ(toShortString(v), "FEFDFCFB...");
EXPECT_EQ(*v.data(), 0xfe);
EXPECT_EQ(v.signum(), 1);
EXPECT_FALSE(!v);
EXPECT_FALSE(v.isZero());
EXPECT_TRUE(v.isNonZero());
BaseUInt96 complement{~ascending};
uset.insert(complement);
EXPECT_EQ(to_string(complement), "FEFDFCFBFAF9F8F7F6F5F4F3");
EXPECT_EQ(toShortString(complement), "FEFDFCFB...");
EXPECT_EQ(*complement.data(), 0xfe);
EXPECT_EQ(complement.signum(), 1);
EXPECT_FALSE(!complement);
EXPECT_FALSE(complement.isZero());
EXPECT_TRUE(complement.isNonZero());
t = 0xff;
for (auto& d : v)
{
EXPECT_EQ(d, --t);
}
expectedByte = 0xff;
for (auto& byte : complement)
EXPECT_EQ(byte, --expectedByte);
EXPECT_LT(u, v);
EXPECT_GT(v, u);
EXPECT_LT(ascending, complement);
EXPECT_GT(complement, ascending);
v = u;
EXPECT_EQ(v, u);
complement = ascending;
EXPECT_EQ(complement, ascending);
BaseUInt96 z{beast::kZero};
uset.insert(z);
EXPECT_EQ(to_string(z), "000000000000000000000000");
EXPECT_EQ(toShortString(z), "00000000...");
EXPECT_EQ(*z.data(), 0);
EXPECT_EQ(*z.begin(), 0);
EXPECT_EQ(*std::prev(z.end(), 1), 0);
EXPECT_EQ(z.signum(), 0);
EXPECT_TRUE(!z);
EXPECT_TRUE(z.isZero());
EXPECT_FALSE(z.isNonZero());
for (auto& d : z)
{
EXPECT_EQ(d, 0);
}
BaseUInt96 zero{beast::kZero};
uset.insert(zero);
EXPECT_EQ(to_string(zero), "000000000000000000000000");
EXPECT_EQ(toShortString(zero), "00000000...");
EXPECT_EQ(*zero.data(), 0);
EXPECT_EQ(*zero.begin(), 0);
EXPECT_EQ(*std::prev(zero.end(), 1), 0);
EXPECT_EQ(zero.signum(), 0);
EXPECT_TRUE(!zero);
EXPECT_TRUE(zero.isZero());
EXPECT_FALSE(zero.isNonZero());
for (auto& byte : zero)
EXPECT_EQ(byte, 0);
{
// There are several ways to create a zero. beast::kZero is tested above. Test some
// others.
BaseUInt96 const z1;
EXPECT_EQ(z1, z) << to_string(z1);
BaseUInt96 const defaultZero;
EXPECT_EQ(defaultZero, zero) << to_string(defaultZero);
BaseUInt96 const z2{};
EXPECT_EQ(z2, z) << to_string(z2);
BaseUInt96 const bracedZero{};
EXPECT_EQ(bracedZero, zero) << to_string(bracedZero);
BaseUInt96 const z3{0u};
EXPECT_EQ(z3, z) << to_string(z3);
BaseUInt96 const zeroFromUInt{0u};
EXPECT_EQ(zeroFromUInt, zero) << to_string(zeroFromUInt);
}
BaseUInt96 n{z};
n++;
EXPECT_EQ(n, BaseUInt96(1));
n--;
EXPECT_EQ(n, beast::kZero);
EXPECT_EQ(n, z);
n--;
EXPECT_EQ(to_string(n), "FFFFFFFFFFFFFFFFFFFFFFFF");
EXPECT_EQ(toShortString(n), "FFFFFFFF...");
n = beast::kZero;
EXPECT_EQ(n, z);
BaseUInt96 counter{zero};
counter++;
EXPECT_EQ(counter, BaseUInt96(1));
counter--;
EXPECT_EQ(counter, beast::kZero);
EXPECT_EQ(counter, zero);
counter--;
EXPECT_EQ(to_string(counter), "FFFFFFFFFFFFFFFFFFFFFFFF");
EXPECT_EQ(toShortString(counter), "FFFFFFFF...");
counter = beast::kZero;
EXPECT_EQ(counter, zero);
BaseUInt96 zp1{z};
zp1++;
BaseUInt96 zm1{z};
zm1--;
BaseUInt96 const x{zm1 ^ zp1};
uset.insert(x);
EXPECT_EQ(to_string(x), "FFFFFFFFFFFFFFFFFFFFFFFE") << to_string(x);
EXPECT_EQ(toShortString(x), "FFFFFFFF...") << toShortString(x);
BaseUInt96 zeroPlusOne{zero};
zeroPlusOne++;
BaseUInt96 zeroMinusOne{zero};
zeroMinusOne--;
BaseUInt96 const xored{zeroMinusOne ^ zeroPlusOne};
uset.insert(xored);
EXPECT_EQ(to_string(xored), "FFFFFFFFFFFFFFFFFFFFFFFE") << to_string(xored);
EXPECT_EQ(toShortString(xored), "FFFFFFFF...") << toShortString(xored);
EXPECT_EQ(uset.size(), 4);
BaseUInt96 tmp;
EXPECT_TRUE(tmp.parseHex(to_string(u)));
EXPECT_EQ(tmp, u);
tmp = z;
BaseUInt96 parsed;
EXPECT_TRUE(parsed.parseHex(to_string(ascending)));
EXPECT_EQ(parsed, ascending);
parsed = zero;
// fails with extra char
EXPECT_FALSE(tmp.parseHex("A" + to_string(u)));
tmp = z;
EXPECT_FALSE(parsed.parseHex("A" + to_string(ascending)));
parsed = zero;
// fails with extra char at end
EXPECT_FALSE(tmp.parseHex(to_string(u) + "A"));
EXPECT_FALSE(parsed.parseHex(to_string(ascending) + "A"));
// fails with a non-hex character at some point in the string:
tmp = z;
parsed = zero;
for (std::size_t i = 0; i != 24; ++i)
{
std::string x = to_string(z);
x[i] = ('G' + (i % 10));
EXPECT_FALSE(tmp.parseHex(x));
std::string xored = to_string(zero);
xored[i] = ('G' + (i % 10));
EXPECT_FALSE(parsed.parseHex(xored));
}
// Walking 1s:
@@ -332,8 +327,8 @@ TEST_F(BaseUintTest, base_uint)
std::string s1 = "000000000000000000000000";
s1[i] = '1';
EXPECT_TRUE(tmp.parseHex(s1));
EXPECT_EQ(to_string(tmp), s1);
EXPECT_TRUE(parsed.parseHex(s1));
EXPECT_EQ(to_string(parsed), s1);
}
// Walking 0s:
@@ -342,8 +337,8 @@ TEST_F(BaseUintTest, base_uint)
std::string s1 = "111111111111111111111111";
s1[i] = '0';
EXPECT_TRUE(tmp.parseHex(s1));
EXPECT_EQ(to_string(tmp), s1);
EXPECT_TRUE(parsed.parseHex(s1));
EXPECT_EQ(to_string(parsed), s1);
}
// Constexpr constructors
@@ -357,39 +352,27 @@ TEST_F(BaseUintTest, base_uint)
// Using the constexpr constructor in a non-constexpr context
// with an error in the parsing throws an exception.
{
// Invalid length for string.
bool caught = false;
try
{
// Try to prevent constant evaluation.
std::vector<char> str(23, '7');
// Invalid length for string. The vector keeps this out of a constant
// expression, so the constructor throws instead of failing to compile.
auto tooShort = [] {
std::vector<char> const str(23, '7');
std::string_view const sView(str.data(), str.size());
[[maybe_unused]] BaseUInt96 const t96(sView);
}
catch (std::invalid_argument const& e)
{
EXPECT_EQ(e.what(), std::string("invalid length for hex string"));
caught = true;
}
EXPECT_TRUE(caught);
};
EXPECT_THAT(
tooShort,
::testing::ThrowsMessage<std::invalid_argument>("invalid length for hex string"));
}
{
// Invalid character in string.
bool caught = false;
try
{
// Try to prevent constant evaluation.
auto badCharacter = [] {
std::vector<char> str(23, '7');
str.push_back('G');
std::string_view const sView(str.data(), str.size());
[[maybe_unused]] BaseUInt96 const t96(sView);
}
catch (std::range_error const& e)
{
EXPECT_EQ(e.what(), std::string("invalid hex character"));
caught = true;
}
EXPECT_TRUE(caught);
};
EXPECT_THAT(
badCharacter, ::testing::ThrowsMessage<std::range_error>("invalid hex character"));
}
// Verify that constexpr base_uints interpret a string the same
@@ -412,11 +395,11 @@ TEST_F(BaseUintTest, base_uint)
"fFfFfFfFfFfFfFfFfFfFfFfF",
});
for (StrBaseUInt const& t : kTestCases)
for (StrBaseUInt const& expectedByte : kTestCases)
{
BaseUInt96 t96;
EXPECT_TRUE(t96.parseHex(t.str));
EXPECT_EQ(t96, t.tst);
EXPECT_TRUE(t96.parseHex(expectedByte.str));
EXPECT_EQ(t96, expectedByte.tst);
}
}
}

View File

@@ -16,7 +16,6 @@
#include <helpers/TestSink.h>
#include <shamap/common.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
@@ -113,7 +112,7 @@ protected:
intToVuc(std::uint8_t v)
{
Buffer vuc{32};
std::fill_n(vuc.data(), vuc.size(), v);
vuc.fill(v);
return vuc;
}
};