1 /*
2 * Copyright (c) 2018, 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 "thread/child_table.hpp"
38
39 namespace ot {
40
41 static Instance *sInstance;
42
43 enum
44 {
45 kMaxChildren = OPENTHREAD_CONFIG_MLE_MAX_CHILDREN,
46 };
47
48 struct TestChild
49 {
50 Child::State mState;
51 uint16_t mRloc16;
52 otExtAddress mExtAddress;
53 };
54
55 const Child::StateFilter kAllFilters[] = {
56 Child::kInStateValid,
57 Child::kInStateValidOrRestoring,
58 Child::kInStateChildIdRequest,
59 Child::kInStateValidOrAttaching,
60 Child::kInStateAnyExceptInvalid,
61 };
62
63 // Checks whether a `Child` matches the `TestChild` struct.
ChildMatches(const Child & aChild,const TestChild & aTestChild)64 static bool ChildMatches(const Child &aChild, const TestChild &aTestChild)
65 {
66 return (aChild.GetState() == aTestChild.mState) && (aChild.GetRloc16() == aTestChild.mRloc16) &&
67 (aChild.GetExtAddress() == static_cast<const Mac::ExtAddress &>(aTestChild.mExtAddress));
68 }
69
70 // Checks whether a `Child::State` matches a `Child::StateFilter`.
StateMatchesFilter(Child::State aState,Child::StateFilter aFilter)71 static bool StateMatchesFilter(Child::State aState, Child::StateFilter aFilter)
72 {
73 bool rval = false;
74 Child child;
75
76 child.SetState(aState);
77
78 switch (aFilter)
79 {
80 case Child::kInStateAnyExceptInvalid:
81 rval = (aState != Child::kStateInvalid);
82 break;
83
84 case Child::kInStateValid:
85 rval = (aState == Child::kStateValid);
86 break;
87
88 case Child::kInStateValidOrRestoring:
89 rval = child.IsStateValidOrRestoring();
90 break;
91
92 case Child::kInStateChildIdRequest:
93 rval = (aState == Child::kStateChildIdRequest);
94 break;
95
96 case Child::kInStateValidOrAttaching:
97 rval = child.IsStateValidOrAttaching();
98 break;
99
100 case Child::kInStateInvalid:
101 rval = child.IsStateInvalid();
102 break;
103
104 case Child::kInStateAnyExceptValidOrRestoring:
105 rval = !child.IsStateValidOrRestoring();
106 break;
107
108 case Child::kInStateAny:
109 rval = true;
110 break;
111 }
112
113 return rval;
114 }
115
116 // Verifies that `ChildTable` contains a given list of `TestChild` entries.
VerifyChildTableContent(ChildTable & aTable,uint16_t aChildListLength,const TestChild * aChildList)117 void VerifyChildTableContent(ChildTable &aTable, uint16_t aChildListLength, const TestChild *aChildList)
118 {
119 printf("Test ChildTable with %d entries", aChildListLength);
120
121 for (Child::StateFilter filter : kAllFilters)
122 {
123 // Verify that we can find all children from given list by rloc or extended address.
124
125 for (uint16_t listIndex = 0; listIndex < aChildListLength; listIndex++)
126 {
127 Child *child;
128 Mac::Address address;
129
130 if (!StateMatchesFilter(aChildList[listIndex].mState, filter))
131 {
132 continue;
133 }
134
135 child = aTable.FindChild(aChildList[listIndex].mRloc16, filter);
136 VerifyOrQuit(child != nullptr, "FindChild(rloc) failed");
137 VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(rloc) returned incorrect child");
138
139 child = aTable.FindChild(static_cast<const Mac::ExtAddress &>(aChildList[listIndex].mExtAddress), filter);
140 VerifyOrQuit(child != nullptr, "FindChild(ExtAddress) failed");
141 VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(ExtAddress) returned incorrect child");
142
143 address.SetShort(aChildList[listIndex].mRloc16);
144 child = aTable.FindChild(address, filter);
145 VerifyOrQuit(child != nullptr, "FindChild(address) failed");
146 VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child");
147
148 address.SetExtended(static_cast<const Mac::ExtAddress &>(aChildList[listIndex].mExtAddress));
149 child = aTable.FindChild(address, filter);
150 VerifyOrQuit(child != nullptr, "FindChild(address) failed");
151 VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child");
152 }
153
154 // Verify `ChildTable::Iterator` behavior.
155
156 {
157 ChildTable::Iterator iter(*sInstance, filter);
158 bool childObserved[kMaxChildren];
159 uint16_t numChildren = 0;
160
161 memset(childObserved, 0, sizeof(childObserved));
162
163 // Use the iterator and verify that each returned `Child` entry is in the expected list.
164
165 for (; !iter.IsDone(); iter++)
166 {
167 Child *child = iter.GetChild();
168 Child &childRef = *iter;
169 bool didFind = false;
170 uint16_t childIndex;
171
172 VerifyOrQuit(child != nullptr, "iter.GetChild() failed");
173 VerifyOrQuit(&childRef == child, "iter.operator*() failed");
174 VerifyOrQuit(iter->GetRloc16() == child->GetRloc16(), "iter.operator->() failed");
175
176 childIndex = aTable.GetChildIndex(*child);
177 VerifyOrQuit(childIndex < aTable.GetMaxChildrenAllowed(), "Child Index is out of bound");
178 VerifyOrQuit(aTable.GetChildAtIndex(childIndex) == child, "GetChildAtIndex() failed");
179
180 for (uint16_t index = 0; index < aChildListLength; index++)
181 {
182 if (ChildMatches(*iter.GetChild(), aChildList[index]))
183 {
184 childObserved[index] = true;
185 numChildren++;
186 didFind = true;
187 break;
188 }
189 }
190
191 VerifyOrQuit(didFind, "ChildTable::Iterator returned an entry not in the expected list");
192 }
193
194 // Verify that when iterator is done, it points to `nullptr`.
195
196 VerifyOrQuit(iter.GetChild() == nullptr);
197
198 iter++;
199 VerifyOrQuit(iter.IsDone(), "iterator Advance() (after iterator is done) failed");
200 VerifyOrQuit(iter.GetChild() == nullptr);
201
202 // Verify that the number of children matches the number of entries we get from iterator.
203
204 VerifyOrQuit(aTable.GetNumChildren(filter) == numChildren);
205 VerifyOrQuit(aTable.HasChildren(filter) == (numChildren != 0));
206
207 // Verify that there is no missing or extra entry between the expected list
208 // and what was observed/returned by the iterator.
209
210 for (uint16_t index = 0; index < aChildListLength; index++)
211 {
212 if (StateMatchesFilter(aChildList[index].mState, filter))
213 {
214 VerifyOrQuit(childObserved[index], "iterator failed to return an expected entry");
215 }
216 else
217 {
218 VerifyOrQuit(!childObserved[index], "iterator returned an extra unexpected entry");
219 }
220 }
221
222 // Verify the behavior of range-based `for` iteration.
223
224 iter.Reset();
225
226 for (Child &child : aTable.Iterate(filter))
227 {
228 VerifyOrQuit(&child == iter.GetChild(), "range-based for loop Iterate() failed");
229 iter++;
230 }
231
232 VerifyOrQuit(iter.IsDone(), "range-based for loop Iterate() did not return all entries");
233 }
234 }
235
236 printf(" -- PASS\n");
237 }
238
TestChildTable(void)239 void TestChildTable(void)
240 {
241 const TestChild testChildList[] = {
242 {
243 Child::kStateValid,
244 0x8001,
245 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16}},
246 },
247 {
248 Child::kStateParentRequest,
249 0x8002,
250 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x17}},
251 },
252 {
253 Child::kStateValid,
254 0x8003,
255 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x18}},
256 },
257 {
258 Child::kStateValid,
259 0x8004,
260 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x19}},
261 },
262 {
263 Child::kStateRestored,
264 0x8005,
265 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x20}},
266 },
267 {
268 Child::kStateValid,
269 0x8006,
270 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x21}},
271 },
272 {
273 Child::kStateChildIdRequest,
274 0x8007,
275 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x22}},
276 },
277 {
278 Child::kStateChildUpdateRequest,
279 0x8008,
280 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x23}},
281 },
282 {
283 Child::kStateParentResponse,
284 0x8009,
285 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x24}},
286 },
287 {
288 Child::kStateRestored,
289 0x800a,
290 {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x25}},
291 },
292 };
293
294 const uint16_t testListLength = GetArrayLength(testChildList);
295
296 uint16_t testNumAllowedChildren = 2;
297
298 ChildTable *table;
299 Error error;
300
301 sInstance = testInitInstance();
302 VerifyOrQuit(sInstance != nullptr);
303
304 table = &sInstance->Get<ChildTable>();
305
306 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
307 printf("Checking initial state after child table is constructed");
308
309 VerifyOrQuit(table->GetMaxChildrenAllowed() == table->GetMaxChildren(),
310 "GetMaxChildrenAllowed() initial value is incorrect ");
311
312 for (Child::StateFilter filter : kAllFilters)
313 {
314 VerifyOrQuit(table->HasChildren(filter) == false);
315 VerifyOrQuit(table->GetNumChildren(filter) == 0);
316 }
317
318 printf(" -- PASS\n");
319
320 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
321
322 VerifyChildTableContent(*table, 0, testChildList);
323
324 VerifyOrQuit(table->GetMaxChildrenAllowed() >= testListLength,
325 "Default child table size is too small for the unit test");
326
327 // Add the child entries from test list one by one and verify the table content
328 for (uint16_t i = 0; i < testListLength; i++)
329 {
330 Child *child;
331
332 child = table->GetNewChild();
333 VerifyOrQuit(child != nullptr, "GetNewChild() failed");
334
335 child->SetState(testChildList[i].mState);
336 child->SetRloc16(testChildList[i].mRloc16);
337 child->SetExtAddress((static_cast<const Mac::ExtAddress &>(testChildList[i].mExtAddress)));
338
339 VerifyChildTableContent(*table, i + 1, testChildList);
340 }
341
342 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
343 // Verify Child Table clear
344
345 table->Clear();
346
347 VerifyChildTableContent(*table, 0, testChildList);
348
349 // Add the child entries from test list in reverse order and verify the table content
350 for (uint16_t i = testListLength; i > 0; i--)
351 {
352 Child *child;
353
354 child = table->GetNewChild();
355 VerifyOrQuit(child != nullptr, "GetNewChild() failed");
356
357 child->SetState(testChildList[i - 1].mState);
358 child->SetRloc16(testChildList[i - 1].mRloc16);
359 child->SetExtAddress((static_cast<const Mac::ExtAddress &>(testChildList[i - 1].mExtAddress)));
360
361 VerifyChildTableContent(*table, testListLength - i + 1, &testChildList[i - 1]);
362 }
363
364 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
365 printf("Test Get/SetMaxChildrenAllowed");
366
367 error = table->SetMaxChildrenAllowed(kMaxChildren - 1);
368 VerifyOrQuit(error == kErrorInvalidState, "SetMaxChildrenAllowed() should fail when table is not empty");
369
370 table->Clear();
371 error = table->SetMaxChildrenAllowed(kMaxChildren + 1);
372 VerifyOrQuit(error == kErrorInvalidArgs, "SetMaxChildrenAllowed() did not fail with an invalid arg");
373
374 error = table->SetMaxChildrenAllowed(0);
375 VerifyOrQuit(error == kErrorInvalidArgs, "SetMaxChildrenAllowed() did not fail with an invalid arg");
376
377 error = table->SetMaxChildrenAllowed(testNumAllowedChildren);
378 VerifyOrQuit(error == kErrorNone, "SetMaxChildrenAllowed() failed");
379 VerifyOrQuit(table->GetMaxChildrenAllowed() == testNumAllowedChildren);
380
381 for (uint16_t num = 0; num < testNumAllowedChildren; num++)
382 {
383 Child *child = table->GetNewChild();
384
385 VerifyOrQuit(child != nullptr, "GetNewChild() failed");
386 child->SetState(Child::kStateValid);
387 }
388
389 VerifyOrQuit(table->GetNewChild() == nullptr, "GetNewChild() did not fail when table was full");
390
391 printf(" -- PASS\n");
392
393 testFreeInstance(sInstance);
394 }
395
396 } // namespace ot
397
main(void)398 int main(void)
399 {
400 ot::TestChildTable();
401 printf("\nAll tests passed.\n");
402 return 0;
403 }
404