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