mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-27 23:38:08 +00:00
Review of the preceding commits found a clang-tidy failure and a convention break, both rooted in the same place: the enum-to-label helpers were put in ConsensusSpanNames.h, which pulled two domain headers into it. misc-include-cleaner rejected the new test: it used xrpl::LedgerCloseReason without directly including ConsensusTypes.h, relying on the transitive include. misc-* is enabled and this path is not in IgnoreHeaders, so it would have failed CI. ConsensusSpanNames.h had also become the only one of the eight *SpanNames.h headers to include anything beyond SpanNames.h. That cost is paid by every consumer: PeerImp.cpp, ConsensusReceiveTracing.h and RCLConsensus.cpp want only name and key constants, but were newly compiling ConsensusTypes.h and DisputedTx.h through it. Move both helpers to a new ConsensusSpanLabels.h, which owns the domain includes. ConsensusSpanNames.h is dependency-free again like its siblings, and the labels reach their only production caller, Consensus.h, directly. Also from the review: - phaseOpen() had grown to 81 lines, over the 80-line limit. Extract annotateOpenStart() and annotateOpenClose(), which also removes the repeated span guards. phaseOpen is 72 lines; startRoundInternal drops 103 to 93, still over the limit but it was 99 before this work began. - Note at the CLOG why the log text keeps the shouldCloseLedger name: existing consumers match on it. - whyCloseLedger's doc claimed "both log identically", implying the wrapper logs too. It delegates, so the logging happens once either way. - Cross-reference proposers_validated and proposers_finished, which sit eight lines apart and count different things: validators of the previous ledger versus those already past it. - The two static_asserts no longer sit inside TEST bodies with SUCCEED(); they fire at compile time regardless. Also "consteval-safe" was wrong; they are constexpr. - SpanGuardFactory.cpp claimed a libxrpl test cannot include the consensus span-name header. The new test in the same directory does exactly that, so the claim is corrected to name the real constraint: the rpc_* constants it needs live in an xrpld-level header.
118 lines
5.4 KiB
C++
118 lines
5.4 KiB
C++
#include <xrpl/consensus/ConsensusSpanNames.h>
|
|
|
|
#include <xrpl/consensus/ConsensusParms.h>
|
|
#include <xrpl/consensus/ConsensusSpanLabels.h>
|
|
#include <xrpl/consensus/ConsensusTypes.h>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <string_view>
|
|
|
|
/**
|
|
* Contract tests for the consensus phase-span attribute constants.
|
|
*
|
|
* The keys in ConsensusSpanNames.h are the single source of truth (L1) that
|
|
* `.github/scripts/otel-naming/check_otel_naming.py` derives its valid key
|
|
* set from, and that the collector's spanmetrics dimensions, the Tempo span
|
|
* filters and the Grafana dashboards query by literal string. A silent rename
|
|
* here compiles cleanly but blanks panels, so these tests pin the wire values.
|
|
* They need no telemetry runtime and run in every build.
|
|
*
|
|
* Scope: the attributes carried by `consensus.phase.open` and
|
|
* `consensus.establish`. The round-level attrs are covered by the
|
|
* pre-existing key set and are deliberately NOT duplicated onto the phase
|
|
* children (a child span does not inherit parent attributes, but copying
|
|
* `ledger_seq` down would store the same value twice per trace).
|
|
*/
|
|
|
|
using namespace xrpl::telemetry::consensus::span;
|
|
|
|
TEST(ConsensusSpanNames, phase_open_start_attribute_keys)
|
|
{
|
|
// Set once when the open-phase span is created in startRoundInternal().
|
|
EXPECT_EQ(std::string_view(attr::startReason), "start_reason");
|
|
EXPECT_EQ(std::string_view(attr::previousCloseAgree), "previous_close_agree");
|
|
EXPECT_EQ(std::string_view(attr::peerPositionsAtOpen), "peer_positions_at_open");
|
|
EXPECT_EQ(std::string_view(attr::earlyCloseTriggered), "early_close_triggered");
|
|
}
|
|
|
|
TEST(ConsensusSpanNames, phase_open_end_attribute_keys)
|
|
{
|
|
// Existing end-of-phase metadata, pinned alongside the new key so a rename
|
|
// of either shows up here.
|
|
EXPECT_EQ(std::string_view(attr::openDurationMs), "open_duration_ms");
|
|
EXPECT_EQ(std::string_view(attr::peerPositionsAtClose), "peer_positions_at_close");
|
|
EXPECT_EQ(std::string_view(attr::txSetsAcquired), "tx_sets_acquired");
|
|
EXPECT_EQ(std::string_view(attr::closeReason), "close_reason");
|
|
EXPECT_EQ(std::string_view(attr::proposersValidated), "proposers_validated");
|
|
}
|
|
|
|
TEST(ConsensusSpanNames, close_reason_values_are_the_close_paths)
|
|
{
|
|
// One per branch of whyCloseLedger() that closes the ledger. keep_open is
|
|
// never emitted (the attribute is only set on the closing path) but is
|
|
// labelled rather than left blank so the mapping is total.
|
|
EXPECT_EQ(std::string_view(val::closeKeepOpen), "keep_open");
|
|
EXPECT_EQ(std::string_view(val::closeAnomaly), "anomaly");
|
|
EXPECT_EQ(std::string_view(val::closeOthersClosed), "others_closed");
|
|
EXPECT_EQ(std::string_view(val::closeIdle), "idle");
|
|
EXPECT_EQ(std::string_view(val::closeNormal), "normal");
|
|
}
|
|
|
|
TEST(ConsensusSpanNames, close_reason_label_maps_every_enum_state)
|
|
{
|
|
// A missed branch would attribute a close to the wrong cause, which is the
|
|
// whole point of the attribute, so every enumerator is asserted.
|
|
EXPECT_EQ(closeReasonLabel(xrpl::LedgerCloseReason::KeepOpen), "keep_open");
|
|
EXPECT_EQ(closeReasonLabel(xrpl::LedgerCloseReason::Anomaly), "anomaly");
|
|
EXPECT_EQ(closeReasonLabel(xrpl::LedgerCloseReason::OthersClosed), "others_closed");
|
|
EXPECT_EQ(closeReasonLabel(xrpl::LedgerCloseReason::Idle), "idle");
|
|
EXPECT_EQ(closeReasonLabel(xrpl::LedgerCloseReason::Normal), "normal");
|
|
}
|
|
|
|
TEST(ConsensusSpanNames, establish_attribute_keys)
|
|
{
|
|
// Qualified: DisputedTx tracks a second, per-transaction avalanche.
|
|
EXPECT_EQ(std::string_view(attr::closeTimeAvalancheState), "close_time_avalanche_state");
|
|
}
|
|
|
|
TEST(ConsensusSpanNames, start_reason_values_are_the_two_entry_paths)
|
|
{
|
|
// startRoundInternal() is entered fresh, or re-entered by handleWrongLedger
|
|
// after acquiring the correct prior ledger. A round that recovers emits a
|
|
// SECOND consensus.phase.open span, so the label is what tells them apart.
|
|
EXPECT_EQ(std::string_view(val::startInitial), "initial");
|
|
EXPECT_EQ(std::string_view(val::startRecovered), "recovered");
|
|
}
|
|
|
|
TEST(ConsensusSpanNames, avalanche_state_values_match_the_parms_enum)
|
|
{
|
|
EXPECT_EQ(std::string_view(val::avalancheInit), "init");
|
|
EXPECT_EQ(std::string_view(val::avalancheMid), "mid");
|
|
EXPECT_EQ(std::string_view(val::avalancheLate), "late");
|
|
EXPECT_EQ(std::string_view(val::avalancheStuck), "stuck");
|
|
EXPECT_EQ(std::string_view(val::unknown), "unknown");
|
|
}
|
|
|
|
TEST(ConsensusSpanNames, avalanche_state_label_maps_every_enum_state)
|
|
{
|
|
// A missed branch here would silently report the wrong convergence regime
|
|
// for the round, so every enumerator is asserted explicitly rather than
|
|
// round-tripped through a table.
|
|
using AvalancheState = xrpl::ConsensusParms::AvalancheState;
|
|
|
|
EXPECT_EQ(avalancheStateLabel(AvalancheState::Init), "init");
|
|
EXPECT_EQ(avalancheStateLabel(AvalancheState::Mid), "mid");
|
|
EXPECT_EQ(avalancheStateLabel(AvalancheState::Late), "late");
|
|
EXPECT_EQ(avalancheStateLabel(AvalancheState::Stuck), "stuck");
|
|
}
|
|
|
|
// Both mappings are constexpr, so the labels cost nothing at their call sites.
|
|
// These fire at compile time and need no test body.
|
|
static_assert(
|
|
closeReasonLabel(xrpl::LedgerCloseReason::Idle) == "idle",
|
|
"closeReasonLabel must be constexpr-evaluable");
|
|
static_assert(
|
|
avalancheStateLabel(xrpl::ConsensusParms::AvalancheState::Stuck) == "stuck",
|
|
"avalancheStateLabel must be constexpr-evaluable");
|