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