1 /*
2 * Copyright (c) 2023, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "test_platform.h"
30
31 #include <openthread/config.h>
32
33 #include "test_util.h"
34 #include "common/array.hpp"
35 #include "common/code_utils.hpp"
36 #include "instance/instance.hpp"
37 #include "mac/mac_types.hpp"
38 #include "thread/child_table.hpp"
39
40 namespace ot {
41
42 static Instance *sInstance;
43
44 #if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
45
46 using namespace Utils;
47
48 static uint32_t sNow = 10000;
49
50 extern "C" {
51
otPlatAlarmMilliGetNow(void)52 uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
53
54 } // extern "C"
55
56 struct TestChild
57 {
58 Child::State mState;
59 otExtAddress mExtAddress;
60 uint16_t mThreadVersion;
61 };
62
63 class UnitTester
64 {
65 public:
66 static void TestLinkMetricsManager(void);
67
68 private:
69 static void SetTestLinkMetricsValues(otLinkMetricsValues &aLinkMetricsValues,
70 uint8_t aLinkMarginValue,
71 int8_t aRssiValue);
72
73 static const TestChild mTestChildList[];
74 };
75
76 const TestChild UnitTester::mTestChildList[] = {
77 {
78 Child::kStateValid,
79 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16}},
80 kThreadVersion1p2,
81 },
82 {
83 Child::kStateValid,
84 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x17}},
85 kThreadVersion1p2,
86 },
87 {
88 Child::kStateParentRequest,
89 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x18}},
90 kThreadVersion1p2,
91 },
92 };
93
SetTestLinkMetricsValues(otLinkMetricsValues & aLinkMetricsValues,uint8_t aLinkMarginValue,int8_t aRssiValue)94 void UnitTester::SetTestLinkMetricsValues(otLinkMetricsValues &aLinkMetricsValues,
95 uint8_t aLinkMarginValue,
96 int8_t aRssiValue)
97 {
98 aLinkMetricsValues.mMetrics.mLinkMargin = true;
99 aLinkMetricsValues.mMetrics.mRssi = true;
100
101 aLinkMetricsValues.mLinkMarginValue = aLinkMarginValue;
102 aLinkMetricsValues.mRssiValue = aRssiValue;
103 }
104
TestLinkMetricsManager(void)105 void UnitTester::TestLinkMetricsManager(void)
106 {
107 ChildTable *childTable;
108 const uint16_t testListLength = GetArrayLength(mTestChildList);
109 Ip6::Address linkLocalAddr;
110 LinkMetricsManager::Subject *subject1 = nullptr;
111 LinkMetricsManager::Subject *subject2 = nullptr;
112 LinkMetricsManager *linkMetricsMgr = nullptr;
113 otLinkMetricsValues linkMetricsValues;
114 otShortAddress anyShortAddress = 0x1234;
115
116 sInstance = testInitInstance();
117 VerifyOrQuit(sInstance != nullptr);
118
119 childTable = &sInstance->Get<ChildTable>();
120
121 sInstance->Get<Mle::Mle>().SetRole(Mle::kRoleRouter);
122 // Add the child entries from test list
123 for (uint16_t i = 0; i < testListLength; i++)
124 {
125 Child *child;
126
127 child = childTable->GetNewChild();
128 VerifyOrQuit(child != nullptr, "GetNewChild() failed");
129
130 child->SetState(mTestChildList[i].mState);
131 child->SetExtAddress((AsCoreType(&mTestChildList[i].mExtAddress)));
132 child->SetVersion(mTestChildList[i].mThreadVersion);
133 }
134
135 linkMetricsMgr = &sInstance->Get<LinkMetricsManager>();
136 linkMetricsMgr->SetEnabled(true);
137 // Update the subjects for the first time.
138 linkMetricsMgr->UpdateSubjects();
139
140 // Expect there are 2 subjects in NotConfigured state.
141 {
142 uint16_t index = 0;
143 for (LinkMetricsManager::Subject &subject : linkMetricsMgr->mSubjectList)
144 {
145 VerifyOrQuit(subject.mExtAddress == AsCoreType(&mTestChildList[1 - index].mExtAddress));
146 VerifyOrQuit(subject.mState == LinkMetricsManager::SubjectState::kNotConfigured);
147 index++;
148 }
149 }
150 subject1 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[0].mExtAddress));
151 subject2 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[1].mExtAddress));
152 VerifyOrQuit(subject1 != nullptr);
153 VerifyOrQuit(subject2 != nullptr);
154
155 // Update the state of the subjects
156 linkMetricsMgr->UpdateLinkMetricsStates();
157 // Expect the 2 subjects are in Configuring state.
158 for (LinkMetricsManager::Subject &subject : linkMetricsMgr->mSubjectList)
159 {
160 VerifyOrQuit(subject.mState == LinkMetricsManager::SubjectState::kConfiguring);
161 }
162
163 // subject1 received a response with a success status code
164 linkLocalAddr.SetToLinkLocalAddress(AsCoreType(&mTestChildList[0].mExtAddress));
165 linkMetricsMgr->HandleMgmtResponse(&linkLocalAddr, MapEnum(LinkMetrics::Status::kStatusSuccess));
166 VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kActive);
167
168 // subject1 received Enhanced ACK IE and updated the link metrics data
169 {
170 constexpr uint8_t kTestLinkMargin = 100;
171 constexpr int8_t kTestRssi = -30;
172 SetTestLinkMetricsValues(linkMetricsValues, kTestLinkMargin, kTestRssi);
173 linkMetricsMgr->HandleEnhAckIe(anyShortAddress, &mTestChildList[0].mExtAddress, &linkMetricsValues);
174 VerifyOrQuit(subject1->mData.mLinkMargin == kTestLinkMargin);
175 VerifyOrQuit(subject1->mData.mRssi == kTestRssi);
176 }
177
178 // subject2 didn't receive any responses after a few attempts and marked as UnSupported.
179 for (uint8_t i = 0; i < LinkMetricsManager::kConfigureLinkMetricsMaxAttempts; i++)
180 {
181 linkMetricsMgr->Update();
182 }
183 VerifyOrQuit(subject2->mState == LinkMetricsManager::SubjectState::kNotSupported);
184
185 // neighbor2 is removed and subject2 should also be removed.
186 Child *child2 = childTable->FindChild(subject2->mExtAddress, Child::kInStateValid);
187 child2->SetState(Child::kStateInvalid);
188 linkMetricsMgr->Update();
189 subject2 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[1].mExtAddress));
190 VerifyOrQuit(subject2 == nullptr);
191
192 // subject1 still existed
193 subject1 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[0].mExtAddress));
194 VerifyOrQuit(subject1 != nullptr);
195
196 // Make subject1 renew
197 sNow += LinkMetricsManager::kStateUpdateIntervalMilliSec + 1;
198 linkMetricsMgr->Update();
199 VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kRenewing);
200
201 // subject1 got Enh-ACK IE when in kRenewing state
202 sNow += 1;
203 linkMetricsMgr->HandleEnhAckIe(anyShortAddress, &mTestChildList[0].mExtAddress, &linkMetricsValues);
204 VerifyOrQuit(subject1->mLastUpdateTime == TimeMilli(sNow));
205
206 // subject1 got response and become active again
207 linkMetricsMgr->HandleMgmtResponse(&linkLocalAddr, MapEnum(LinkMetrics::Status::kStatusSuccess));
208 VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kActive);
209 }
210
211 #endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
212
213 } // namespace ot
214
main(void)215 int main(void)
216 {
217 #if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
218 ot::UnitTester::TestLinkMetricsManager();
219 #endif
220 printf("\nAll tests passed.\n");
221 return 0;
222 }
223