test_astobj2.c 60 KB

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