Files
openthread/tests/nexus/test_full_network_reset.cpp
Abtin Keshavarzian a2fb36cd0b [nexus] add mechanism to emulate node reset (#11662)
This commit adds a new mechanism to emulate a node reset on
`Nexus::Node`. This is realized by resetting all platform components
while ensuring the non-volatile `mSettings` remains unchanged, then
reinitializing the `ot::Instance` by invoking its constructor.

This is used to add a new `test_full_network_reset` test, which
emulates a full simultaneous reset of all nodes in a large network,
tracking how long it takes for the network to stabilize after the
reset event.
2025-07-08 15:10:30 -07:00

191 lines
6.2 KiB
C++

/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include "platform/nexus_core.hpp"
#include "platform/nexus_node.hpp"
namespace ot {
namespace Nexus {
static constexpr uint16_t kNumberOfRoles = (Mle::kRoleLeader + 1);
typedef uint16_t RoleStats[kNumberOfRoles];
static void CalculateRoleStats(Core &aNexus, RoleStats &aRoleStats)
{
ClearAllBytes(aRoleStats);
for (Node &node : aNexus.GetNodes())
{
aRoleStats[node.Get<Mle::Mle>().GetRole()]++;
}
}
static bool CheckRoleStats(const RoleStats &aRoleStats)
{
return (aRoleStats[Mle::kRoleLeader] == 1) && (aRoleStats[Mle::kRoleDetached] == 0) &&
(aRoleStats[Mle::kRoleDisabled] == 0);
}
void Test(void)
{
static constexpr uint16_t kNumNodes = 200;
// All times in msec
static constexpr uint32_t kMaxWaitTime = 20 * Time::kOneMinuteInMsec;
static constexpr uint32_t kStatCollectionInterval = 500;
static constexpr uint32_t kExtraSimulTimeAfterPass = 5 * Time::kOneSecondInMsec;
Core nexus;
Node *leader;
RoleStats roleStats;
TimeMilli resetTime;
for (uint16_t i = 0; i < kNumNodes; i++)
{
nexus.CreateNode();
}
nexus.AdvanceTime(0);
Log("Starting %u nodes staggered", kNumNodes);
leader = nexus.GetNodes().GetHead();
leader->Form();
for (Node &node : nexus.GetNodes())
{
if (&node == leader)
{
continue;
}
node.Join(*leader);
nexus.AdvanceTime(Time::kOneSecondInMsec);
}
for (uint32_t step = 0; step < kMaxWaitTime / kStatCollectionInterval; step++)
{
if ((step % 20) == 0)
{
Log("+----------+----------+----------+----------+----------+");
Log("| Leader | Router | Child | Detached | Disabled |");
Log("+----------+----------+----------+----------+----------+");
}
CalculateRoleStats(nexus, roleStats);
Log("| %8u | %8u | %8u | %8u | %8u |", roleStats[Mle::kRoleLeader], roleStats[Mle::kRoleRouter],
roleStats[Mle::kRoleChild], roleStats[Mle::kRoleDetached], roleStats[Mle::kRoleDisabled]);
nexus.AdvanceTime(kStatCollectionInterval);
if (CheckRoleStats(roleStats))
{
break;
}
}
VerifyOrQuit(CheckRoleStats(roleStats));
Log("=========================================================");
Log("All nodes are now part of the same partition");
Log("Resetting all the nodes in the network");
Log("=========================================================");
resetTime = nexus.GetNow();
for (Node &node : nexus.GetNodes())
{
node.Reset();
}
for (Node &node : nexus.GetNodes())
{
node.Get<ThreadNetif>().Up();
SuccessOrQuit(node.Get<Mle::Mle>().Start());
}
for (uint32_t step = 0; step < kMaxWaitTime / kStatCollectionInterval; step++)
{
if ((step % 20) == 0)
{
Log("+----------+----------+----------+----------+----------+");
Log("| Leader | Router | Child | Detached | Disabled |");
Log("+----------+----------+----------+----------+----------+");
}
CalculateRoleStats(nexus, roleStats);
Log("| %8u | %8u | %8u | %8u | %8u |", roleStats[Mle::kRoleLeader], roleStats[Mle::kRoleRouter],
roleStats[Mle::kRoleChild], roleStats[Mle::kRoleDetached], roleStats[Mle::kRoleDisabled]);
nexus.AdvanceTime(kStatCollectionInterval);
if (CheckRoleStats(roleStats))
{
break;
}
}
Log("=========================================================");
Log("All nodes are restored");
Log("Network stabilized after %u sec", (nexus.GetNow() - resetTime) / Time::kOneSecondInMsec);
Log("=========================================================");
for (uint32_t step = 0; step < kExtraSimulTimeAfterPass / kStatCollectionInterval; step++)
{
if ((step % 20) == 0)
{
Log("+----------+----------+----------+----------+----------+");
Log("| Leader | Router | Child | Detached | Disabled |");
Log("+----------+----------+----------+----------+----------+");
}
CalculateRoleStats(nexus, roleStats);
Log("| %8u | %8u | %8u | %8u | %8u |", roleStats[Mle::kRoleLeader], roleStats[Mle::kRoleRouter],
roleStats[Mle::kRoleChild], roleStats[Mle::kRoleDetached], roleStats[Mle::kRoleDisabled]);
nexus.AdvanceTime(kStatCollectionInterval);
}
}
} // namespace Nexus
} // namespace ot
int main(void)
{
ot::Nexus::Test();
printf("All tests passed\n");
return 0;
}