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