Asterisk - The Open Source Telephony Project  21.4.1
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 
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. */
78  /*! Container object key */
79  int i;
80  /*! Identifier for duplicate object key tests. */
82 };
83 
84 /*! Partial search key +/- matching 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 static 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  */
214 static 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  */
239  obj2 = ao2_t_callback(clone, OBJ_POINTER | OBJ_UNLINK, ao2_match_by_addr, obj,
240  "test clone");
241  if (obj2) {
242  ao2_t_ref(obj2, -1, "test clone");
243  continue;
244  }
245  ast_test_status_update(test,
246  "Orig container has an object %p not in the clone container.\n", obj);
247  res = AST_TEST_FAIL;
248  }
249  ao2_iterator_destroy(&iter);
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  */
276 static 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  */
315 static 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  */
354 static 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  */
391 static 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. */
398  partial_key_match_range = 0;
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 
419 static 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) {
439  case TEST_CONTAINER_LIST:
440  /* Lists just have one bucket. */
441  n_buckets = 1;
442  c1 = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
443  use_sort ? test_sort_cb : NULL, test_cmp_cb, "test");
444  break;
445  case TEST_CONTAINER_HASH:
446  n_buckets = (ast_random() % ((lim / 4) + 1)) + 1;
447  c1 = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, n_buckets,
448  test_hash_cb, use_sort ? test_sort_cb : NULL, test_cmp_cb, "test");
449  break;
450  case TEST_CONTAINER_RBTREE:
451  /* RBTrees just have one bucket. */
452  n_buckets = 1;
453  c1 = ao2_t_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
454  test_sort_cb, test_cmp_cb, "test");
455  break;
456  }
457  c2 = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL, "test");
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 */
611  ao2_t_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, all_but_one_cb, NULL, "test multiple");
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 
626 cleanup:
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 
646 AST_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 
688 AST_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 
713  c = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, test_cmp_cb);
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 
815 cleanup:
816  if (c) {
817  ao2_ref(c, -1);
818  }
819 
820  return res;
821 }
822 
823 static AO2_GLOBAL_OBJ_STATIC(astobj2_holder);
824 
825 AST_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) {
928  ast_test_status_update(test,
929  "all destructors were not called, destructor count is %d\n",
930  destructor_count);
931  res = AST_TEST_FAIL;
932  } else if (destructor_count < 0) {
933  ast_test_status_update(test,
934  "Destructor was called too many times, destructor count is %d\n",
935  destructor_count);
936  res = AST_TEST_FAIL;
937  }
938 
939 cleanup:
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  */
965 static struct ao2_container *test_make_nonsorted(enum test_container_type type, int options)
966 {
967  struct ao2_container *container;
968 
969  container = NULL;
970  switch (type) {
971  case TEST_CONTAINER_LIST:
973  NULL, test_cmp_cb);
974  break;
975  case TEST_CONTAINER_HASH:
976  container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, options, 5,
977  test_hash_cb, NULL, test_cmp_cb);
978  break;
979  case TEST_CONTAINER_RBTREE:
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  */
998 static struct ao2_container *test_make_sorted(enum test_container_type type, int options)
999 {
1000  struct ao2_container *container;
1001 
1002  container = NULL;
1003  switch (type) {
1004  case TEST_CONTAINER_LIST:
1005  container = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, options,
1006  test_sort_cb, test_cmp_cb, "test");
1007  break;
1008  case TEST_CONTAINER_HASH:
1009  container = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, options, 5,
1010  test_hash_cb, test_sort_cb, test_cmp_cb, "test");
1011  break;
1012  case TEST_CONTAINER_RBTREE:
1013  container = ao2_t_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, options,
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  */
1038 static 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");
1057  if (ao2_container_check(container, 0)) {
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) {
1064  ast_test_status_update(test,
1065  "%s: Unexpected container count. Expected:%d Got:%d\n",
1066  prefix, idx + 1, ao2_container_count(container));
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  */
1090 static 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 
1131  if (ao2_container_check(container, 0)) {
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 
1145  if (ao2_container_check(container, 0)) {
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  */
1169 static 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  */
1230 static int test_ao2_callback_traversal(int res, struct ao2_container *container,
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  */
1287 static int test_expected_duplicates(int res, struct ao2_container *container,
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) {
1323  ast_test_status_update(test,
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  */
1346 static 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. */
1405  c2 = test_make_nonsorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN);
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) {
1418  case TEST_CONTAINER_LIST:
1419  res = test_ao2_iteration(res, c1, 0,
1420  test_initial, ARRAY_LEN(test_initial),
1421  "Iteration (ascending, insert end)", test);
1422  res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
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);
1429  res = test_ao2_iteration(res, c2, AO2_ITERATOR_DESCENDING,
1430  test_initial, ARRAY_LEN(test_initial),
1431  "Iteration (descending, insert begin)", test);
1432  break;
1433  case TEST_CONTAINER_HASH:
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);
1437  res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
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);
1444  res = test_ao2_iteration(res, c2, AO2_ITERATOR_DESCENDING,
1445  test_hash_begin_backward, ARRAY_LEN(test_hash_begin_backward),
1446  "Iteration (descending, insert begin)", test);
1447  break;
1448  case TEST_CONTAINER_RBTREE:
1449  break;
1450  }
1451 
1452  /* Check container traversal directions */
1453  switch (type) {
1454  case TEST_CONTAINER_LIST:
1455  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1456  test_initial, ARRAY_LEN(test_initial),
1457  "Traversal (ascending, insert end)", test);
1458  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
1459  test_reverse, ARRAY_LEN(test_reverse),
1460  "Traversal (descending, insert end)", test);
1461 
1462  res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_ASCENDING, NULL, NULL,
1463  test_reverse, ARRAY_LEN(test_reverse),
1464  "Traversal (ascending, insert begin)", test);
1465  res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_DESCENDING, NULL, NULL,
1466  test_initial, ARRAY_LEN(test_initial),
1467  "Traversal (descending, insert begin)", test);
1468  break;
1469  case TEST_CONTAINER_HASH:
1470  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1471  test_hash_end_forward, ARRAY_LEN(test_hash_end_forward),
1472  "Traversal (ascending, insert end)", test);
1473  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
1474  test_hash_end_backward, ARRAY_LEN(test_hash_end_backward),
1475  "Traversal (descending, insert end)", test);
1476 
1477  res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_ASCENDING, NULL, NULL,
1478  test_hash_begin_forward, ARRAY_LEN(test_hash_begin_forward),
1479  "Traversal (ascending, insert begin)", test);
1480  res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_DESCENDING, NULL, NULL,
1481  test_hash_begin_backward, ARRAY_LEN(test_hash_begin_backward),
1482  "Traversal (descending, insert begin)", test);
1483  break;
1484  case TEST_CONTAINER_RBTREE:
1485  break;
1486  }
1487 
1488  /* Check traversal with OBJ_PARTIAL_KEY search range. */
1489  partial = 6;
1490  partial_key_match_range = 1;
1491  switch (type) {
1492  case TEST_CONTAINER_LIST:
1493  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1494  test_cmp_cb, &partial,
1495  test_list_partial_forward, ARRAY_LEN(test_list_partial_forward),
1496  "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1497  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
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;
1502  case TEST_CONTAINER_HASH:
1503  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1504  test_cmp_cb, &partial,
1505  test_hash_partial_forward, ARRAY_LEN(test_hash_partial_forward),
1506  "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1507  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
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;
1512  case TEST_CONTAINER_RBTREE:
1513  break;
1514  }
1515 
1516 test_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) {
1526  ast_test_status_update(test,
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) {
1531  ast_test_status_update(test,
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  */
1552 static 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. */
1619  c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW);
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) {
1651  case TEST_CONTAINER_RBTREE:
1652  case TEST_CONTAINER_LIST:
1653  res = test_ao2_iteration(res, c1, 0,
1654  test_forward, ARRAY_LEN(test_forward),
1655  "Iteration (ascending)", test);
1656  res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
1657  test_backward, ARRAY_LEN(test_backward),
1658  "Iteration (descending)", test);
1659  break;
1660  case TEST_CONTAINER_HASH:
1661  res = test_ao2_iteration(res, c1, 0,
1662  test_hash_forward, ARRAY_LEN(test_hash_forward),
1663  "Iteration (ascending)", test);
1664  res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
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) {
1672  case TEST_CONTAINER_RBTREE:
1673  case TEST_CONTAINER_LIST:
1674  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1675  test_forward, ARRAY_LEN(test_forward),
1676  "Traversal (ascending)", test);
1677  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
1678  test_backward, ARRAY_LEN(test_backward),
1679  "Traversal (descending)", test);
1680  break;
1681  case TEST_CONTAINER_HASH:
1682  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
1683  test_hash_forward, ARRAY_LEN(test_hash_forward),
1684  "Traversal (ascending, insert end)", test);
1685  res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
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;
1693  partial_key_match_range = 1;
1694  switch (type) {
1695  case TEST_CONTAINER_RBTREE:
1696  case TEST_CONTAINER_LIST:
1697  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1698  test_cmp_cb, &partial,
1699  test_partial_forward, ARRAY_LEN(test_partial_forward),
1700  "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1701  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
1702  test_cmp_cb, &partial,
1703  test_partial_backward, ARRAY_LEN(test_partial_backward),
1704  "Traversal OBJ_PARTIAL_KEY (descending)", test);
1705  break;
1706  case TEST_CONTAINER_HASH:
1707  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
1708  test_cmp_cb, &partial,
1709  test_hash_partial_forward, ARRAY_LEN(test_hash_partial_forward),
1710  "Traversal OBJ_PARTIAL_KEY (ascending)", test);
1711  res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
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. */
1749  c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT);
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. */
1792  c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT);
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. */
1835  c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE);
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 
1872 test_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) {
1882  ast_test_status_update(test,
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) {
1887  ast_test_status_update(test,
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 
1896 AST_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 
1913  res = test_traversal_nonsorted(res, 1, TEST_CONTAINER_LIST, test);
1914  res = test_traversal_nonsorted(res, 2, TEST_CONTAINER_HASH, test);
1915 
1916  res = test_traversal_sorted(res, 3, TEST_CONTAINER_LIST, test);
1917  res = test_traversal_sorted(res, 4, TEST_CONTAINER_HASH, test);
1918  res = test_traversal_sorted(res, 5, TEST_CONTAINER_RBTREE, test);
1919 
1920  return res;
1921 }
1922 
1923 static 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) {
1937  case TEST_CONTAINER_HASH:
1939  test_hash_cb, test_sort_cb, test_cmp_cb);
1940  break;
1941  case TEST_CONTAINER_LIST:
1943  test_sort_cb, test_cmp_cb);
1944  break;
1945  case TEST_CONTAINER_RBTREE:
1947  test_sort_cb, test_cmp_cb);
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 
1982 test_cleanup:
1983  for (i = 0; i < OBJS ; i++) {
1984  ao2_cleanup(tobj[i]);
1985  }
1986  ao2_cleanup(c1);
1987  return res;
1988 }
1989 
1990 static 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 
2011 AST_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 
2039  res = testloop(test, TEST_CONTAINER_LIST, 0, ITERATIONS);
2040  if (!res) {
2041  return res;
2042  }
2043  res = testloop(test, TEST_CONTAINER_HASH, 0, ITERATIONS);
2044  if (!res) {
2045  return res;
2046  }
2047  res = testloop(test, TEST_CONTAINER_RBTREE, 0, ITERATIONS);
2048 
2049  return res;
2050 }
2051 
2052 static 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 
2062 static 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);
2069  return AST_MODULE_LOAD_SUCCESS;
2070 }
2071 
2072 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ASTOBJ2 Unit Tests");
int * destructor_count
Definition: test_astobj2.c:77
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.
static int test_traversal_sorted(int res, int tst_num, enum test_container_type type, struct ast_test *test)
void( ao2_prnt_fn)(void *where, const char *fmt,...)
Print output.
Definition: astobj2.h:1435
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
int partial_key_match_range
Definition: test_astobj2.c:85
int( ao2_callback_fn)(void *obj, void *arg, int flags)
Type of a generic callback function.
Definition: astobj2.h:1226
#define OBJ_KEY
Definition: astobj2.h:1151
#define ao2_container_clone(orig, flags)
Create a clone/copy of the given container.
Definition: astobj2.h:1419
#define OBJ_POINTER
Definition: astobj2.h:1150
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
Allow objects with duplicate keys in container.
Definition: astobj2.h:1181
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
Test Framework API.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
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
search_flags
Flags passed to ao2_callback_fn(), ao2_hash_fn(), and ao2_sort_fn() to modify behaviour.
Definition: astobj2.h:1034
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.
Insert objects at the beginning of the container. (Otherwise it is the opposite; insert at the end...
Definition: astobj2.h:1172
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
Utility functions.
Number structure.
Definition: app_followme.c:154
Traverse in ascending order (First to last container object)
Definition: astobj2.h:1121
int ao2_container_check(struct ao2_container *self, enum search_flags flags)
Perform an integrity check on the specified container.
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition: res_stasis.c:327
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
struct ao2_container * container
Definition: res_fax.c:501
Traverse in descending order (Last to first container object)
Definition: astobj2.h:1123
#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
static int test_traversal_nonsorted(int res, int tst_num, enum test_container_type type, struct ast_test *test)
int ao2_match_by_addr(void *obj, void *arg, int flags)
A common ao2_callback is one that matches by address.
static enum ast_test_result_state test_performance(struct ast_test *test, enum test_container_type type, unsigned int copt)
int dup_number
Definition: test_astobj2.c:81
Replace objects with duplicate keys in container.
Definition: astobj2.h:1211
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
#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
Reject objects with duplicate keys in container.
Definition: astobj2.h:1188
AST_TEST_DEFINE(astobj2_test_perf)
Generic container type.
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
Definition: astobj2.h:847
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
ao2_iterator_flags
Definition: astobj2.h:1835
Asterisk module definitions.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
Reject duplicate objects in container.
Definition: astobj2.h:1201
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532