diff --git a/.cspell.config.yaml b/.cspell.config.yaml index 21b0145f43..bb763e9935 100644 --- a/.cspell.config.yaml +++ b/.cspell.config.yaml @@ -365,6 +365,7 @@ words: - xchain - ximinez - XMACRO + - xored - xrpkuwait - xrpl - xrpld diff --git a/cmake/XrplCov.cmake b/cmake/XrplCov.cmake index 86ba534a88..05d9ed3806 100644 --- a/cmake/XrplCov.cmake +++ b/cmake/XrplCov.cmake @@ -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" diff --git a/include/xrpl/basics/Buffer.h b/include/xrpl/basics/Buffer.h index 705a5ef51a..00a6b7ecf9 100644 --- a/include/xrpl/basics/Buffer.h +++ b/include/xrpl/basics/Buffer.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -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. diff --git a/src/benchmarks/libxrpl/nodestore/Backend.cpp b/src/benchmarks/libxrpl/nodestore/Backend.cpp index cd3e15bd65..9d5937f869 100644 --- a/src/benchmarks/libxrpl/nodestore/Backend.cpp +++ b/src/benchmarks/libxrpl/nodestore/Backend.cpp @@ -41,10 +41,11 @@ struct RunState release() { harness.reset(); - Batch{}.swap(present); - Batch{}.swap(recent); - std::vector{}.swap(missing); - std::vector{}.swap(shuffle); + present = Batch{}; + recent = Batch{}; + missing = std::vector{}; + shuffle = std::vector{}; + avgPayload = 0; } }; @@ -239,9 +240,13 @@ registerWorkload(BackendConfig const& bc, Workload const& w) if (!w.pinToPool) { auto rs = std::make_shared(); - 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; } diff --git a/src/benchmarks/libxrpl/nodestore/NodeStoreBench.h b/src/benchmarks/libxrpl/nodestore/NodeStoreBench.h index debdc5d47a..6122dd2535 100644 --- a/src/benchmarks/libxrpl/nodestore/NodeStoreBench.h +++ b/src/benchmarks/libxrpl/nodestore/NodeStoreBench.h @@ -297,12 +297,11 @@ struct BackendConfig inline std::vector const& backendConfigs() { + // Use factory settings for each DB static std::vector 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; diff --git a/src/tests/libxrpl/basics/Buffer.cpp b/src/tests/libxrpl/basics/Buffer.cpp index 9cdf610282..a3f78e8bcf 100644 --- a/src/tests/libxrpl/basics/Buffer.cpp +++ b/src/tests/libxrpl/basics/Buffer.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -12,8 +13,18 @@ namespace xrpl::test { +static_assert(std::is_nothrow_move_constructible_v); +static_assert(std::is_nothrow_move_assignable_v); + struct BufferTest : public ::testing::Test { + static constexpr auto kRandomData = std::to_array( + {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(emptyBuffer)}; + EXPECT_TRUE(sane(fromEmpty)); + EXPECT_EQ(fromEmpty, emptyBuffer); + + Buffer const fromNonEmpty{static_cast(whole)}; + EXPECT_TRUE(sane(fromNonEmpty)); + EXPECT_EQ(fromNonEmpty, whole); +} + +TEST_F(BufferTest, assignment_from_slice) +{ + Buffer b; + + // empty <- empty slice + b = static_cast(emptyBuffer); + EXPECT_TRUE(sane(b)); + EXPECT_EQ(b, emptyBuffer); + + // empty <- non-empty slice + b = static_cast(firstHalf); + EXPECT_TRUE(sane(b)); + EXPECT_EQ(b, firstHalf); + + // non-empty <- non-empty slice + b = static_cast(secondHalf); + EXPECT_TRUE(sane(b)); + EXPECT_EQ(b, secondHalf); + + // non-empty <- empty slice + b = static_cast(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); - static_assert(std::is_nothrow_move_assignable_v); - - { // 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(b0)}; - EXPECT_TRUE(sane(w)); - EXPECT_EQ(w, b0); - - Buffer x{static_cast(b1)}; - EXPECT_TRUE(sane(x)); - EXPECT_EQ(x, b1); - - Buffer y{static_cast(b2)}; - EXPECT_TRUE(sane(y)); - EXPECT_EQ(y, b2); - - Buffer z{static_cast(b3)}; - EXPECT_TRUE(sane(z)); - EXPECT_EQ(z, b3); - - // Assign empty slice to empty buffer - w = static_cast(b0); - EXPECT_TRUE(sane(w)); - EXPECT_EQ(w, b0); - - // Assign non-empty slice to empty buffer - w = static_cast(b1); - EXPECT_TRUE(sane(w)); - EXPECT_EQ(w, b1); - - // Assign non-empty slice to non-empty buffer - x = static_cast(b2); - EXPECT_TRUE(sane(x)); - EXPECT_EQ(x, b2); - - // Assign non-empty slice to non-empty buffer - y = static_cast(z); - EXPECT_TRUE(sane(y)); - EXPECT_EQ(y, z); - - // Assign empty slice to non-empty buffer: - z = static_cast(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 diff --git a/src/tests/libxrpl/basics/IntrusiveShared.cpp b/src/tests/libxrpl/basics/IntrusiveShared.cpp index b9f8930b7b..c6c9fcfef0 100644 --- a/src/tests/libxrpl/basics/IntrusiveShared.cpp +++ b/src/tests/libxrpl/basics/IntrusiveShared.cpp @@ -92,6 +92,7 @@ public: static constexpr std::size_t kMaxStates = 128; static std::array, kMaxStates> state; static std::atomic 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) {}; @@ -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); } diff --git a/src/tests/libxrpl/basics/base_uint.cpp b/src/tests/libxrpl/basics/base_uint.cpp index 10795f4563..969705b5b7 100644 --- a/src/tests/libxrpl/basics/base_uint.cpp +++ b/src/tests/libxrpl/basics/base_uint.cpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -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(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(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 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 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("invalid length for hex string")); } { // Invalid character in string. - bool caught = false; - try - { - // Try to prevent constant evaluation. + auto badCharacter = [] { std::vector 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("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); } } } diff --git a/src/tests/libxrpl/shamap/SHAMap.cpp b/src/tests/libxrpl/shamap/SHAMap.cpp index e662e16be4..c84cdf504f 100644 --- a/src/tests/libxrpl/shamap/SHAMap.cpp +++ b/src/tests/libxrpl/shamap/SHAMap.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -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; } };