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