astobj2: Remove OBJ_CONTINUE support.
[asterisk/asterisk.git] / tests / test_astobj2.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * David Vossel <dvossel@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief astobj2 test module
22  *
23  * \author David Vossel <dvossel@digium.com>
24  */
25
26 /*** MODULEINFO
27         <depend>TEST_FRAMEWORK</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/utils.h"
36 #include "asterisk/module.h"
37 #include "asterisk/test.h"
38 #include "asterisk/astobj2.h"
39
40 /* Uncomment the following line to dump the container contents during tests. */
41 //#define TEST_CONTAINER_DEBUG_DUMP             1
42
43 enum test_container_type {
44         TEST_CONTAINER_LIST,
45         TEST_CONTAINER_HASH,
46         TEST_CONTAINER_RBTREE,
47 };
48
49 /*!
50  * \internal
51  * \brief Convert the container type enum to string.
52  * \since 12.0.0
53  *
54  * \param type Container type value to convert to string.
55  *
56  * \return String value of container type.
57  */
58 static const char *test_container2str(enum test_container_type type)
59 {
60         const char *c_type;
61
62         c_type = "Unknown";
63         switch (type) {
64         case TEST_CONTAINER_LIST:
65                 c_type = "List";
66                 break;
67         case TEST_CONTAINER_HASH:
68                 c_type = "Hash";
69                 break;
70         case TEST_CONTAINER_RBTREE:
71                 c_type = "RBTree";
72                 break;
73         }
74         return c_type;
75 }
76
77 struct test_obj {
78         /*! What to decrement when object is destroyed. */
79         int *destructor_count;
80         /*! Container object key */
81         int i;
82         /*! Identifier for duplicate object key tests. */
83         int dup_number;
84 };
85
86 /*! Partial search key +/- matching range. */
87 int partial_key_match_range;
88
89 static void test_obj_destructor(void *v_obj)
90 {
91         struct test_obj *obj = (struct test_obj *) v_obj;
92
93         if (obj->destructor_count) {
94                 --*obj->destructor_count;
95         }
96 }
97
98 static int increment_cb(void *obj, void *arg, int flag)
99 {
100         int *i = (int *) arg;
101
102         *i = *i + 1;
103         return 0;
104 }
105
106 static int all_but_one_cb(void *obj, void *arg, int flag)
107 {
108         struct test_obj *cmp_obj = (struct test_obj *) obj;
109
110         return (cmp_obj->i) ? CMP_MATCH : 0;
111 }
112
113 static int multiple_cb(void *obj, void *arg, int flag)
114 {
115         int *i = (int *) arg;
116         struct test_obj *cmp_obj = (struct test_obj *) obj;
117
118         return (cmp_obj->i < *i) ? CMP_MATCH : 0;
119 }
120
121 static int test_cmp_cb(void *obj, void *arg, int flags)
122 {
123         struct test_obj *cmp_obj = (struct test_obj *) obj;
124
125         if (flags & OBJ_KEY) {
126                 int *i = (int *) arg;
127
128                 return (cmp_obj->i == *i) ? CMP_MATCH : 0;
129         } else if (flags & OBJ_PARTIAL_KEY) {
130                 int *i = (int *) arg;
131
132                 return (*i - partial_key_match_range <= cmp_obj->i
133                         && cmp_obj->i <= *i + partial_key_match_range) ? CMP_MATCH : 0;
134         } else {
135                 struct test_obj *arg_obj = (struct test_obj *) arg;
136
137                 return (cmp_obj->i == arg_obj->i) ? CMP_MATCH : 0;
138         }
139 }
140
141 static int test_hash_cb(const void *obj, const int flags)
142 {
143         if (flags & OBJ_KEY) {
144                 const int *i = obj;
145
146                 return *i;
147         } else if (flags & OBJ_PARTIAL_KEY) {
148                 /* This is absolutely wrong to be called with this flag value. */
149                 abort();
150                 /* Just in case abort() doesn't work or something else super silly */
151                 *((int *) 0) = 0;
152                 return 0;
153         } else {
154                 const struct test_obj *hash_obj = obj;
155
156                 return hash_obj->i;
157         }
158 }
159
160 static int test_sort_cb(const void *obj_left, const void *obj_right, int flags)
161 {
162         const struct test_obj *test_left = obj_left;
163
164         if (flags & OBJ_KEY) {
165                 const int *i = obj_right;
166
167                 return test_left->i - *i;
168         } else if (flags & OBJ_PARTIAL_KEY) {
169                 int *i = (int *) obj_right;
170
171                 if (*i - partial_key_match_range <= test_left->i
172                         && test_left->i <= *i + partial_key_match_range) {
173                         return 0;
174                 }
175
176                 return test_left->i - *i;
177         } else {
178                 const struct test_obj *test_right = obj_right;
179
180                 return test_left->i - test_right->i;
181         }
182 }
183
184 #if defined(TEST_CONTAINER_DEBUG_DUMP)
185 /*!
186  * \internal
187  * \brief Print test object key.
188  * \since 12.0.0
189  *
190  * \param v_obj A pointer to the object we want the key printed.
191  * \param where User data needed by prnt to determine where to put output.
192  * \param prnt Print output callback function to use.
193  *
194  * \return Nothing
195  */
196 static void test_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt)
197 {
198         struct test_obj *obj = v_obj;
199
200         if (!obj) {
201                 return;
202         }
203         prnt(where, "%6d-%d", obj->i, obj->dup_number);
204 }
205 #endif  /* defined(TEST_CONTAINER_DEBUG_DUMP) */
206
207 /*!
208  * \internal
209  * \brief Test container cloning.
210  * \since 12.0.0
211  *
212  * \param res Passed in enum ast_test_result_state.
213  * \param orig Container to clone.
214  * \param test Test output controller.
215  *
216  * \return enum ast_test_result_state
217  */
218 static int test_container_clone(int res, struct ao2_container *orig, struct ast_test *test)
219 {
220         struct ao2_container *clone;
221         struct test_obj *obj;
222         struct test_obj *obj2;
223         struct ao2_iterator iter;
224
225         clone = ao2_container_clone(orig, 0);
226         if (!clone) {
227                 ast_test_status_update(test, "ao2_container_clone failed.\n");
228                 return AST_TEST_FAIL;
229         }
230         if (ao2_container_check(clone, 0)) {
231                 ast_test_status_update(test, "container integrity check failed\n");
232                 res = AST_TEST_FAIL;
233         } else if (ao2_container_count(orig) != ao2_container_count(clone)) {
234                 ast_test_status_update(test, "Cloned container does not have the same number of objects.\n");
235                 res = AST_TEST_FAIL;
236         } else {
237                 iter = ao2_iterator_init(orig, 0);
238                 for (; (obj = ao2_t_iterator_next(&iter, "test orig")); ao2_t_ref(obj, -1, "test orig")) {
239                         /*
240                          * Unlink the matching object from the cloned container to make
241                          * the next search faster.  This is a big speed optimization!
242                          */
243                         obj2 = ao2_t_callback(clone, OBJ_POINTER | OBJ_UNLINK, ao2_match_by_addr, obj,
244                                 "test clone");
245                         if (obj2) {
246                                 ao2_t_ref(obj2, -1, "test clone");
247                                 continue;
248                         }
249                         ast_test_status_update(test,
250                                 "Orig container has an object %p not in the clone container.\n", obj);
251                         res = AST_TEST_FAIL;
252                 }
253                 ao2_iterator_destroy(&iter);
254                 if (ao2_container_count(clone)) {
255                         ast_test_status_update(test, "Cloned container still has objects.\n");
256                         res = AST_TEST_FAIL;
257                 }
258                 if (ao2_container_check(clone, 0)) {
259                         ast_test_status_update(test, "container integrity check failed\n");
260                         res = AST_TEST_FAIL;
261                 }
262         }
263         ao2_t_ref(clone, -1, "bye clone");
264
265         return res;
266 }
267
268 /*!
269  * \internal
270  * \brief Test ao2_find with no flags.
271  * \since 12.0.0
272  *
273  * \param res Passed in enum ast_test_result_state.
274  * \param look_in Container to search.
275  * \param limit Container contains objects 0 - (limit - 1).
276  * \param test Test output controller.
277  *
278  * \return enum ast_test_result_state
279  */
280 static int test_ao2_find_w_no_flags(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
281 {
282         int i;
283         int num;
284         struct test_obj tmp_obj = { 0, };
285         struct test_obj *obj;
286
287         for (num = 100; num--;) {
288                 i = ast_random() % limit; /* find a random object */
289
290                 tmp_obj.i = i;
291                 obj = ao2_find(look_in, &tmp_obj, 0);
292                 if (!obj) {
293                         ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with no flags failed.\n", i);
294                         res = AST_TEST_FAIL;
295                 } else {
296                         if (obj->i != i) {
297                                 ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
298                                 res = AST_TEST_FAIL;
299                         }
300                         ao2_t_ref(obj, -1, "test");
301                 }
302         }
303
304         return res;
305 }
306
307 /*!
308  * \internal
309  * \brief Test ao2_find with OBJ_POINTER.
310  * \since 12.0.0
311  *
312  * \param res Passed in enum ast_test_result_state.
313  * \param look_in Container to search.
314  * \param limit Container contains objects 0 - (limit - 1).
315  * \param test Test output controller.
316  *
317  * \return enum ast_test_result_state
318  */
319 static int test_ao2_find_w_OBJ_POINTER(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
320 {
321         int i;
322         int num;
323         struct test_obj tmp_obj = { 0, };
324         struct test_obj *obj;
325
326         for (num = 75; num--;) {
327                 i = ast_random() % limit; /* find a random object */
328
329                 tmp_obj.i = i;
330                 obj = ao2_find(look_in, &tmp_obj, OBJ_POINTER);
331                 if (!obj) {
332                         ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_POINTER flag failed.\n", i);
333                         res = AST_TEST_FAIL;
334                 } else {
335                         if (obj->i != i) {
336                                 ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
337                                 res = AST_TEST_FAIL;
338                         }
339                         ao2_t_ref(obj, -1, "test");
340                 }
341         }
342
343         return res;
344 }
345
346 /*!
347  * \internal
348  * \brief Test ao2_find with OBJ_KEY.
349  * \since 12.0.0
350  *
351  * \param res Passed in enum ast_test_result_state.
352  * \param look_in Container to search.
353  * \param limit Container contains objects 0 - (limit - 1).
354  * \param test Test output controller.
355  *
356  * \return enum ast_test_result_state
357  */
358 static int test_ao2_find_w_OBJ_KEY(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
359 {
360         int i;
361         int num;
362         struct test_obj *obj;
363
364         for (num = 75; num--;) {
365                 i = ast_random() % limit; /* find a random object */
366
367                 obj = ao2_find(look_in, &i, OBJ_KEY);
368                 if (!obj) {
369                         ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_KEY flag failed.\n", i);
370                         res = AST_TEST_FAIL;
371                 } else {
372                         if (obj->i != i) {
373                                 ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
374                                 res = AST_TEST_FAIL;
375                         }
376                         ao2_t_ref(obj, -1, "test");
377                 }
378         }
379
380         return res;
381 }
382
383 /*!
384  * \internal
385  * \brief Test ao2_find with OBJ_PARTIAL_KEY.
386  * \since 12.0.0
387  *
388  * \param res Passed in enum ast_test_result_state.
389  * \param look_in Container to search.
390  * \param limit Container contains objects 0 - (limit - 1).
391  * \param test Test output controller.
392  *
393  * \return enum ast_test_result_state
394  */
395 static int test_ao2_find_w_OBJ_PARTIAL_KEY(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
396 {
397         int i;
398         int num;
399         struct test_obj *obj;
400
401         /* Set partial match to find exactly. */
402         partial_key_match_range = 0;
403
404         for (num = 100; num--;) {
405                 i = ast_random() % limit; /* find a random object */
406
407                 obj = ao2_find(look_in, &i, OBJ_PARTIAL_KEY);
408                 if (!obj) {
409                         ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_PARTIAL_KEY flag failed.\n", i);
410                         res = AST_TEST_FAIL;
411                 } else {
412                         if (obj->i != i) {
413                                 ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
414                                 res = AST_TEST_FAIL;
415                         }
416                         ao2_t_ref(obj, -1, "test");
417                 }
418         }
419
420         return res;
421 }
422
423 static int astobj2_test_1_helper(int tst_num, enum test_container_type type, int use_sort, unsigned int lim, struct ast_test *test)
424 {
425         const char *c_type;
426         struct ao2_container *c1;
427         struct ao2_container *c2;
428         struct ao2_iterator it;
429         struct ao2_iterator *mult_it;
430         struct test_obj *obj;
431         int n_buckets = 0;
432         int increment = 0;
433         int destructor_count = 0;
434         int num;
435         int res = AST_TEST_PASS;
436
437         c_type = test_container2str(type);
438         ast_test_status_update(test, "Test %d, %s containers (%s).\n",
439                 tst_num, c_type, use_sort ? "sorted" : "non-sorted");
440
441         c1 = NULL;
442         switch (type) {
443         case TEST_CONTAINER_LIST:
444                 /* Lists just have one bucket. */
445                 n_buckets = 1;
446                 c1 = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
447                         use_sort ? test_sort_cb : NULL, test_cmp_cb, "test");
448                 break;
449         case TEST_CONTAINER_HASH:
450                 n_buckets = (ast_random() % ((lim / 4) + 1)) + 1;
451                 c1 = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, n_buckets,
452                         test_hash_cb, use_sort ? test_sort_cb : NULL, test_cmp_cb, "test");
453                 break;
454         case TEST_CONTAINER_RBTREE:
455                 /* RBTrees just have one bucket. */
456                 n_buckets = 1;
457                 c1 = ao2_t_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
458                         test_sort_cb, test_cmp_cb, "test");
459                 break;
460         }
461         c2 = ao2_t_container_alloc(1, NULL, NULL, "test");
462
463         if (!c1 || !c2) {
464                 ast_test_status_update(test, "ao2_container_alloc failed.\n");
465                 res = AST_TEST_FAIL;
466                 goto cleanup;
467         }
468
469         /* Create objects and link into container */
470         for (num = 0; num < lim; ++num) {
471                 if (!(obj = ao2_t_alloc(sizeof(struct test_obj), test_obj_destructor, "making zombies"))) {
472                         ast_test_status_update(test, "ao2_alloc failed.\n");
473                         res = AST_TEST_FAIL;
474                         goto cleanup;
475                 }
476                 ++destructor_count;
477                 obj->destructor_count = &destructor_count;
478                 obj->i = num;
479                 ao2_link(c1, obj);
480                 ao2_t_ref(obj, -1, "test");
481                 if (ao2_container_check(c1, 0)) {
482                         ast_test_status_update(test, "container integrity check failed linking obj num:%d\n", num);
483                         res = AST_TEST_FAIL;
484                         goto cleanup;
485                 }
486                 if (ao2_container_count(c1) != num + 1) {
487                         ast_test_status_update(test, "container did not link correctly\n");
488                         res = AST_TEST_FAIL;
489                 }
490         }
491
492         ast_test_status_update(test, "%s container created: buckets: %d, items: %d\n",
493                 c_type, n_buckets, lim);
494
495         /* Testing ao2_container_clone */
496         res = test_container_clone(res, c1, test);
497
498         /* Testing ao2_find with no flags */
499         res = test_ao2_find_w_no_flags(res, c1, lim, test);
500
501         /* Testing ao2_find with OBJ_POINTER */
502         res = test_ao2_find_w_OBJ_POINTER(res, c1, lim, test);
503
504         /* Testing ao2_find with OBJ_KEY */
505         res = test_ao2_find_w_OBJ_KEY(res, c1, lim, test);
506
507         /* Testing ao2_find with OBJ_PARTIAL_KEY */
508         res = test_ao2_find_w_OBJ_PARTIAL_KEY(res, c1, lim, test);
509
510         /* Test Callback with no flags. */
511         increment = 0;
512         ao2_t_callback(c1, 0, increment_cb, &increment, "test callback");
513         if (increment != lim) {
514                 ast_test_status_update(test, "callback with no flags failed. Increment is %d\n", increment);
515                 res = AST_TEST_FAIL;
516         }
517
518         /* Test Callback with OBJ_NODATA. This should do nothing different than with no flags here. */
519         increment = 0;
520         ao2_t_callback(c1, OBJ_NODATA, increment_cb, &increment, "test callback");
521         if (increment != lim) {
522                 ast_test_status_update(test, "callback with OBJ_NODATA failed. Increment is %d\n", increment);
523                 res = AST_TEST_FAIL;
524         }
525
526         /* Test OBJ_MULTIPLE with OBJ_UNLINK, add items back afterwards */
527         num = lim < 25 ? lim : 25;
528         if (!(mult_it = ao2_t_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK, multiple_cb, &num, "test multiple"))) {
529                 ast_test_status_update(test, "OBJ_MULTIPLE with OBJ_UNLINK test failed.\n");
530                 res = AST_TEST_FAIL;
531         } else {
532                 /* make sure num items unlinked is as expected */
533                 if ((lim - ao2_container_count(c1)) != num) {
534                         ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK test failed, did not unlink correct number of objects.\n");
535                         res = AST_TEST_FAIL;
536                 }
537                 if (ao2_container_check(c1, 0)) {
538                         ast_test_status_update(test, "container integrity check failed\n");
539                         res = AST_TEST_FAIL;
540                         goto cleanup;
541                 }
542
543                 /* link what was unlinked back into c1 */
544                 while ((obj = ao2_t_iterator_next(mult_it, "test"))) {
545                         ao2_t_link(c1, obj, "test");
546                         ao2_t_ref(obj, -1, "test"); /* remove ref from iterator */
547                 }
548                 ao2_iterator_destroy(mult_it);
549                 if (ao2_container_check(c1, 0)) {
550                         ast_test_status_update(test, "container integrity check failed\n");
551                         res = AST_TEST_FAIL;
552                         goto cleanup;
553                 }
554         }
555
556         /* Test OBJ_MULTIPLE without unlink and iterate the returned container */
557         num = 5;
558         if (!(mult_it = ao2_t_callback(c1, OBJ_MULTIPLE, multiple_cb, &num, "test multiple"))) {
559                 ast_test_status_update(test, "OBJ_MULTIPLE without OBJ_UNLINK test failed.\n");
560                 res = AST_TEST_FAIL;
561         } else {
562                 while ((obj = ao2_t_iterator_next(mult_it, "test"))) {
563                         ao2_t_ref(obj, -1, "test"); /* remove ref from iterator */
564                 }
565                 ao2_iterator_destroy(mult_it);
566         }
567
568         /* Test OBJ_MULTIPLE without unlink and no iterating */
569         num = 5;
570         if (!(mult_it = ao2_t_callback(c1, OBJ_MULTIPLE, multiple_cb, &num, "test multiple"))) {
571                 ast_test_status_update(test, "OBJ_MULTIPLE with no OBJ_UNLINK and no iterating failed.\n");
572                 res = AST_TEST_FAIL;
573         } else {
574                 ao2_iterator_destroy(mult_it);
575         }
576
577         /* Is the container count what we expect after all the finds and unlinks? */
578         if (ao2_container_count(c1) != lim) {
579                 ast_test_status_update(test, "container count does not match what is expected after ao2_find tests.\n");
580                 res = AST_TEST_FAIL;
581         }
582
583         /* Testing iterator.  Unlink a single object and break. do not add item back */
584         it = ao2_iterator_init(c1, 0);
585         num = ast_random() % lim; /* remove a random object */
586         if (!num) {
587                 /*
588                  * Well we cannot remove object zero because of test with
589                  * all_but_one_cb later.
590                  */
591                 num = 1;
592         }
593         while ((obj = ao2_t_iterator_next(&it, "test"))) {
594                 if (obj->i == num) {
595                         ao2_t_unlink(c1, obj, "test");
596                         ao2_t_ref(obj, -1, "test");
597                         break;
598                 }
599                 ao2_t_ref(obj, -1, "test");
600         }
601         ao2_iterator_destroy(&it);
602
603         /* Is the container count what we expect after removing a single item? */
604         if (ao2_container_count(c1) != (lim - 1)) {
605                 ast_test_status_update(test, "unlink during iterator failed. Number %d was not removed.\n", num);
606                 res = AST_TEST_FAIL;
607         }
608         if (ao2_container_check(c1, 0)) {
609                 ast_test_status_update(test, "container integrity check failed\n");
610                 res = AST_TEST_FAIL;
611                 goto cleanup;
612         }
613
614         /* Test unlink all with OBJ_MULTIPLE, leave a single object for the container to destroy */
615         ao2_t_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, all_but_one_cb, NULL, "test multiple");
616         /* check to make sure all test_obj destructors were called except for 1 */
617         if (destructor_count != 1) {
618                 ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA failed. destructor count %d\n", destructor_count);
619                 res = AST_TEST_FAIL;
620         }
621         if (ao2_container_check(c1, 0)) {
622                 ast_test_status_update(test, "container integrity check failed\n");
623                 res = AST_TEST_FAIL;
624         }
625 #if defined(TEST_CONTAINER_DEBUG_DUMP)
626         ao2_container_dump(c1, 0, "test_1 c1", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
627         ao2_container_stats(c1, 0, "test_1 c1", (void *) test, (ao2_prnt_fn *) ast_test_debug);
628 #endif  /* defined(TEST_CONTAINER_DEBUG_DUMP) */
629
630 cleanup:
631         /* destroy containers */
632         if (c1) {
633                 ao2_t_ref(c1, -1, "bye c1");
634         }
635         if (c2) {
636                 ao2_t_ref(c2, -1, "bye c2");
637         }
638
639         if (destructor_count > 0) {
640                 ast_test_status_update(test, "all destructors were not called, destructor count is %d\n", destructor_count);
641                 res = AST_TEST_FAIL;
642         } else if (destructor_count < 0) {
643                 ast_test_status_update(test, "Destructor was called too many times, destructor count is %d\n", destructor_count);
644                 res = AST_TEST_FAIL;
645         }
646
647         return res;
648 }
649
650 AST_TEST_DEFINE(astobj2_test_1)
651 {
652         int res = AST_TEST_PASS;
653
654         switch (cmd) {
655         case TEST_INIT:
656                 info->name = "astobj2_test1";
657                 info->category = "/main/astobj2/";
658                 info->summary = "Test ao2 objects, containers, callbacks, and iterators";
659                 info->description =
660                         "Builds ao2_containers with various item numbers, bucket sizes, cmp and hash "
661                         "functions. Runs a series of tests to manipulate the container using callbacks "
662                         "and iterators.  Verifies expected behavior.";
663                 return AST_TEST_NOT_RUN;
664         case TEST_EXECUTE:
665                 break;
666         }
667
668         /* Test number, container_type, use_sort, number of objects. */
669         if ((res = astobj2_test_1_helper(1, TEST_CONTAINER_LIST, 0, 50, test)) == AST_TEST_FAIL) {
670                 return res;
671         }
672
673         if ((res = astobj2_test_1_helper(2, TEST_CONTAINER_LIST, 1, 50, test)) == AST_TEST_FAIL) {
674                 return res;
675         }
676
677         if ((res = astobj2_test_1_helper(3, TEST_CONTAINER_HASH, 0, 1000, test)) == AST_TEST_FAIL) {
678                 return res;
679         }
680
681         if ((res = astobj2_test_1_helper(4, TEST_CONTAINER_HASH, 1, 1000, test)) == AST_TEST_FAIL) {
682                 return res;
683         }
684
685         if ((res = astobj2_test_1_helper(4, TEST_CONTAINER_RBTREE, 1, 1000, test)) == AST_TEST_FAIL) {
686                 return res;
687         }
688
689         return res;
690 }
691
692 AST_TEST_DEFINE(astobj2_test_2)
693 {
694         int res = AST_TEST_PASS;
695         struct ao2_container *c;
696         struct ao2_iterator i;
697         struct test_obj *obj;
698         int num;
699         static const int NUM_OBJS = 5;
700         int destructor_count = NUM_OBJS;
701         struct test_obj tmp_obj = { 0, };
702
703         switch (cmd) {
704         case TEST_INIT:
705                 info->name = "astobj2_test2";
706                 info->category = "/main/astobj2/";
707                 info->summary = "Test a certain scenario using ao2 iterators";
708                 info->description =
709                         "This test is aimed at testing for a specific regression that occurred. "
710                         "Add some objects into a container.  Mix finds and iteration and make "
711                         "sure that the iterator still sees all objects.";
712                 return AST_TEST_NOT_RUN;
713         case TEST_EXECUTE:
714                 break;
715         }
716
717         c = ao2_container_alloc(1, NULL, test_cmp_cb);
718         if (!c) {
719                 ast_test_status_update(test, "ao2_container_alloc failed.\n");
720                 res = AST_TEST_FAIL;
721                 goto cleanup;
722         }
723
724         for (num = 1; num <= NUM_OBJS; num++) {
725                 if (!(obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor))) {
726                         ast_test_status_update(test, "ao2_alloc failed.\n");
727                         res = AST_TEST_FAIL;
728                         goto cleanup;
729                 }
730                 obj->destructor_count = &destructor_count;
731                 obj->i = num;
732                 ao2_link(c, obj);
733                 ao2_ref(obj, -1);
734                 if (ao2_container_count(c) != num) {
735                         ast_test_status_update(test, "container did not link correctly\n");
736                         res = AST_TEST_FAIL;
737                 }
738         }
739         if (ao2_container_check(c, 0)) {
740                 ast_test_status_update(test, "container integrity check failed\n");
741                 res = AST_TEST_FAIL;
742                 goto cleanup;
743         }
744
745         /*
746          * Iteration take 1.  Just make sure we see all NUM_OBJS objects.
747          */
748         num = 0;
749         i = ao2_iterator_init(c, 0);
750         while ((obj = ao2_iterator_next(&i))) {
751                 num++;
752                 ao2_ref(obj, -1);
753         }
754         ao2_iterator_destroy(&i);
755
756         if (num != NUM_OBJS) {
757                 ast_test_status_update(test, "iterate take 1, expected '%d', only saw '%d' objects\n",
758                                 NUM_OBJS, num);
759                 res = AST_TEST_FAIL;
760         }
761
762         /*
763          * Iteration take 2.  Do a find for the last object, then iterate and make
764          * sure we find all NUM_OBJS objects.
765          */
766         tmp_obj.i = NUM_OBJS;
767         obj = ao2_find(c, &tmp_obj, OBJ_POINTER);
768         if (!obj) {
769                 ast_test_status_update(test, "ao2_find() failed.\n");
770                 res = AST_TEST_FAIL;
771         } else {
772                 ao2_ref(obj, -1);
773         }
774
775         num = 0;
776         i = ao2_iterator_init(c, 0);
777         while ((obj = ao2_iterator_next(&i))) {
778                 num++;
779                 ao2_ref(obj, -1);
780         }
781         ao2_iterator_destroy(&i);
782
783         if (num != NUM_OBJS) {
784                 ast_test_status_update(test, "iterate take 2, expected '%d', only saw '%d' objects\n",
785                                 NUM_OBJS, num);
786                 res = AST_TEST_FAIL;
787         }
788
789         /*
790          * Iteration take 3.  Do a find for an object while in the middle
791          * of iterating;
792          */
793         num = 0;
794         i = ao2_iterator_init(c, 0);
795         while ((obj = ao2_iterator_next(&i))) {
796                 if (num == 1) {
797                         struct test_obj *obj2;
798                         tmp_obj.i = NUM_OBJS - 1;
799                         obj2 = ao2_find(c, &tmp_obj, OBJ_POINTER);
800                         if (!obj2) {
801                                 ast_test_status_update(test, "ao2_find() failed.\n");
802                                 res = AST_TEST_FAIL;
803                         } else {
804                                 ao2_ref(obj2, -1);
805                         }
806                 }
807                 num++;
808                 ao2_ref(obj, -1);
809         }
810         ao2_iterator_destroy(&i);
811
812         if (num != NUM_OBJS) {
813                 ast_test_status_update(test, "iterate take 3, expected '%d', only saw '%d' objects\n",
814                                 NUM_OBJS, num);
815                 res = AST_TEST_FAIL;
816         }
817
818
819 cleanup:
820         if (c) {
821                 ao2_ref(c, -1);
822         }
823
824         return res;
825 }
826
827 static AO2_GLOBAL_OBJ_STATIC(astobj2_holder);
828
829 AST_TEST_DEFINE(astobj2_test_3)
830 {
831         int res = AST_TEST_PASS;
832         int destructor_count = 0;
833         int num_objects = 0;
834         struct test_obj *obj = NULL;
835         struct test_obj *obj2 = NULL;
836         struct test_obj *obj3 = NULL;
837
838         switch (cmd) {
839         case TEST_INIT:
840                 info->name = "astobj2_test3";
841                 info->category = "/main/astobj2/";
842                 info->summary = "Test global ao2 holder";
843                 info->description =
844                         "This test is to see if the global ao2 holder works as intended.";
845                 return AST_TEST_NOT_RUN;
846         case TEST_EXECUTE:
847                 break;
848         }
849
850         /* Put an object in the holder */
851         obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
852         if (!obj) {
853                 ast_test_status_update(test, "ao2_alloc failed.\n");
854                 res = AST_TEST_FAIL;
855                 goto cleanup;
856         }
857         obj->destructor_count = &destructor_count;
858         obj->i = ++num_objects;
859         obj2 = ao2_t_global_obj_replace(astobj2_holder, obj, "Save object in the holder");
860         if (obj2) {
861                 ast_test_status_update(test, "Returned object not expected.\n");
862                 res = AST_TEST_FAIL;
863                 goto cleanup;
864         }
865         /* Save object for next check. */
866         obj3 = obj;
867
868         /* Replace an object in the holder */
869         obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
870         if (!obj) {
871                 ast_test_status_update(test, "ao2_alloc failed.\n");
872                 res = AST_TEST_FAIL;
873                 goto cleanup;
874         }
875         obj->destructor_count = &destructor_count;
876         obj->i = ++num_objects;
877         obj2 = ao2_t_global_obj_replace(astobj2_holder, obj, "Replace object in the holder");
878         if (!obj2) {
879                 ast_test_status_update(test, "Expected an object.\n");
880                 res = AST_TEST_FAIL;
881                 goto cleanup;
882         }
883         if (obj2 != obj3) {
884                 ast_test_status_update(test, "Replaced object not expected object.\n");
885                 res = AST_TEST_FAIL;
886                 goto cleanup;
887         }
888         ao2_ref(obj3, -1);
889         obj3 = NULL;
890         ao2_ref(obj2, -1);
891         obj2 = NULL;
892         ao2_ref(obj, -1);
893
894         /* Replace with unref of an object in the holder */
895         obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
896         if (!obj) {
897                 ast_test_status_update(test, "ao2_alloc failed.\n");
898                 res = AST_TEST_FAIL;
899                 goto cleanup;
900         }
901         obj->destructor_count = &destructor_count;
902         obj->i = ++num_objects;
903         if (!ao2_t_global_obj_replace_unref(astobj2_holder, obj, "Replace w/ unref object in the holder")) {
904                 ast_test_status_update(test, "Expected an object to be replaced.\n");
905                 res = AST_TEST_FAIL;
906                 goto cleanup;
907         }
908         /* Save object for next check. */
909         obj3 = obj;
910
911         /* Get reference to held object. */
912         obj = ao2_t_global_obj_ref(astobj2_holder, "Get a held object reference");
913         if (!obj) {
914                 ast_test_status_update(test, "Expected an object.\n");
915                 res = AST_TEST_FAIL;
916                 goto cleanup;
917         }
918         if (obj != obj3) {
919                 ast_test_status_update(test, "Referenced object not expected object.\n");
920                 res = AST_TEST_FAIL;
921                 goto cleanup;
922         }
923         ao2_ref(obj3, -1);
924         obj3 = NULL;
925         ao2_ref(obj, -1);
926         obj = NULL;
927
928         /* Release the object in the global holder. */
929         ao2_t_global_obj_release(astobj2_holder, "Check release all objects");
930         destructor_count += num_objects;
931         if (0 < destructor_count) {
932                 ast_test_status_update(test,
933                         "all destructors were not called, destructor count is %d\n",
934                         destructor_count);
935                 res = AST_TEST_FAIL;
936         } else if (destructor_count < 0) {
937                 ast_test_status_update(test,
938                         "Destructor was called too many times, destructor count is %d\n",
939                         destructor_count);
940                 res = AST_TEST_FAIL;
941         }
942
943 cleanup:
944         if (obj) {
945                 ao2_t_ref(obj, -1, "Test cleanup external object 1");
946         }
947         if (obj2) {
948                 ao2_t_ref(obj2, -1, "Test cleanup external object 2");
949         }
950         if (obj3) {
951                 ao2_t_ref(obj3, -1, "Test cleanup external object 3");
952         }
953         ao2_t_global_obj_release(astobj2_holder, "Test cleanup holder");
954
955         return res;
956 }
957
958 /*!
959  * \internal
960  * \brief Make a nonsorted container for astobj2 testing.
961  * \since 12.0.0
962  *
963  * \param type Container type to create.
964  * \param options Container options
965  *
966  * \retval container on success.
967  * \retval NULL on error.
968  */
969 static struct ao2_container *test_make_nonsorted(enum test_container_type type, int options)
970 {
971         struct ao2_container *container;
972
973         container = NULL;
974         switch (type) {
975         case TEST_CONTAINER_LIST:
976                 container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, options,
977                         NULL, test_cmp_cb);
978                 break;
979         case TEST_CONTAINER_HASH:
980                 container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, options, 5,
981                         test_hash_cb, NULL, test_cmp_cb);
982                 break;
983         case TEST_CONTAINER_RBTREE:
984                 /* Container type must be sorted. */
985                 break;
986         }
987
988         return container;
989 }
990
991 /*!
992  * \internal
993  * \brief Make a sorted container for astobj2 testing.
994  * \since 12.0.0
995  *
996  * \param type Container type to create.
997  * \param options Container options
998  *
999  * \retval container on success.
1000  * \retval NULL on error.
1001  */
1002 static struct ao2_container *test_make_sorted(enum test_container_type type, int options)
1003 {
1004         struct ao2_container *container;
1005
1006         container = NULL;
1007         switch (type) {
1008         case TEST_CONTAINER_LIST:
1009                 container = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, options,
1010                         test_sort_cb, test_cmp_cb, "test");
1011                 break;
1012         case TEST_CONTAINER_HASH:
1013                 container = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, options, 5,
1014                         test_hash_cb, test_sort_cb, test_cmp_cb, "test");
1015                 break;
1016         case TEST_CONTAINER_RBTREE:
1017                 container = ao2_t_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, options,
1018                         test_sort_cb, test_cmp_cb, "test");
1019                 break;
1020         }
1021
1022         return container;
1023 }
1024
1025 /*!
1026  * \internal
1027  * \brief Insert the given test vector into the given container.
1028  * \since 12.0.0
1029  *
1030  * \note The given test vector must not have any duplicates.
1031  *
1032  * \param container Container to insert the test vector.
1033  * \param destroy_counter What to increment when the object is destroyed.
1034  * \param vector Test vector to insert.
1035  * \param count Number of objects in the vector.
1036  * \param prefix Test output prefix string.
1037  * \param test Test output controller.
1038  *
1039  * \retval 0 on success.
1040  * \retval -1 on error.
1041  */
1042 static int insert_test_vector(struct ao2_container *container, int *destroy_counter, const int *vector, int count, const char *prefix, struct ast_test *test)
1043 {
1044         int idx;
1045         struct test_obj *obj;
1046
1047         for (idx = 0; idx < count; ++idx) {
1048                 obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
1049                 if (!obj) {
1050                         ast_test_status_update(test, "%s: ao2_alloc failed.\n", prefix);
1051                         return -1;
1052                 }
1053                 if (destroy_counter) {
1054                         /* This object ultimately needs to be destroyed. */
1055                         ++*destroy_counter;
1056                 }
1057                 obj->destructor_count = destroy_counter;
1058                 obj->i = vector[idx];
1059                 ao2_link(container, obj);
1060                 ao2_t_ref(obj, -1, "test");
1061                 if (ao2_container_check(container, 0)) {
1062                         ast_test_status_update(test, "%s: Container integrity check failed linking vector[%d]:%d\n",
1063                                 prefix, idx, vector[idx]);
1064                         return -1;
1065                 }
1066
1067                 if (ao2_container_count(container) != idx + 1) {
1068                         ast_test_status_update(test,
1069                                 "%s: Unexpected container count.  Expected:%d Got:%d\n",
1070                                 prefix, idx + 1, ao2_container_count(container));
1071                         return -1;
1072                 }
1073         }
1074
1075         return 0;
1076 }
1077
1078 /*!
1079  * \internal
1080  * \brief Insert duplicates of number into the given container.
1081  * \since 12.0.0
1082  *
1083  * \note The given container must not already have the number in it.
1084  *
1085  * \param container Container to insert the duplicates.
1086  * \param destroy_counter What to increment when the object is destroyed.
1087  * \param number Number of object to duplicate.
1088  * \param prefix Test output prefix string.
1089  * \param test Test output controller.
1090  *
1091  * \retval 0 on success.
1092  * \retval -1 on error.
1093  */
1094 static int insert_test_duplicates(struct ao2_container *container, int *destroy_counter, int number, const char *prefix, struct ast_test *test)
1095 {
1096         int count;
1097         struct test_obj *obj;
1098         struct test_obj *obj_dup;
1099
1100         /* Check if object already exists in the container. */
1101         obj = ao2_find(container, &number, OBJ_KEY);
1102         if (obj) {
1103                 ast_test_status_update(test, "%s: Object %d already exists.\n", prefix, number);
1104                 ao2_t_ref(obj, -1, "test");
1105                 return -1;
1106         }
1107
1108         /* Add three duplicate keyed objects. */
1109         obj_dup = NULL;
1110         for (count = 0; count < 4; ++count) {
1111                 obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
1112                 if (!obj) {
1113                         ast_test_status_update(test, "%s: ao2_alloc failed.\n", prefix);
1114                         if (obj_dup) {
1115                                 ao2_t_ref(obj_dup, -1, "test");
1116                         }
1117                         return -1;
1118                 }
1119                 if (destroy_counter) {
1120                         /* This object ultimately needs to be destroyed. */
1121                         ++*destroy_counter;
1122                 }
1123                 obj->destructor_count = destroy_counter;
1124                 obj->i = number;
1125                 obj->dup_number = count;
1126                 ao2_link(container, obj);
1127
1128                 if (count == 2) {
1129                         /* Duplicate this object. */
1130                         obj_dup = obj;
1131                 } else {
1132                         ao2_t_ref(obj, -1, "test");
1133                 }
1134
1135                 if (ao2_container_check(container, 0)) {
1136                         ast_test_status_update(test, "%s: Container integrity check failed linking num:%d dup:%d\n",
1137                                 prefix, number, count);
1138                         if (obj_dup) {
1139                                 ao2_t_ref(obj_dup, -1, "test");
1140                         }
1141                         return -1;
1142                 }
1143         }
1144
1145         /* Add the duplicate object. */
1146         ao2_link(container, obj_dup);
1147         ao2_t_ref(obj_dup, -1, "test");
1148
1149         if (ao2_container_check(container, 0)) {
1150                 ast_test_status_update(test, "%s: Container integrity check failed linking obj_dup\n",
1151                         prefix);
1152                 return -1;
1153         }
1154
1155         return 0;
1156 }
1157
1158 /*!
1159  * \internal
1160  * \brief Iterate over the container and compare the objects with the given vector.
1161  * \since 12.0.0
1162  *
1163  * \param res Passed in enum ast_test_result_state.
1164  * \param container Container to iterate.
1165  * \param flags Flags controlling the iteration.
1166  * \param vector Expected vector to find.
1167  * \param count Number of objects in the vector.
1168  * \param prefix Test output prefix string.
1169  * \param test Test output controller.
1170  *
1171  * \return enum ast_test_result_state
1172  */
1173 static int test_ao2_iteration(int res, struct ao2_container *container,
1174         enum ao2_iterator_flags flags,
1175         const int *vector, int count, const char *prefix, struct ast_test *test)
1176 {
1177         struct ao2_iterator iter;
1178         struct test_obj *obj;
1179         int idx;
1180
1181         if (ao2_container_count(container) != count) {
1182                 ast_test_status_update(test, "%s: Container count doesn't match vector count.\n",
1183                         prefix);
1184                 res = AST_TEST_FAIL;
1185         }
1186
1187         iter = ao2_iterator_init(container, flags);
1188
1189         /* Check iterated objects against the given vector. */
1190         for (idx = 0; idx < count; ++idx) {
1191                 obj = ao2_iterator_next(&iter);
1192                 if (!obj) {
1193                         ast_test_status_update(test, "%s: Too few objects found.\n", prefix);
1194                         res = AST_TEST_FAIL;
1195                         break;
1196                 }
1197                 if (vector[idx] != obj->i) {
1198                         ast_test_status_update(test, "%s: Object %d != vector[%d] %d.\n",
1199                                 prefix, obj->i, idx, vector[idx]);
1200                         res = AST_TEST_FAIL;
1201                 }
1202                 ao2_ref(obj, -1); /* remove ref from iterator */
1203         }
1204         obj = ao2_iterator_next(&iter);
1205         if (obj) {
1206                 ast_test_status_update(test, "%s: Too many objects found.  Object %d\n",
1207                         prefix, obj->i);
1208                 ao2_ref(obj, -1); /* remove ref from iterator */
1209                 res = AST_TEST_FAIL;
1210         }
1211
1212         ao2_iterator_destroy(&iter);
1213
1214         return res;
1215 }
1216
1217 /*!
1218  * \internal
1219  * \brief Run an ao2_callback() and compare the returned vector with the given vector.
1220  * \since 12.0.0
1221  *
1222  * \param res Passed in enum ast_test_result_state.
1223  * \param container Container to traverse.
1224  * \param flags Callback flags controlling the traversal.
1225  * \param cmp_fn Compare function to select objects.
1226  * \param arg Optional argument.
1227  * \param vector Expected vector to find.
1228  * \param count Number of objects in the vector.
1229  * \param prefix Test output prefix string.
1230  * \param test Test output controller.
1231  *
1232  * \return enum ast_test_result_state
1233  */
1234 static int test_ao2_callback_traversal(int res, struct ao2_container *container,
1235         enum search_flags flags, ao2_callback_fn *cmp_fn, void *arg,
1236         const int *vector, int count, const char *prefix, struct ast_test *test)
1237 {
1238         struct ao2_iterator *mult_iter;
1239         struct test_obj *obj;
1240         int idx;
1241
1242         mult_iter = ao2_callback(container, flags | OBJ_MULTIPLE, cmp_fn, arg);
1243         if (!mult_iter) {
1244                 ast_test_status_update(test, "%s: Did not return iterator.\n", prefix);
1245                 return AST_TEST_FAIL;
1246         }
1247
1248         /* Check matching objects against the given vector. */
1249         for (idx = 0; idx < count; ++idx) {
1250                 obj = ao2_iterator_next(mult_iter);
1251                 if (!obj) {
1252                         ast_test_status_update(test, "%s: Too few objects found.\n", prefix);
1253                         res = AST_TEST_FAIL;
1254                         break;
1255                 }
1256                 if (vector[idx] != obj->i) {
1257                         ast_test_status_update(test, "%s: Object %d != vector[%d] %d.\n",
1258                                 prefix, obj->i, idx, vector[idx]);
1259                         res = AST_TEST_FAIL;
1260                 }
1261                 ao2_ref(obj, -1); /* remove ref from iterator */
1262         }
1263         obj = ao2_iterator_next(mult_iter);
1264         if (obj) {
1265                 ast_test_status_update(test, "%s: Too many objects found.  Object %d\n",
1266                         prefix, obj->i);
1267                 ao2_ref(obj, -1); /* remove ref from iterator */
1268                 res = AST_TEST_FAIL;
1269         }
1270         ao2_iterator_destroy(mult_iter);
1271
1272         return res;
1273 }
1274
1275 /*!
1276  * \internal
1277  * \brief Run an ao2_find() for duplicates and compare the returned vector with the given vector.
1278  * \since 12.0.0
1279  *
1280  * \param res Passed in enum ast_test_result_state.
1281  * \param container Container to traverse.
1282  * \param flags Callback flags controlling the traversal.
1283  * \param number Number of object to find all duplicates.
1284  * \param vector Expected vector to find.
1285  * \param count Number of objects in the vector.
1286  * \param prefix Test output prefix string.
1287  * \param test Test output controller.
1288  *
1289  * \return enum ast_test_result_state
1290  */
1291 static int test_expected_duplicates(int res, struct ao2_container *container,
1292         enum search_flags flags, int number,
1293         const int *vector, int count, const char *prefix, struct ast_test *test)
1294 {
1295         struct ao2_iterator *mult_iter;
1296         struct test_obj *obj;
1297         int idx;
1298
1299         mult_iter = ao2_find(container, &number, flags | OBJ_MULTIPLE | OBJ_KEY);
1300         if (!mult_iter) {
1301                 ast_test_status_update(test, "%s: Did not return iterator.\n", prefix);
1302                 return AST_TEST_FAIL;
1303         }
1304
1305         /* Check matching objects against the given vector. */
1306         for (idx = 0; idx < count; ++idx) {
1307                 obj = ao2_iterator_next(mult_iter);
1308                 if (!obj) {
1309                         ast_test_status_update(test, "%s: Too few objects found.\n", prefix);
1310                         res = AST_TEST_FAIL;
1311                         break;
1312                 }
1313                 if (number != obj->i) {
1314                         ast_test_status_update(test, "%s: Object %d != %d.\n",
1315                                 prefix, obj->i, number);
1316                         res = AST_TEST_FAIL;
1317                 }
1318                 if (vector[idx] != obj->dup_number) {
1319                         ast_test_status_update(test, "%s: Object dup id %d != vector[%d] %d.\n",
1320                                 prefix, obj->dup_number, idx, vector[idx]);
1321                         res = AST_TEST_FAIL;
1322                 }
1323                 ao2_ref(obj, -1); /* remove ref from iterator */
1324         }
1325         obj = ao2_iterator_next(mult_iter);
1326         if (obj) {
1327                 ast_test_status_update(test,
1328                         "%s: Too many objects found.  Object %d, dup id %d\n",
1329                         prefix, obj->i, obj->dup_number);
1330                 ao2_ref(obj, -1); /* remove ref from iterator */
1331                 res = AST_TEST_FAIL;
1332         }
1333         ao2_iterator_destroy(mult_iter);
1334
1335         return res;
1336 }
1337
1338 /*!
1339  * \internal
1340  * \brief Test nonsorted container traversal.
1341  * \since 12.0.0
1342  *
1343  * \param res Passed in enum ast_test_result_state.
1344  * \param tst_num Test number.
1345  * \param type Container type to test.
1346  * \param test Test output controller.
1347  *
1348  * \return enum ast_test_result_state
1349  */
1350 static int test_traversal_nonsorted(int res, int tst_num, enum test_container_type type, struct ast_test *test)
1351 {
1352         struct ao2_container *c1;
1353         struct ao2_container *c2 = NULL;
1354         int partial;
1355         int destructor_count = 0;
1356
1357         /*! Container object insertion vector. */
1358         static const int test_initial[] = {
1359                 1, 0, 2, 6, 4, 7, 5, 3, 9, 8
1360         };
1361
1362         /*! Container object insertion vector reversed. */
1363         static const int test_reverse[] = {
1364                 8, 9, 3, 5, 7, 4, 6, 2, 0, 1
1365         };
1366         static const int test_list_partial_forward[] = {
1367                 6, 7, 5
1368         };
1369         static const int test_list_partial_backward[] = {
1370                 5, 7, 6
1371         };
1372
1373         /* The hash orders assume that there are 5 buckets. */
1374         static const int test_hash_end_forward[] = {
1375                 0, 5, 1, 6, 2, 7, 3, 8, 4, 9
1376         };
1377         static const int test_hash_end_backward[] = {
1378                 9, 4, 8, 3, 7, 2, 6, 1, 5, 0
1379         };
1380         static const int test_hash_begin_forward[] = {
1381                 5, 0, 6, 1, 7, 2, 8, 3, 9, 4
1382         };
1383         static const int test_hash_begin_backward[] = {
1384                 4, 9, 3, 8, 2, 7, 1, 6, 0, 5
1385         };
1386         static const int test_hash_partial_forward[] = {
1387                 5, 6, 7
1388         };
1389         static const int test_hash_partial_backward[] = {
1390                 7, 6, 5
1391         };
1392
1393         ast_test_status_update(test, "Test %d, %s containers.\n",
1394                 tst_num, test_container2str(type));
1395
1396         /* Create container that inserts objects at the end. */
1397         c1 = test_make_nonsorted(type, 0);
1398         if (!c1) {
1399                 ast_test_status_update(test, "Container c1 creation failed.\n");
1400                 res = AST_TEST_FAIL;
1401                 goto test_cleanup;
1402         }
1403         if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1", test)) {
1404                 res = AST_TEST_FAIL;
1405                 goto test_cleanup;
1406         }
1407
1408         /* Create container that inserts objects at the beginning. */
1409         c2 = test_make_nonsorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN);
1410         if (!c2) {
1411                 ast_test_status_update(test, "Container c2 creation failed.\n");
1412                 res = AST_TEST_FAIL;
1413                 goto test_cleanup;
1414         }
1415         if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2", test)) {
1416                 res = AST_TEST_FAIL;
1417                 goto test_cleanup;
1418         }
1419
1420         /* Check container iteration directions */
1421         switch (type) {
1422         case TEST_CONTAINER_LIST:
1423                 res = test_ao2_iteration(res, c1, 0,
1424                         test_initial, ARRAY_LEN(test_initial),
1425                         "Iteration (ascending, insert end)", test);
1426                 res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
1427                         test_reverse, ARRAY_LEN(test_reverse),
1428                         "Iteration (descending, insert end)", test);
1429
1430                 res = test_ao2_iteration(res, c2, 0,
1431                         test_reverse, ARRAY_LEN(test_reverse),
1432                         "Iteration (ascending, insert begin)", test);
1433                 res = test_ao2_iteration(res, c2, AO2_ITERATOR_DESCENDING,
1434                         test_initial, ARRAY_LEN(test_initial),
1435                         "Iteration (descending, insert begin)", test);
1436                 break;
1437         case TEST_CONTAINER_HASH:
1438                 res = test_ao2_iteration(res, c1, 0,
1439                         test_hash_end_forward, ARRAY_LEN(test_hash_end_forward),
1440                         "Iteration (ascending, insert end)", test);
1441                 res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
1442                         test_hash_end_backward, ARRAY_LEN(test_hash_end_backward),
1443                         "Iteration (descending, insert end)", test);
1444
1445                 res = test_ao2_iteration(res, c2, 0,
1446                         test_hash_begin_forward, ARRAY_LEN(test_hash_begin_forward),
1447                         "Iteration (ascending, insert begin)", test);
1448                 res = test_ao2_iteration(res, c2, AO2_ITERATOR_DESCENDING,
1449                         test_hash_begin_backward, ARRAY_LEN(test_hash_begin_backward),
1450                         "Iteration (descending, insert begin)", test);
1451                 break;
1452         case TEST_CONTAINER_RBTREE:
1453                 break;
1454         }
1455
1456         /* Check container traversal directions */
1457         switch (type) {
1458         case TEST_CONTAINER_LIST:
1459                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1460                         test_initial, ARRAY_LEN(test_initial),
1461                         "Traversal (ascending, insert end)", test);
1462                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
1463                         test_reverse, ARRAY_LEN(test_reverse),
1464                         "Traversal (descending, insert end)", test);
1465
1466                 res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_ASCENDING, NULL, NULL,
1467                         test_reverse, ARRAY_LEN(test_reverse),
1468                         "Traversal (ascending, insert begin)", test);
1469                 res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_DESCENDING, NULL, NULL,
1470                         test_initial, ARRAY_LEN(test_initial),
1471                         "Traversal (descending, insert begin)", test);
1472                 break;
1473         case TEST_CONTAINER_HASH:
1474                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1475                         test_hash_end_forward, ARRAY_LEN(test_hash_end_forward),
1476                         "Traversal (ascending, insert end)", test);
1477                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
1478                         test_hash_end_backward, ARRAY_LEN(test_hash_end_backward),
1479                         "Traversal (descending, insert end)", test);
1480
1481                 res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_ASCENDING, NULL, NULL,
1482                         test_hash_begin_forward, ARRAY_LEN(test_hash_begin_forward),
1483                         "Traversal (ascending, insert begin)", test);
1484                 res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_DESCENDING, NULL, NULL,
1485                         test_hash_begin_backward, ARRAY_LEN(test_hash_begin_backward),
1486                         "Traversal (descending, insert begin)", test);
1487                 break;
1488         case TEST_CONTAINER_RBTREE:
1489                 break;
1490         }
1491
1492         /* Check traversal with OBJ_PARTIAL_KEY search range. */
1493         partial = 6;
1494         partial_key_match_range = 1;
1495         switch (type) {
1496         case TEST_CONTAINER_LIST:
1497                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1498                         test_cmp_cb, &partial,
1499                         test_list_partial_forward, ARRAY_LEN(test_list_partial_forward),
1500                         "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1501                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
1502                         test_cmp_cb, &partial,
1503                         test_list_partial_backward, ARRAY_LEN(test_list_partial_backward),
1504                         "Traversal OBJ_PARTIAL_KEY (descending)", test);
1505                 break;
1506         case TEST_CONTAINER_HASH:
1507                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1508                         test_cmp_cb, &partial,
1509                         test_hash_partial_forward, ARRAY_LEN(test_hash_partial_forward),
1510                         "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1511                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
1512                         test_cmp_cb, &partial,
1513                         test_hash_partial_backward, ARRAY_LEN(test_hash_partial_backward),
1514                         "Traversal OBJ_PARTIAL_KEY (descending)", test);
1515                 break;
1516         case TEST_CONTAINER_RBTREE:
1517                 break;
1518         }
1519
1520 test_cleanup:
1521         /* destroy containers */
1522         if (c1) {
1523                 ao2_t_ref(c1, -1, "bye c1");
1524         }
1525         if (c2) {
1526                 ao2_t_ref(c2, -1, "bye c2");
1527         }
1528
1529         if (destructor_count > 0) {
1530                 ast_test_status_update(test,
1531                         "all destructors were not called, destructor count is %d\n",
1532                         destructor_count);
1533                 res = AST_TEST_FAIL;
1534         } else if (destructor_count < 0) {
1535                 ast_test_status_update(test,
1536                         "Destructor was called too many times, destructor count is %d\n",
1537                         destructor_count);
1538                 res = AST_TEST_FAIL;
1539         }
1540
1541         return res;
1542 }
1543
1544 /*!
1545  * \internal
1546  * \brief Test sorted container traversal.
1547  * \since 12.0.0
1548  *
1549  * \param res Passed in enum ast_test_result_state.
1550  * \param tst_num Test number.
1551  * \param type Container type to test.
1552  * \param test Test output controller.
1553  *
1554  * \return enum ast_test_result_state
1555  */
1556 static int test_traversal_sorted(int res, int tst_num, enum test_container_type type, struct ast_test *test)
1557 {
1558         struct ao2_container *c1;
1559         struct ao2_container *c2 = NULL;
1560         int partial;
1561         int destructor_count = 0;
1562         int duplicate_number = 100;
1563
1564         /*! Container object insertion vector. */
1565         static const int test_initial[] = {
1566                 1, 0, 2, 6, 4, 7, 5, 3, 9, 8
1567         };
1568
1569         /*! Container forward traversal/iteration. */
1570         static const int test_forward[] = {
1571                 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
1572         };
1573         /*! Container backward traversal/iteration. */
1574         static const int test_backward[] = {
1575                 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
1576         };
1577
1578         static const int test_partial_forward[] = {
1579                 5, 6, 7
1580         };
1581         static const int test_partial_backward[] = {
1582                 7, 6, 5
1583         };
1584
1585         /* The hash orders assume that there are 5 buckets. */
1586         static const int test_hash_forward[] = {
1587                 0, 5, 1, 6, 2, 7, 3, 8, 4, 9
1588         };
1589         static const int test_hash_backward[] = {
1590                 9, 4, 8, 3, 7, 2, 6, 1, 5, 0
1591         };
1592         static const int test_hash_partial_forward[] = {
1593                 5, 6, 7
1594         };
1595         static const int test_hash_partial_backward[] = {
1596                 7, 6, 5
1597         };
1598
1599         /* Duplicate identifier order */
1600         static const int test_dup_allow_forward[] = {
1601                 0, 1, 2, 3, 2
1602         };
1603         static const int test_dup_allow_backward[] = {
1604                 2, 3, 2, 1, 0
1605         };
1606         static const int test_dup_reject[] = {
1607                 0
1608         };
1609         static const int test_dup_obj_reject_forward[] = {
1610                 0, 1, 2, 3
1611         };
1612         static const int test_dup_obj_reject_backward[] = {
1613                 3, 2, 1, 0
1614         };
1615         static const int test_dup_replace[] = {
1616                 2
1617         };
1618
1619         ast_test_status_update(test, "Test %d, %s containers.\n",
1620                 tst_num, test_container2str(type));
1621
1622         /* Create container that inserts duplicate objects after matching objects. */
1623         c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW);
1624         if (!c1) {
1625                 ast_test_status_update(test, "Container c1 creation failed.\n");
1626                 res = AST_TEST_FAIL;
1627                 goto test_cleanup;
1628         }
1629         if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_ALLOW)", test)) {
1630                 res = AST_TEST_FAIL;
1631                 goto test_cleanup;
1632         }
1633
1634         /* Create container that inserts duplicate objects before matching objects. */
1635         c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW);
1636         if (!c2) {
1637                 ast_test_status_update(test, "Container c2 creation failed.\n");
1638                 res = AST_TEST_FAIL;
1639                 goto test_cleanup;
1640         }
1641         if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_ALLOW)", test)) {
1642                 res = AST_TEST_FAIL;
1643                 goto test_cleanup;
1644         }
1645
1646 #if defined(TEST_CONTAINER_DEBUG_DUMP)
1647         ao2_container_dump(c1, 0, "c1(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
1648         ao2_container_stats(c1, 0, "c1(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug);
1649         ao2_container_dump(c2, 0, "c2(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
1650         ao2_container_stats(c2, 0, "c2(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug);
1651 #endif  /* defined(TEST_CONTAINER_DEBUG_DUMP) */
1652
1653         /* Check container iteration directions */
1654         switch (type) {
1655         case TEST_CONTAINER_RBTREE:
1656         case TEST_CONTAINER_LIST:
1657                 res = test_ao2_iteration(res, c1, 0,
1658                         test_forward, ARRAY_LEN(test_forward),
1659                         "Iteration (ascending)", test);
1660                 res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
1661                         test_backward, ARRAY_LEN(test_backward),
1662                         "Iteration (descending)", test);
1663                 break;
1664         case TEST_CONTAINER_HASH:
1665                 res = test_ao2_iteration(res, c1, 0,
1666                         test_hash_forward, ARRAY_LEN(test_hash_forward),
1667                         "Iteration (ascending)", test);
1668                 res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
1669                         test_hash_backward, ARRAY_LEN(test_hash_backward),
1670                         "Iteration (descending)", test);
1671                 break;
1672         }
1673
1674         /* Check container traversal directions */
1675         switch (type) {
1676         case TEST_CONTAINER_RBTREE:
1677         case TEST_CONTAINER_LIST:
1678                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1679                         test_forward, ARRAY_LEN(test_forward),
1680                         "Traversal (ascending)", test);
1681                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
1682                         test_backward, ARRAY_LEN(test_backward),
1683                         "Traversal (descending)", test);
1684                 break;
1685         case TEST_CONTAINER_HASH:
1686                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1687                         test_hash_forward, ARRAY_LEN(test_hash_forward),
1688                         "Traversal (ascending, insert end)", test);
1689                 res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
1690                         test_hash_backward, ARRAY_LEN(test_hash_backward),
1691                         "Traversal (descending)", test);
1692                 break;
1693         }
1694
1695         /* Check traversal with OBJ_PARTIAL_KEY search range. */
1696         partial = 6;
1697         partial_key_match_range = 1;
1698         switch (type) {
1699         case TEST_CONTAINER_RBTREE:
1700         case TEST_CONTAINER_LIST:
1701                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1702                         test_cmp_cb, &partial,
1703                         test_partial_forward, ARRAY_LEN(test_partial_forward),
1704                         "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1705                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
1706                         test_cmp_cb, &partial,
1707                         test_partial_backward, ARRAY_LEN(test_partial_backward),
1708                         "Traversal OBJ_PARTIAL_KEY (descending)", test);
1709                 break;
1710         case TEST_CONTAINER_HASH:
1711                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1712                         test_cmp_cb, &partial,
1713                         test_hash_partial_forward, ARRAY_LEN(test_hash_partial_forward),
1714                         "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1715                 res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
1716                         test_cmp_cb, &partial,
1717                         test_hash_partial_backward, ARRAY_LEN(test_hash_partial_backward),
1718                         "Traversal OBJ_PARTIAL_KEY (descending)", test);
1719                 break;
1720         }
1721
1722         /* Add duplicates to initial containers that allow duplicates */
1723         if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_ALLOW)", test)) {
1724                 res = AST_TEST_FAIL;
1725                 goto test_cleanup;
1726         }
1727         if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_ALLOW)", test)) {
1728                 res = AST_TEST_FAIL;
1729                 goto test_cleanup;
1730         }
1731
1732 #if defined(TEST_CONTAINER_DEBUG_DUMP)
1733         ao2_container_dump(c1, 0, "c1(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
1734         ao2_container_stats(c1, 0, "c1(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug);
1735         ao2_container_dump(c2, 0, "c2(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
1736         ao2_container_stats(c2, 0, "c2(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug);
1737 #endif  /* defined(TEST_CONTAINER_DEBUG_DUMP) */
1738
1739         /* Check duplicates in containers that allow duplicates. */
1740         res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
1741                 test_dup_allow_forward, ARRAY_LEN(test_dup_allow_forward),
1742                 "Duplicates (ascending, DUPS_ALLOW)", test);
1743         res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
1744                 test_dup_allow_backward, ARRAY_LEN(test_dup_allow_backward),
1745                 "Duplicates (descending, DUPS_ALLOW)", test);
1746
1747         ao2_t_ref(c1, -1, "bye c1");
1748         c1 = NULL;
1749         ao2_t_ref(c2, -1, "bye c2");
1750         c2 = NULL;
1751
1752         /* Create containers that reject duplicate keyed objects. */
1753         c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT);
1754         if (!c1) {
1755                 ast_test_status_update(test, "Container c1 creation failed.\n");
1756                 res = AST_TEST_FAIL;
1757                 goto test_cleanup;
1758         }
1759         if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_REJECT)", test)) {
1760                 res = AST_TEST_FAIL;
1761                 goto test_cleanup;
1762         }
1763         if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_REJECT)", test)) {
1764                 res = AST_TEST_FAIL;
1765                 goto test_cleanup;
1766         }
1767         c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT);
1768         if (!c2) {
1769                 ast_test_status_update(test, "Container c2 creation failed.\n");
1770                 res = AST_TEST_FAIL;
1771                 goto test_cleanup;
1772         }
1773         if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_REJECT)", test)) {
1774                 res = AST_TEST_FAIL;
1775                 goto test_cleanup;
1776         }
1777         if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_REJECT)", test)) {
1778                 res = AST_TEST_FAIL;
1779                 goto test_cleanup;
1780         }
1781
1782         /* Check duplicates in containers that reject duplicate keyed objects. */
1783         res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
1784                 test_dup_reject, ARRAY_LEN(test_dup_reject),
1785                 "Duplicates (ascending, DUPS_REJECT)", test);
1786         res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
1787                 test_dup_reject, ARRAY_LEN(test_dup_reject),
1788                 "Duplicates (descending, DUPS_REJECT)", test);
1789
1790         ao2_t_ref(c1, -1, "bye c1");
1791         c1 = NULL;
1792         ao2_t_ref(c2, -1, "bye c2");
1793         c2 = NULL;
1794
1795         /* Create containers that reject duplicate objects. */
1796         c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT);
1797         if (!c1) {
1798                 ast_test_status_update(test, "Container c1 creation failed.\n");
1799                 res = AST_TEST_FAIL;
1800                 goto test_cleanup;
1801         }
1802         if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_OBJ_REJECT)", test)) {
1803                 res = AST_TEST_FAIL;
1804                 goto test_cleanup;
1805         }
1806         if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_OBJ_REJECT)", test)) {
1807                 res = AST_TEST_FAIL;
1808                 goto test_cleanup;
1809         }
1810         c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT);
1811         if (!c2) {
1812                 ast_test_status_update(test, "Container c2 creation failed.\n");
1813                 res = AST_TEST_FAIL;
1814                 goto test_cleanup;
1815         }
1816         if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_OBJ_REJECT)", test)) {
1817                 res = AST_TEST_FAIL;
1818                 goto test_cleanup;
1819         }
1820         if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_OBJ_REJECT)", test)) {
1821                 res = AST_TEST_FAIL;
1822                 goto test_cleanup;
1823         }
1824
1825         /* Check duplicates in containers that reject duplicate objects. */
1826         res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
1827                 test_dup_obj_reject_forward, ARRAY_LEN(test_dup_obj_reject_forward),
1828                 "Duplicates (ascending, DUPS_OBJ_REJECT)", test);
1829         res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
1830                 test_dup_obj_reject_backward, ARRAY_LEN(test_dup_obj_reject_backward),
1831                 "Duplicates (descending, DUPS_OBJ_REJECT)", test);
1832
1833         ao2_t_ref(c1, -1, "bye c1");
1834         c1 = NULL;
1835         ao2_t_ref(c2, -1, "bye c2");
1836         c2 = NULL;
1837
1838         /* Create container that replaces duplicate keyed objects. */
1839         c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE);
1840         if (!c1) {
1841                 ast_test_status_update(test, "Container c1 creation failed.\n");
1842                 res = AST_TEST_FAIL;
1843                 goto test_cleanup;
1844         }
1845         if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_REJECT)", test)) {
1846                 res = AST_TEST_FAIL;
1847                 goto test_cleanup;
1848         }
1849         if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_REJECT)", test)) {
1850                 res = AST_TEST_FAIL;
1851                 goto test_cleanup;
1852         }
1853         c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE);
1854         if (!c2) {
1855                 ast_test_status_update(test, "Container c2 creation failed.\n");
1856                 res = AST_TEST_FAIL;
1857                 goto test_cleanup;
1858         }
1859         if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_REPLACE)", test)) {
1860                 res = AST_TEST_FAIL;
1861                 goto test_cleanup;
1862         }
1863         if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_REPLACE)", test)) {
1864                 res = AST_TEST_FAIL;
1865                 goto test_cleanup;
1866         }
1867
1868         /* Check duplicates in containers that replaces duplicate keyed objects. */
1869         res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
1870                 test_dup_replace, ARRAY_LEN(test_dup_replace),
1871                 "Duplicates (ascending, DUPS_REPLACE)", test);
1872         res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
1873                 test_dup_replace, ARRAY_LEN(test_dup_replace),
1874                 "Duplicates (descending, DUPS_REPLACE)", test);
1875
1876 test_cleanup:
1877         /* destroy containers */
1878         if (c1) {
1879                 ao2_t_ref(c1, -1, "bye c1");
1880         }
1881         if (c2) {
1882                 ao2_t_ref(c2, -1, "bye c2");
1883         }
1884
1885         if (destructor_count > 0) {
1886                 ast_test_status_update(test,
1887                         "all destructors were not called, destructor count is %d\n",
1888                         destructor_count);
1889                 res = AST_TEST_FAIL;
1890         } else if (destructor_count < 0) {
1891                 ast_test_status_update(test,
1892                         "Destructor was called too many times, destructor count is %d\n",
1893                         destructor_count);
1894                 res = AST_TEST_FAIL;
1895         }
1896
1897         return res;
1898 }
1899
1900 AST_TEST_DEFINE(astobj2_test_4)
1901 {
1902         int res = AST_TEST_PASS;
1903
1904         switch (cmd) {
1905         case TEST_INIT:
1906                 info->name = "astobj2_test4";
1907                 info->category = "/main/astobj2/";
1908                 info->summary = "Test container traversal/iteration";
1909                 info->description =
1910                         "This test is to see if the container traversal/iteration works "
1911                         "as intended for each supported container type.";
1912                 return AST_TEST_NOT_RUN;
1913         case TEST_EXECUTE:
1914                 break;
1915         }
1916
1917         res = test_traversal_nonsorted(res, 1, TEST_CONTAINER_LIST, test);
1918         res = test_traversal_nonsorted(res, 2, TEST_CONTAINER_HASH, test);
1919
1920         res = test_traversal_sorted(res, 3, TEST_CONTAINER_LIST, test);
1921         res = test_traversal_sorted(res, 4, TEST_CONTAINER_HASH, test);
1922         res = test_traversal_sorted(res, 5, TEST_CONTAINER_RBTREE, test);
1923
1924         return res;
1925 }
1926
1927 static int unload_module(void)
1928 {
1929         AST_TEST_UNREGISTER(astobj2_test_1);
1930         AST_TEST_UNREGISTER(astobj2_test_2);
1931         AST_TEST_UNREGISTER(astobj2_test_3);
1932         AST_TEST_UNREGISTER(astobj2_test_4);
1933         return 0;
1934 }
1935
1936 static int load_module(void)
1937 {
1938         AST_TEST_REGISTER(astobj2_test_1);
1939         AST_TEST_REGISTER(astobj2_test_2);
1940         AST_TEST_REGISTER(astobj2_test_3);
1941         AST_TEST_REGISTER(astobj2_test_4);
1942         return AST_MODULE_LOAD_SUCCESS;
1943 }
1944
1945 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ASTOBJ2 Unit Tests");