12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2010, Digium, Inc.
- *
- * David Vossel <dvossel@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*!
- * \file
- * \brief astobj2 test module
- *
- * \author David Vossel <dvossel@digium.com>
- */
- /*** MODULEINFO
- <depend>TEST_FRAMEWORK</depend>
- <support_level>core</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include "asterisk/utils.h"
- #include "asterisk/module.h"
- #include "asterisk/test.h"
- #include "asterisk/astobj2.h"
- /* Uncomment the following line to dump the container contents during tests. */
- //#define TEST_CONTAINER_DEBUG_DUMP 1
- enum test_container_type {
- TEST_CONTAINER_LIST,
- TEST_CONTAINER_HASH,
- TEST_CONTAINER_RBTREE,
- };
- /*!
- * \internal
- * \brief Convert the container type enum to string.
- * \since 12.0.0
- *
- * \param type Container type value to convert to string.
- *
- * \return String value of container type.
- */
- static const char *test_container2str(enum test_container_type type)
- {
- const char *c_type;
- c_type = "Unknown";
- switch (type) {
- case TEST_CONTAINER_LIST:
- c_type = "List";
- break;
- case TEST_CONTAINER_HASH:
- c_type = "Hash";
- break;
- case TEST_CONTAINER_RBTREE:
- c_type = "RBTree";
- break;
- }
- return c_type;
- }
- struct test_obj {
- /*! What to decrement when object is destroyed. */
- int *destructor_count;
- /*! Container object key */
- int i;
- /*! Identifier for duplicate object key tests. */
- int dup_number;
- };
- /*! Partial search key +/- matching range. */
- int partial_key_match_range;
- static void test_obj_destructor(void *v_obj)
- {
- struct test_obj *obj = (struct test_obj *) v_obj;
- if (obj->destructor_count) {
- --*obj->destructor_count;
- }
- }
- static int increment_cb(void *obj, void *arg, int flag)
- {
- int *i = (int *) arg;
- *i = *i + 1;
- return 0;
- }
- static int all_but_one_cb(void *obj, void *arg, int flag)
- {
- struct test_obj *cmp_obj = (struct test_obj *) obj;
- return (cmp_obj->i) ? CMP_MATCH : 0;
- }
- static int multiple_cb(void *obj, void *arg, int flag)
- {
- int *i = (int *) arg;
- struct test_obj *cmp_obj = (struct test_obj *) obj;
- return (cmp_obj->i < *i) ? CMP_MATCH : 0;
- }
- static int test_cmp_cb(void *obj, void *arg, int flags)
- {
- struct test_obj *cmp_obj = (struct test_obj *) obj;
- if (flags & OBJ_KEY) {
- int *i = (int *) arg;
- return (cmp_obj->i == *i) ? CMP_MATCH : 0;
- } else if (flags & OBJ_PARTIAL_KEY) {
- int *i = (int *) arg;
- return (*i - partial_key_match_range <= cmp_obj->i
- && cmp_obj->i <= *i + partial_key_match_range) ? CMP_MATCH : 0;
- } else {
- struct test_obj *arg_obj = (struct test_obj *) arg;
- return (cmp_obj->i == arg_obj->i) ? CMP_MATCH : 0;
- }
- }
- static int test_hash_cb(const void *obj, const int flags)
- {
- if (flags & OBJ_KEY) {
- const int *i = obj;
- return *i;
- } else if (flags & OBJ_PARTIAL_KEY) {
- /* This is absolutely wrong to be called with this flag value. */
- abort();
- /* Just in case abort() doesn't work or something else super silly */
- *((int *) 0) = 0;
- return 0;
- } else {
- const struct test_obj *hash_obj = obj;
- return hash_obj->i;
- }
- }
- static int test_sort_cb(const void *obj_left, const void *obj_right, int flags)
- {
- const struct test_obj *test_left = obj_left;
- if (flags & OBJ_KEY) {
- const int *i = obj_right;
- return test_left->i - *i;
- } else if (flags & OBJ_PARTIAL_KEY) {
- int *i = (int *) obj_right;
- if (*i - partial_key_match_range <= test_left->i
- && test_left->i <= *i + partial_key_match_range) {
- return 0;
- }
- return test_left->i - *i;
- } else {
- const struct test_obj *test_right = obj_right;
- return test_left->i - test_right->i;
- }
- }
- #if defined(TEST_CONTAINER_DEBUG_DUMP)
- /*!
- * \internal
- * \brief Print test object key.
- * \since 12.0.0
- *
- * \param v_obj A pointer to the object we want the key printed.
- * \param where User data needed by prnt to determine where to put output.
- * \param prnt Print output callback function to use.
- *
- * \return Nothing
- */
- static void test_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt)
- {
- struct test_obj *obj = v_obj;
- if (!obj) {
- return;
- }
- prnt(where, "%6d-%d", obj->i, obj->dup_number);
- }
- #endif /* defined(TEST_CONTAINER_DEBUG_DUMP) */
- /*!
- * \internal
- * \brief Test container cloning.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param orig Container to clone.
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_container_clone(int res, struct ao2_container *orig, struct ast_test *test)
- {
- struct ao2_container *clone;
- struct test_obj *obj;
- struct test_obj *obj2;
- struct ao2_iterator iter;
- clone = ao2_container_clone(orig, 0);
- if (!clone) {
- ast_test_status_update(test, "ao2_container_clone failed.\n");
- return AST_TEST_FAIL;
- }
- if (ao2_container_check(clone, 0)) {
- ast_test_status_update(test, "container integrity check failed\n");
- res = AST_TEST_FAIL;
- } else if (ao2_container_count(orig) != ao2_container_count(clone)) {
- ast_test_status_update(test, "Cloned container does not have the same number of objects.\n");
- res = AST_TEST_FAIL;
- } else {
- iter = ao2_iterator_init(orig, 0);
- for (; (obj = ao2_t_iterator_next(&iter, "test orig")); ao2_t_ref(obj, -1, "test orig")) {
- /*
- * Unlink the matching object from the cloned container to make
- * the next search faster. This is a big speed optimization!
- */
- obj2 = ao2_t_callback(clone, OBJ_POINTER | OBJ_UNLINK, ao2_match_by_addr, obj,
- "test clone");
- if (obj2) {
- ao2_t_ref(obj2, -1, "test clone");
- continue;
- }
- ast_test_status_update(test,
- "Orig container has an object %p not in the clone container.\n", obj);
- res = AST_TEST_FAIL;
- }
- ao2_iterator_destroy(&iter);
- if (ao2_container_count(clone)) {
- ast_test_status_update(test, "Cloned container still has objects.\n");
- res = AST_TEST_FAIL;
- }
- if (ao2_container_check(clone, 0)) {
- ast_test_status_update(test, "container integrity check failed\n");
- res = AST_TEST_FAIL;
- }
- }
- ao2_t_ref(clone, -1, "bye clone");
- return res;
- }
- /*!
- * \internal
- * \brief Test ao2_find with no flags.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param look_in Container to search.
- * \param limit Container contains objects 0 - (limit - 1).
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_ao2_find_w_no_flags(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
- {
- int i;
- int num;
- struct test_obj tmp_obj = { 0, };
- struct test_obj *obj;
- for (num = 100; num--;) {
- i = ast_random() % limit; /* find a random object */
- tmp_obj.i = i;
- obj = ao2_find(look_in, &tmp_obj, 0);
- if (!obj) {
- ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with no flags failed.\n", i);
- res = AST_TEST_FAIL;
- } else {
- if (obj->i != i) {
- ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
- res = AST_TEST_FAIL;
- }
- ao2_t_ref(obj, -1, "test");
- }
- }
- return res;
- }
- /*!
- * \internal
- * \brief Test ao2_find with OBJ_POINTER.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param look_in Container to search.
- * \param limit Container contains objects 0 - (limit - 1).
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_ao2_find_w_OBJ_POINTER(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
- {
- int i;
- int num;
- struct test_obj tmp_obj = { 0, };
- struct test_obj *obj;
- for (num = 75; num--;) {
- i = ast_random() % limit; /* find a random object */
- tmp_obj.i = i;
- obj = ao2_find(look_in, &tmp_obj, OBJ_POINTER);
- if (!obj) {
- ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_POINTER flag failed.\n", i);
- res = AST_TEST_FAIL;
- } else {
- if (obj->i != i) {
- ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
- res = AST_TEST_FAIL;
- }
- ao2_t_ref(obj, -1, "test");
- }
- }
- return res;
- }
- /*!
- * \internal
- * \brief Test ao2_find with OBJ_KEY.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param look_in Container to search.
- * \param limit Container contains objects 0 - (limit - 1).
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_ao2_find_w_OBJ_KEY(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
- {
- int i;
- int num;
- struct test_obj *obj;
- for (num = 75; num--;) {
- i = ast_random() % limit; /* find a random object */
- obj = ao2_find(look_in, &i, OBJ_KEY);
- if (!obj) {
- ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_KEY flag failed.\n", i);
- res = AST_TEST_FAIL;
- } else {
- if (obj->i != i) {
- ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
- res = AST_TEST_FAIL;
- }
- ao2_t_ref(obj, -1, "test");
- }
- }
- return res;
- }
- /*!
- * \internal
- * \brief Test ao2_find with OBJ_PARTIAL_KEY.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param look_in Container to search.
- * \param limit Container contains objects 0 - (limit - 1).
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_ao2_find_w_OBJ_PARTIAL_KEY(int res, struct ao2_container *look_in, int limit, struct ast_test *test)
- {
- int i;
- int num;
- struct test_obj *obj;
- /* Set partial match to find exactly. */
- partial_key_match_range = 0;
- for (num = 100; num--;) {
- i = ast_random() % limit; /* find a random object */
- obj = ao2_find(look_in, &i, OBJ_PARTIAL_KEY);
- if (!obj) {
- ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_PARTIAL_KEY flag failed.\n", i);
- res = AST_TEST_FAIL;
- } else {
- if (obj->i != i) {
- ast_test_status_update(test, "object %d does not match %d\n", obj->i, i);
- res = AST_TEST_FAIL;
- }
- ao2_t_ref(obj, -1, "test");
- }
- }
- return res;
- }
- static int astobj2_test_1_helper(int tst_num, enum test_container_type type, int use_sort, unsigned int lim, struct ast_test *test)
- {
- const char *c_type;
- struct ao2_container *c1;
- struct ao2_container *c2;
- struct ao2_iterator it;
- struct ao2_iterator *mult_it;
- struct test_obj *obj;
- int n_buckets = 0;
- int increment = 0;
- int destructor_count = 0;
- int num;
- int res = AST_TEST_PASS;
- c_type = test_container2str(type);
- ast_test_status_update(test, "Test %d, %s containers (%s).\n",
- tst_num, c_type, use_sort ? "sorted" : "non-sorted");
- c1 = NULL;
- switch (type) {
- case TEST_CONTAINER_LIST:
- /* Lists just have one bucket. */
- n_buckets = 1;
- c1 = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
- use_sort ? test_sort_cb : NULL, test_cmp_cb, "test");
- break;
- case TEST_CONTAINER_HASH:
- n_buckets = (ast_random() % ((lim / 4) + 1)) + 1;
- c1 = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, n_buckets,
- test_hash_cb, use_sort ? test_sort_cb : NULL, test_cmp_cb, "test");
- break;
- case TEST_CONTAINER_RBTREE:
- /* RBTrees just have one bucket. */
- n_buckets = 1;
- c1 = ao2_t_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
- test_sort_cb, test_cmp_cb, "test");
- break;
- }
- c2 = ao2_t_container_alloc(1, NULL, NULL, "test");
- if (!c1 || !c2) {
- ast_test_status_update(test, "ao2_container_alloc failed.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- /* Create objects and link into container */
- for (num = 0; num < lim; ++num) {
- if (!(obj = ao2_t_alloc(sizeof(struct test_obj), test_obj_destructor, "making zombies"))) {
- ast_test_status_update(test, "ao2_alloc failed.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- ++destructor_count;
- obj->destructor_count = &destructor_count;
- obj->i = num;
- ao2_link(c1, obj);
- ao2_t_ref(obj, -1, "test");
- if (ao2_container_check(c1, 0)) {
- ast_test_status_update(test, "container integrity check failed linking obj num:%d\n", num);
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- if (ao2_container_count(c1) != num + 1) {
- ast_test_status_update(test, "container did not link correctly\n");
- res = AST_TEST_FAIL;
- }
- }
- ast_test_status_update(test, "%s container created: buckets: %d, items: %u\n",
- c_type, n_buckets, lim);
- /* Testing ao2_container_clone */
- res = test_container_clone(res, c1, test);
- /* Testing ao2_find with no flags */
- res = test_ao2_find_w_no_flags(res, c1, lim, test);
- /* Testing ao2_find with OBJ_POINTER */
- res = test_ao2_find_w_OBJ_POINTER(res, c1, lim, test);
- /* Testing ao2_find with OBJ_KEY */
- res = test_ao2_find_w_OBJ_KEY(res, c1, lim, test);
- /* Testing ao2_find with OBJ_PARTIAL_KEY */
- res = test_ao2_find_w_OBJ_PARTIAL_KEY(res, c1, lim, test);
- /* Test Callback with no flags. */
- increment = 0;
- ao2_t_callback(c1, 0, increment_cb, &increment, "test callback");
- if (increment != lim) {
- ast_test_status_update(test, "callback with no flags failed. Increment is %d\n", increment);
- res = AST_TEST_FAIL;
- }
- /* Test Callback with OBJ_NODATA. This should do nothing different than with no flags here. */
- increment = 0;
- ao2_t_callback(c1, OBJ_NODATA, increment_cb, &increment, "test callback");
- if (increment != lim) {
- ast_test_status_update(test, "callback with OBJ_NODATA failed. Increment is %d\n", increment);
- res = AST_TEST_FAIL;
- }
- /* Test OBJ_MULTIPLE with OBJ_UNLINK, add items back afterwards */
- num = lim < 25 ? lim : 25;
- if (!(mult_it = ao2_t_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK, multiple_cb, &num, "test multiple"))) {
- ast_test_status_update(test, "OBJ_MULTIPLE with OBJ_UNLINK test failed.\n");
- res = AST_TEST_FAIL;
- } else {
- /* make sure num items unlinked is as expected */
- if ((lim - ao2_container_count(c1)) != num) {
- ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK test failed, did not unlink correct number of objects.\n");
- res = AST_TEST_FAIL;
- }
- if (ao2_container_check(c1, 0)) {
- ast_test_status_update(test, "container integrity check failed\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- /* link what was unlinked back into c1 */
- while ((obj = ao2_t_iterator_next(mult_it, "test"))) {
- ao2_t_link(c1, obj, "test");
- ao2_t_ref(obj, -1, "test"); /* remove ref from iterator */
- }
- ao2_iterator_destroy(mult_it);
- if (ao2_container_check(c1, 0)) {
- ast_test_status_update(test, "container integrity check failed\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- }
- /* Test OBJ_MULTIPLE without unlink and iterate the returned container */
- num = 5;
- if (!(mult_it = ao2_t_callback(c1, OBJ_MULTIPLE, multiple_cb, &num, "test multiple"))) {
- ast_test_status_update(test, "OBJ_MULTIPLE without OBJ_UNLINK test failed.\n");
- res = AST_TEST_FAIL;
- } else {
- while ((obj = ao2_t_iterator_next(mult_it, "test"))) {
- ao2_t_ref(obj, -1, "test"); /* remove ref from iterator */
- }
- ao2_iterator_destroy(mult_it);
- }
- /* Test OBJ_MULTIPLE without unlink and no iterating */
- num = 5;
- if (!(mult_it = ao2_t_callback(c1, OBJ_MULTIPLE, multiple_cb, &num, "test multiple"))) {
- ast_test_status_update(test, "OBJ_MULTIPLE with no OBJ_UNLINK and no iterating failed.\n");
- res = AST_TEST_FAIL;
- } else {
- ao2_iterator_destroy(mult_it);
- }
- /* Is the container count what we expect after all the finds and unlinks? */
- if (ao2_container_count(c1) != lim) {
- ast_test_status_update(test, "container count does not match what is expected after ao2_find tests.\n");
- res = AST_TEST_FAIL;
- }
- /* Testing iterator. Unlink a single object and break. do not add item back */
- it = ao2_iterator_init(c1, 0);
- num = ast_random() % lim; /* remove a random object */
- if (!num) {
- /*
- * Well we cannot remove object zero because of test with
- * all_but_one_cb later.
- */
- num = 1;
- }
- while ((obj = ao2_t_iterator_next(&it, "test"))) {
- if (obj->i == num) {
- ao2_t_unlink(c1, obj, "test");
- ao2_t_ref(obj, -1, "test");
- break;
- }
- ao2_t_ref(obj, -1, "test");
- }
- ao2_iterator_destroy(&it);
- /* Is the container count what we expect after removing a single item? */
- if (ao2_container_count(c1) != (lim - 1)) {
- ast_test_status_update(test, "unlink during iterator failed. Number %d was not removed.\n", num);
- res = AST_TEST_FAIL;
- }
- if (ao2_container_check(c1, 0)) {
- ast_test_status_update(test, "container integrity check failed\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- /* Test unlink all with OBJ_MULTIPLE, leave a single object for the container to destroy */
- ao2_t_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, all_but_one_cb, NULL, "test multiple");
- /* check to make sure all test_obj destructors were called except for 1 */
- if (destructor_count != 1) {
- ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA failed. destructor count %d\n", destructor_count);
- res = AST_TEST_FAIL;
- }
- if (ao2_container_check(c1, 0)) {
- ast_test_status_update(test, "container integrity check failed\n");
- res = AST_TEST_FAIL;
- }
- #if defined(TEST_CONTAINER_DEBUG_DUMP)
- ao2_container_dump(c1, 0, "test_1 c1", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
- ao2_container_stats(c1, 0, "test_1 c1", (void *) test, (ao2_prnt_fn *) ast_test_debug);
- #endif /* defined(TEST_CONTAINER_DEBUG_DUMP) */
- cleanup:
- /* destroy containers */
- if (c1) {
- ao2_t_ref(c1, -1, "bye c1");
- }
- if (c2) {
- ao2_t_ref(c2, -1, "bye c2");
- }
- if (destructor_count > 0) {
- ast_test_status_update(test, "all destructors were not called, destructor count is %d\n", destructor_count);
- res = AST_TEST_FAIL;
- } else if (destructor_count < 0) {
- ast_test_status_update(test, "Destructor was called too many times, destructor count is %d\n", destructor_count);
- res = AST_TEST_FAIL;
- }
- return res;
- }
- AST_TEST_DEFINE(astobj2_test_1)
- {
- int res = AST_TEST_PASS;
- switch (cmd) {
- case TEST_INIT:
- info->name = "astobj2_test1";
- info->category = "/main/astobj2/";
- info->summary = "Test ao2 objects, containers, callbacks, and iterators";
- info->description =
- "Builds ao2_containers with various item numbers, bucket sizes, cmp and hash "
- "functions. Runs a series of tests to manipulate the container using callbacks "
- "and iterators. Verifies expected behavior.";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- /* Test number, container_type, use_sort, number of objects. */
- if ((res = astobj2_test_1_helper(1, TEST_CONTAINER_LIST, 0, 50, test)) == AST_TEST_FAIL) {
- return res;
- }
- if ((res = astobj2_test_1_helper(2, TEST_CONTAINER_LIST, 1, 50, test)) == AST_TEST_FAIL) {
- return res;
- }
- if ((res = astobj2_test_1_helper(3, TEST_CONTAINER_HASH, 0, 1000, test)) == AST_TEST_FAIL) {
- return res;
- }
- if ((res = astobj2_test_1_helper(4, TEST_CONTAINER_HASH, 1, 1000, test)) == AST_TEST_FAIL) {
- return res;
- }
- if ((res = astobj2_test_1_helper(4, TEST_CONTAINER_RBTREE, 1, 1000, test)) == AST_TEST_FAIL) {
- return res;
- }
- return res;
- }
- AST_TEST_DEFINE(astobj2_test_2)
- {
- int res = AST_TEST_PASS;
- struct ao2_container *c;
- struct ao2_iterator i;
- struct test_obj *obj;
- int num;
- static const int NUM_OBJS = 5;
- int destructor_count = NUM_OBJS;
- struct test_obj tmp_obj = { 0, };
- switch (cmd) {
- case TEST_INIT:
- info->name = "astobj2_test2";
- info->category = "/main/astobj2/";
- info->summary = "Test a certain scenario using ao2 iterators";
- info->description =
- "This test is aimed at testing for a specific regression that occurred. "
- "Add some objects into a container. Mix finds and iteration and make "
- "sure that the iterator still sees all objects.";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- c = ao2_container_alloc(1, NULL, test_cmp_cb);
- if (!c) {
- ast_test_status_update(test, "ao2_container_alloc failed.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- for (num = 1; num <= NUM_OBJS; num++) {
- if (!(obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor))) {
- ast_test_status_update(test, "ao2_alloc failed.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- obj->destructor_count = &destructor_count;
- obj->i = num;
- ao2_link(c, obj);
- ao2_ref(obj, -1);
- if (ao2_container_count(c) != num) {
- ast_test_status_update(test, "container did not link correctly\n");
- res = AST_TEST_FAIL;
- }
- }
- if (ao2_container_check(c, 0)) {
- ast_test_status_update(test, "container integrity check failed\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- /*
- * Iteration take 1. Just make sure we see all NUM_OBJS objects.
- */
- num = 0;
- i = ao2_iterator_init(c, 0);
- while ((obj = ao2_iterator_next(&i))) {
- num++;
- ao2_ref(obj, -1);
- }
- ao2_iterator_destroy(&i);
- if (num != NUM_OBJS) {
- ast_test_status_update(test, "iterate take 1, expected '%d', only saw '%d' objects\n",
- NUM_OBJS, num);
- res = AST_TEST_FAIL;
- }
- /*
- * Iteration take 2. Do a find for the last object, then iterate and make
- * sure we find all NUM_OBJS objects.
- */
- tmp_obj.i = NUM_OBJS;
- obj = ao2_find(c, &tmp_obj, OBJ_POINTER);
- if (!obj) {
- ast_test_status_update(test, "ao2_find() failed.\n");
- res = AST_TEST_FAIL;
- } else {
- ao2_ref(obj, -1);
- }
- num = 0;
- i = ao2_iterator_init(c, 0);
- while ((obj = ao2_iterator_next(&i))) {
- num++;
- ao2_ref(obj, -1);
- }
- ao2_iterator_destroy(&i);
- if (num != NUM_OBJS) {
- ast_test_status_update(test, "iterate take 2, expected '%d', only saw '%d' objects\n",
- NUM_OBJS, num);
- res = AST_TEST_FAIL;
- }
- /*
- * Iteration take 3. Do a find for an object while in the middle
- * of iterating;
- */
- num = 0;
- i = ao2_iterator_init(c, 0);
- while ((obj = ao2_iterator_next(&i))) {
- if (num == 1) {
- struct test_obj *obj2;
- tmp_obj.i = NUM_OBJS - 1;
- obj2 = ao2_find(c, &tmp_obj, OBJ_POINTER);
- if (!obj2) {
- ast_test_status_update(test, "ao2_find() failed.\n");
- res = AST_TEST_FAIL;
- } else {
- ao2_ref(obj2, -1);
- }
- }
- num++;
- ao2_ref(obj, -1);
- }
- ao2_iterator_destroy(&i);
- if (num != NUM_OBJS) {
- ast_test_status_update(test, "iterate take 3, expected '%d', only saw '%d' objects\n",
- NUM_OBJS, num);
- res = AST_TEST_FAIL;
- }
- cleanup:
- if (c) {
- ao2_ref(c, -1);
- }
- return res;
- }
- static AO2_GLOBAL_OBJ_STATIC(astobj2_holder);
- AST_TEST_DEFINE(astobj2_test_3)
- {
- int res = AST_TEST_PASS;
- int destructor_count = 0;
- int num_objects = 0;
- struct test_obj *obj = NULL;
- struct test_obj *obj2 = NULL;
- struct test_obj *obj3 = NULL;
- switch (cmd) {
- case TEST_INIT:
- info->name = "astobj2_test3";
- info->category = "/main/astobj2/";
- info->summary = "Test global ao2 holder";
- info->description =
- "This test is to see if the global ao2 holder works as intended.";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- /* Put an object in the holder */
- obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
- if (!obj) {
- ast_test_status_update(test, "ao2_alloc failed.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- obj->destructor_count = &destructor_count;
- obj->i = ++num_objects;
- obj2 = ao2_t_global_obj_replace(astobj2_holder, obj, "Save object in the holder");
- if (obj2) {
- ast_test_status_update(test, "Returned object not expected.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- /* Save object for next check. */
- obj3 = obj;
- /* Replace an object in the holder */
- obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
- if (!obj) {
- ast_test_status_update(test, "ao2_alloc failed.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- obj->destructor_count = &destructor_count;
- obj->i = ++num_objects;
- obj2 = ao2_t_global_obj_replace(astobj2_holder, obj, "Replace object in the holder");
- if (!obj2) {
- ast_test_status_update(test, "Expected an object.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- if (obj2 != obj3) {
- ast_test_status_update(test, "Replaced object not expected object.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- ao2_ref(obj3, -1);
- obj3 = NULL;
- ao2_ref(obj2, -1);
- obj2 = NULL;
- ao2_ref(obj, -1);
- /* Replace with unref of an object in the holder */
- obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
- if (!obj) {
- ast_test_status_update(test, "ao2_alloc failed.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- obj->destructor_count = &destructor_count;
- obj->i = ++num_objects;
- if (!ao2_t_global_obj_replace_unref(astobj2_holder, obj, "Replace w/ unref object in the holder")) {
- ast_test_status_update(test, "Expected an object to be replaced.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- /* Save object for next check. */
- obj3 = obj;
- /* Get reference to held object. */
- obj = ao2_t_global_obj_ref(astobj2_holder, "Get a held object reference");
- if (!obj) {
- ast_test_status_update(test, "Expected an object.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- if (obj != obj3) {
- ast_test_status_update(test, "Referenced object not expected object.\n");
- res = AST_TEST_FAIL;
- goto cleanup;
- }
- ao2_ref(obj3, -1);
- obj3 = NULL;
- ao2_ref(obj, -1);
- obj = NULL;
- /* Release the object in the global holder. */
- ao2_t_global_obj_release(astobj2_holder, "Check release all objects");
- destructor_count += num_objects;
- if (0 < destructor_count) {
- ast_test_status_update(test,
- "all destructors were not called, destructor count is %d\n",
- destructor_count);
- res = AST_TEST_FAIL;
- } else if (destructor_count < 0) {
- ast_test_status_update(test,
- "Destructor was called too many times, destructor count is %d\n",
- destructor_count);
- res = AST_TEST_FAIL;
- }
- cleanup:
- if (obj) {
- ao2_t_ref(obj, -1, "Test cleanup external object 1");
- }
- if (obj2) {
- ao2_t_ref(obj2, -1, "Test cleanup external object 2");
- }
- if (obj3) {
- ao2_t_ref(obj3, -1, "Test cleanup external object 3");
- }
- ao2_t_global_obj_release(astobj2_holder, "Test cleanup holder");
- return res;
- }
- /*!
- * \internal
- * \brief Make a nonsorted container for astobj2 testing.
- * \since 12.0.0
- *
- * \param type Container type to create.
- * \param options Container options
- *
- * \retval container on success.
- * \retval NULL on error.
- */
- static struct ao2_container *test_make_nonsorted(enum test_container_type type, int options)
- {
- struct ao2_container *container;
- container = NULL;
- switch (type) {
- case TEST_CONTAINER_LIST:
- container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, options,
- NULL, test_cmp_cb);
- break;
- case TEST_CONTAINER_HASH:
- container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, options, 5,
- test_hash_cb, NULL, test_cmp_cb);
- break;
- case TEST_CONTAINER_RBTREE:
- /* Container type must be sorted. */
- break;
- }
- return container;
- }
- /*!
- * \internal
- * \brief Make a sorted container for astobj2 testing.
- * \since 12.0.0
- *
- * \param type Container type to create.
- * \param options Container options
- *
- * \retval container on success.
- * \retval NULL on error.
- */
- static struct ao2_container *test_make_sorted(enum test_container_type type, int options)
- {
- struct ao2_container *container;
- container = NULL;
- switch (type) {
- case TEST_CONTAINER_LIST:
- container = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, options,
- test_sort_cb, test_cmp_cb, "test");
- break;
- case TEST_CONTAINER_HASH:
- container = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, options, 5,
- test_hash_cb, test_sort_cb, test_cmp_cb, "test");
- break;
- case TEST_CONTAINER_RBTREE:
- container = ao2_t_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, options,
- test_sort_cb, test_cmp_cb, "test");
- break;
- }
- return container;
- }
- /*!
- * \internal
- * \brief Insert the given test vector into the given container.
- * \since 12.0.0
- *
- * \note The given test vector must not have any duplicates.
- *
- * \param container Container to insert the test vector.
- * \param destroy_counter What to increment when the object is destroyed.
- * \param vector Test vector to insert.
- * \param count Number of objects in the vector.
- * \param prefix Test output prefix string.
- * \param test Test output controller.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
- static int insert_test_vector(struct ao2_container *container, int *destroy_counter, const int *vector, int count, const char *prefix, struct ast_test *test)
- {
- int idx;
- struct test_obj *obj;
- for (idx = 0; idx < count; ++idx) {
- obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
- if (!obj) {
- ast_test_status_update(test, "%s: ao2_alloc failed.\n", prefix);
- return -1;
- }
- if (destroy_counter) {
- /* This object ultimately needs to be destroyed. */
- ++*destroy_counter;
- }
- obj->destructor_count = destroy_counter;
- obj->i = vector[idx];
- ao2_link(container, obj);
- ao2_t_ref(obj, -1, "test");
- if (ao2_container_check(container, 0)) {
- ast_test_status_update(test, "%s: Container integrity check failed linking vector[%d]:%d\n",
- prefix, idx, vector[idx]);
- return -1;
- }
- if (ao2_container_count(container) != idx + 1) {
- ast_test_status_update(test,
- "%s: Unexpected container count. Expected:%d Got:%d\n",
- prefix, idx + 1, ao2_container_count(container));
- return -1;
- }
- }
- return 0;
- }
- /*!
- * \internal
- * \brief Insert duplicates of number into the given container.
- * \since 12.0.0
- *
- * \note The given container must not already have the number in it.
- *
- * \param container Container to insert the duplicates.
- * \param destroy_counter What to increment when the object is destroyed.
- * \param number Number of object to duplicate.
- * \param prefix Test output prefix string.
- * \param test Test output controller.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
- static int insert_test_duplicates(struct ao2_container *container, int *destroy_counter, int number, const char *prefix, struct ast_test *test)
- {
- int count;
- struct test_obj *obj;
- struct test_obj *obj_dup;
- /* Check if object already exists in the container. */
- obj = ao2_find(container, &number, OBJ_KEY);
- if (obj) {
- ast_test_status_update(test, "%s: Object %d already exists.\n", prefix, number);
- ao2_t_ref(obj, -1, "test");
- return -1;
- }
- /* Add three duplicate keyed objects. */
- obj_dup = NULL;
- for (count = 0; count < 4; ++count) {
- obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
- if (!obj) {
- ast_test_status_update(test, "%s: ao2_alloc failed.\n", prefix);
- if (obj_dup) {
- ao2_t_ref(obj_dup, -1, "test");
- }
- return -1;
- }
- if (destroy_counter) {
- /* This object ultimately needs to be destroyed. */
- ++*destroy_counter;
- }
- obj->destructor_count = destroy_counter;
- obj->i = number;
- obj->dup_number = count;
- ao2_link(container, obj);
- if (count == 2) {
- /* Duplicate this object. */
- obj_dup = obj;
- } else {
- ao2_t_ref(obj, -1, "test");
- }
- if (ao2_container_check(container, 0)) {
- ast_test_status_update(test, "%s: Container integrity check failed linking num:%d dup:%d\n",
- prefix, number, count);
- if (obj_dup) {
- ao2_t_ref(obj_dup, -1, "test");
- }
- return -1;
- }
- }
- /* Add the duplicate object. */
- ao2_link(container, obj_dup);
- ao2_t_ref(obj_dup, -1, "test");
- if (ao2_container_check(container, 0)) {
- ast_test_status_update(test, "%s: Container integrity check failed linking obj_dup\n",
- prefix);
- return -1;
- }
- return 0;
- }
- /*!
- * \internal
- * \brief Iterate over the container and compare the objects with the given vector.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param container Container to iterate.
- * \param flags Flags controlling the iteration.
- * \param vector Expected vector to find.
- * \param count Number of objects in the vector.
- * \param prefix Test output prefix string.
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_ao2_iteration(int res, struct ao2_container *container,
- enum ao2_iterator_flags flags,
- const int *vector, int count, const char *prefix, struct ast_test *test)
- {
- struct ao2_iterator iter;
- struct test_obj *obj;
- int idx;
- if (ao2_container_count(container) != count) {
- ast_test_status_update(test, "%s: Container count doesn't match vector count.\n",
- prefix);
- res = AST_TEST_FAIL;
- }
- iter = ao2_iterator_init(container, flags);
- /* Check iterated objects against the given vector. */
- for (idx = 0; idx < count; ++idx) {
- obj = ao2_iterator_next(&iter);
- if (!obj) {
- ast_test_status_update(test, "%s: Too few objects found.\n", prefix);
- res = AST_TEST_FAIL;
- break;
- }
- if (vector[idx] != obj->i) {
- ast_test_status_update(test, "%s: Object %d != vector[%d] %d.\n",
- prefix, obj->i, idx, vector[idx]);
- res = AST_TEST_FAIL;
- }
- ao2_ref(obj, -1); /* remove ref from iterator */
- }
- obj = ao2_iterator_next(&iter);
- if (obj) {
- ast_test_status_update(test, "%s: Too many objects found. Object %d\n",
- prefix, obj->i);
- ao2_ref(obj, -1); /* remove ref from iterator */
- res = AST_TEST_FAIL;
- }
- ao2_iterator_destroy(&iter);
- return res;
- }
- /*!
- * \internal
- * \brief Run an ao2_callback() and compare the returned vector with the given vector.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param container Container to traverse.
- * \param flags Callback flags controlling the traversal.
- * \param cmp_fn Compare function to select objects.
- * \param arg Optional argument.
- * \param vector Expected vector to find.
- * \param count Number of objects in the vector.
- * \param prefix Test output prefix string.
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_ao2_callback_traversal(int res, struct ao2_container *container,
- enum search_flags flags, ao2_callback_fn *cmp_fn, void *arg,
- const int *vector, int count, const char *prefix, struct ast_test *test)
- {
- struct ao2_iterator *mult_iter;
- struct test_obj *obj;
- int idx;
- mult_iter = ao2_callback(container, flags | OBJ_MULTIPLE, cmp_fn, arg);
- if (!mult_iter) {
- ast_test_status_update(test, "%s: Did not return iterator.\n", prefix);
- return AST_TEST_FAIL;
- }
- /* Check matching objects against the given vector. */
- for (idx = 0; idx < count; ++idx) {
- obj = ao2_iterator_next(mult_iter);
- if (!obj) {
- ast_test_status_update(test, "%s: Too few objects found.\n", prefix);
- res = AST_TEST_FAIL;
- break;
- }
- if (vector[idx] != obj->i) {
- ast_test_status_update(test, "%s: Object %d != vector[%d] %d.\n",
- prefix, obj->i, idx, vector[idx]);
- res = AST_TEST_FAIL;
- }
- ao2_ref(obj, -1); /* remove ref from iterator */
- }
- obj = ao2_iterator_next(mult_iter);
- if (obj) {
- ast_test_status_update(test, "%s: Too many objects found. Object %d\n",
- prefix, obj->i);
- ao2_ref(obj, -1); /* remove ref from iterator */
- res = AST_TEST_FAIL;
- }
- ao2_iterator_destroy(mult_iter);
- return res;
- }
- /*!
- * \internal
- * \brief Run an ao2_find() for duplicates and compare the returned vector with the given vector.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param container Container to traverse.
- * \param flags Callback flags controlling the traversal.
- * \param number Number of object to find all duplicates.
- * \param vector Expected vector to find.
- * \param count Number of objects in the vector.
- * \param prefix Test output prefix string.
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_expected_duplicates(int res, struct ao2_container *container,
- enum search_flags flags, int number,
- const int *vector, int count, const char *prefix, struct ast_test *test)
- {
- struct ao2_iterator *mult_iter;
- struct test_obj *obj;
- int idx;
- mult_iter = ao2_find(container, &number, flags | OBJ_MULTIPLE | OBJ_KEY);
- if (!mult_iter) {
- ast_test_status_update(test, "%s: Did not return iterator.\n", prefix);
- return AST_TEST_FAIL;
- }
- /* Check matching objects against the given vector. */
- for (idx = 0; idx < count; ++idx) {
- obj = ao2_iterator_next(mult_iter);
- if (!obj) {
- ast_test_status_update(test, "%s: Too few objects found.\n", prefix);
- res = AST_TEST_FAIL;
- break;
- }
- if (number != obj->i) {
- ast_test_status_update(test, "%s: Object %d != %d.\n",
- prefix, obj->i, number);
- res = AST_TEST_FAIL;
- }
- if (vector[idx] != obj->dup_number) {
- ast_test_status_update(test, "%s: Object dup id %d != vector[%d] %d.\n",
- prefix, obj->dup_number, idx, vector[idx]);
- res = AST_TEST_FAIL;
- }
- ao2_ref(obj, -1); /* remove ref from iterator */
- }
- obj = ao2_iterator_next(mult_iter);
- if (obj) {
- ast_test_status_update(test,
- "%s: Too many objects found. Object %d, dup id %d\n",
- prefix, obj->i, obj->dup_number);
- ao2_ref(obj, -1); /* remove ref from iterator */
- res = AST_TEST_FAIL;
- }
- ao2_iterator_destroy(mult_iter);
- return res;
- }
- /*!
- * \internal
- * \brief Test nonsorted container traversal.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param tst_num Test number.
- * \param type Container type to test.
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_traversal_nonsorted(int res, int tst_num, enum test_container_type type, struct ast_test *test)
- {
- struct ao2_container *c1;
- struct ao2_container *c2 = NULL;
- int partial;
- int destructor_count = 0;
- /*! Container object insertion vector. */
- static const int test_initial[] = {
- 1, 0, 2, 6, 4, 7, 5, 3, 9, 8
- };
- /*! Container object insertion vector reversed. */
- static const int test_reverse[] = {
- 8, 9, 3, 5, 7, 4, 6, 2, 0, 1
- };
- static const int test_list_partial_forward[] = {
- 6, 7, 5
- };
- static const int test_list_partial_backward[] = {
- 5, 7, 6
- };
- /* The hash orders assume that there are 5 buckets. */
- static const int test_hash_end_forward[] = {
- 0, 5, 1, 6, 2, 7, 3, 8, 4, 9
- };
- static const int test_hash_end_backward[] = {
- 9, 4, 8, 3, 7, 2, 6, 1, 5, 0
- };
- static const int test_hash_begin_forward[] = {
- 5, 0, 6, 1, 7, 2, 8, 3, 9, 4
- };
- static const int test_hash_begin_backward[] = {
- 4, 9, 3, 8, 2, 7, 1, 6, 0, 5
- };
- static const int test_hash_partial_forward[] = {
- 5, 6, 7
- };
- static const int test_hash_partial_backward[] = {
- 7, 6, 5
- };
- ast_test_status_update(test, "Test %d, %s containers.\n",
- tst_num, test_container2str(type));
- /* Create container that inserts objects at the end. */
- c1 = test_make_nonsorted(type, 0);
- if (!c1) {
- ast_test_status_update(test, "Container c1 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- /* Create container that inserts objects at the beginning. */
- c2 = test_make_nonsorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN);
- if (!c2) {
- ast_test_status_update(test, "Container c2 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- /* Check container iteration directions */
- switch (type) {
- case TEST_CONTAINER_LIST:
- res = test_ao2_iteration(res, c1, 0,
- test_initial, ARRAY_LEN(test_initial),
- "Iteration (ascending, insert end)", test);
- res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
- test_reverse, ARRAY_LEN(test_reverse),
- "Iteration (descending, insert end)", test);
- res = test_ao2_iteration(res, c2, 0,
- test_reverse, ARRAY_LEN(test_reverse),
- "Iteration (ascending, insert begin)", test);
- res = test_ao2_iteration(res, c2, AO2_ITERATOR_DESCENDING,
- test_initial, ARRAY_LEN(test_initial),
- "Iteration (descending, insert begin)", test);
- break;
- case TEST_CONTAINER_HASH:
- res = test_ao2_iteration(res, c1, 0,
- test_hash_end_forward, ARRAY_LEN(test_hash_end_forward),
- "Iteration (ascending, insert end)", test);
- res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
- test_hash_end_backward, ARRAY_LEN(test_hash_end_backward),
- "Iteration (descending, insert end)", test);
- res = test_ao2_iteration(res, c2, 0,
- test_hash_begin_forward, ARRAY_LEN(test_hash_begin_forward),
- "Iteration (ascending, insert begin)", test);
- res = test_ao2_iteration(res, c2, AO2_ITERATOR_DESCENDING,
- test_hash_begin_backward, ARRAY_LEN(test_hash_begin_backward),
- "Iteration (descending, insert begin)", test);
- break;
- case TEST_CONTAINER_RBTREE:
- break;
- }
- /* Check container traversal directions */
- switch (type) {
- case TEST_CONTAINER_LIST:
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
- test_initial, ARRAY_LEN(test_initial),
- "Traversal (ascending, insert end)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
- test_reverse, ARRAY_LEN(test_reverse),
- "Traversal (descending, insert end)", test);
- res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_ASCENDING, NULL, NULL,
- test_reverse, ARRAY_LEN(test_reverse),
- "Traversal (ascending, insert begin)", test);
- res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_DESCENDING, NULL, NULL,
- test_initial, ARRAY_LEN(test_initial),
- "Traversal (descending, insert begin)", test);
- break;
- case TEST_CONTAINER_HASH:
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
- test_hash_end_forward, ARRAY_LEN(test_hash_end_forward),
- "Traversal (ascending, insert end)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
- test_hash_end_backward, ARRAY_LEN(test_hash_end_backward),
- "Traversal (descending, insert end)", test);
- res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_ASCENDING, NULL, NULL,
- test_hash_begin_forward, ARRAY_LEN(test_hash_begin_forward),
- "Traversal (ascending, insert begin)", test);
- res = test_ao2_callback_traversal(res, c2, OBJ_ORDER_DESCENDING, NULL, NULL,
- test_hash_begin_backward, ARRAY_LEN(test_hash_begin_backward),
- "Traversal (descending, insert begin)", test);
- break;
- case TEST_CONTAINER_RBTREE:
- break;
- }
- /* Check traversal with OBJ_PARTIAL_KEY search range. */
- partial = 6;
- partial_key_match_range = 1;
- switch (type) {
- case TEST_CONTAINER_LIST:
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
- test_cmp_cb, &partial,
- test_list_partial_forward, ARRAY_LEN(test_list_partial_forward),
- "Traversal OBJ_PARTIAL_KEY (ascending)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
- test_cmp_cb, &partial,
- test_list_partial_backward, ARRAY_LEN(test_list_partial_backward),
- "Traversal OBJ_PARTIAL_KEY (descending)", test);
- break;
- case TEST_CONTAINER_HASH:
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
- test_cmp_cb, &partial,
- test_hash_partial_forward, ARRAY_LEN(test_hash_partial_forward),
- "Traversal OBJ_PARTIAL_KEY (ascending)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
- test_cmp_cb, &partial,
- test_hash_partial_backward, ARRAY_LEN(test_hash_partial_backward),
- "Traversal OBJ_PARTIAL_KEY (descending)", test);
- break;
- case TEST_CONTAINER_RBTREE:
- break;
- }
- test_cleanup:
- /* destroy containers */
- if (c1) {
- ao2_t_ref(c1, -1, "bye c1");
- }
- if (c2) {
- ao2_t_ref(c2, -1, "bye c2");
- }
- if (destructor_count > 0) {
- ast_test_status_update(test,
- "all destructors were not called, destructor count is %d\n",
- destructor_count);
- res = AST_TEST_FAIL;
- } else if (destructor_count < 0) {
- ast_test_status_update(test,
- "Destructor was called too many times, destructor count is %d\n",
- destructor_count);
- res = AST_TEST_FAIL;
- }
- return res;
- }
- /*!
- * \internal
- * \brief Test sorted container traversal.
- * \since 12.0.0
- *
- * \param res Passed in enum ast_test_result_state.
- * \param tst_num Test number.
- * \param type Container type to test.
- * \param test Test output controller.
- *
- * \return enum ast_test_result_state
- */
- static int test_traversal_sorted(int res, int tst_num, enum test_container_type type, struct ast_test *test)
- {
- struct ao2_container *c1;
- struct ao2_container *c2 = NULL;
- int partial;
- int destructor_count = 0;
- int duplicate_number = 100;
- /*! Container object insertion vector. */
- static const int test_initial[] = {
- 1, 0, 2, 6, 4, 7, 5, 3, 9, 8
- };
- /*! Container forward traversal/iteration. */
- static const int test_forward[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
- };
- /*! Container backward traversal/iteration. */
- static const int test_backward[] = {
- 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
- };
- static const int test_partial_forward[] = {
- 5, 6, 7
- };
- static const int test_partial_backward[] = {
- 7, 6, 5
- };
- /* The hash orders assume that there are 5 buckets. */
- static const int test_hash_forward[] = {
- 0, 5, 1, 6, 2, 7, 3, 8, 4, 9
- };
- static const int test_hash_backward[] = {
- 9, 4, 8, 3, 7, 2, 6, 1, 5, 0
- };
- static const int test_hash_partial_forward[] = {
- 5, 6, 7
- };
- static const int test_hash_partial_backward[] = {
- 7, 6, 5
- };
- /* Duplicate identifier order */
- static const int test_dup_allow_forward[] = {
- 0, 1, 2, 3, 2
- };
- static const int test_dup_allow_backward[] = {
- 2, 3, 2, 1, 0
- };
- static const int test_dup_reject[] = {
- 0
- };
- static const int test_dup_obj_reject_forward[] = {
- 0, 1, 2, 3
- };
- static const int test_dup_obj_reject_backward[] = {
- 3, 2, 1, 0
- };
- static const int test_dup_replace[] = {
- 2
- };
- ast_test_status_update(test, "Test %d, %s containers.\n",
- tst_num, test_container2str(type));
- /* Create container that inserts duplicate objects after matching objects. */
- c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW);
- if (!c1) {
- ast_test_status_update(test, "Container c1 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_ALLOW)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- /* Create container that inserts duplicate objects before matching objects. */
- c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW);
- if (!c2) {
- ast_test_status_update(test, "Container c2 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_ALLOW)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- #if defined(TEST_CONTAINER_DEBUG_DUMP)
- ao2_container_dump(c1, 0, "c1(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
- ao2_container_stats(c1, 0, "c1(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug);
- ao2_container_dump(c2, 0, "c2(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
- ao2_container_stats(c2, 0, "c2(DUPS_ALLOW)", (void *) test, (ao2_prnt_fn *) ast_test_debug);
- #endif /* defined(TEST_CONTAINER_DEBUG_DUMP) */
- /* Check container iteration directions */
- switch (type) {
- case TEST_CONTAINER_RBTREE:
- case TEST_CONTAINER_LIST:
- res = test_ao2_iteration(res, c1, 0,
- test_forward, ARRAY_LEN(test_forward),
- "Iteration (ascending)", test);
- res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
- test_backward, ARRAY_LEN(test_backward),
- "Iteration (descending)", test);
- break;
- case TEST_CONTAINER_HASH:
- res = test_ao2_iteration(res, c1, 0,
- test_hash_forward, ARRAY_LEN(test_hash_forward),
- "Iteration (ascending)", test);
- res = test_ao2_iteration(res, c1, AO2_ITERATOR_DESCENDING,
- test_hash_backward, ARRAY_LEN(test_hash_backward),
- "Iteration (descending)", test);
- break;
- }
- /* Check container traversal directions */
- switch (type) {
- case TEST_CONTAINER_RBTREE:
- case TEST_CONTAINER_LIST:
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
- test_forward, ARRAY_LEN(test_forward),
- "Traversal (ascending)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
- test_backward, ARRAY_LEN(test_backward),
- "Traversal (descending)", test);
- break;
- case TEST_CONTAINER_HASH:
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_ASCENDING, NULL, NULL,
- test_hash_forward, ARRAY_LEN(test_hash_forward),
- "Traversal (ascending, insert end)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_ORDER_DESCENDING, NULL, NULL,
- test_hash_backward, ARRAY_LEN(test_hash_backward),
- "Traversal (descending)", test);
- break;
- }
- /* Check traversal with OBJ_PARTIAL_KEY search range. */
- partial = 6;
- partial_key_match_range = 1;
- switch (type) {
- case TEST_CONTAINER_RBTREE:
- case TEST_CONTAINER_LIST:
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
- test_cmp_cb, &partial,
- test_partial_forward, ARRAY_LEN(test_partial_forward),
- "Traversal OBJ_PARTIAL_KEY (ascending)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
- test_cmp_cb, &partial,
- test_partial_backward, ARRAY_LEN(test_partial_backward),
- "Traversal OBJ_PARTIAL_KEY (descending)", test);
- break;
- case TEST_CONTAINER_HASH:
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_ASCENDING,
- test_cmp_cb, &partial,
- test_hash_partial_forward, ARRAY_LEN(test_hash_partial_forward),
- "Traversal OBJ_PARTIAL_KEY (ascending)", test);
- res = test_ao2_callback_traversal(res, c1, OBJ_PARTIAL_KEY | OBJ_ORDER_DESCENDING,
- test_cmp_cb, &partial,
- test_hash_partial_backward, ARRAY_LEN(test_hash_partial_backward),
- "Traversal OBJ_PARTIAL_KEY (descending)", test);
- break;
- }
- /* Add duplicates to initial containers that allow duplicates */
- if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_ALLOW)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_ALLOW)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- #if defined(TEST_CONTAINER_DEBUG_DUMP)
- ao2_container_dump(c1, 0, "c1(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
- ao2_container_stats(c1, 0, "c1(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug);
- ao2_container_dump(c2, 0, "c2(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug, test_prnt_obj);
- ao2_container_stats(c2, 0, "c2(DUPS_ALLOW) w/ dups", (void *) test, (ao2_prnt_fn *) ast_test_debug);
- #endif /* defined(TEST_CONTAINER_DEBUG_DUMP) */
- /* Check duplicates in containers that allow duplicates. */
- res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
- test_dup_allow_forward, ARRAY_LEN(test_dup_allow_forward),
- "Duplicates (ascending, DUPS_ALLOW)", test);
- res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
- test_dup_allow_backward, ARRAY_LEN(test_dup_allow_backward),
- "Duplicates (descending, DUPS_ALLOW)", test);
- ao2_t_ref(c1, -1, "bye c1");
- c1 = NULL;
- ao2_t_ref(c2, -1, "bye c2");
- c2 = NULL;
- /* Create containers that reject duplicate keyed objects. */
- c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT);
- if (!c1) {
- ast_test_status_update(test, "Container c1 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT);
- if (!c2) {
- ast_test_status_update(test, "Container c2 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- /* Check duplicates in containers that reject duplicate keyed objects. */
- res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
- test_dup_reject, ARRAY_LEN(test_dup_reject),
- "Duplicates (ascending, DUPS_REJECT)", test);
- res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
- test_dup_reject, ARRAY_LEN(test_dup_reject),
- "Duplicates (descending, DUPS_REJECT)", test);
- ao2_t_ref(c1, -1, "bye c1");
- c1 = NULL;
- ao2_t_ref(c2, -1, "bye c2");
- c2 = NULL;
- /* Create containers that reject duplicate objects. */
- c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT);
- if (!c1) {
- ast_test_status_update(test, "Container c1 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_OBJ_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_OBJ_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT);
- if (!c2) {
- ast_test_status_update(test, "Container c2 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_OBJ_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_OBJ_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- /* Check duplicates in containers that reject duplicate objects. */
- res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
- test_dup_obj_reject_forward, ARRAY_LEN(test_dup_obj_reject_forward),
- "Duplicates (ascending, DUPS_OBJ_REJECT)", test);
- res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
- test_dup_obj_reject_backward, ARRAY_LEN(test_dup_obj_reject_backward),
- "Duplicates (descending, DUPS_OBJ_REJECT)", test);
- ao2_t_ref(c1, -1, "bye c1");
- c1 = NULL;
- ao2_t_ref(c2, -1, "bye c2");
- c2 = NULL;
- /* Create container that replaces duplicate keyed objects. */
- c1 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE);
- if (!c1) {
- ast_test_status_update(test, "Container c1 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c1, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c1(DUPS_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_duplicates(c1, &destructor_count, duplicate_number, "c1(DUPS_REJECT)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- c2 = test_make_sorted(type, AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN | AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE);
- if (!c2) {
- ast_test_status_update(test, "Container c2 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_vector(c2, &destructor_count, test_initial, ARRAY_LEN(test_initial), "c2(DUPS_REPLACE)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- if (insert_test_duplicates(c2, &destructor_count, duplicate_number, "c2(DUPS_REPLACE)", test)) {
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- /* Check duplicates in containers that replaces duplicate keyed objects. */
- res = test_expected_duplicates(res, c1, OBJ_ORDER_ASCENDING, duplicate_number,
- test_dup_replace, ARRAY_LEN(test_dup_replace),
- "Duplicates (ascending, DUPS_REPLACE)", test);
- res = test_expected_duplicates(res, c1, OBJ_ORDER_DESCENDING, duplicate_number,
- test_dup_replace, ARRAY_LEN(test_dup_replace),
- "Duplicates (descending, DUPS_REPLACE)", test);
- test_cleanup:
- /* destroy containers */
- if (c1) {
- ao2_t_ref(c1, -1, "bye c1");
- }
- if (c2) {
- ao2_t_ref(c2, -1, "bye c2");
- }
- if (destructor_count > 0) {
- ast_test_status_update(test,
- "all destructors were not called, destructor count is %d\n",
- destructor_count);
- res = AST_TEST_FAIL;
- } else if (destructor_count < 0) {
- ast_test_status_update(test,
- "Destructor was called too many times, destructor count is %d\n",
- destructor_count);
- res = AST_TEST_FAIL;
- }
- return res;
- }
- AST_TEST_DEFINE(astobj2_test_4)
- {
- int res = AST_TEST_PASS;
- switch (cmd) {
- case TEST_INIT:
- info->name = "astobj2_test4";
- info->category = "/main/astobj2/";
- info->summary = "Test container traversal/iteration";
- info->description =
- "This test is to see if the container traversal/iteration works "
- "as intended for each supported container type.";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- res = test_traversal_nonsorted(res, 1, TEST_CONTAINER_LIST, test);
- res = test_traversal_nonsorted(res, 2, TEST_CONTAINER_HASH, test);
- res = test_traversal_sorted(res, 3, TEST_CONTAINER_LIST, test);
- res = test_traversal_sorted(res, 4, TEST_CONTAINER_HASH, test);
- res = test_traversal_sorted(res, 5, TEST_CONTAINER_RBTREE, test);
- return res;
- }
- static enum ast_test_result_state test_performance(struct ast_test *test,
- enum test_container_type type, unsigned int copt)
- {
- /*!
- * \brief The number of objects inserted and searched for in the container under test.
- */
- #define OBJS 73
- int res = AST_TEST_PASS;
- struct ao2_container *c1 = NULL;
- struct test_obj *tobj[OBJS];
- struct test_obj *tobj2;
- int i;
- switch (type) {
- case TEST_CONTAINER_HASH:
- c1 = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, copt, 17,
- test_hash_cb, test_sort_cb, test_cmp_cb);
- break;
- case TEST_CONTAINER_LIST:
- c1 = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, copt,
- test_sort_cb, test_cmp_cb);
- break;
- case TEST_CONTAINER_RBTREE:
- c1 = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, copt,
- test_sort_cb, test_cmp_cb);
- break;
- }
- for (i = 0; i < OBJS; i++) {
- tobj[i] = NULL;
- }
- if (!c1) {
- ast_test_status_update(test, "Container c1 creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- for (i = 0; i < OBJS; i++) {
- tobj[i] = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
- if (!tobj[i]) {
- ast_test_status_update(test, "test object creation failed.\n");
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- tobj[i]->i = i;
- ao2_link(c1, tobj[i]);
- }
- for (i = 0; i < OBJS; i++) {
- if ((!(tobj2 = ao2_find(c1, &i, OBJ_KEY)))) {
- ast_test_status_update(test, "Should have found object %d in container.\n", i);
- res = AST_TEST_FAIL;
- goto test_cleanup;
- }
- ao2_ref(tobj2, -1);
- tobj2 = NULL;
- }
- test_cleanup:
- for (i = 0; i < OBJS ; i++) {
- ao2_cleanup(tobj[i]);
- }
- ao2_cleanup(c1);
- return res;
- }
- static enum ast_test_result_state testloop(struct ast_test *test,
- enum test_container_type type, int copt, int iterations)
- {
- int res = AST_TEST_PASS;
- int i;
- struct timeval start;
- start = ast_tvnow();
- for (i = 1 ; i <= iterations && res == AST_TEST_PASS ; i++) {
- res = test_performance(test, type, copt);
- }
- ast_test_status_update(test, "%5.2fK traversals, %9s : %5lu ms\n",
- iterations / 1000.0, test_container2str(type), (unsigned long)ast_tvdiff_ms(ast_tvnow(), start));
- return res;
- }
- AST_TEST_DEFINE(astobj2_test_perf)
- {
- /*!
- * \brief The number of iteration of testloop to be performed.
- * \note
- * In order to keep the elapsed time sane, if AO2_DEBUG is defined in menuselect,
- * only 25000 iterations are performed. Otherwise 100000.
- */
- #ifdef AO2_DEBUG
- #define ITERATIONS 25000
- #else
- #define ITERATIONS 100000
- #endif
- int res = AST_TEST_PASS;
- switch (cmd) {
- case TEST_INIT:
- info->name = "astobj2_test_perf";
- info->category = "/main/astobj2/perf";
- info->summary = "Test container performance";
- info->description =
- "Runs container traversal tests.";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- res = testloop(test, TEST_CONTAINER_LIST, 0, ITERATIONS);
- if (!res) {
- return res;
- }
- res = testloop(test, TEST_CONTAINER_HASH, 0, ITERATIONS);
- if (!res) {
- return res;
- }
- res = testloop(test, TEST_CONTAINER_RBTREE, 0, ITERATIONS);
- return res;
- }
- static int unload_module(void)
- {
- AST_TEST_UNREGISTER(astobj2_test_1);
- AST_TEST_UNREGISTER(astobj2_test_2);
- AST_TEST_UNREGISTER(astobj2_test_3);
- AST_TEST_UNREGISTER(astobj2_test_4);
- AST_TEST_UNREGISTER(astobj2_test_perf);
- return 0;
- }
- static int load_module(void)
- {
- AST_TEST_REGISTER(astobj2_test_1);
- AST_TEST_REGISTER(astobj2_test_2);
- AST_TEST_REGISTER(astobj2_test_3);
- AST_TEST_REGISTER(astobj2_test_4);
- AST_TEST_REGISTER(astobj2_test_perf);
- return AST_MODULE_LOAD_SUCCESS;
- }
- AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ASTOBJ2 Unit Tests");
|