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