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