1 /*
2 * Copyright (c) 2019, 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/debug.hpp"
37 #include "common/linked_list.hpp"
38 #include "common/owning_list.hpp"
39 #include "instance/instance.hpp"
40
41 #include "test_util.h"
42
43 namespace ot {
44
45 struct EntryBase
46 {
47 EntryBase *mNext;
48 };
49
50 struct Entry : public EntryBase, LinkedListEntry<Entry>
51 {
52 public:
53 enum class Type : uint8_t
54 {
55 kAlpha,
56 kBeta,
57 };
58
Entryot::Entry59 Entry(const char *aName, uint16_t aId, Type aType = Type::kAlpha)
60 : mName(aName)
61 , mId(aId)
62 , mType(aType)
63 , mWasFreed(false)
64 {
65 }
66
GetNameot::Entry67 const char *GetName(void) const { return mName; }
GetIdot::Entry68 uint16_t GetId(void) const { return mId; }
Matchesot::Entry69 bool Matches(const char *aName) const { return strcmp(mName, aName) == 0; }
Matchesot::Entry70 bool Matches(uint16_t aId) const { return mId == aId; }
Matchesot::Entry71 bool Matches(Type aType) const { return mType == aType; }
Freeot::Entry72 void Free(void) { mWasFreed = true; }
73
ResetTestFlagsot::Entry74 void ResetTestFlags(void) { mWasFreed = false; }
WasFreedot::Entry75 bool WasFreed(void) const { return mWasFreed; }
76
77 private:
78 const char *mName;
79 uint16_t mId;
80 Type mType;
81 bool mWasFreed;
82 };
83
84 constexpr Entry::Type kAlphaType = Entry::Type::kAlpha;
85 constexpr Entry::Type kBetaType = Entry::Type::kBeta;
86
87 // This function verifies the content of the linked list matches a given list of entries.
VerifyLinkedListContent(const LinkedList<Entry> * aList,...)88 void VerifyLinkedListContent(const LinkedList<Entry> *aList, ...)
89 {
90 va_list args;
91 Entry *argEntry;
92 Entry *argPrev = nullptr;
93 const Entry *prev;
94 uint16_t unusedId = 100;
95
96 va_start(args, aList);
97
98 for (const Entry &entry : *aList)
99 {
100 argEntry = va_arg(args, Entry *);
101 VerifyOrQuit(argEntry != nullptr, "List contains more entries than expected");
102 VerifyOrQuit(argEntry == &entry, "List does not contain the same entry");
103 VerifyOrQuit(aList->Contains(*argEntry));
104 VerifyOrQuit(aList->ContainsMatching(argEntry->GetName()));
105 VerifyOrQuit(aList->ContainsMatching(argEntry->GetId()));
106
107 SuccessOrQuit(aList->Find(*argEntry, prev));
108 VerifyOrQuit(prev == argPrev, "List::Find() returned prev entry is incorrect");
109
110 VerifyOrQuit(aList->FindMatching(argEntry->GetName(), prev) == argEntry);
111 VerifyOrQuit(prev == argPrev, "List::FindMatching() returned prev entry is incorrect");
112
113 VerifyOrQuit(aList->FindMatching(argEntry->GetId(), prev) == argEntry);
114 VerifyOrQuit(prev == argPrev, "List::FindMatching() returned prev entry is incorrect");
115
116 VerifyOrQuit(!argEntry->WasFreed());
117
118 argPrev = argEntry;
119 }
120
121 argEntry = va_arg(args, Entry *);
122 VerifyOrQuit(argEntry == nullptr, "List contains less entries than expected");
123
124 VerifyOrQuit(aList->GetTail() == argPrev);
125
126 VerifyOrQuit(!aList->ContainsMatching("none"), "succeeded for a missing entry");
127 VerifyOrQuit(!aList->ContainsMatching(unusedId), "succeeded for a missing entry");
128
129 VerifyOrQuit(aList->FindMatching("none", prev) == nullptr, "succeeded for a missing entry");
130 VerifyOrQuit(aList->FindMatching(unusedId, prev) == nullptr, "succeeded for a missing entry");
131 }
132
TestLinkedList(void)133 void TestLinkedList(void)
134 {
135 Entry a("a", 1, kAlphaType), b("b", 2, kAlphaType), c("c", 3, kBetaType);
136 Entry d("d", 4, kBetaType), e("e", 5, kAlphaType), f("f", 6, kBetaType);
137 Entry *prev;
138 LinkedList<Entry> list;
139 LinkedList<Entry> removedList;
140
141 printf("TestLinkedList\n");
142
143 VerifyOrQuit(list.IsEmpty(), "failed after init");
144 VerifyOrQuit(list.GetHead() == nullptr, "failed after init");
145 VerifyOrQuit(list.Pop() == nullptr, "failed when empty");
146 VerifyOrQuit(list.Find(a, prev) == kErrorNotFound, "succeeded when empty");
147
148 VerifyLinkedListContent(&list, nullptr);
149
150 list.Push(a);
151 VerifyOrQuit(!list.IsEmpty());
152 VerifyLinkedListContent(&list, &a, nullptr);
153 VerifyOrQuit(list.Find(b, prev) == kErrorNotFound, "succeeded for a missing entry");
154
155 SuccessOrQuit(list.Add(b));
156 VerifyLinkedListContent(&list, &b, &a, nullptr);
157 VerifyOrQuit(list.Find(c, prev) == kErrorNotFound, "succeeded for a missing entry");
158
159 list.Push(c);
160 VerifyLinkedListContent(&list, &c, &b, &a, nullptr);
161
162 SuccessOrQuit(list.Add(d));
163 VerifyLinkedListContent(&list, &d, &c, &b, &a, nullptr);
164
165 SuccessOrQuit(list.Add(e));
166 VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
167
168 VerifyOrQuit(list.Add(a) == kErrorAlready, "did not detect duplicate");
169 VerifyOrQuit(list.Add(b) == kErrorAlready, "did not detect duplicate");
170 VerifyOrQuit(list.Add(d) == kErrorAlready, "did not detect duplicate");
171 VerifyOrQuit(list.Add(e) == kErrorAlready, "did not detect duplicate");
172
173 VerifyOrQuit(list.Pop() == &e);
174 VerifyLinkedListContent(&list, &d, &c, &b, &a, nullptr);
175 VerifyOrQuit(list.Find(e, prev) == kErrorNotFound, "succeeded for a missing entry");
176
177 VerifyOrQuit(list.FindMatching(d.GetName(), prev) == &d);
178 VerifyOrQuit(prev == nullptr);
179 VerifyOrQuit(list.FindMatching(c.GetId(), prev) == &c);
180 VerifyOrQuit(prev == &d);
181 VerifyOrQuit(list.FindMatching(b.GetName(), prev) == &b);
182 VerifyOrQuit(prev == &c);
183 VerifyOrQuit(list.FindMatching(a.GetId(), prev) == &a);
184 VerifyOrQuit(prev == &b);
185 VerifyOrQuit(list.FindMatching(e.GetId(), prev) == nullptr, "succeeded for a missing entry");
186 VerifyOrQuit(list.FindMatching(e.GetName(), prev) == nullptr, "succeeded for a missing entry");
187
188 list.SetHead(&e);
189 VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
190
191 SuccessOrQuit(list.Remove(c));
192 VerifyLinkedListContent(&list, &e, &d, &b, &a, nullptr);
193
194 VerifyOrQuit(list.Remove(c) == kErrorNotFound);
195 VerifyLinkedListContent(&list, &e, &d, &b, &a, nullptr);
196 VerifyOrQuit(list.Find(c, prev) == kErrorNotFound, "succeeded for a missing entry");
197
198 SuccessOrQuit(list.Remove(e));
199 VerifyLinkedListContent(&list, &d, &b, &a, nullptr);
200 VerifyOrQuit(list.Find(e, prev) == kErrorNotFound, "succeeded for a missing entry");
201
202 SuccessOrQuit(list.Remove(a));
203 VerifyLinkedListContent(&list, &d, &b, nullptr);
204 VerifyOrQuit(list.Find(a, prev) == kErrorNotFound, "succeeded for a missing entry");
205
206 list.Push(a);
207 list.Push(c);
208 list.Push(e);
209 VerifyLinkedListContent(&list, &e, &c, &a, &d, &b, nullptr);
210
211 VerifyOrQuit(list.PopAfter(&a) == &d);
212 VerifyLinkedListContent(&list, &e, &c, &a, &b, nullptr);
213
214 VerifyOrQuit(list.PopAfter(&b) == nullptr);
215 VerifyLinkedListContent(&list, &e, &c, &a, &b, nullptr);
216
217 VerifyOrQuit(list.PopAfter(&e) == &c);
218 VerifyLinkedListContent(&list, &e, &a, &b, nullptr);
219
220 list.PushAfter(c, b);
221 VerifyLinkedListContent(&list, &e, &a, &b, &c, nullptr);
222
223 list.PushAfter(d, a);
224 VerifyLinkedListContent(&list, &e, &a, &d, &b, &c, nullptr);
225
226 VerifyOrQuit(list.PopAfter(nullptr) == &e);
227 VerifyLinkedListContent(&list, &a, &d, &b, &c, nullptr);
228
229 VerifyOrQuit(list.PopAfter(nullptr) == &a);
230 VerifyLinkedListContent(&list, &d, &b, &c, nullptr);
231
232 list.Push(e);
233 list.Push(a);
234 VerifyLinkedListContent(&list, &a, &e, &d, &b, &c, nullptr);
235
236 VerifyOrQuit(list.RemoveMatching(a.GetName()) == &a);
237 VerifyLinkedListContent(&list, &e, &d, &b, &c, nullptr);
238
239 VerifyOrQuit(list.RemoveMatching(c.GetId()) == &c);
240 VerifyLinkedListContent(&list, &e, &d, &b, nullptr);
241
242 VerifyOrQuit(list.RemoveMatching(c.GetId()) == nullptr, "succeeded for missing entry");
243 VerifyOrQuit(list.RemoveMatching(a.GetName()) == nullptr, "succeeded for missing entry");
244
245 VerifyOrQuit(list.RemoveMatching(d.GetId()) == &d);
246 VerifyLinkedListContent(&list, &e, &b, nullptr);
247
248 list.Clear();
249 VerifyOrQuit(list.IsEmpty(), "failed after Clear()");
250 VerifyOrQuit(list.PopAfter(nullptr) == nullptr);
251 VerifyLinkedListContent(&list, nullptr);
252 VerifyOrQuit(list.Find(a, prev) == kErrorNotFound, "succeeded for a missing entry");
253 VerifyOrQuit(list.FindMatching(b.GetName(), prev) == nullptr, "succeeded when empty");
254 VerifyOrQuit(list.FindMatching(c.GetId(), prev) == nullptr, "succeeded when empty");
255 VerifyOrQuit(list.RemoveMatching(a.GetName()) == nullptr, "succeeded when empty");
256 VerifyOrQuit(list.Remove(a) == kErrorNotFound, "succeeded when empty");
257
258 list.Clear();
259 removedList.Clear();
260 list.Push(f);
261 list.Push(e);
262 list.Push(d);
263 list.Push(c);
264 list.Push(b);
265 list.Push(a);
266 VerifyLinkedListContent(&list, &a, &b, &c, &d, &e, &f, nullptr);
267
268 list.RemoveAllMatching(kAlphaType, removedList);
269 VerifyLinkedListContent(&list, &c, &d, &f, nullptr);
270 VerifyLinkedListContent(&removedList, &e, &b, &a, nullptr);
271
272 removedList.Clear();
273 list.RemoveAllMatching(kAlphaType, removedList);
274 VerifyLinkedListContent(&list, &c, &d, &f, nullptr);
275 VerifyOrQuit(removedList.IsEmpty());
276
277 list.RemoveAllMatching(kBetaType, removedList);
278 VerifyOrQuit(list.IsEmpty());
279 VerifyLinkedListContent(&removedList, &f, &d, &c, nullptr);
280
281 removedList.Clear();
282 list.RemoveAllMatching(kAlphaType, removedList);
283 VerifyOrQuit(list.IsEmpty());
284 VerifyOrQuit(removedList.IsEmpty());
285
286 list.Push(f);
287 list.Push(e);
288 list.Push(d);
289 list.Push(c);
290 list.Push(b);
291 list.Push(a);
292 VerifyLinkedListContent(&list, &a, &b, &c, &d, &e, &f, nullptr);
293
294 list.RemoveAllMatching(kBetaType, removedList);
295 VerifyLinkedListContent(&list, &a, &b, &e, nullptr);
296 VerifyLinkedListContent(&removedList, &f, &d, &c, nullptr);
297
298 list.Clear();
299 list.PushAfterTail(a);
300 VerifyLinkedListContent(&list, &a, nullptr);
301 list.PushAfterTail(b);
302 VerifyLinkedListContent(&list, &a, &b, nullptr);
303 list.PushAfterTail(c);
304 VerifyLinkedListContent(&list, &a, &b, &c, nullptr);
305 list.PushAfterTail(d);
306 VerifyLinkedListContent(&list, &a, &b, &c, &d, nullptr);
307 }
308
TestOwningList(void)309 void TestOwningList(void)
310 {
311 Entry a("a", 1, kAlphaType), b("b", 2, kAlphaType), c("c", 3, kBetaType);
312 Entry d("d", 4, kBetaType), e("e", 5, kAlphaType), f("f", 6, kBetaType);
313 OwningList<Entry> list;
314 OwningList<Entry> removedList;
315 OwnedPtr<Entry> ptr;
316 bool didRemove;
317
318 printf("TestOwningList\n");
319
320 VerifyOrQuit(list.IsEmpty());
321 VerifyOrQuit(list.GetHead() == nullptr);
322 VerifyOrQuit(list.Pop().IsNull());
323
324 list.Free();
325 VerifyOrQuit(list.IsEmpty());
326 VerifyOrQuit(list.GetHead() == nullptr);
327 VerifyOrQuit(list.Pop().IsNull());
328
329 // Clear()
330
331 list.Push(a);
332 VerifyLinkedListContent(&list, &a, nullptr);
333 list.Free();
334 VerifyOrQuit(list.IsEmpty());
335 VerifyOrQuit(a.WasFreed());
336
337 // Test removing entry without taking back the ownership
338
339 a.ResetTestFlags();
340 list.Push(a);
341 list.Push(b);
342 list.Push(c);
343 list.Push(d);
344 list.Push(e);
345 VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
346
347 list.Pop();
348 VerifyLinkedListContent(&list, &d, &c, &b, &a, nullptr);
349 VerifyOrQuit(e.WasFreed());
350
351 list.PopAfter(&c);
352 VerifyLinkedListContent(&list, &d, &c, &a, nullptr);
353 VerifyOrQuit(b.WasFreed());
354
355 list.RemoveMatching("c");
356 VerifyLinkedListContent(&list, &d, &a, nullptr);
357 VerifyOrQuit(c.WasFreed());
358
359 list.Free();
360 VerifyLinkedListContent(&list, nullptr);
361 VerifyOrQuit(d.WasFreed());
362 VerifyOrQuit(a.WasFreed());
363
364 // Test removing entry and taking ownership
365
366 a.ResetTestFlags();
367 b.ResetTestFlags();
368 c.ResetTestFlags();
369 d.ResetTestFlags();
370 e.ResetTestFlags();
371 list.Push(a);
372 list.Push(b);
373 list.Push(c);
374 list.Push(d);
375 list.Push(e);
376 VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
377
378 ptr = list.PopAfter(&a);
379 VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
380 VerifyOrQuit(ptr.IsNull());
381
382 ptr = list.PopAfter(&e);
383 VerifyLinkedListContent(&list, &e, &c, &b, &a, nullptr);
384 VerifyOrQuit(ptr.Get() == &d);
385 VerifyOrQuit(!d.WasFreed());
386
387 ptr = list.Pop();
388 VerifyLinkedListContent(&list, &c, &b, &a, nullptr);
389 VerifyOrQuit(ptr.Get() == &e);
390 VerifyOrQuit(!e.WasFreed());
391 VerifyOrQuit(d.WasFreed());
392
393 ptr = list.RemoveMatching<uint8_t>(1);
394 VerifyLinkedListContent(&list, &c, &b, nullptr);
395 VerifyOrQuit(ptr.Get() == &a);
396 VerifyOrQuit(!a.WasFreed());
397 VerifyOrQuit(e.WasFreed());
398
399 list.Clear();
400 VerifyOrQuit(c.WasFreed());
401 VerifyOrQuit(b.WasFreed());
402 VerifyOrQuit(!a.WasFreed());
403 a.Free();
404 VerifyOrQuit(a.WasFreed());
405
406 // Test `RemoveAllMatching()`
407
408 a.ResetTestFlags();
409 b.ResetTestFlags();
410 c.ResetTestFlags();
411 d.ResetTestFlags();
412 e.ResetTestFlags();
413 f.ResetTestFlags();
414 list.Push(a);
415 list.Push(b);
416 list.Push(c);
417 list.Push(d);
418 list.Push(e);
419 list.Push(f);
420 VerifyLinkedListContent(&list, &f, &e, &d, &c, &b, &a, nullptr);
421
422 list.RemoveAllMatching(kAlphaType, removedList);
423 VerifyLinkedListContent(&list, &f, &d, &c, nullptr);
424 VerifyLinkedListContent(&removedList, &a, &b, &e, nullptr);
425 VerifyOrQuit(!a.WasFreed());
426 VerifyOrQuit(!c.WasFreed());
427
428 removedList.Clear();
429 list.RemoveAllMatching(kAlphaType, removedList);
430 VerifyOrQuit(removedList.IsEmpty());
431 VerifyLinkedListContent(&list, &f, &d, &c, nullptr);
432
433 list.RemoveAllMatching(kBetaType, removedList);
434 VerifyOrQuit(list.IsEmpty());
435 VerifyLinkedListContent(&removedList, &c, &d, &f, nullptr);
436 VerifyOrQuit(!c.WasFreed());
437
438 // Test `RemoveAndFreeAllMatching()`
439
440 a.ResetTestFlags();
441 b.ResetTestFlags();
442 c.ResetTestFlags();
443 d.ResetTestFlags();
444 e.ResetTestFlags();
445 f.ResetTestFlags();
446 list.Push(a);
447 list.Push(b);
448 list.Push(c);
449 list.Push(d);
450 list.Push(e);
451 list.Push(f);
452 VerifyLinkedListContent(&list, &f, &e, &d, &c, &b, &a, nullptr);
453
454 didRemove = list.RemoveAndFreeAllMatching(kAlphaType);
455 VerifyLinkedListContent(&list, &f, &d, &c, nullptr);
456 VerifyOrQuit(didRemove);
457 VerifyOrQuit(a.WasFreed());
458 VerifyOrQuit(b.WasFreed());
459 VerifyOrQuit(e.WasFreed());
460 VerifyOrQuit(!c.WasFreed());
461
462 didRemove = list.RemoveAndFreeAllMatching(kAlphaType);
463 VerifyOrQuit(!didRemove);
464 VerifyLinkedListContent(&list, &f, &d, &c, nullptr);
465 VerifyOrQuit(!c.WasFreed());
466
467 didRemove = list.RemoveAndFreeAllMatching(kBetaType);
468 VerifyOrQuit(list.IsEmpty());
469 VerifyOrQuit(didRemove);
470 VerifyOrQuit(c.WasFreed());
471 VerifyOrQuit(d.WasFreed());
472 VerifyOrQuit(f.WasFreed());
473 }
474
475 } // namespace ot
476
main(void)477 int main(void)
478 {
479 ot::TestLinkedList();
480 ot::TestOwningList();
481 printf("All tests passed\n");
482 return 0;
483 }
484