From 35fe1f3fbea56971f0526a59c03a8d28236ecea4 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 6 May 2026 17:41:05 -0700 Subject: [PATCH] [nexus] fix flakiness in history_tracker test (#13070) This commit resolves the flaky test failures occasionally observed in the history_tracker Nexus test during ping verification. The flakiness was caused by two primary issues: 1. Concurrent background Thread control traffic (e.g. multicast Hop-by-Hop Options packets) sometimes interleaving with the Echo Request pings, polluting the HistoryTracker queues and causing the strict chronological checks to fail. 2. The FTD child node upgrading to a Router due to the TooFewRouters network threshold rule, which dynamically changed its RLOC16 and caused NeighborRloc16 history checks to fail. To fix these, we: 1. Set the child node's router eligibility to false after joining to prevent any unwanted topology changes or Rloc16 updates. 2. Refactored the strict Leader TX and Child RX chronological checks with robust iterative loops filtering specifically for the OT_ICMP6_TYPE_ECHO_REQUEST packets. Verified 100% stable after executing a loop of 50 successful runs. --- tests/nexus/test_history_tracker.cpp | 67 ++++++++++++++++------------ 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/tests/nexus/test_history_tracker.cpp b/tests/nexus/test_history_tracker.cpp index ec4cb7159..64d1ee52e 100644 --- a/tests/nexus/test_history_tracker.cpp +++ b/tests/nexus/test_history_tracker.cpp @@ -146,6 +146,7 @@ void TestHistoryTracker(void) Mle::DeviceMode::kModeFullNetworkData); SuccessOrQuit(child.Get().SetDeviceMode(mode)); + SuccessOrQuit(child.Get().SetRouterEligible(false)); nexus.AdvanceTime(kJoinChildTimeMsec); iter.Init(); @@ -166,42 +167,50 @@ void TestHistoryTracker(void) const HistoryTracker::MessageInfo *msgInfo; iter.Init(); - for (int i = 2; i >= 0; --i) + uint8_t txRequestsFound = 0; + while ((msgInfo = leader.Get().IterateTxHistory(iter, age)) != nullptr) { - uint16_t size = kPingSizes[i]; - msgInfo = leader.Get().IterateTxHistory(iter, age); - VerifyOrQuit(msgInfo != nullptr); - VerifyOrQuit(msgInfo->mIcmp6Type == OT_ICMP6_TYPE_ECHO_REQUEST); - VerifyOrQuit(msgInfo->mIpProto == OT_IP6_PROTO_ICMP6); - VerifyOrQuit(msgInfo->mPayloadLength == size + sizeof(Ip6::Icmp::Header)); - VerifyOrQuit(AsCoreType(&msgInfo->mSource.mAddress) == leader.Get().GetMeshLocalEid()); - VerifyOrQuit(AsCoreType(&msgInfo->mDestination.mAddress) == child.Get().GetMeshLocalEid()); - VerifyOrQuit(msgInfo->mLinkSecurity); - VerifyOrQuit(msgInfo->mRadioIeee802154); - VerifyOrQuit(msgInfo->mPriority == OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL); - VerifyOrQuit(msgInfo->mNeighborRloc16 == child.Get().GetRloc16()); - VerifyOrQuit(msgInfo->mChecksum != 0); - VerifyOrQuit(msgInfo->mTxSuccess); + if (msgInfo->mIcmp6Type == OT_ICMP6_TYPE_ECHO_REQUEST) + { + VerifyOrQuit(txRequestsFound < 3); + uint16_t expectedSize = kPingSizes[2 - txRequestsFound]; + VerifyOrQuit(msgInfo->mIpProto == OT_IP6_PROTO_ICMP6); + VerifyOrQuit(msgInfo->mPayloadLength == expectedSize + sizeof(Ip6::Icmp::Header)); + VerifyOrQuit(AsCoreType(&msgInfo->mSource.mAddress) == leader.Get().GetMeshLocalEid()); + VerifyOrQuit(AsCoreType(&msgInfo->mDestination.mAddress) == child.Get().GetMeshLocalEid()); + VerifyOrQuit(msgInfo->mLinkSecurity); + VerifyOrQuit(msgInfo->mRadioIeee802154); + VerifyOrQuit(msgInfo->mPriority == OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL); + VerifyOrQuit(msgInfo->mNeighborRloc16 == child.Get().GetRloc16()); + VerifyOrQuit(msgInfo->mChecksum != 0); + VerifyOrQuit(msgInfo->mTxSuccess); + txRequestsFound++; + } } + VerifyOrQuit(txRequestsFound == 3); // Now check the RX history of the child (should have received the 3 Echo Requests) iter.Init(); - for (int i = 2; i >= 0; --i) + uint8_t rxRequestsFound = 0; + while ((msgInfo = child.Get().IterateRxHistory(iter, age)) != nullptr) { - uint16_t size = kPingSizes[i]; - msgInfo = child.Get().IterateRxHistory(iter, age); - VerifyOrQuit(msgInfo != nullptr); - VerifyOrQuit(msgInfo->mIcmp6Type == OT_ICMP6_TYPE_ECHO_REQUEST); - VerifyOrQuit(msgInfo->mIpProto == OT_IP6_PROTO_ICMP6); - VerifyOrQuit(msgInfo->mPayloadLength == size + sizeof(Ip6::Icmp::Header)); - VerifyOrQuit(AsCoreType(&msgInfo->mSource.mAddress) == leader.Get().GetMeshLocalEid()); - VerifyOrQuit(AsCoreType(&msgInfo->mDestination.mAddress) == child.Get().GetMeshLocalEid()); - VerifyOrQuit(msgInfo->mLinkSecurity); - VerifyOrQuit(msgInfo->mRadioIeee802154); - VerifyOrQuit(msgInfo->mPriority == OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL); - VerifyOrQuit(msgInfo->mNeighborRloc16 == leader.Get().GetRloc16()); - VerifyOrQuit(msgInfo->mChecksum != 0); + if (msgInfo->mIcmp6Type == OT_ICMP6_TYPE_ECHO_REQUEST) + { + VerifyOrQuit(rxRequestsFound < 3); + uint16_t expectedSize = kPingSizes[2 - rxRequestsFound]; + VerifyOrQuit(msgInfo->mIpProto == OT_IP6_PROTO_ICMP6); + VerifyOrQuit(msgInfo->mPayloadLength == expectedSize + sizeof(Ip6::Icmp::Header)); + VerifyOrQuit(AsCoreType(&msgInfo->mSource.mAddress) == leader.Get().GetMeshLocalEid()); + VerifyOrQuit(AsCoreType(&msgInfo->mDestination.mAddress) == child.Get().GetMeshLocalEid()); + VerifyOrQuit(msgInfo->mLinkSecurity); + VerifyOrQuit(msgInfo->mRadioIeee802154); + VerifyOrQuit(msgInfo->mPriority == OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL); + VerifyOrQuit(msgInfo->mNeighborRloc16 == leader.Get().GetRloc16()); + VerifyOrQuit(msgInfo->mChecksum != 0); + rxRequestsFound++; + } } + VerifyOrQuit(rxRequestsFound == 3); // The child then replied, so let's check the RX history of the leader for the 3 Echo Replies iter.Init();