From b8f5a6f2b6a5144d3dd4583294db08fe159e74dd Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Mon, 2 Feb 2026 17:27:04 -0800 Subject: [PATCH] [nexus] add support for writing test_info.json (#12355) This commit adds support for writing test_info.json in the Nexus platform. The test_info.json file contains information about the test topology, including node names, roles, and addresses. This information is used by the packet verification framework to verify the behavior of the network. Changes: - Added `Core::SaveTestInfo` to `nexus_core.cpp` to write node information to JSON. - Added `SetName` and `GetName` to `Node` class in `nexus_node.hpp`. - Updated `test_5_1_1.cpp` to set node names and call `SaveTestInfo`. --- tests/nexus/platform/nexus_core.cpp | 148 ++++++++++++++++++++++++++++ tests/nexus/platform/nexus_core.hpp | 2 + tests/nexus/platform/nexus_node.hpp | 7 +- tests/nexus/test_5_1_1.cpp | 5 + 4 files changed, 161 insertions(+), 1 deletion(-) diff --git a/tests/nexus/platform/nexus_core.cpp b/tests/nexus/platform/nexus_core.cpp index 71f7450cb..5bdc8b5fa 100644 --- a/tests/nexus/platform/nexus_core.cpp +++ b/tests/nexus/platform/nexus_core.cpp @@ -29,6 +29,7 @@ #include "nexus_core.hpp" #include +#include #include "nexus_node.hpp" @@ -59,6 +60,153 @@ Core::Core(void) } } +void Core::SaveTestInfo(const char *aFilename) +{ + FILE *file = fopen(aFilename, "w"); + Node *tail = mNodes.GetTail(); + const char *testcase; + const char *slash; + const char *dot; + const char *version; + int testcaseLen; + + VerifyOrExit(file != nullptr); + + testcase = aFilename; + slash = strrchr(aFilename, '/'); + + if (slash != nullptr) + { + testcase = slash + 1; + } + + dot = strrchr(testcase, '.'); + testcaseLen = (dot != nullptr) ? static_cast(dot - testcase) : static_cast(strlen(testcase)); + + switch (otThreadGetVersion()) + { + case OT_THREAD_VERSION_1_1: + version = "1.1"; + break; + case OT_THREAD_VERSION_1_2: + version = "1.2"; + break; + case OT_THREAD_VERSION_1_3: + version = "1.3"; + break; + case OT_THREAD_VERSION_1_4: + version = "1.4"; + break; + default: + version = "unknown"; + break; + } + + fprintf(file, "{\n"); + fprintf(file, " \"testcase\": \"%.*s\",\n", testcaseLen, testcase); + fprintf(file, " \"pcap\": \"%s\",\n", getenv("OT_NEXUS_PCAP_FILE") ? getenv("OT_NEXUS_PCAP_FILE") : ""); + + if (!mNodes.IsEmpty()) + { + NetworkKey networkKey; + Node &node = *mNodes.GetHead(); + String keyString; + + node.Get().GetNetworkKey(networkKey); + keyString.AppendHexBytes(networkKey.m8, OT_NETWORK_KEY_SIZE); + fprintf(file, " \"network_key\": \"%s\",\n", keyString.AsCString()); + + for (Node &leaderNode : mNodes) + { + if (leaderNode.Get().IsLeader()) + { + Ip6::Address aloc; + leaderNode.Get().GetLeaderAloc(aloc); + fprintf(file, " \"leader_aloc\": \"%s\",\n", aloc.ToString().AsCString()); + break; + } + } + } + + fprintf(file, " \"topology\": {\n"); + for (Node &node : mNodes) + { + fprintf(file, " \"%u\": {\"name\": \"%s\", \"version\": \"%s\"}%s\n", node.GetInstance().GetId(), + node.GetName() ? node.GetName() : "", version, (&node == tail) ? "" : ","); + } + fprintf(file, " },\n"); + + fprintf(file, " \"extaddrs\": {\n"); + for (Node &node : mNodes) + { + fprintf(file, " \"%u\": \"%s\"%s\n", node.GetInstance().GetId(), + node.Get().GetExtAddress().ToString().AsCString(), (&node == tail) ? "" : ","); + } + fprintf(file, " },\n"); + + fprintf(file, " \"rloc16s\": {\n"); + for (Node &node : mNodes) + { + fprintf(file, " \"%u\": \"0x%04x\"%s\n", node.GetInstance().GetId(), node.Get().GetRloc16(), + (&node == tail) ? "" : ","); + } + fprintf(file, " },\n"); + + fprintf(file, " \"mleids\": {\n"); + for (Node &node : mNodes) + { + fprintf(file, " \"%u\": \"%s\"%s\n", node.GetInstance().GetId(), + node.Get().GetMeshLocalEid().ToString().AsCString(), (&node == tail) ? "" : ","); + } + fprintf(file, " },\n"); + + fprintf(file, " \"rlocs\": {\n"); + for (Node &node : mNodes) + { + fprintf(file, " \"%u\": \"%s\"%s\n", node.GetInstance().GetId(), + node.Get().GetMeshLocalRloc().ToString().AsCString(), (&node == tail) ? "" : ","); + } + fprintf(file, " },\n"); + + fprintf(file, " \"ipaddrs\": {\n"); + for (Node &node : mNodes) + { + bool first = true; + + fprintf(file, " \"%u\": [\n", node.GetInstance().GetId()); + for (const Ip6::Netif::UnicastAddress &addr : node.Get().GetUnicastAddresses()) + { + if (!first) + { + fprintf(file, ",\n"); + } + fprintf(file, " \"%s\"", addr.GetAddress().ToString().AsCString()); + first = false; + } + fprintf(file, "\n ]%s\n", (&node == tail) ? "" : ","); + } + fprintf(file, " },\n"); + + fprintf(file, " \"extra_vars\": {\n"); + if (!mNodes.IsEmpty()) + { + Node &node = *mNodes.GetHead(); + Ip6::Prefix prefix; + + prefix.Set(node.Get().GetMeshLocalPrefix()); + fprintf(file, " \"mesh_local_prefix\": \"%s\"\n", prefix.ToString().AsCString()); + } + fprintf(file, " }\n"); + + fprintf(file, "}\n"); + +exit: + if (file != nullptr) + { + fclose(file); + } +} + Core::~Core(void) { sInUse = false; } Node &Core::CreateNode(void) diff --git a/tests/nexus/platform/nexus_core.hpp b/tests/nexus/platform/nexus_core.hpp index 11ed2c36a..4aad4a0cb 100644 --- a/tests/nexus/platform/nexus_core.hpp +++ b/tests/nexus/platform/nexus_core.hpp @@ -56,6 +56,8 @@ public: TimeMilli GetNow(void) { return mNow; } void AdvanceTime(uint32_t aDuration); + void SaveTestInfo(const char *aFilename); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Used by platform implementation diff --git a/tests/nexus/platform/nexus_node.hpp b/tests/nexus/platform/nexus_node.hpp index ca254635c..6cdec99d1 100644 --- a/tests/nexus/platform/nexus_node.hpp +++ b/tests/nexus/platform/nexus_node.hpp @@ -83,6 +83,9 @@ public: void GetTrelSockAddr(Ip6::SockAddr &aSockAddr) const; #endif + void SetName(const char *aName) { IgnoreError(StringCopy(mName, aName)); } + const char *GetName(void) const { return mName; } + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - template Type &Get(void) @@ -113,7 +116,9 @@ public: Node *mNext; private: - Node(void) = default; + Node(void) {} + + char mName[32]; }; inline Node &AsNode(otInstance *aInstance) { return Node::From(aInstance); } diff --git a/tests/nexus/test_5_1_1.cpp b/tests/nexus/test_5_1_1.cpp index 8a60bdec1..c7c4d5be4 100644 --- a/tests/nexus/test_5_1_1.cpp +++ b/tests/nexus/test_5_1_1.cpp @@ -106,6 +106,9 @@ void Test5_1_1(void) Node &leader = nexus.CreateNode(); Node &router = nexus.CreateNode(); + leader.SetName("LEADER"); + router.SetName("ROUTER"); + nexus.AdvanceTime(0); Instance::SetLogLevel(kLogLevelInfo); @@ -194,6 +197,8 @@ void Test5_1_1(void) router.Get().GetLinkLocalAddress().ToString().AsCString()); SendAndVerifyEchoRequest(nexus, leader, router, leaderReceivedEchoReply); Log("Router (as DUT) responded with Echo Reply successfully"); + + nexus.SaveTestInfo("test_5_1_1.json"); } } // namespace Nexus