1 /*
2  *  Copyright (c) 2021, 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 <stdarg.h>
30 #include <string.h>
31 
32 #include "test_platform.h"
33 
34 #include <openthread/config.h>
35 
36 #include "common/array.hpp"
37 #include "common/debug.hpp"
38 #include "common/equatable.hpp"
39 #include "common/type_traits.hpp"
40 #include "instance/instance.hpp"
41 
42 #include "test_util.h"
43 
44 namespace ot {
45 
TestArray(void)46 void TestArray(void)
47 {
48     constexpr uint16_t kMaxSize    = 10;
49     constexpr uint16_t kStartValue = 100;
50 
51     Array<uint16_t, kMaxSize> array;
52     uint8_t                   index;
53     uint16_t                  seed;
54 
55     // All methods after constructor
56     VerifyOrQuit(array.IsEmpty());
57     VerifyOrQuit(!array.IsFull());
58     VerifyOrQuit(array.GetLength() == 0);
59     VerifyOrQuit(array.GetMaxSize() == kMaxSize);
60     VerifyOrQuit(array.At(0) == nullptr);
61     VerifyOrQuit(array.Front() == nullptr);
62     VerifyOrQuit(array.Back() == nullptr);
63     VerifyOrQuit(array.PopBack() == nullptr);
64 
65     seed = kStartValue;
66 
67     for (uint8_t len = 1; len <= kMaxSize; len++)
68     {
69         for (uint8_t iter = 0; iter < 2; iter++)
70         {
71             // On `iter == 0` use `PushBack(aEntry)` and on `iter == 1`
72             // `PushBack()` version which returns a pointer to the newly
73             // added entry.
74 
75             if (iter == 0)
76             {
77                 SuccessOrQuit(array.PushBack(seed + len));
78             }
79             else
80             {
81                 uint16_t *entry = array.PushBack();
82 
83                 VerifyOrQuit(entry != nullptr);
84                 *entry = seed + len;
85             }
86 
87             VerifyOrQuit(!array.IsEmpty());
88             VerifyOrQuit(array.IsFull() == (len == kMaxSize));
89             VerifyOrQuit(array.GetLength() == len);
90 
91             VerifyOrQuit(array.Front() != nullptr);
92             VerifyOrQuit(*array.Front() == seed + 1);
93             VerifyOrQuit(array.Back() != nullptr);
94             VerifyOrQuit(*array.Back() == seed + len);
95 
96             for (index = 0; index < len; index++)
97             {
98                 VerifyOrQuit(array[index] == seed + index + 1);
99                 VerifyOrQuit(array.At(index) != nullptr);
100                 VerifyOrQuit(*array.At(index) == seed + index + 1);
101 
102                 VerifyOrQuit(array.Contains(seed + index + 1));
103                 VerifyOrQuit(array.Find(seed + index + 1) == &array[index]);
104 
105                 VerifyOrQuit(!array.Contains(seed));
106                 VerifyOrQuit(array.Find(seed) == nullptr);
107             }
108 
109             index = 0;
110 
111             for (uint16_t value : array)
112             {
113                 VerifyOrQuit(value == array[index]);
114                 index++;
115             }
116 
117             index = 0;
118 
119             for (uint16_t &entry : array)
120             {
121                 // Uddate the value stored at the entry
122                 entry++;
123 
124                 VerifyOrQuit(entry == array[index]);
125                 VerifyOrQuit(array.IndexOf(entry) == index);
126 
127                 index++;
128             }
129 
130             seed++;
131 
132             // On `iter == 0` we verify `PopBack()` and remove the
133             // last entry. It will be added again from next `iter`
134             // loop (on `iter == 1`).
135 
136             if (iter == 0)
137             {
138                 uint16_t *entry = array.PopBack();
139 
140                 VerifyOrQuit(entry != nullptr);
141                 VerifyOrQuit(*entry == seed + len);
142                 VerifyOrQuit(array.GetLength() == len - 1);
143             }
144         }
145     }
146 
147     VerifyOrQuit(array.IsFull());
148     VerifyOrQuit(array.PushBack(0) == kErrorNoBufs);
149     VerifyOrQuit(array.PushBack() == nullptr);
150 
151     for (uint8_t len = kMaxSize; len >= 1; len--)
152     {
153         uint16_t *entry;
154 
155         VerifyOrQuit(array.GetLength() == len);
156         VerifyOrQuit(array.Back() == &array[len - 1]);
157 
158         entry = array.PopBack();
159         VerifyOrQuit(entry != nullptr);
160         VerifyOrQuit(*entry == seed + len);
161 
162         VerifyOrQuit(array.GetLength() == len - 1);
163         VerifyOrQuit(!array.IsFull());
164     }
165 
166     VerifyOrQuit(array.IsEmpty());
167 
168     SuccessOrQuit(array.PushBack(seed));
169     VerifyOrQuit(!array.IsEmpty());
170 
171     array.Clear();
172     VerifyOrQuit(array.IsEmpty());
173 }
174 
TestArrayCopyAndFindMatching(void)175 void TestArrayCopyAndFindMatching(void)
176 {
177     constexpr uint16_t kMaxSize = 10;
178 
179     enum MatchMode : uint8_t
180     {
181         kMatchAll,
182         kMatchNone,
183         kMatchOddYear,
184         kMatchEvenYear,
185     };
186 
187     struct Entry : public Unequatable<Entry>
188     {
189         Entry(void) = default;
190 
191         Entry(const char *aName, uint16_t aYear)
192             : mName(aName)
193             , mYear(aYear)
194         {
195         }
196 
197         bool operator==(const Entry &aOther) const { return (mName == aOther.mName) && (mYear == aOther.mYear); }
198         bool Matches(const char *aName) const { return strcmp(aName, mName) == 0; }
199         bool Matches(uint16_t aYear) const { return aYear == mYear; }
200 
201         bool Matches(MatchMode aMatchMode) const
202         {
203             bool matches = false;
204 
205             switch (aMatchMode)
206             {
207             case kMatchAll:
208                 matches = true;
209                 break;
210             case kMatchNone:
211                 matches = false;
212                 break;
213             case kMatchOddYear:
214                 matches = ((mYear % 2) != 0);
215                 break;
216             case kMatchEvenYear:
217                 matches = ((mYear % 2) == 0);
218                 break;
219             }
220 
221             return matches;
222         }
223 
224         const char *mName;
225         uint16_t    mYear;
226     };
227 
228     static const MatchMode kMatchModes[] = {kMatchAll, kMatchNone, kMatchEvenYear, kMatchOddYear};
229 
230     static const char *kMatchModeStrings[] = {
231         "kMatchAll",
232         "kMatchNone",
233         "kMatchOddYear",
234         "kMatchEvenYear",
235     };
236 
237     Entry ps1("PS", 1994);
238     Entry ps2("PS2", 1999);
239     Entry ps3("PS3", 2006);
240     Entry ps4("PS4", 2013);
241     Entry ps5("PS5", 2020);
242 
243     Array<Entry, kMaxSize> array1;
244     Array<Entry, kMaxSize> array2;
245     Array<Entry, kMaxSize> array3(array1);
246 
247     VerifyOrQuit(array1.IsEmpty());
248     VerifyOrQuit(array2.IsEmpty());
249     VerifyOrQuit(array3.IsEmpty());
250 
251     SuccessOrQuit(array1.PushBack(ps1));
252     SuccessOrQuit(array1.PushBack(ps2));
253     SuccessOrQuit(array1.PushBack(ps3));
254     SuccessOrQuit(array1.PushBack(ps4));
255     VerifyOrQuit(array1.GetLength() == 4);
256 
257     SuccessOrQuit(array2.PushBack(ps3));
258     SuccessOrQuit(array2.PushBack(ps5));
259     VerifyOrQuit(array2.GetLength() == 2);
260 
261     array3 = array2 = array1;
262 
263     VerifyOrQuit(array1.GetLength() == 4);
264     VerifyOrQuit(array2.GetLength() == 4);
265     VerifyOrQuit(array3.GetLength() == 4);
266 
267     for (uint8_t index = 0; index < array1.GetLength(); index++)
268     {
269         VerifyOrQuit(array1[index] == array2[index]);
270         VerifyOrQuit(array1[index] == array3[index]);
271     }
272 
273     array3.Clear();
274 
275     array1 = array3;
276     VerifyOrQuit(array1.IsEmpty());
277     VerifyOrQuit(array1.GetLength() == 0);
278 
279     {
280         Array<Entry, kMaxSize> array4(array2);
281 
282         VerifyOrQuit(array4.GetLength() == 4);
283 
284         for (uint8_t index = 0; index < array1.GetLength(); index++)
285         {
286             VerifyOrQuit(array2[index] == array4[index]);
287         }
288     }
289 
290     SuccessOrQuit(array2.PushBack(ps5));
291     VerifyOrQuit(array2.GetLength() == 5);
292 
293     for (const Entry &entry : array2)
294     {
295         Entry *match;
296 
297         printf("- Name:%-3s Year:%d\n", entry.mName, entry.mYear);
298 
299         match = array2.FindMatching(entry.mName);
300         VerifyOrQuit(match != nullptr);
301         VerifyOrQuit(match == &entry);
302         VerifyOrQuit(array2.ContainsMatching(entry.mName));
303 
304         match = array2.FindMatching(entry.mYear);
305         VerifyOrQuit(match != nullptr);
306         VerifyOrQuit(match == &entry);
307         VerifyOrQuit(array2.ContainsMatching(entry.mYear));
308     }
309 
310     VerifyOrQuit(array2.FindMatching("PS6") == nullptr);
311     VerifyOrQuit(!array2.ContainsMatching("PS6"));
312     VerifyOrQuit(array2.FindMatching(static_cast<uint16_t>(2001)) == nullptr);
313     VerifyOrQuit(!array2.ContainsMatching(static_cast<uint16_t>(2001)));
314 
315     // Test removing of entries at every index.
316 
317     array1 = array2;
318 
319     for (const Entry &entryToRemove : array1)
320     {
321         Entry *match;
322 
323         // Test `Remove()`
324 
325         array2 = array1;
326         match  = array2.Find(entryToRemove);
327         VerifyOrQuit(match != nullptr);
328         array2.Remove(*match);
329 
330         VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1);
331 
332         for (const Entry &entry : array2)
333         {
334             VerifyOrQuit(entry != entryToRemove);
335             VerifyOrQuit(array1.Contains(entry));
336         }
337 
338         // Test `RemoveMatching()`
339 
340         array2 = array1;
341         array2.RemoveMatching(entryToRemove.mName);
342 
343         printf("\n- - - - - - - - - - - - - - - - - - - - - - - - ");
344         printf("\nArray after `RemoveMatching()` on entry %s\n", entryToRemove.mName);
345 
346         VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1);
347 
348         for (const Entry &entry : array2)
349         {
350             printf("- Name:%-3s Year:%d\n", entry.mName, entry.mYear);
351             VerifyOrQuit(entry != entryToRemove);
352             VerifyOrQuit(array1.Contains(entry));
353         }
354 
355         // Test `RemoveMatchin()` with a non-existing match
356 
357         array2.RemoveMatching(entryToRemove.mName);
358         VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1);
359 
360         // Test `RemoveAllMatching()` removing a single matching entry.
361 
362         array2 = array1;
363         array2.RemoveAllMatching(entryToRemove.mName);
364 
365         VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1);
366 
367         for (const Entry &entry : array2)
368         {
369             VerifyOrQuit(entry != entryToRemove);
370             VerifyOrQuit(array1.Contains(entry));
371         }
372 
373         array2.RemoveAllMatching(entryToRemove.mName);
374         VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1);
375 
376         // Test `RemoveAllMatching()` using different match modes
377         // removing different subsets.
378 
379         for (MatchMode matchMode : kMatchModes)
380         {
381             array3 = array2;
382 
383             array3.RemoveAllMatching(matchMode);
384 
385             printf("\nArray after `RemoveAllMatching(%s)\n", kMatchModeStrings[matchMode]);
386 
387             for (const Entry &entry : array3)
388             {
389                 VerifyOrQuit(!entry.Matches(matchMode));
390                 VerifyOrQuit(array2.Contains(entry));
391                 printf("- Name:%-3s Year:%d\n", entry.mName, entry.mYear);
392             }
393 
394             for (const Entry &entry : array2)
395             {
396                 if (!entry.Matches(matchMode))
397                 {
398                     VerifyOrQuit(array2.Contains(entry));
399                 }
400             }
401 
402             array3.RemoveAllMatching(kMatchAll);
403             VerifyOrQuit(array3.IsEmpty());
404         }
405     }
406 
407     printf("\n");
408 }
409 
TestArrayIndexType(void)410 void TestArrayIndexType(void)
411 {
412     typedef Array<uint16_t, 255>           Array1;
413     typedef Array<uint16_t, 256>           Array2;
414     typedef Array<uint16_t, 100, uint16_t> Array3;
415 
416     static_assert(TypeTraits::IsSame<Array1::IndexType, uint8_t>::kValue, "Array1::IndexType is incorrect");
417     static_assert(TypeTraits::IsSame<Array2::IndexType, uint16_t>::kValue, "Array2::IndexType is incorrect");
418     static_assert(TypeTraits::IsSame<Array3::IndexType, uint16_t>::kValue, "Array3::IndexType is incorrect");
419 }
420 
421 } // namespace ot
422 
main(void)423 int main(void)
424 {
425     ot::TestArray();
426     ot::TestArrayCopyAndFindMatching();
427     ot::TestArrayIndexType();
428 
429     printf("All tests passed\n");
430     return 0;
431 }
432