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