diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index 9cefce828..4fdd306a8 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -396,6 +396,7 @@ ot_nexus_test(dtls "core;nexus") ot_nexus_test(form_join "core;nexus") ot_nexus_test(ipv6_source_selection "core;nexus") ot_nexus_test(log_override "core;nexus") +ot_nexus_test(mac_scan "core;nexus") ot_nexus_test(mle_blocking_downgrade "core;nexus") ot_nexus_test(nat64_translator "core;nexus") ot_nexus_test(reed_address_solicit_rejected "core;nexus") diff --git a/tests/nexus/test_mac_scan.cpp b/tests/nexus/test_mac_scan.cpp new file mode 100644 index 000000000..23fc5d79c --- /dev/null +++ b/tests/nexus/test_mac_scan.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2026, 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 +#include +#include + +#include "platform/nexus_core.hpp" +#include "platform/nexus_node.hpp" + +namespace ot { +namespace Nexus { + +struct MacScanContext +{ + static constexpr uint16_t kMaxResults = 16; + + void Clear(void) + { + mScanDone = false; + mScanResults.Clear(); + } + + bool mScanDone; + Array mScanResults; +}; + +void HandleMacScanResult(otActiveScanResult *aResult, void *aContext) +{ + MacScanContext *context = static_cast(aContext); + ScanResult *result = AsCoreTypePtr(aResult); + + VerifyOrQuit(aContext != nullptr); + + Log(" HandleMacScanResult() called%s", result == nullptr ? " (done)" : ""); + + if (result == nullptr) + { + context->mScanDone = true; + } + else + { + VerifyOrQuit(!context->mScanDone); + SuccessOrQuit(context->mScanResults.PushBack(*result)); + } +} + +void TestMacScan(void) +{ + Core nexus; + Node &leader = nexus.CreateNode(); + Node &router = nexus.CreateNode(); + MacScanContext resultContext; + ScanResult *result; + + Log("------------------------------------------------------------------------------------------------------"); + Log("TestMacScan"); + + nexus.AdvanceTime(0); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Form the network"); + + leader.Form(); + nexus.AdvanceTime(10 * Time::kOneSecondInMsec); + + leader.AllowList(router); + router.AllowList(leader); + + router.Join(leader); + nexus.AdvanceTime(50 * Time::kOneSecondInMsec); + + VerifyOrQuit(leader.Get().IsLeader()); + VerifyOrQuit(router.Get().IsAttached()); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Perform MAC scan from leader to find router"); + + resultContext.Clear(); + + SuccessOrQuit(otLinkActiveScan(&leader.GetInstance(), 0, 0, HandleMacScanResult, &resultContext)); + + nexus.AdvanceTime(10 * Time::kOneSecondInMsec); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Check the Scan Result"); + + VerifyOrQuit(resultContext.mScanDone); + VerifyOrQuit(resultContext.mScanResults.GetLength() == 1); + + bool foundRouter = false; + for (uint16_t i = 0; i < resultContext.mScanResults.GetLength(); i++) + { + result = &resultContext.mScanResults[i]; + Log(" Found result: extaddr:%s channel:%u", result->GetExtAddress().ToString().AsCString(), + result->GetChannel()); + + if (result->GetExtAddress() == router.Get().GetExtAddress()) + { + foundRouter = true; + VerifyOrQuit(result->GetChannel() == leader.Get().GetPanChannel()); + } + } + + VerifyOrQuit(foundRouter, "Leader did not find Router in MAC scan"); + Log("Leader found Router in MAC scan successfully"); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestMacScan(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/scripts/thread-cert/test_mac_scan.py b/tests/scripts/thread-cert/test_mac_scan.py deleted file mode 100755 index 7c023c4cf..000000000 --- a/tests/scripts/thread-cert/test_mac_scan.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2016, 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. -# -import logging -import unittest - -import config -import thread_cert - -LEADER = 1 -ROUTER = 2 - -CHANNEL = 12 - - -class Test_MacScan(thread_cert.TestCase): - USE_MESSAGE_FACTORY = False - SUPPORT_NCP = False - - TOPOLOGY = { - LEADER: { - 'channel': CHANNEL, - 'mode': 'rdn', - 'network_name': 'OpenThread', - 'allowlist': [ROUTER] - }, - ROUTER: { - 'channel': CHANNEL, - 'mode': 'rdn', - 'network_name': 'OpenThread', - 'allowlist': [LEADER] - }, - } - - def test(self): - self.nodes[LEADER].start() - self.nodes[LEADER].set_state('leader') - self.assertEqual(self.nodes[LEADER].get_state(), 'leader') - - self.nodes[ROUTER].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) - self.assertEqual(self.nodes[ROUTER].get_state(), 'router') - - results = self.nodes[LEADER].scan(result=True) - logging.info('scan result: %r', results) - self.assertEqual(len(results), 1) - network = results[0] - self.assertEqual(network['extaddr'], self.nodes[ROUTER].get_addr64()) - self.assertEqual(network['channel'], CHANNEL) - - -if __name__ == '__main__': - unittest.main()