[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`.
This commit is contained in:
Jonathan Hui
2026-02-02 17:27:04 -08:00
committed by GitHub
parent 0f6f831352
commit b8f5a6f2b6
4 changed files with 161 additions and 1 deletions
+148
View File
@@ -29,6 +29,7 @@
#include "nexus_core.hpp"
#include <cstdlib>
#include <cstring>
#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<int>(dot - testcase) : static_cast<int>(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<OT_NETWORK_KEY_SIZE * 2 + 1> keyString;
node.Get<KeyManager>().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<Mle::Mle>().IsLeader())
{
Ip6::Address aloc;
leaderNode.Get<Mle::Mle>().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<Mac::Mac>().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<Mle::Mle>().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<Mle::Mle>().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<Mle::Mle>().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<ThreadNetif>().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<Mle::Mle>().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)
+2
View File
@@ -56,6 +56,8 @@ public:
TimeMilli GetNow(void) { return mNow; }
void AdvanceTime(uint32_t aDuration);
void SaveTestInfo(const char *aFilename);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Used by platform implementation
+6 -1
View File
@@ -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 <typename Type> 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); }
+5
View File
@@ -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<Mle::Mle>().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