Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-04-29 20:24:52 +01:00
1526 changed files with 75760 additions and 25422 deletions

View File

@@ -32,20 +32,9 @@ xrpl_add_test(json)
target_link_libraries(xrpl.test.json PRIVATE xrpl.imports.test)
add_dependencies(xrpl.tests xrpl.test.json)
# protocol_autogen tests use explicit source list (not GLOB) because sources are generated
# Mark generated sources so CMake knows they'll be created at build time
set_source_files_properties(
${PROTOCOL_AUTOGEN_TEST_SOURCES}
PROPERTIES GENERATED TRUE
)
add_executable(xrpl.test.protocol_autogen ${PROTOCOL_AUTOGEN_TEST_SOURCES})
xrpl_add_test(protocol_autogen)
target_link_libraries(xrpl.test.protocol_autogen PRIVATE xrpl.imports.test)
add_dependencies(xrpl.tests xrpl.test.protocol_autogen)
add_test(NAME xrpl.test.protocol_autogen COMMAND xrpl.test.protocol_autogen)
# Ensure code generation runs before compiling tests
if(TARGET protocol_autogen_generate)
add_dependencies(xrpl.test.protocol_autogen protocol_autogen_generate)
endif()
# Network unit tests are currently not supported on Windows
if(NOT WIN32)

View File

@@ -0,0 +1,213 @@
#include <xrpl/basics/MallocTrim.h>
#include <xrpl/beast/utility/Journal.h>
#include <boost/predef.h>
#include <gtest/gtest.h>
#include <string>
using namespace xrpl;
// cSpell:ignore statm
#if defined(__GLIBC__) && BOOST_OS_LINUX
namespace xrpl::detail {
long
parseStatmRSSkB(std::string const& statm);
} // namespace xrpl::detail
#endif
TEST(MallocTrimReport, structure)
{
// Test default construction
MallocTrimReport report;
EXPECT_EQ(report.supported, false);
EXPECT_EQ(report.trimResult, -1);
EXPECT_EQ(report.rssBeforeKB, -1);
EXPECT_EQ(report.rssAfterKB, -1);
EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1});
EXPECT_EQ(report.minfltDelta, -1);
EXPECT_EQ(report.majfltDelta, -1);
EXPECT_EQ(report.deltaKB(), 0);
// Test deltaKB calculation - memory freed
report.rssBeforeKB = 1000;
report.rssAfterKB = 800;
EXPECT_EQ(report.deltaKB(), -200);
// Test deltaKB calculation - memory increased
report.rssBeforeKB = 500;
report.rssAfterKB = 600;
EXPECT_EQ(report.deltaKB(), 100);
// Test deltaKB calculation - no change
report.rssBeforeKB = 1234;
report.rssAfterKB = 1234;
EXPECT_EQ(report.deltaKB(), 0);
}
#if defined(__GLIBC__) && BOOST_OS_LINUX
TEST(parseStatmRSSkB, standard_format)
{
using xrpl::detail::parseStatmRSSkB;
// Test standard format: size resident shared text lib data dt
// Assuming 4KB page size: resident=1000 pages = 4000 KB
{
std::string const statm = "25365 1000 2377 0 0 5623 0";
long const result = parseStatmRSSkB(statm);
// Note: actual result depends on system page size
// On most systems it's 4KB, so 1000 pages = 4000 KB
EXPECT_GT(result, 0);
}
// Test with newline
{
std::string const statm = "12345 2000 1234 0 0 3456 0\n";
long const result = parseStatmRSSkB(statm);
EXPECT_GT(result, 0);
}
// Test with tabs
{
std::string const statm = "12345\t2000\t1234\t0\t0\t3456\t0";
long const result = parseStatmRSSkB(statm);
EXPECT_GT(result, 0);
}
// Test zero resident pages
{
std::string const statm = "25365 0 2377 0 0 5623 0";
long const result = parseStatmRSSkB(statm);
EXPECT_EQ(result, 0);
}
// Test with extra whitespace
{
std::string const statm = " 25365 1000 2377 ";
long const result = parseStatmRSSkB(statm);
EXPECT_GT(result, 0);
}
// Test empty string
{
std::string const statm;
long const result = parseStatmRSSkB(statm);
EXPECT_EQ(result, -1);
}
// Test malformed data (only one field)
{
std::string const statm = "25365";
long const result = parseStatmRSSkB(statm);
EXPECT_EQ(result, -1);
}
// Test malformed data (non-numeric)
{
std::string const statm = "abc def ghi";
long const result = parseStatmRSSkB(statm);
EXPECT_EQ(result, -1);
}
// Test malformed data (second field non-numeric)
{
std::string const statm = "25365 abc 2377";
long const result = parseStatmRSSkB(statm);
EXPECT_EQ(result, -1);
}
}
#endif
TEST(mallocTrim, without_debug_logging)
{
beast::Journal const journal{beast::Journal::getNullSink()};
MallocTrimReport const report = mallocTrim("without_debug", journal);
#if defined(__GLIBC__) && BOOST_OS_LINUX
EXPECT_EQ(report.supported, true);
EXPECT_GE(report.trimResult, 0);
EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1});
EXPECT_EQ(report.minfltDelta, -1);
EXPECT_EQ(report.majfltDelta, -1);
#else
EXPECT_EQ(report.supported, false);
EXPECT_EQ(report.trimResult, -1);
EXPECT_EQ(report.rssBeforeKB, -1);
EXPECT_EQ(report.rssAfterKB, -1);
EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1});
EXPECT_EQ(report.minfltDelta, -1);
EXPECT_EQ(report.majfltDelta, -1);
#endif
}
TEST(mallocTrim, empty_tag)
{
beast::Journal const journal{beast::Journal::getNullSink()};
MallocTrimReport const report = mallocTrim("", journal);
#if defined(__GLIBC__) && BOOST_OS_LINUX
EXPECT_EQ(report.supported, true);
EXPECT_GE(report.trimResult, 0);
#else
EXPECT_EQ(report.supported, false);
#endif
}
TEST(mallocTrim, with_debug_logging)
{
struct DebugSink : public beast::Journal::Sink
{
DebugSink() : Sink(beast::severities::kDebug, false)
{
}
void
write(beast::severities::Severity, std::string const&) override
{
}
void
writeAlways(beast::severities::Severity, std::string const&) override
{
}
};
DebugSink sink;
beast::Journal const journal{sink};
MallocTrimReport const report = mallocTrim("debug_test", journal);
#if defined(__GLIBC__) && BOOST_OS_LINUX
EXPECT_EQ(report.supported, true);
EXPECT_GE(report.trimResult, 0);
EXPECT_GE(report.durationUs.count(), 0);
EXPECT_GE(report.minfltDelta, 0);
EXPECT_GE(report.majfltDelta, 0);
#else
EXPECT_EQ(report.supported, false);
EXPECT_EQ(report.trimResult, -1);
EXPECT_EQ(report.durationUs, std::chrono::microseconds{-1});
EXPECT_EQ(report.minfltDelta, -1);
EXPECT_EQ(report.majfltDelta, -1);
#endif
}
TEST(mallocTrim, repeated_calls)
{
beast::Journal const journal{beast::Journal::getNullSink()};
// Call malloc_trim multiple times to ensure it's safe
for (int i = 0; i < 5; ++i)
{
MallocTrimReport const report = mallocTrim("iteration_" + std::to_string(i), journal);
#if defined(__GLIBC__) && BOOST_OS_LINUX
EXPECT_EQ(report.supported, true);
EXPECT_GE(report.trimResult, 0);
#else
EXPECT_EQ(report.supported, false);
#endif
}
}

View File

@@ -6,6 +6,8 @@
#include <mutex>
#include <shared_mutex>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
using namespace xrpl;
@@ -183,7 +185,7 @@ TEST_F(MutexConstCorrectnessTest, non_const_allows_modification)
TEST_F(MutexConstCorrectnessTest, const_reference_provides_const_access)
{
Mutex<std::vector<int>> m({1, 2, 3, 4, 5, 6});
Mutex<std::vector<int>> const m({1, 2, 3, 4, 5, 6});
Mutex<std::vector<int>> const& const_ref = m;
auto lock = const_ref.lock();
static_assert(std::is_const_v<std::remove_reference_t<decltype(*lock)>>);
@@ -225,7 +227,7 @@ struct MutexSharedMutexTest : ::testing::Test
TEST_F(MutexSharedMutexTest, shared_lock_for_const_access)
{
Mutex<int, std::shared_mutex> m(100);
Mutex<int, std::shared_mutex> const m(100);
Mutex<int, std::shared_mutex> const& const_ref = m;
auto lock = const_ref.lock<std::shared_lock>();
EXPECT_EQ(*lock, 100);

View File

@@ -1,5 +1,8 @@
#include <xrpl/basics/RangeSet.h>
#include <boost/icl/concept/interval_associator.hpp>
#include <boost/icl/concept/interval_set.hpp>
#include <gtest/gtest.h>
#include <cstdint>

View File

@@ -3,6 +3,7 @@
#include <gtest/gtest.h>
#include <array>
#include <cstddef>
#include <cstdint>
using namespace xrpl;

View File

@@ -3,7 +3,6 @@
#include <gtest/gtest.h>
#include <stdexcept>
#include <string>
using namespace xrpl;

View File

@@ -2,6 +2,8 @@
#include <gtest/gtest.h>
#include <utility>
using namespace xrpl;
TEST(scope, scope_exit)
@@ -10,7 +12,7 @@ TEST(scope, scope_exit)
// unless release() is called
int i = 0;
{
scope_exit x{[&i]() { i = 1; }};
scope_exit const x{[&i]() { i = 1; }};
}
EXPECT_EQ(i, 1);
{
@@ -32,7 +34,7 @@ TEST(scope, scope_exit)
{
try
{
scope_exit x{[&i]() { i = 5; }};
scope_exit const x{[&i]() { i = 5; }};
throw 1;
}
catch (...) // NOLINT(bugprone-empty-catch)
@@ -60,7 +62,7 @@ TEST(scope, scope_fail)
// if an exception is unwinding, unless release() is called
int i = 0;
{
scope_fail x{[&i]() { i = 1; }};
scope_fail const x{[&i]() { i = 1; }};
}
EXPECT_EQ(i, 0);
{
@@ -82,7 +84,7 @@ TEST(scope, scope_fail)
{
try
{
scope_fail x{[&i]() { i = 5; }};
scope_fail const x{[&i]() { i = 5; }};
throw 1;
}
catch (...) // NOLINT(bugprone-empty-catch)
@@ -110,7 +112,7 @@ TEST(scope, scope_success)
// if an exception is not unwinding, unless release() is called
int i = 0;
{
scope_success x{[&i]() { i = 1; }};
scope_success const x{[&i]() { i = 1; }};
}
EXPECT_EQ(i, 1);
{
@@ -132,7 +134,7 @@ TEST(scope, scope_success)
{
try
{
scope_success x{[&i]() { i = 5; }};
scope_success const x{[&i]() { i = 5; }};
throw 1;
}
catch (...) // NOLINT(bugprone-empty-catch)

View File

@@ -2,6 +2,7 @@
#include <gtest/gtest.h>
#include <cstdint>
#include <type_traits>
using namespace xrpl;
@@ -21,85 +22,85 @@ using TagUInt3 = tagged_integer<std::uint64_t, Tag1>;
// Check construction of tagged_integers
static_assert(
std::is_constructible<TagUInt1, std::uint32_t>::value,
std::is_constructible_v<TagUInt1, std::uint32_t>,
"TagUInt1 should be constructible using a std::uint32_t");
static_assert(
!std::is_constructible<TagUInt1, std::uint64_t>::value,
!std::is_constructible_v<TagUInt1, std::uint64_t>,
"TagUInt1 should not be constructible using a std::uint64_t");
static_assert(
std::is_constructible<TagUInt3, std::uint32_t>::value,
std::is_constructible_v<TagUInt3, std::uint32_t>,
"TagUInt3 should be constructible using a std::uint32_t");
static_assert(
std::is_constructible<TagUInt3, std::uint64_t>::value,
std::is_constructible_v<TagUInt3, std::uint64_t>,
"TagUInt3 should be constructible using a std::uint64_t");
// Check assignment of tagged_integers
static_assert(
!std::is_assignable<TagUInt1, std::uint32_t>::value,
!std::is_assignable_v<TagUInt1, std::uint32_t>,
"TagUInt1 should not be assignable with a std::uint32_t");
static_assert(
!std::is_assignable<TagUInt1, std::uint64_t>::value,
!std::is_assignable_v<TagUInt1, std::uint64_t>,
"TagUInt1 should not be assignable with a std::uint64_t");
static_assert(
!std::is_assignable<TagUInt3, std::uint32_t>::value,
!std::is_assignable_v<TagUInt3, std::uint32_t>,
"TagUInt3 should not be assignable with a std::uint32_t");
static_assert(
!std::is_assignable<TagUInt3, std::uint64_t>::value,
!std::is_assignable_v<TagUInt3, std::uint64_t>,
"TagUInt3 should not be assignable with a std::uint64_t");
static_assert(
std::is_assignable<TagUInt1, TagUInt1>::value,
std::is_assignable_v<TagUInt1, TagUInt1>,
"TagUInt1 should be assignable with a TagUInt1");
static_assert(
!std::is_assignable<TagUInt1, TagUInt2>::value,
!std::is_assignable_v<TagUInt1, TagUInt2>,
"TagUInt1 should not be assignable with a TagUInt2");
static_assert(
std::is_assignable<TagUInt3, TagUInt3>::value,
std::is_assignable_v<TagUInt3, TagUInt3>,
"TagUInt3 should be assignable with a TagUInt1");
static_assert(
!std::is_assignable<TagUInt1, TagUInt3>::value,
!std::is_assignable_v<TagUInt1, TagUInt3>,
"TagUInt1 should not be assignable with a TagUInt3");
static_assert(
!std::is_assignable<TagUInt3, TagUInt1>::value,
!std::is_assignable_v<TagUInt3, TagUInt1>,
"TagUInt3 should not be assignable with a TagUInt1");
// Check convertibility of tagged_integers
static_assert(
!std::is_convertible<std::uint32_t, TagUInt1>::value,
!std::is_convertible_v<std::uint32_t, TagUInt1>,
"std::uint32_t should not be convertible to a TagUInt1");
static_assert(
!std::is_convertible<std::uint32_t, TagUInt3>::value,
!std::is_convertible_v<std::uint32_t, TagUInt3>,
"std::uint32_t should not be convertible to a TagUInt3");
static_assert(
!std::is_convertible<std::uint64_t, TagUInt3>::value,
!std::is_convertible_v<std::uint64_t, TagUInt3>,
"std::uint64_t should not be convertible to a TagUInt3");
static_assert(
!std::is_convertible<std::uint64_t, TagUInt2>::value,
!std::is_convertible_v<std::uint64_t, TagUInt2>,
"std::uint64_t should not be convertible to a TagUInt2");
static_assert(
!std::is_convertible<TagUInt1, TagUInt2>::value,
!std::is_convertible_v<TagUInt1, TagUInt2>,
"TagUInt1 should not be convertible to TagUInt2");
static_assert(
!std::is_convertible<TagUInt1, TagUInt3>::value,
!std::is_convertible_v<TagUInt1, TagUInt3>,
"TagUInt1 should not be convertible to TagUInt3");
static_assert(
!std::is_convertible<TagUInt2, TagUInt3>::value,
!std::is_convertible_v<TagUInt2, TagUInt3>,
"TagUInt2 should not be convertible to a TagUInt3");
using TagInt = tagged_integer<std::int32_t, Tag1>;
@@ -147,7 +148,7 @@ TEST(tagged_integer, increment_decrement_operators)
TEST(tagged_integer, arithmetic_operators)
{
TagInt a{-2};
TagInt const a{-2};
EXPECT_EQ(+a, TagInt{-2});
EXPECT_EQ(-a, TagInt{2});
EXPECT_EQ(TagInt{-3} + TagInt{4}, TagInt{1});

View File

@@ -2,6 +2,8 @@
#include <gtest/gtest.h>
#include <cstdint>
using namespace xrpl;
TEST(csprng, get_values)

View File

@@ -1,8 +1,11 @@
#include <boost/predef.h>
#include <helpers/TestSink.h>
#include <xrpl/beast/utility/Journal.h>
#include <boost/predef.h>
#include <cstdlib> // for getenv
#include <string>
#if BOOST_OS_WINDOWS
#include <io.h> // for _isatty, _fileno

View File

@@ -1,4 +1,5 @@
#include <xrpl/json/Output.h>
#include <xrpl/json/json_reader.h>
#include <xrpl/json/json_writer.h>

View File

@@ -1,5 +1,6 @@
#include <xrpl/beast/core/LexicalCast.h>
#include <xrpl/json/json_errors.h>
#include <xrpl/json/json_forwards.h>
#include <xrpl/json/json_reader.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>
@@ -7,10 +8,15 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <exception>
#include <limits>
#include <numbers>
#include <regex>
#include <sstream>
#include <string>
#include <utility>
namespace xrpl {
@@ -38,7 +44,7 @@ TEST(json_value, construct_and_compare_Json_StaticString)
EXPECT_EQ(test1, test2);
EXPECT_NE(test1, test3);
std::string str{sample};
std::string const str{sample};
EXPECT_EQ(str, test2);
EXPECT_NE(str, test3);
EXPECT_EQ(test2, str);
@@ -52,7 +58,7 @@ TEST(json_value, different_types)
auto testCopy = [](Json::ValueType typ) {
Json::Value val{typ};
Json::Value cpy{val};
Json::Value const cpy{val};
EXPECT_EQ(val.type(), typ);
EXPECT_EQ(cpy.type(), typ);
return val;
@@ -135,7 +141,7 @@ TEST(json_value, different_types)
{
Json::Value const staticStrV{staticStr};
{
Json::Value cpy{staticStrV};
Json::Value const cpy{staticStrV};
EXPECT_EQ(staticStrV.type(), Json::stringValue);
EXPECT_EQ(cpy.type(), Json::stringValue);
}
@@ -588,13 +594,13 @@ TEST(json_value, bad_json)
TEST(json_value, edge_cases)
{
std::uint32_t max_uint = std::numeric_limits<std::uint32_t>::max();
std::int32_t max_int = std::numeric_limits<std::int32_t>::max();
std::int32_t min_int = std::numeric_limits<std::int32_t>::min();
std::uint32_t const max_uint = std::numeric_limits<std::uint32_t>::max();
std::int32_t const max_int = std::numeric_limits<std::int32_t>::max();
std::int32_t const min_int = std::numeric_limits<std::int32_t>::min();
std::uint32_t a_uint = max_uint - 1978;
std::int32_t a_large_int = max_int - 1978;
std::int32_t a_small_int = min_int + 1978;
std::uint32_t const a_uint = max_uint - 1978;
std::int32_t const a_large_int = max_int - 1978;
std::int32_t const a_small_int = min_int + 1978;
{
std::string json = "{\"max_uint\":" + std::to_string(max_uint);
@@ -628,7 +634,7 @@ TEST(json_value, edge_cases)
EXPECT_LT(j1["a_small_int"], a_uint);
}
std::uint64_t overflow = std::uint64_t(max_uint) + 1;
std::uint64_t const overflow = std::uint64_t(max_uint) + 1;
{
std::string json = "{\"overflow\":";
json += std::to_string(overflow);
@@ -640,7 +646,7 @@ TEST(json_value, edge_cases)
EXPECT_FALSE(r2.parse(json, j2));
}
std::int64_t underflow = std::int64_t(min_int) - 1;
std::int64_t const underflow = std::int64_t(min_int) - 1;
{
std::string json = "{\"underflow\":";
json += std::to_string(underflow);
@@ -654,8 +660,8 @@ TEST(json_value, edge_cases)
{
Json::Value intString{std::to_string(overflow)};
EXPECT_THROW(intString.asUInt(), beast::BadLexicalCast);
EXPECT_THROW(intString.asAbsUInt(), Json::error);
EXPECT_THROW([&] { return intString.asUInt(); }(), beast::BadLexicalCast);
EXPECT_THROW([&] { return intString.asAbsUInt(); }(), Json::error);
intString = "4294967295";
EXPECT_EQ(intString.asUInt(), 4294967295u);
@@ -666,17 +672,17 @@ TEST(json_value, edge_cases)
EXPECT_EQ(intString.asAbsUInt(), 0);
intString = "-1";
EXPECT_THROW(intString.asUInt(), beast::BadLexicalCast);
EXPECT_THROW([&] { return intString.asUInt(); }(), beast::BadLexicalCast);
EXPECT_EQ(intString.asAbsUInt(), 1);
intString = "-4294967295";
EXPECT_EQ(intString.asAbsUInt(), 4294967295);
intString = "-4294967296";
EXPECT_THROW(intString.asAbsUInt(), Json::error);
EXPECT_THROW([&] { return intString.asAbsUInt(); }(), Json::error);
intString = "2147483648";
EXPECT_THROW(intString.asInt(), beast::BadLexicalCast);
EXPECT_THROW([&] { return intString.asInt(); }(), beast::BadLexicalCast);
EXPECT_EQ(intString.asAbsUInt(), 2147483648);
intString = "2147483647";
@@ -688,14 +694,14 @@ TEST(json_value, edge_cases)
EXPECT_EQ(intString.asAbsUInt(), 2147483648LL);
intString = "-2147483649";
EXPECT_THROW(intString.asInt(), beast::BadLexicalCast);
EXPECT_THROW([&] { return intString.asInt(); }(), beast::BadLexicalCast);
EXPECT_EQ(intString.asAbsUInt(), 2147483649);
}
{
Json::Value intReal{4294967297.0};
EXPECT_THROW(intReal.asUInt(), Json::error);
EXPECT_THROW(intReal.asAbsUInt(), Json::error);
EXPECT_THROW([&] { return intReal.asUInt(); }(), Json::error);
EXPECT_THROW([&] { return intReal.asAbsUInt(); }(), Json::error);
intReal = 4294967295.0;
EXPECT_EQ(intReal.asUInt(), 4294967295u);
@@ -706,17 +712,17 @@ TEST(json_value, edge_cases)
EXPECT_EQ(intReal.asAbsUInt(), 0);
intReal = -1.0;
EXPECT_THROW(intReal.asUInt(), Json::error);
EXPECT_THROW([&] { return intReal.asUInt(); }(), Json::error);
EXPECT_EQ(intReal.asAbsUInt(), 1);
intReal = -4294967295.0;
EXPECT_EQ(intReal.asAbsUInt(), 4294967295);
intReal = -4294967296.0;
EXPECT_THROW(intReal.asAbsUInt(), Json::error);
EXPECT_THROW([&] { return intReal.asAbsUInt(); }(), Json::error);
intReal = 2147483648.0;
EXPECT_THROW(intReal.asInt(), Json::error);
EXPECT_THROW([&] { return intReal.asInt(); }(), Json::error);
EXPECT_EQ(intReal.asAbsUInt(), 2147483648);
intReal = 2147483647.0;
@@ -728,7 +734,7 @@ TEST(json_value, edge_cases)
EXPECT_EQ(intReal.asAbsUInt(), 2147483648LL);
intReal = -2147483649.0;
EXPECT_THROW(intReal.asInt(), Json::error);
EXPECT_THROW([&] { return intReal.asInt(); }(), Json::error);
EXPECT_EQ(intReal.asAbsUInt(), 2147483649);
}
}
@@ -739,7 +745,7 @@ TEST(json_value, copy)
EXPECT_TRUE(v1.isDouble());
EXPECT_EQ(v1.asDouble(), 2.5);
Json::Value v2 = v1;
Json::Value const v2 = v1;
EXPECT_TRUE(v1.isDouble());
EXPECT_EQ(v1.asDouble(), 2.5);
EXPECT_TRUE(v2.isDouble());
@@ -819,7 +825,7 @@ TEST(json_value, comparisons)
b["a"] = Json::Int(-1);
testGreaterThan("negative");
Json::Int big = std::numeric_limits<int>::max();
Json::Int const big = std::numeric_limits<int>::max();
Json::UInt bigger = big;
bigger++;
@@ -859,7 +865,7 @@ TEST(json_value, conversions)
// TODO: What's the thinking here?
{
// null
Json::Value val;
Json::Value const val;
EXPECT_TRUE(val.isNull());
// val.asCString() should trigger an assertion failure
EXPECT_EQ(val.asString(), "");
@@ -880,12 +886,12 @@ TEST(json_value, conversions)
}
{
// int
Json::Value val = -1234;
Json::Value const val = -1234;
EXPECT_TRUE(val.isInt());
// val.asCString() should trigger an assertion failure
EXPECT_EQ(val.asString(), "-1234");
EXPECT_EQ(val.asInt(), -1234);
EXPECT_THROW(val.asUInt(), Json::error);
EXPECT_THROW([&] { return val.asUInt(); }(), Json::error);
EXPECT_EQ(val.asAbsUInt(), 1234u);
EXPECT_EQ(val.asDouble(), -1234.0);
EXPECT_TRUE(val.asBool());
@@ -901,7 +907,7 @@ TEST(json_value, conversions)
}
{
// uint
Json::Value val = 1234U;
Json::Value const val = 1234U;
EXPECT_TRUE(val.isUInt());
// val.asCString() should trigger an assertion failure
EXPECT_EQ(val.asString(), "1234");
@@ -922,7 +928,7 @@ TEST(json_value, conversions)
}
{
// real
Json::Value val = 2.0;
Json::Value const val = 2.0;
EXPECT_TRUE(val.isDouble());
// val.asCString() should trigger an assertion failure
EXPECT_TRUE(std::regex_match(val.asString(), std::regex("^2\\.0*$")));
@@ -943,14 +949,14 @@ TEST(json_value, conversions)
}
{
// numeric string
Json::Value val = "54321";
Json::Value const val = "54321";
EXPECT_TRUE(val.isString());
EXPECT_EQ(strcmp(val.asCString(), "54321"), 0);
EXPECT_EQ(val.asString(), "54321");
EXPECT_EQ(val.asInt(), 54321);
EXPECT_EQ(val.asUInt(), 54321u);
EXPECT_EQ(val.asAbsUInt(), 54321);
EXPECT_THROW(val.asDouble(), Json::error);
EXPECT_THROW([&] { return val.asDouble(); }(), Json::error);
EXPECT_TRUE(val.asBool());
EXPECT_FALSE(val.isConvertibleTo(Json::nullValue));
@@ -964,14 +970,14 @@ TEST(json_value, conversions)
}
{
// non-numeric string
Json::Value val(Json::stringValue);
Json::Value const val(Json::stringValue);
EXPECT_TRUE(val.isString());
EXPECT_EQ(val.asCString(), nullptr);
EXPECT_EQ(val.asString(), "");
EXPECT_THROW(val.asInt(), std::exception);
EXPECT_THROW(val.asUInt(), std::exception);
EXPECT_THROW(val.asAbsUInt(), std::exception);
EXPECT_THROW(val.asDouble(), std::exception);
EXPECT_THROW([&] { return val.asInt(); }(), std::exception);
EXPECT_THROW([&] { return val.asUInt(); }(), std::exception);
EXPECT_THROW([&] { return val.asAbsUInt(); }(), std::exception);
EXPECT_THROW([&] { return val.asDouble(); }(), std::exception);
EXPECT_TRUE(val.asBool() == false);
EXPECT_TRUE(val.isConvertibleTo(Json::nullValue));
@@ -985,7 +991,7 @@ TEST(json_value, conversions)
}
{
// bool false
Json::Value val = false;
Json::Value const val = false;
EXPECT_TRUE(val.isBool());
// val.asCString() should trigger an assertion failure
EXPECT_EQ(val.asString(), "false");
@@ -1006,7 +1012,7 @@ TEST(json_value, conversions)
}
{
// bool true
Json::Value val = true;
Json::Value const val = true;
EXPECT_TRUE(val.isBool());
// val.asCString() should trigger an assertion failure
EXPECT_EQ(val.asString(), "true");
@@ -1027,14 +1033,14 @@ TEST(json_value, conversions)
}
{
// array type
Json::Value val(Json::arrayValue);
Json::Value const val(Json::arrayValue);
EXPECT_TRUE(val.isArray());
// val.asCString should trigger an assertion failure
EXPECT_THROW(val.asString(), Json::error);
EXPECT_THROW(val.asInt(), Json::error);
EXPECT_THROW(val.asUInt(), Json::error);
EXPECT_THROW(val.asAbsUInt(), Json::error);
EXPECT_THROW(val.asDouble(), Json::error);
EXPECT_THROW([&] { return val.asString(); }(), Json::error);
EXPECT_THROW([&] { return val.asInt(); }(), Json::error);
EXPECT_THROW([&] { return val.asUInt(); }(), Json::error);
EXPECT_THROW([&] { return val.asAbsUInt(); }(), Json::error);
EXPECT_THROW([&] { return val.asDouble(); }(), Json::error);
EXPECT_FALSE(val.asBool()); // empty or not
EXPECT_TRUE(val.isConvertibleTo(Json::nullValue));
@@ -1048,14 +1054,14 @@ TEST(json_value, conversions)
}
{
// object type
Json::Value val(Json::objectValue);
Json::Value const val(Json::objectValue);
EXPECT_TRUE(val.isObject());
// val.asCString should trigger an assertion failure
EXPECT_THROW(val.asString(), Json::error);
EXPECT_THROW(val.asInt(), Json::error);
EXPECT_THROW(val.asUInt(), Json::error);
EXPECT_THROW(val.asAbsUInt(), Json::error);
EXPECT_THROW(val.asDouble(), Json::error);
EXPECT_THROW([&] { return val.asString(); }(), Json::error);
EXPECT_THROW([&] { return val.asInt(); }(), Json::error);
EXPECT_THROW([&] { return val.asUInt(); }(), Json::error);
EXPECT_THROW([&] { return val.asAbsUInt(); }(), Json::error);
EXPECT_THROW([&] { return val.asDouble(); }(), Json::error);
EXPECT_FALSE(val.asBool()); // empty or not
EXPECT_TRUE(val.isConvertibleTo(Json::nullValue));
@@ -1099,7 +1105,7 @@ TEST(json_value, access_members)
EXPECT_FALSE(val.isValidIndex(0));
EXPECT_FALSE(val.isMember("key"));
val = 3.14159;
val = std::numbers::pi;
EXPECT_EQ(val.type(), Json::realValue);
EXPECT_EQ(val.size(), 0);
EXPECT_FALSE(val.isValidIndex(0));

View File

@@ -1,6 +1,8 @@
#include <xrpl/json/Writer.h>
#include <google/protobuf/stubs/port.h>
#include <xrpl/json/Output.h>
#include <xrpl/json/json_value.h>
#include <gtest/gtest.h>
#include <memory>

View File

@@ -1,24 +1,29 @@
#include <xrpl/basics/Log.h>
#include <xrpl/net/HTTPClient.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/co_spawn.hpp>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp> // IWYU pragma: keep
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/socket_base.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/use_future.hpp> // IWYU pragma: keep
#include <boost/beast/core.hpp> // IWYU pragma: keep
#include <boost/beast/http.hpp> // IWYU pragma: keep
#include <gtest/gtest.h>
#include <helpers/TestSink.h>
#include <atomic>
#include <chrono>
#include <exception>
#include <map>
#include <memory>
#include <semaphore>
#include <thread>
#include <string>
#include <utility>
#include <vector>
using namespace xrpl;
@@ -74,7 +79,7 @@ public:
return ioc_;
}
unsigned short
[[nodiscard]] unsigned short
port() const
{
return port_;
@@ -105,7 +110,7 @@ public:
acceptor_.close();
}
bool
[[nodiscard]] bool
finished() const
{
return finished_;
@@ -267,7 +272,7 @@ protected:
TEST_F(HTTPClientTest, case_insensitive_content_length)
{
// Test different cases of Content-Length header
std::vector<std::string> headerCases = {
std::vector<std::string> const headerCases = {
"Content-Length", // Standard case
"content-length", // Lowercase - this tests the regex icase fix
"CONTENT-LENGTH", // Uppercase
@@ -278,7 +283,7 @@ TEST_F(HTTPClientTest, case_insensitive_content_length)
for (auto const& headerName : headerCases)
{
TestHTTPServer server;
std::string testBody = "Hello World!";
std::string const testBody = "Hello World!";
server.setResponseBody(testBody);
server.setHeader(headerName, std::to_string(testBody.size()));
@@ -287,7 +292,7 @@ TEST_F(HTTPClientTest, case_insensitive_content_length)
std::string resultData;
boost::system::error_code resultError;
bool testCompleted =
bool const testCompleted =
runHTTPTest(server, "/test", completed, resultStatus, resultData, resultError);
// Verify results
EXPECT_TRUE(testCompleted);
@@ -300,7 +305,7 @@ TEST_F(HTTPClientTest, case_insensitive_content_length)
TEST_F(HTTPClientTest, basic_http_request)
{
TestHTTPServer server;
std::string testBody = "Test response body";
std::string const testBody = "Test response body";
server.setResponseBody(testBody);
server.setHeader("Content-Type", "text/plain");
@@ -309,7 +314,7 @@ TEST_F(HTTPClientTest, basic_http_request)
std::string resultData;
boost::system::error_code resultError;
bool testCompleted =
bool const testCompleted =
runHTTPTest(server, "/basic", completed, resultStatus, resultData, resultError);
EXPECT_TRUE(testCompleted);
@@ -329,7 +334,7 @@ TEST_F(HTTPClientTest, empty_response)
std::string resultData;
boost::system::error_code resultError;
bool testCompleted =
bool const testCompleted =
runHTTPTest(server, "/empty", completed, resultStatus, resultData, resultError);
EXPECT_TRUE(testCompleted);
@@ -340,7 +345,7 @@ TEST_F(HTTPClientTest, empty_response)
TEST_F(HTTPClientTest, different_status_codes)
{
std::vector<unsigned int> statusCodes = {200, 404, 500};
std::vector<unsigned int> const statusCodes = {200, 404, 500};
for (auto status : statusCodes)
{
@@ -353,7 +358,7 @@ TEST_F(HTTPClientTest, different_status_codes)
std::string resultData;
boost::system::error_code resultError;
bool testCompleted =
bool const testCompleted =
runHTTPTest(server, "/status", completed, resultStatus, resultData, resultError);
EXPECT_TRUE(testCompleted);

View File

@@ -0,0 +1,3 @@
# This disables all checks for this directory and its subdirectories
Checks: "-*"
InheritParentConfig: false

View File

@@ -1,70 +0,0 @@
#include <xrpl/protocol/MPTAmount.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol_autogen/STObjectValidation.h>
#include <gtest/gtest.h>
namespace xrpl {
TEST(STObjectValidation, validate_required_field)
{
SOTemplate format{{sfFlags, soeREQUIRED}};
STObject obj(sfGeneric);
obj.setFieldU32(sfFlags, 0);
EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format));
}
TEST(STObjectValidation, validate_missing_required_field)
{
SOTemplate format{{sfFlags, soeREQUIRED}};
STObject obj(sfGeneric);
EXPECT_FALSE(protocol_autogen::validateSTObject(obj, format));
}
TEST(STObjectValidation, validate_optional_field)
{
SOTemplate format{{sfFlags, soeOPTIONAL}};
STObject obj(sfGeneric);
obj.setFieldU32(sfFlags, 0);
EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format));
}
TEST(STObjectValidation, validate_missing_optional_field)
{
SOTemplate format{{sfFlags, soeOPTIONAL}};
STObject obj(sfGeneric);
EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format));
}
TEST(STObjectValidation, validate_mpt_amount_supported)
{
SOTemplate format{{sfAmount, soeREQUIRED, soeMPTSupported}};
STObject obj(sfGeneric);
obj.setFieldAmount(sfAmount, STAmount{MPTAmount{Number{1}}, MPTIssue{}});
EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format));
}
TEST(STObjectValidation, validate_mpt_amount_not_supported)
{
SOTemplate format{{sfAmount, soeREQUIRED, soeMPTNotSupported}};
STObject obj(sfGeneric);
obj.setFieldAmount(sfAmount, STAmount{MPTAmount{Number{1}}, MPTIssue{}});
EXPECT_FALSE(protocol_autogen::validateSTObject(obj, format));
}
TEST(STObjectValidation, validate_mpt_issue_supported)
{
SOTemplate format{{sfAsset, soeREQUIRED, soeMPTSupported}};
STObject obj(sfGeneric);
obj.setFieldIssue(sfAsset, STIssue{sfAsset, MPTIssue{}});
EXPECT_TRUE(protocol_autogen::validateSTObject(obj, format));
}
TEST(STObjectValidation, validate_mpt_issue_not_supported)
{
SOTemplate format{{sfAsset, soeREQUIRED, soeMPTNotSupported}};
STObject obj(sfGeneric);
obj.setFieldIssue(sfAsset, STIssue{sfAsset, MPTIssue{}});
EXPECT_FALSE(protocol_autogen::validateSTObject(obj, format));
}
} // namespace xrpl

View File

@@ -23,8 +23,10 @@ TEST(DirectoryNodeTests, BuilderSettersRoundTrip)
auto const ownerValue = canonical_ACCOUNT();
auto const takerPaysCurrencyValue = canonical_UINT160();
auto const takerPaysIssuerValue = canonical_UINT160();
auto const takerPaysMPTValue = canonical_UINT192();
auto const takerGetsCurrencyValue = canonical_UINT160();
auto const takerGetsIssuerValue = canonical_UINT160();
auto const takerGetsMPTValue = canonical_UINT192();
auto const exchangeRateValue = canonical_UINT64();
auto const indexesValue = canonical_VECTOR256();
auto const rootIndexValue = canonical_UINT256();
@@ -43,8 +45,10 @@ TEST(DirectoryNodeTests, BuilderSettersRoundTrip)
builder.setOwner(ownerValue);
builder.setTakerPaysCurrency(takerPaysCurrencyValue);
builder.setTakerPaysIssuer(takerPaysIssuerValue);
builder.setTakerPaysMPT(takerPaysMPTValue);
builder.setTakerGetsCurrency(takerGetsCurrencyValue);
builder.setTakerGetsIssuer(takerGetsIssuerValue);
builder.setTakerGetsMPT(takerGetsMPTValue);
builder.setExchangeRate(exchangeRateValue);
builder.setIndexNext(indexNextValue);
builder.setIndexPrevious(indexPreviousValue);
@@ -98,6 +102,14 @@ TEST(DirectoryNodeTests, BuilderSettersRoundTrip)
EXPECT_TRUE(entry.hasTakerPaysIssuer());
}
{
auto const& expected = takerPaysMPTValue;
auto const actualOpt = entry.getTakerPaysMPT();
ASSERT_TRUE(actualOpt.has_value());
expectEqualField(expected, *actualOpt, "sfTakerPaysMPT");
EXPECT_TRUE(entry.hasTakerPaysMPT());
}
{
auto const& expected = takerGetsCurrencyValue;
auto const actualOpt = entry.getTakerGetsCurrency();
@@ -114,6 +126,14 @@ TEST(DirectoryNodeTests, BuilderSettersRoundTrip)
EXPECT_TRUE(entry.hasTakerGetsIssuer());
}
{
auto const& expected = takerGetsMPTValue;
auto const actualOpt = entry.getTakerGetsMPT();
ASSERT_TRUE(actualOpt.has_value());
expectEqualField(expected, *actualOpt, "sfTakerGetsMPT");
EXPECT_TRUE(entry.hasTakerGetsMPT());
}
{
auto const& expected = exchangeRateValue;
auto const actualOpt = entry.getExchangeRate();
@@ -186,8 +206,10 @@ TEST(DirectoryNodeTests, BuilderFromSleRoundTrip)
auto const ownerValue = canonical_ACCOUNT();
auto const takerPaysCurrencyValue = canonical_UINT160();
auto const takerPaysIssuerValue = canonical_UINT160();
auto const takerPaysMPTValue = canonical_UINT192();
auto const takerGetsCurrencyValue = canonical_UINT160();
auto const takerGetsIssuerValue = canonical_UINT160();
auto const takerGetsMPTValue = canonical_UINT192();
auto const exchangeRateValue = canonical_UINT64();
auto const indexesValue = canonical_VECTOR256();
auto const rootIndexValue = canonical_UINT256();
@@ -203,8 +225,10 @@ TEST(DirectoryNodeTests, BuilderFromSleRoundTrip)
sle->at(sfOwner) = ownerValue;
sle->at(sfTakerPaysCurrency) = takerPaysCurrencyValue;
sle->at(sfTakerPaysIssuer) = takerPaysIssuerValue;
sle->at(sfTakerPaysMPT) = takerPaysMPTValue;
sle->at(sfTakerGetsCurrency) = takerGetsCurrencyValue;
sle->at(sfTakerGetsIssuer) = takerGetsIssuerValue;
sle->at(sfTakerGetsMPT) = takerGetsMPTValue;
sle->at(sfExchangeRate) = exchangeRateValue;
sle->at(sfIndexes) = indexesValue;
sle->at(sfRootIndex) = rootIndexValue;
@@ -283,6 +307,19 @@ TEST(DirectoryNodeTests, BuilderFromSleRoundTrip)
expectEqualField(expected, *fromBuilderOpt, "sfTakerPaysIssuer");
}
{
auto const& expected = takerPaysMPTValue;
auto const fromSleOpt = entryFromSle.getTakerPaysMPT();
auto const fromBuilderOpt = entryFromBuilder.getTakerPaysMPT();
ASSERT_TRUE(fromSleOpt.has_value());
ASSERT_TRUE(fromBuilderOpt.has_value());
expectEqualField(expected, *fromSleOpt, "sfTakerPaysMPT");
expectEqualField(expected, *fromBuilderOpt, "sfTakerPaysMPT");
}
{
auto const& expected = takerGetsCurrencyValue;
@@ -309,6 +346,19 @@ TEST(DirectoryNodeTests, BuilderFromSleRoundTrip)
expectEqualField(expected, *fromBuilderOpt, "sfTakerGetsIssuer");
}
{
auto const& expected = takerGetsMPTValue;
auto const fromSleOpt = entryFromSle.getTakerGetsMPT();
auto const fromBuilderOpt = entryFromBuilder.getTakerGetsMPT();
ASSERT_TRUE(fromSleOpt.has_value());
ASSERT_TRUE(fromBuilderOpt.has_value());
expectEqualField(expected, *fromSleOpt, "sfTakerGetsMPT");
expectEqualField(expected, *fromBuilderOpt, "sfTakerGetsMPT");
}
{
auto const& expected = exchangeRateValue;
@@ -462,10 +512,14 @@ TEST(DirectoryNodeTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(entry.getTakerPaysCurrency().has_value());
EXPECT_FALSE(entry.hasTakerPaysIssuer());
EXPECT_FALSE(entry.getTakerPaysIssuer().has_value());
EXPECT_FALSE(entry.hasTakerPaysMPT());
EXPECT_FALSE(entry.getTakerPaysMPT().has_value());
EXPECT_FALSE(entry.hasTakerGetsCurrency());
EXPECT_FALSE(entry.getTakerGetsCurrency().has_value());
EXPECT_FALSE(entry.hasTakerGetsIssuer());
EXPECT_FALSE(entry.getTakerGetsIssuer().has_value());
EXPECT_FALSE(entry.hasTakerGetsMPT());
EXPECT_FALSE(entry.getTakerGetsMPT().has_value());
EXPECT_FALSE(entry.hasExchangeRate());
EXPECT_FALSE(entry.getExchangeRate().has_value());
EXPECT_FALSE(entry.hasIndexNext());

View File

@@ -1,8 +0,0 @@
#include <gtest/gtest.h>
int
main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,106 @@
#include <xrpl/telemetry/SpanGuard.h>
#include <xrpl/telemetry/SpanNames.h>
#include <gtest/gtest.h>
#include <cstdint>
#include <exception>
#include <stdexcept>
#include <utility>
using namespace xrpl;
using namespace xrpl::telemetry;
TEST(SpanGuardFactory, null_guard_methods_are_safe)
{
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "nonexistent");
EXPECT_FALSE(span);
span.setAttribute("key", "value");
span.setAttribute("int_key", static_cast<int64_t>(42));
span.setAttribute("bool_key", true);
span.setOk();
span.setError("test");
span.addEvent("event");
}
TEST(SpanGuardFactory, category_span_returns_null_when_disabled)
{
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "test");
EXPECT_FALSE(span);
span.setAttribute("xrpl.rpc.command", "test");
span.setAttribute("xrpl.rpc.status", "success");
}
TEST(SpanGuardFactory, child_span_null_when_no_parent)
{
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "parent");
auto child = span.childSpan("child.test");
EXPECT_FALSE(child);
}
TEST(SpanGuardFactory, linked_span_null_when_no_context)
{
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "source");
auto linked = span.linkedSpan("linked.test");
EXPECT_FALSE(linked);
}
TEST(SpanGuardFactory, capture_context_returns_invalid_on_null)
{
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "ctx");
auto ctx = span.captureContext();
EXPECT_FALSE(ctx.isValid());
}
TEST(SpanGuardFactory, move_construction_transfers_ownership)
{
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "move");
auto moved = std::move(span);
EXPECT_FALSE(span); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
moved.setAttribute("key", "value");
}
TEST(SpanGuardFactory, record_exception_safe_on_null)
{
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc.command", "test");
try
{
throw std::runtime_error("test error");
}
catch (std::exception const& e)
{
span.recordException(e);
}
}
TEST(SpanGuardFactory, discard_safe_on_null)
{
auto span = SpanGuard::span(TraceCategory::Transactions, "tx", "process");
span.discard();
EXPECT_FALSE(span);
}
TEST(SpanGuardFactory, consensus_close_time_attributes)
{
// Verify the consensus attribute pattern compiles and
// doesn't crash with null SpanGuard.
{
auto span = telemetry::SpanGuard::span(
telemetry::TraceCategory::Consensus, telemetry::seg::consensus, "accept.apply");
span.setAttribute("xrpl.consensus.ledger.seq", static_cast<int64_t>(42));
span.setAttribute("xrpl.consensus.close_time", static_cast<int64_t>(780000000));
span.setAttribute("xrpl.consensus.close_time_correct", true);
span.setAttribute("xrpl.consensus.close_resolution_ms", static_cast<int64_t>(30000));
span.setAttribute("xrpl.consensus.state", std::string("finished"));
span.setAttribute("xrpl.consensus.proposing", true);
span.setAttribute("xrpl.consensus.round_time_ms", static_cast<int64_t>(3500));
}
{
auto span = telemetry::SpanGuard::span(
telemetry::TraceCategory::Consensus, telemetry::seg::consensus, "accept.apply");
span.setAttribute("xrpl.consensus.close_time_correct", false);
span.setAttribute("xrpl.consensus.state", std::string("moved_on"));
}
}

View File

@@ -1,4 +1,5 @@
#include <xrpl/basics/BasicConfig.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/telemetry/Telemetry.h>
#include <gtest/gtest.h>
@@ -9,12 +10,11 @@ using namespace xrpl;
TEST(TelemetryConfig, setup_defaults)
{
telemetry::Telemetry::Setup s;
telemetry::Telemetry::Setup const s;
EXPECT_FALSE(s.enabled);
EXPECT_EQ(s.serviceName, "rippled");
EXPECT_EQ(s.serviceName, "xrpld");
EXPECT_TRUE(s.serviceVersion.empty());
EXPECT_TRUE(s.serviceInstanceId.empty());
EXPECT_EQ(s.exporterType, "otlp_http");
EXPECT_EQ(s.exporterEndpoint, "http://localhost:4318/v1/traces");
EXPECT_FALSE(s.useTls);
EXPECT_TRUE(s.tlsCertPath.empty());
@@ -33,14 +33,13 @@ TEST(TelemetryConfig, setup_defaults)
TEST(TelemetryConfig, parse_empty_section)
{
Section section;
auto setup = telemetry::setup_Telemetry(section, "nHUtest123", "2.0.0");
Section const section;
auto setup = telemetry::setup_Telemetry(section, "nHUtest123", "2.0.0", 0);
EXPECT_FALSE(setup.enabled);
EXPECT_EQ(setup.serviceName, "rippled");
EXPECT_EQ(setup.serviceName, "xrpld");
EXPECT_EQ(setup.serviceVersion, "2.0.0");
EXPECT_EQ(setup.serviceInstanceId, "nHUtest123");
EXPECT_EQ(setup.exporterType, "otlp_http");
EXPECT_DOUBLE_EQ(setup.samplingRatio, 1.0);
EXPECT_TRUE(setup.traceRpc);
EXPECT_TRUE(setup.traceTransactions);
@@ -69,12 +68,11 @@ TEST(TelemetryConfig, parse_full_section)
section.set("trace_peer", "1");
section.set("trace_ledger", "0");
auto setup = telemetry::setup_Telemetry(section, "nHUtest123", "2.0.0");
auto setup = telemetry::setup_Telemetry(section, "nHUtest123", "2.0.0", 1);
EXPECT_TRUE(setup.enabled);
EXPECT_EQ(setup.serviceName, "my-rippled");
EXPECT_EQ(setup.serviceInstanceId, "custom-id");
EXPECT_EQ(setup.exporterType, "otlp_http");
EXPECT_EQ(setup.exporterEndpoint, "http://collector:4318/v1/traces");
EXPECT_TRUE(setup.useTls);
EXPECT_EQ(setup.tlsCertPath, "/etc/ssl/ca.pem");
@@ -95,7 +93,7 @@ TEST(TelemetryConfig, null_telemetry_factory)
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
beast::Journal const j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
EXPECT_TRUE(tel != nullptr);
EXPECT_FALSE(tel->isEnabled());
@@ -109,3 +107,16 @@ TEST(TelemetryConfig, null_telemetry_factory)
tel->start();
tel->stop();
}
TEST(TelemetryConfig, sampling_ratio_clamped)
{
Section section;
section.set("sampling_ratio", "2.5");
auto setup = telemetry::setup_Telemetry(section, "nHUtest123", "2.0.0", 0);
EXPECT_DOUBLE_EQ(setup.samplingRatio, 1.0);
Section section2;
section2.set("sampling_ratio", "-0.5");
auto setup2 = telemetry::setup_Telemetry(section2, "nHUtest123", "2.0.0", 0);
EXPECT_DOUBLE_EQ(setup2.samplingRatio, 0.0);
}

View File

@@ -1,170 +0,0 @@
#include <xrpld/telemetry/TracingInstrumentation.h>
#include <xrpl/telemetry/Telemetry.h>
#include <gtest/gtest.h>
using namespace xrpl;
TEST(TracingMacros, macros_with_null_telemetry)
{
telemetry::Telemetry::Setup setup;
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
tel->start();
// Each macro should compile and execute without crashing.
{
XRPL_TRACE_RPC(*tel, "rpc.test.command");
XRPL_TRACE_SET_ATTR("xrpl.rpc.command", "test");
XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "success");
}
{
XRPL_TRACE_TX(*tel, "tx.test.process");
XRPL_TRACE_SET_ATTR("xrpl.tx.hash", "abc123");
}
{
XRPL_TRACE_CONSENSUS(*tel, "consensus.test");
XRPL_TRACE_SET_ATTR("xrpl.consensus.mode", "Proposing");
}
{
XRPL_TRACE_PEER(*tel, "peer.test");
}
{
XRPL_TRACE_LEDGER(*tel, "ledger.test");
}
tel->stop();
}
TEST(TracingMacros, separate_scopes)
{
// Multiple macros in separate scopes should not collide on
// the _xrpl_guard_ variable name.
telemetry::Telemetry::Setup setup;
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
{
XRPL_TRACE_RPC(*tel, "rpc.outer");
}
{
XRPL_TRACE_TX(*tel, "tx.inner");
}
{
XRPL_TRACE_CONSENSUS(*tel, "consensus.other");
}
}
TEST(TracingMacros, conditional_guards)
{
// NullTelemetry returns false for all shouldTrace* methods.
// XRPL_TRACE_SET_ATTR on an empty guard must be safe.
telemetry::Telemetry::Setup setup;
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
EXPECT_FALSE(tel->shouldTraceRpc());
EXPECT_FALSE(tel->shouldTraceTransactions());
EXPECT_FALSE(tel->shouldTraceConsensus());
EXPECT_FALSE(tel->shouldTracePeer());
EXPECT_FALSE(tel->shouldTraceLedger());
{
XRPL_TRACE_RPC(*tel, "should.not.create");
XRPL_TRACE_SET_ATTR("key", "value");
}
}
TEST(TracingMacros, consensus_close_time_attributes)
{
// Verify the consensus.accept.apply attribute pattern compiles and
// doesn't crash with NullTelemetry. Mirrors the real instrumentation
// in RCLConsensus::Adaptor::doAccept().
telemetry::Telemetry::Setup setup;
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
{
XRPL_TRACE_CONSENSUS(*tel, "consensus.accept.apply");
XRPL_TRACE_SET_ATTR("xrpl.consensus.ledger.seq", static_cast<int64_t>(42));
XRPL_TRACE_SET_ATTR("xrpl.consensus.close_time", static_cast<int64_t>(780000000));
XRPL_TRACE_SET_ATTR("xrpl.consensus.close_time_correct", true);
XRPL_TRACE_SET_ATTR("xrpl.consensus.close_resolution_ms", static_cast<int64_t>(30000));
XRPL_TRACE_SET_ATTR("xrpl.consensus.state", std::string("finished"));
XRPL_TRACE_SET_ATTR("xrpl.consensus.proposing", true);
XRPL_TRACE_SET_ATTR("xrpl.consensus.round_time_ms", static_cast<int64_t>(3500));
}
// close_time_correct=false path (agreed to disagree)
{
XRPL_TRACE_CONSENSUS(*tel, "consensus.accept.apply");
XRPL_TRACE_SET_ATTR("xrpl.consensus.close_time_correct", false);
XRPL_TRACE_SET_ATTR("xrpl.consensus.state", std::string("moved_on"));
}
}
#ifdef XRPL_ENABLE_TELEMETRY
TEST(TracingMacros, span_guard_raii)
{
telemetry::Telemetry::Setup setup;
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
auto span = tel->startSpan("test.guard");
{
telemetry::SpanGuard guard(span);
guard.setAttribute("key", "value");
guard.addEvent("test_event");
guard.setOk();
}
}
TEST(TracingMacros, span_guard_move)
{
telemetry::Telemetry::Setup setup;
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
auto span = tel->startSpan("test.move");
std::optional<telemetry::SpanGuard> opt;
opt.emplace(span);
EXPECT_TRUE(opt.has_value());
opt.reset();
}
TEST(TracingMacros, span_guard_exception)
{
telemetry::Telemetry::Setup setup;
setup.enabled = false;
beast::Journal::Sink& sink = beast::Journal::getNullSink();
beast::Journal j(sink);
auto tel = telemetry::make_Telemetry(setup, j);
auto span = tel->startSpan("test.exception");
{
telemetry::SpanGuard guard(span);
try
{
throw std::runtime_error("test error");
}
catch (std::exception const& e)
{
guard.recordException(e);
}
}
}
#endif // XRPL_ENABLE_TELEMETRY