test.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2009-2010, Digium, Inc.
  5. *
  6. * David Vossel <dvossel@digium.com>
  7. * Russell Bryant <russell@digium.com>
  8. *
  9. * See http://www.asterisk.org for more information about
  10. * the Asterisk project. Please do not directly contact
  11. * any of the maintainers of this project for assistance;
  12. * the project provides a web site, mailing lists and IRC
  13. * channels for your use.
  14. *
  15. * This program is free software, distributed under the terms of
  16. * the GNU General Public License Version 2. See the LICENSE file
  17. * at the top of the source tree.
  18. */
  19. /*!
  20. * \file
  21. * \brief Unit Test Framework
  22. *
  23. * \author David Vossel <dvossel@digium.com>
  24. * \author Russell Bryant <russell@digium.com>
  25. */
  26. /*** MODULEINFO
  27. <support_level>core</support_level>
  28. ***/
  29. #include "asterisk.h"
  30. ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
  31. #include "asterisk/_private.h"
  32. #ifdef TEST_FRAMEWORK
  33. #include "asterisk/test.h"
  34. #include "asterisk/logger.h"
  35. #include "asterisk/linkedlists.h"
  36. #include "asterisk/utils.h"
  37. #include "asterisk/cli.h"
  38. #include "asterisk/term.h"
  39. #include "asterisk/ast_version.h"
  40. #include "asterisk/paths.h"
  41. #include "asterisk/time.h"
  42. #include "asterisk/stasis.h"
  43. #include "asterisk/json.h"
  44. #include "asterisk/astobj2.h"
  45. #include "asterisk/stasis.h"
  46. #include "asterisk/json.h"
  47. /*! \since 12
  48. * \brief The topic for test suite messages
  49. */
  50. struct stasis_topic *test_suite_topic;
  51. /*! This array corresponds to the values defined in the ast_test_state enum */
  52. static const char * const test_result2str[] = {
  53. [AST_TEST_NOT_RUN] = "NOT RUN",
  54. [AST_TEST_PASS] = "PASS",
  55. [AST_TEST_FAIL] = "FAIL",
  56. };
  57. /*! holds all the information pertaining to a single defined test */
  58. struct ast_test {
  59. struct ast_test_info info; /*!< holds test callback information */
  60. /*!
  61. * \brief Test defined status output from last execution
  62. */
  63. struct ast_str *status_str;
  64. /*!
  65. * \brief CLI arguments, if tests being run from the CLI
  66. *
  67. * If this is set, status updates from the tests will be sent to the
  68. * CLI in addition to being saved off in status_str.
  69. */
  70. struct ast_cli_args *cli;
  71. enum ast_test_result_state state; /*!< current test state */
  72. unsigned int time; /*!< time in ms test took */
  73. ast_test_cb_t *cb; /*!< test callback function */
  74. ast_test_init_cb_t *init_cb; /*!< test init function */
  75. ast_test_cleanup_cb_t *cleanup_cb; /*!< test cleanup function */
  76. AST_LIST_ENTRY(ast_test) entry;
  77. };
  78. /*! global structure containing both total and last test execution results */
  79. static struct ast_test_execute_results {
  80. unsigned int total_tests; /*!< total number of tests, regardless if they have been executed or not */
  81. unsigned int total_passed; /*!< total number of executed tests passed */
  82. unsigned int total_failed; /*!< total number of executed tests failed */
  83. unsigned int total_time; /*!< total time of all executed tests */
  84. unsigned int last_passed; /*!< number of passed tests during last execution */
  85. unsigned int last_failed; /*!< number of failed tests during last execution */
  86. unsigned int last_time; /*!< total time of the last test execution */
  87. } last_results;
  88. enum test_mode {
  89. TEST_ALL = 0,
  90. TEST_CATEGORY = 1,
  91. TEST_NAME_CATEGORY = 2,
  92. };
  93. /*! List of registered test definitions */
  94. static AST_LIST_HEAD_STATIC(tests, ast_test);
  95. static struct ast_test *test_alloc(ast_test_cb_t *cb);
  96. static struct ast_test *test_free(struct ast_test *test);
  97. static int test_insert(struct ast_test *test);
  98. static struct ast_test *test_remove(ast_test_cb_t *cb);
  99. static int test_cat_cmp(const char *cat1, const char *cat2);
  100. static int registration_errors = 0;
  101. void ast_test_debug(struct ast_test *test, const char *fmt, ...)
  102. {
  103. struct ast_str *buf = NULL;
  104. va_list ap;
  105. buf = ast_str_create(128);
  106. if (!buf) {
  107. return;
  108. }
  109. va_start(ap, fmt);
  110. ast_str_set_va(&buf, 0, fmt, ap);
  111. va_end(ap);
  112. if (test->cli) {
  113. ast_cli(test->cli->fd, "%s", ast_str_buffer(buf));
  114. }
  115. ast_free(buf);
  116. }
  117. int __ast_test_status_update(const char *file, const char *func, int line, struct ast_test *test, const char *fmt, ...)
  118. {
  119. struct ast_str *buf = NULL;
  120. va_list ap;
  121. if (!(buf = ast_str_create(128))) {
  122. return -1;
  123. }
  124. va_start(ap, fmt);
  125. ast_str_set_va(&buf, 0, fmt, ap);
  126. va_end(ap);
  127. if (test->cli) {
  128. ast_cli(test->cli->fd, "[%s:%s:%d]: %s",
  129. file, func, line, ast_str_buffer(buf));
  130. }
  131. ast_str_append(&test->status_str, 0, "[%s:%s:%d]: %s",
  132. file, func, line, ast_str_buffer(buf));
  133. ast_free(buf);
  134. return 0;
  135. }
  136. int ast_test_register_init(const char *category, ast_test_init_cb_t *cb)
  137. {
  138. struct ast_test *test;
  139. int registered = 1;
  140. AST_LIST_LOCK(&tests);
  141. AST_LIST_TRAVERSE(&tests, test, entry) {
  142. if (!(test_cat_cmp(test->info.category, category))) {
  143. test->init_cb = cb;
  144. registered = 0;
  145. }
  146. }
  147. AST_LIST_UNLOCK(&tests);
  148. return registered;
  149. }
  150. int ast_test_register_cleanup(const char *category, ast_test_cleanup_cb_t *cb)
  151. {
  152. struct ast_test *test;
  153. int registered = 1;
  154. AST_LIST_LOCK(&tests);
  155. AST_LIST_TRAVERSE(&tests, test, entry) {
  156. if (!(test_cat_cmp(test->info.category, category))) {
  157. test->cleanup_cb = cb;
  158. registered = 0;
  159. }
  160. }
  161. AST_LIST_UNLOCK(&tests);
  162. return registered;
  163. }
  164. int ast_test_register(ast_test_cb_t *cb)
  165. {
  166. struct ast_test *test;
  167. if (!cb) {
  168. ast_log(LOG_ERROR, "Attempted to register test without all required information\n");
  169. registration_errors++;
  170. return -1;
  171. }
  172. if (!(test = test_alloc(cb))) {
  173. registration_errors++;
  174. return -1;
  175. }
  176. if (test_insert(test)) {
  177. test_free(test);
  178. registration_errors++;
  179. return -1;
  180. }
  181. return 0;
  182. }
  183. int ast_test_unregister(ast_test_cb_t *cb)
  184. {
  185. struct ast_test *test;
  186. if (!(test = test_remove(cb))) {
  187. return -1; /* not found */
  188. }
  189. test_free(test);
  190. return 0;
  191. }
  192. /*!
  193. * \internal
  194. * \brief executes a single test, storing the results in the test->result structure.
  195. *
  196. * \note The last_results structure which contains global statistics about test execution
  197. * must be updated when using this function. See use in test_execute_multiple().
  198. */
  199. static void test_execute(struct ast_test *test)
  200. {
  201. struct timeval begin;
  202. enum ast_test_result_state result;
  203. ast_str_reset(test->status_str);
  204. begin = ast_tvnow();
  205. if (test->init_cb && test->init_cb(&test->info, test)) {
  206. test->state = AST_TEST_FAIL;
  207. goto exit;
  208. }
  209. test->state = AST_TEST_NOT_RUN;
  210. result = test->cb(&test->info, TEST_EXECUTE, test);
  211. if (test->state != AST_TEST_FAIL) {
  212. test->state = result;
  213. }
  214. if (test->cleanup_cb && test->cleanup_cb(&test->info, test)) {
  215. test->state = AST_TEST_FAIL;
  216. }
  217. exit:
  218. test->time = ast_tvdiff_ms(ast_tvnow(), begin);
  219. }
  220. void ast_test_set_result(struct ast_test *test, enum ast_test_result_state state)
  221. {
  222. if (test->state == AST_TEST_FAIL || state == AST_TEST_NOT_RUN) {
  223. return;
  224. }
  225. test->state = state;
  226. }
  227. /*
  228. * These are the Java reserved words we need to munge so Jenkins
  229. * doesn't barf on them.
  230. */
  231. static char *reserved_words[] = {
  232. "abstract", "arguments", "as", "assert", "await",
  233. "boolean", "break", "byte", "case", "catch", "char", "class",
  234. "const", "continue", "debugger", "def", "default", "delete", "do",
  235. "double", "else", "enum", "eval", "export", "extends", "false",
  236. "final", "finally", "float", "for", "function", "goto", "if",
  237. "implements", "import", "in", "instanceof", "int", "interface",
  238. "let", "long", "native", "new", "null", "package", "private",
  239. "protected", "public", "return", "short", "static", "strictfp",
  240. "string", "super", "switch", "synchronized", "this", "throw", "throws",
  241. "trait", "transient", "true", "try", "typeof", "var", "void",
  242. "volatile", "while", "with", "yield" };
  243. static int is_reserved_word(const char *word)
  244. {
  245. int i;
  246. for (i = 0; i < ARRAY_LEN(reserved_words); i++) {
  247. if (strcmp(word, reserved_words[i]) == 0) {
  248. return 1;
  249. }
  250. }
  251. return 0;
  252. }
  253. static void test_xml_entry(struct ast_test *test, FILE *f)
  254. {
  255. /* We need a copy of the category skipping past the initial '/' */
  256. char *test_cat = ast_strdupa(test->info.category + 1);
  257. char *next_cat;
  258. char *test_name = (char *)test->info.name;
  259. struct ast_str *category = ast_str_create(strlen(test->info.category) + 32);
  260. if (!category || test->state == AST_TEST_NOT_RUN) {
  261. ast_free(category);
  262. return;
  263. }
  264. while ((next_cat = ast_strsep(&test_cat, '/', AST_STRSEP_TRIM))) {
  265. char *prefix = "";
  266. if (is_reserved_word(next_cat)) {
  267. prefix = "_";
  268. }
  269. ast_str_append(&category, 0, ".%s%s", prefix, next_cat);
  270. }
  271. test_cat = ast_str_buffer(category);
  272. /* Skip past the initial '.' */
  273. test_cat++;
  274. if (is_reserved_word(test->info.name)) {
  275. size_t name_length = strlen(test->info.name) + 2;
  276. test_name = ast_alloca(name_length);
  277. snprintf(test_name, name_length, "_%s", test->info.name);
  278. }
  279. fprintf(f, "\t\t<testcase time=\"%u.%u\" classname=\"%s\" name=\"%s\"%s>\n",
  280. test->time / 1000, test->time % 1000,
  281. test_cat, test_name,
  282. test->state == AST_TEST_PASS ? "/" : "");
  283. ast_free(category);
  284. if (test->state == AST_TEST_FAIL) {
  285. fprintf(f, "\t\t\t<failure><![CDATA[\n%s\n\t\t]]></failure>\n",
  286. S_OR(ast_str_buffer(test->status_str), "NA"));
  287. fprintf(f, "\t\t</testcase>\n");
  288. }
  289. }
  290. static void test_txt_entry(struct ast_test *test, FILE *f)
  291. {
  292. if (!f || !test) {
  293. return;
  294. }
  295. fprintf(f, "\nName: %s\n", test->info.name);
  296. fprintf(f, "Category: %s\n", test->info.category);
  297. fprintf(f, "Summary: %s\n", test->info.summary);
  298. fprintf(f, "Description: %s\n", test->info.description);
  299. fprintf(f, "Result: %s\n", test_result2str[test->state]);
  300. if (test->state != AST_TEST_NOT_RUN) {
  301. fprintf(f, "Time: %u\n", test->time);
  302. }
  303. if (test->state == AST_TEST_FAIL) {
  304. fprintf(f, "Error Description: %s\n\n", S_OR(ast_str_buffer(test->status_str), "NA"));
  305. }
  306. }
  307. /*!
  308. * \internal
  309. * \brief Executes registered unit tests
  310. *
  311. * \param name of test to run (optional)
  312. * \param test category to run (optional)
  313. * \param cli args for cli test updates (optional)
  314. *
  315. * \return number of tests executed.
  316. *
  317. * \note This function has three modes of operation
  318. * -# When given a name and category, a matching individual test will execute if found.
  319. * -# When given only a category all matching tests within that category will execute.
  320. * -# If given no name or category all registered tests will execute.
  321. */
  322. static int test_execute_multiple(const char *name, const char *category, struct ast_cli_args *cli)
  323. {
  324. char result_buf[32] = { 0 };
  325. struct ast_test *test = NULL;
  326. enum test_mode mode = TEST_ALL; /* 3 modes, 0 = run all, 1 = only by category, 2 = only by name and category */
  327. int execute = 0;
  328. int res = 0;
  329. if (!ast_strlen_zero(category)) {
  330. if (!ast_strlen_zero(name)) {
  331. mode = TEST_NAME_CATEGORY;
  332. } else {
  333. mode = TEST_CATEGORY;
  334. }
  335. }
  336. AST_LIST_LOCK(&tests);
  337. /* clear previous execution results */
  338. memset(&last_results, 0, sizeof(last_results));
  339. AST_LIST_TRAVERSE(&tests, test, entry) {
  340. execute = 0;
  341. switch (mode) {
  342. case TEST_CATEGORY:
  343. if (!test_cat_cmp(test->info.category, category) && !test->info.explicit_only) {
  344. execute = 1;
  345. }
  346. break;
  347. case TEST_NAME_CATEGORY:
  348. if (!(test_cat_cmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
  349. execute = 1;
  350. }
  351. break;
  352. case TEST_ALL:
  353. execute = !test->info.explicit_only;
  354. }
  355. if (execute) {
  356. if (cli) {
  357. ast_cli(cli->fd, "START %s - %s \n", test->info.category, test->info.name);
  358. }
  359. /* set the test status update argument. it is ok if cli is NULL */
  360. test->cli = cli;
  361. /* execute the test and save results */
  362. test_execute(test);
  363. test->cli = NULL;
  364. /* update execution specific counts here */
  365. last_results.last_time += test->time;
  366. if (test->state == AST_TEST_PASS) {
  367. last_results.last_passed++;
  368. } else if (test->state == AST_TEST_FAIL) {
  369. last_results.last_failed++;
  370. }
  371. if (cli) {
  372. term_color(result_buf,
  373. test_result2str[test->state],
  374. (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
  375. 0,
  376. sizeof(result_buf));
  377. ast_cli(cli->fd, "END %s - %s Time: %s%ums Result: %s\n",
  378. test->info.category,
  379. test->info.name,
  380. test->time ? "" : "<",
  381. test->time ? test->time : 1,
  382. result_buf);
  383. }
  384. }
  385. /* update total counts as well during this iteration
  386. * even if the current test did not execute this time */
  387. last_results.total_time += test->time;
  388. last_results.total_tests++;
  389. if (test->state != AST_TEST_NOT_RUN) {
  390. if (test->state == AST_TEST_PASS) {
  391. last_results.total_passed++;
  392. } else {
  393. last_results.total_failed++;
  394. }
  395. }
  396. }
  397. res = last_results.last_passed + last_results.last_failed;
  398. AST_LIST_UNLOCK(&tests);
  399. return res;
  400. }
  401. /*!
  402. * \internal
  403. * \brief Generate test results.
  404. *
  405. * \param name of test result to generate (optional)
  406. * \param test category to generate (optional)
  407. * \param path to xml file to generate. (optional)
  408. * \param path to txt file to generate, (optional)
  409. *
  410. * \retval 0 success
  411. * \retval -1 failure
  412. *
  413. * \note This function has three modes of operation.
  414. * -# When given both a name and category, results will be generated for that single test.
  415. * -# When given only a category, results for every test within the category will be generated.
  416. * -# When given no name or category, results for every registered test will be generated.
  417. *
  418. * In order for the results to be generated, an xml and or txt file path must be provided.
  419. */
  420. static int test_generate_results(const char *name, const char *category, const char *xml_path, const char *txt_path)
  421. {
  422. enum test_mode mode = TEST_ALL; /* 0 generate all, 1 generate by category only, 2 generate by name and category */
  423. FILE *f_xml = NULL, *f_txt = NULL;
  424. int res = 0;
  425. struct ast_test *test = NULL;
  426. /* verify at least one output file was given */
  427. if (ast_strlen_zero(xml_path) && ast_strlen_zero(txt_path)) {
  428. return -1;
  429. }
  430. /* define what mode is to be used */
  431. if (!ast_strlen_zero(category)) {
  432. if (!ast_strlen_zero(name)) {
  433. mode = TEST_NAME_CATEGORY;
  434. } else {
  435. mode = TEST_CATEGORY;
  436. }
  437. }
  438. /* open files for writing */
  439. if (!ast_strlen_zero(xml_path)) {
  440. if (!(f_xml = fopen(xml_path, "w"))) {
  441. ast_log(LOG_WARNING, "Could not open file %s for xml test results\n", xml_path);
  442. res = -1;
  443. goto done;
  444. }
  445. }
  446. if (!ast_strlen_zero(txt_path)) {
  447. if (!(f_txt = fopen(txt_path, "w"))) {
  448. ast_log(LOG_WARNING, "Could not open file %s for text output of test results\n", txt_path);
  449. res = -1;
  450. goto done;
  451. }
  452. }
  453. AST_LIST_LOCK(&tests);
  454. /* xml header information */
  455. if (f_xml) {
  456. /*
  457. * http://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo
  458. */
  459. fprintf(f_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  460. fprintf(f_xml, "<testsuites>\n");
  461. fprintf(f_xml, "\t<testsuite errors=\"0\" time=\"%u.%u\" tests=\"%u\" "
  462. "name=\"AsteriskUnitTests\">\n",
  463. last_results.total_time / 1000, last_results.total_time % 1000,
  464. last_results.total_tests);
  465. fprintf(f_xml, "\t\t<properties>\n");
  466. fprintf(f_xml, "\t\t\t<property name=\"version\" value=\"%s\"/>\n", ast_get_version());
  467. fprintf(f_xml, "\t\t</properties>\n");
  468. }
  469. /* txt header information */
  470. if (f_txt) {
  471. fprintf(f_txt, "Asterisk Version: %s\n", ast_get_version());
  472. fprintf(f_txt, "Asterisk Version Number: %s\n", ast_get_version_num());
  473. fprintf(f_txt, "Number of Tests: %u\n", last_results.total_tests);
  474. fprintf(f_txt, "Number of Tests Executed: %u\n", (last_results.total_passed + last_results.total_failed));
  475. fprintf(f_txt, "Passed Tests: %u\n", last_results.total_passed);
  476. fprintf(f_txt, "Failed Tests: %u\n", last_results.total_failed);
  477. fprintf(f_txt, "Total Execution Time: %u\n", last_results.total_time);
  478. }
  479. /* export each individual test */
  480. AST_LIST_TRAVERSE(&tests, test, entry) {
  481. switch (mode) {
  482. case TEST_CATEGORY:
  483. if (!test_cat_cmp(test->info.category, category)) {
  484. test_xml_entry(test, f_xml);
  485. test_txt_entry(test, f_txt);
  486. }
  487. break;
  488. case TEST_NAME_CATEGORY:
  489. if (!(strcmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
  490. test_xml_entry(test, f_xml);
  491. test_txt_entry(test, f_txt);
  492. }
  493. break;
  494. case TEST_ALL:
  495. test_xml_entry(test, f_xml);
  496. test_txt_entry(test, f_txt);
  497. }
  498. }
  499. AST_LIST_UNLOCK(&tests);
  500. done:
  501. if (f_xml) {
  502. fprintf(f_xml, "\t</testsuite>\n");
  503. fprintf(f_xml, "</testsuites>\n");
  504. fclose(f_xml);
  505. }
  506. if (f_txt) {
  507. fclose(f_txt);
  508. }
  509. return res;
  510. }
  511. /*!
  512. * \internal
  513. * \brief adds test to container sorted first by category then by name
  514. *
  515. * \retval 0 success
  516. * \retval -1 failure
  517. */
  518. static int test_insert(struct ast_test *test)
  519. {
  520. /* This is a slow operation that may need to be optimized in the future
  521. * as the test framework expands. At the moment we are doing string
  522. * comparisons on every item within the list to insert in sorted order. */
  523. AST_LIST_LOCK(&tests);
  524. AST_LIST_INSERT_SORTALPHA(&tests, test, entry, info.category);
  525. AST_LIST_UNLOCK(&tests);
  526. return 0;
  527. }
  528. /*!
  529. * \internal
  530. * \brief removes test from container
  531. *
  532. * \return ast_test removed from list on success, or NULL on failure
  533. */
  534. static struct ast_test *test_remove(ast_test_cb_t *cb)
  535. {
  536. struct ast_test *cur = NULL;
  537. AST_LIST_LOCK(&tests);
  538. AST_LIST_TRAVERSE_SAFE_BEGIN(&tests, cur, entry) {
  539. if (cur->cb == cb) {
  540. AST_LIST_REMOVE_CURRENT(entry);
  541. break;
  542. }
  543. }
  544. AST_LIST_TRAVERSE_SAFE_END;
  545. AST_LIST_UNLOCK(&tests);
  546. return cur;
  547. }
  548. /*!
  549. * \brief compares two test categories to determine if cat1 resides in cat2
  550. * \internal
  551. *
  552. * \retval 0 true
  553. * \retval non-zero false
  554. */
  555. static int test_cat_cmp(const char *cat1, const char *cat2)
  556. {
  557. int len1 = 0;
  558. int len2 = 0;
  559. if (!cat1 || !cat2) {
  560. return -1;
  561. }
  562. len1 = strlen(cat1);
  563. len2 = strlen(cat2);
  564. if (len2 > len1) {
  565. return -1;
  566. }
  567. return strncmp(cat1, cat2, len2) ? 1 : 0;
  568. }
  569. /*!
  570. * \internal
  571. * \brief free an ast_test object and all it's data members
  572. */
  573. static struct ast_test *test_free(struct ast_test *test)
  574. {
  575. if (!test) {
  576. return NULL;
  577. }
  578. ast_free(test->status_str);
  579. ast_free(test);
  580. return NULL;
  581. }
  582. /*!
  583. * \internal
  584. * \brief allocate an ast_test object.
  585. */
  586. static struct ast_test *test_alloc(ast_test_cb_t *cb)
  587. {
  588. struct ast_test *test;
  589. test = ast_calloc(1, sizeof(*test));
  590. if (!test) {
  591. ast_log(LOG_ERROR, "Failed to allocate test, registration failed.\n");
  592. return NULL;
  593. }
  594. test->cb = cb;
  595. test->cb(&test->info, TEST_INIT, test);
  596. if (ast_strlen_zero(test->info.name)) {
  597. ast_log(LOG_ERROR, "Test has no name, test registration refused.\n");
  598. return test_free(test);
  599. }
  600. if (ast_strlen_zero(test->info.category)) {
  601. ast_log(LOG_ERROR, "Test %s has no category, test registration refused.\n",
  602. test->info.name);
  603. return test_free(test);
  604. }
  605. if (test->info.category[0] != '/' || test->info.category[strlen(test->info.category) - 1] != '/') {
  606. ast_log(LOG_WARNING, "Test category '%s' for test '%s' is missing a leading or trailing slash.\n",
  607. test->info.category, test->info.name);
  608. /*
  609. * Flag an error anyways so test_registrations fails but allow the
  610. * test to be registered.
  611. */
  612. ++registration_errors;
  613. }
  614. if (ast_strlen_zero(test->info.summary)) {
  615. ast_log(LOG_ERROR, "Test %s%s has no summary, test registration refused.\n",
  616. test->info.category, test->info.name);
  617. return test_free(test);
  618. }
  619. if (test->info.summary[strlen(test->info.summary) - 1] == '\n') {
  620. ast_log(LOG_WARNING, "Test %s%s summary has a trailing newline.\n",
  621. test->info.category, test->info.name);
  622. /*
  623. * Flag an error anyways so test_registrations fails but allow the
  624. * test to be registered.
  625. */
  626. ++registration_errors;
  627. }
  628. if (ast_strlen_zero(test->info.description)) {
  629. ast_log(LOG_ERROR, "Test %s%s has no description, test registration refused.\n",
  630. test->info.category, test->info.name);
  631. return test_free(test);
  632. }
  633. if (test->info.description[strlen(test->info.description) - 1] == '\n') {
  634. ast_log(LOG_WARNING, "Test %s%s description has a trailing newline.\n",
  635. test->info.category, test->info.name);
  636. /*
  637. * Flag an error anyways so test_registrations fails but allow the
  638. * test to be registered.
  639. */
  640. ++registration_errors;
  641. }
  642. if (!(test->status_str = ast_str_create(128))) {
  643. ast_log(LOG_ERROR, "Failed to allocate status_str for %s%s, test registration failed.\n",
  644. test->info.category, test->info.name);
  645. return test_free(test);
  646. }
  647. return test;
  648. }
  649. static char *complete_test_category(const char *word)
  650. {
  651. int wordlen = strlen(word);
  652. struct ast_test *test;
  653. AST_LIST_LOCK(&tests);
  654. AST_LIST_TRAVERSE(&tests, test, entry) {
  655. if (!strncasecmp(word, test->info.category, wordlen)) {
  656. if (ast_cli_completion_add(ast_strdup(test->info.category))) {
  657. break;
  658. }
  659. }
  660. }
  661. AST_LIST_UNLOCK(&tests);
  662. return NULL;
  663. }
  664. static char *complete_test_name(const char *word, const char *category)
  665. {
  666. int wordlen = strlen(word);
  667. struct ast_test *test;
  668. AST_LIST_LOCK(&tests);
  669. AST_LIST_TRAVERSE(&tests, test, entry) {
  670. if (!test_cat_cmp(test->info.category, category) && !strncasecmp(word, test->info.name, wordlen)) {
  671. if (ast_cli_completion_add(ast_strdup(test->info.name))) {
  672. break;
  673. }
  674. }
  675. }
  676. AST_LIST_UNLOCK(&tests);
  677. return NULL;
  678. }
  679. /* CLI commands */
  680. static char *test_cli_show_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  681. {
  682. #define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n"
  683. static const char * const option1[] = { "all", "category", NULL };
  684. static const char * const option2[] = { "name", NULL };
  685. struct ast_test *test = NULL;
  686. int count = 0;
  687. switch (cmd) {
  688. case CLI_INIT:
  689. e->command = "test show registered";
  690. e->usage =
  691. "Usage: 'test show registered' can be used in three ways.\n"
  692. " 1. 'test show registered all' shows all registered tests\n"
  693. " 2. 'test show registered category [test category]' shows all tests in the given\n"
  694. " category.\n"
  695. " 3. 'test show registered category [test category] name [test name]' shows all\n"
  696. " tests in a given category matching a given name\n";
  697. return NULL;
  698. case CLI_GENERATE:
  699. if (a->pos == 3) {
  700. return ast_cli_complete(a->word, option1, -1);
  701. }
  702. if (a->pos == 4 && !strcasecmp(a->argv[3], "category")) {
  703. return complete_test_category(a->word);
  704. }
  705. if (a->pos == 5) {
  706. return ast_cli_complete(a->word, option2, -1);
  707. }
  708. if (a->pos == 6) {
  709. return complete_test_name(a->word, a->argv[4]);
  710. }
  711. return NULL;
  712. case CLI_HANDLER:
  713. if ((a->argc < 4) || (a->argc == 6) || (a->argc > 7) ||
  714. ((a->argc == 4) && strcasecmp(a->argv[3], "all")) ||
  715. ((a->argc == 7) && strcasecmp(a->argv[5], "name"))) {
  716. return CLI_SHOWUSAGE;
  717. }
  718. ast_cli(a->fd, FORMAT, "Category", "Name", "Summary", "Test Result");
  719. ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
  720. AST_LIST_LOCK(&tests);
  721. AST_LIST_TRAVERSE(&tests, test, entry) {
  722. if ((a->argc == 4) ||
  723. ((a->argc == 5) && !test_cat_cmp(test->info.category, a->argv[4])) ||
  724. ((a->argc == 7) && !strcmp(test->info.category, a->argv[4]) && !strcmp(test->info.name, a->argv[6]))) {
  725. ast_cli(a->fd, FORMAT, test->info.category, test->info.name,
  726. test->info.summary, test_result2str[test->state]);
  727. count++;
  728. }
  729. }
  730. AST_LIST_UNLOCK(&tests);
  731. ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
  732. ast_cli(a->fd, "\n%d Registered Tests Matched\n", count);
  733. default:
  734. return NULL;
  735. }
  736. return CLI_SUCCESS;
  737. }
  738. static char *test_cli_execute_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  739. {
  740. static const char * const option1[] = { "all", "category", NULL };
  741. static const char * const option2[] = { "name", NULL };
  742. switch (cmd) {
  743. case CLI_INIT:
  744. e->command = "test execute";
  745. e->usage =
  746. "Usage: test execute can be used in three ways.\n"
  747. " 1. 'test execute all' runs all registered tests\n"
  748. " 2. 'test execute category [test category]' runs all tests in the given\n"
  749. " category.\n"
  750. " 3. 'test execute category [test category] name [test name]' runs all\n"
  751. " tests in a given category matching a given name\n";
  752. return NULL;
  753. case CLI_GENERATE:
  754. if (a->pos == 2) {
  755. return ast_cli_complete(a->word, option1, -1);
  756. }
  757. if (a->pos == 3 && !strcasecmp(a->argv[2], "category")) {
  758. return complete_test_category(a->word);
  759. }
  760. if (a->pos == 4) {
  761. return ast_cli_complete(a->word, option2, -1);
  762. }
  763. if (a->pos == 5) {
  764. return complete_test_name(a->word, a->argv[3]);
  765. }
  766. return NULL;
  767. case CLI_HANDLER:
  768. if (a->argc < 3|| a->argc > 6) {
  769. return CLI_SHOWUSAGE;
  770. }
  771. if ((a->argc == 3) && !strcasecmp(a->argv[2], "all")) { /* run all registered tests */
  772. ast_cli(a->fd, "Running all available tests...\n\n");
  773. test_execute_multiple(NULL, NULL, a);
  774. } else if (a->argc == 4) { /* run only tests within a category */
  775. ast_cli(a->fd, "Running all available tests matching category %s\n\n", a->argv[3]);
  776. test_execute_multiple(NULL, a->argv[3], a);
  777. } else if (a->argc == 6) { /* run only a single test matching the category and name */
  778. ast_cli(a->fd, "Running all available tests matching category %s and name %s\n\n", a->argv[3], a->argv[5]);
  779. test_execute_multiple(a->argv[5], a->argv[3], a);
  780. } else {
  781. return CLI_SHOWUSAGE;
  782. }
  783. AST_LIST_LOCK(&tests);
  784. if (!(last_results.last_passed + last_results.last_failed)) {
  785. ast_cli(a->fd, "--- No Tests Found! ---\n");
  786. }
  787. ast_cli(a->fd, "\n%u Test(s) Executed %u Passed %u Failed\n",
  788. (last_results.last_passed + last_results.last_failed),
  789. last_results.last_passed,
  790. last_results.last_failed);
  791. AST_LIST_UNLOCK(&tests);
  792. default:
  793. return NULL;
  794. }
  795. return CLI_SUCCESS;
  796. }
  797. static char *test_cli_show_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  798. {
  799. #define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n"
  800. #define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n"
  801. static const char * const option1[] = { "all", "failed", "passed", NULL };
  802. char result_buf[32] = { 0 };
  803. struct ast_test *test = NULL;
  804. int failed = 0;
  805. int passed = 0;
  806. int mode; /* 0 for show all, 1 for show fail, 2 for show passed */
  807. switch (cmd) {
  808. case CLI_INIT:
  809. e->command = "test show results";
  810. e->usage =
  811. "Usage: test show results can be used in three ways\n"
  812. " 1. 'test show results all' Displays results for all executed tests.\n"
  813. " 2. 'test show results passed' Displays results for all passed tests.\n"
  814. " 3. 'test show results failed' Displays results for all failed tests.\n";
  815. return NULL;
  816. case CLI_GENERATE:
  817. if (a->pos == 3) {
  818. return ast_cli_complete(a->word, option1, -1);
  819. }
  820. return NULL;
  821. case CLI_HANDLER:
  822. /* verify input */
  823. if (a->argc != 4) {
  824. return CLI_SHOWUSAGE;
  825. } else if (!strcasecmp(a->argv[3], "passed")) {
  826. mode = 2;
  827. } else if (!strcasecmp(a->argv[3], "failed")) {
  828. mode = 1;
  829. } else if (!strcasecmp(a->argv[3], "all")) {
  830. mode = 0;
  831. } else {
  832. return CLI_SHOWUSAGE;
  833. }
  834. ast_cli(a->fd, FORMAT_RES_ALL1, "Result", "", "Name", "Category", "Time");
  835. AST_LIST_LOCK(&tests);
  836. AST_LIST_TRAVERSE(&tests, test, entry) {
  837. if (test->state == AST_TEST_NOT_RUN) {
  838. continue;
  839. }
  840. test->state == AST_TEST_FAIL ? failed++ : passed++;
  841. if (!mode || ((mode == 1) && (test->state == AST_TEST_FAIL)) || ((mode == 2) && (test->state == AST_TEST_PASS))) {
  842. /* give our results pretty colors */
  843. term_color(result_buf, test_result2str[test->state],
  844. (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
  845. 0, sizeof(result_buf));
  846. ast_cli(a->fd, FORMAT_RES_ALL2,
  847. result_buf,
  848. " ",
  849. test->info.name,
  850. test->info.category,
  851. test->time ? " " : "<",
  852. test->time ? test->time : 1);
  853. }
  854. }
  855. AST_LIST_UNLOCK(&tests);
  856. ast_cli(a->fd, "%d Test(s) Executed %d Passed %d Failed\n", (failed + passed), passed, failed);
  857. default:
  858. return NULL;
  859. }
  860. return CLI_SUCCESS;
  861. }
  862. static char *test_cli_generate_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  863. {
  864. static const char * const option[] = { "xml", "txt", NULL };
  865. const char *file = NULL;
  866. const char *type = "";
  867. int isxml = 0;
  868. int res = 0;
  869. struct ast_str *buf = NULL;
  870. struct timeval time = ast_tvnow();
  871. switch (cmd) {
  872. case CLI_INIT:
  873. e->command = "test generate results";
  874. e->usage =
  875. "Usage: 'test generate results'\n"
  876. " Generates test results in either xml or txt format. An optional \n"
  877. " file path may be provided to specify the location of the xml or\n"
  878. " txt file\n"
  879. " \nExample usage:\n"
  880. " 'test generate results xml' this writes to a default file\n"
  881. " 'test generate results xml /path/to/file.xml' writes to specified file\n";
  882. return NULL;
  883. case CLI_GENERATE:
  884. if (a->pos == 3) {
  885. return ast_cli_complete(a->word, option, -1);
  886. }
  887. return NULL;
  888. case CLI_HANDLER:
  889. /* verify input */
  890. if (a->argc < 4 || a->argc > 5) {
  891. return CLI_SHOWUSAGE;
  892. } else if (!strcasecmp(a->argv[3], "xml")) {
  893. type = "xml";
  894. isxml = 1;
  895. } else if (!strcasecmp(a->argv[3], "txt")) {
  896. type = "txt";
  897. } else {
  898. return CLI_SHOWUSAGE;
  899. }
  900. if (a->argc == 5) {
  901. file = a->argv[4];
  902. } else {
  903. if (!(buf = ast_str_create(256))) {
  904. return NULL;
  905. }
  906. ast_str_set(&buf, 0, "%s/asterisk_test_results-%ld.%s", ast_config_AST_LOG_DIR, (long) time.tv_sec, type);
  907. file = ast_str_buffer(buf);
  908. }
  909. if (isxml) {
  910. res = test_generate_results(NULL, NULL, file, NULL);
  911. } else {
  912. res = test_generate_results(NULL, NULL, NULL, file);
  913. }
  914. if (!res) {
  915. ast_cli(a->fd, "Results Generated Successfully: %s\n", S_OR(file, ""));
  916. } else {
  917. ast_cli(a->fd, "Results Could Not Be Generated: %s\n", S_OR(file, ""));
  918. }
  919. ast_free(buf);
  920. default:
  921. return NULL;
  922. }
  923. return CLI_SUCCESS;
  924. }
  925. static struct ast_cli_entry test_cli[] = {
  926. AST_CLI_DEFINE(test_cli_show_registered, "show registered tests"),
  927. AST_CLI_DEFINE(test_cli_execute_registered, "execute registered tests"),
  928. AST_CLI_DEFINE(test_cli_show_results, "show last test results"),
  929. AST_CLI_DEFINE(test_cli_generate_results, "generate test results to file"),
  930. };
  931. struct stasis_topic *ast_test_suite_topic(void)
  932. {
  933. return test_suite_topic;
  934. }
  935. /*!
  936. * \since 12
  937. * \brief A wrapper object that can be ao2 ref counted around an \ref ast_json blob
  938. */
  939. struct ast_test_suite_message_payload {
  940. struct ast_json *blob; /*!< The actual blob that we want to deliver */
  941. };
  942. /*! \internal
  943. * \since 12
  944. * \brief Destructor for \ref ast_test_suite_message_payload
  945. */
  946. static void test_suite_message_payload_dtor(void *obj)
  947. {
  948. struct ast_test_suite_message_payload *payload = obj;
  949. if (payload->blob) {
  950. ast_json_unref(payload->blob);
  951. }
  952. }
  953. struct ast_json *ast_test_suite_get_blob(struct ast_test_suite_message_payload *payload)
  954. {
  955. return payload->blob;
  956. }
  957. static struct ast_manager_event_blob *test_suite_event_to_ami(struct stasis_message *msg)
  958. {
  959. RAII_VAR(struct ast_str *, packet_string, ast_str_create(128), ast_free);
  960. struct ast_test_suite_message_payload *payload;
  961. struct ast_json *blob;
  962. const char *type;
  963. payload = stasis_message_data(msg);
  964. if (!payload) {
  965. return NULL;
  966. }
  967. blob = ast_test_suite_get_blob(payload);
  968. if (!blob) {
  969. return NULL;
  970. }
  971. type = ast_json_string_get(ast_json_object_get(blob, "type"));
  972. if (ast_strlen_zero(type) || strcmp("testevent", type)) {
  973. return NULL;
  974. }
  975. ast_str_append(&packet_string, 0, "Type: StateChange\r\n");
  976. ast_str_append(&packet_string, 0, "State: %s\r\n",
  977. ast_json_string_get(ast_json_object_get(blob, "state")));
  978. ast_str_append(&packet_string, 0, "AppFile: %s\r\n",
  979. ast_json_string_get(ast_json_object_get(blob, "appfile")));
  980. ast_str_append(&packet_string, 0, "AppFunction: %s\r\n",
  981. ast_json_string_get(ast_json_object_get(blob, "appfunction")));
  982. ast_str_append(&packet_string, 0, "AppLine: %jd\r\n",
  983. ast_json_integer_get(ast_json_object_get(blob, "line")));
  984. ast_str_append(&packet_string, 0, "%s\r\n",
  985. ast_json_string_get(ast_json_object_get(blob, "data")));
  986. return ast_manager_event_blob_create(EVENT_FLAG_REPORTING,
  987. "TestEvent",
  988. "%s",
  989. ast_str_buffer(packet_string));
  990. }
  991. /*! \since 12
  992. * \brief The message type for test suite messages
  993. */
  994. STASIS_MESSAGE_TYPE_DEFN(ast_test_suite_message_type,
  995. .to_ami = test_suite_event_to_ami);
  996. void __ast_test_suite_event_notify(const char *file, const char *func, int line, const char *state, const char *fmt, ...)
  997. {
  998. RAII_VAR(struct ast_test_suite_message_payload *, payload,
  999. NULL,
  1000. ao2_cleanup);
  1001. RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
  1002. RAII_VAR(struct ast_str *, buf, NULL, ast_free);
  1003. va_list ap;
  1004. if (!ast_test_suite_message_type()) {
  1005. return;
  1006. }
  1007. buf = ast_str_create(128);
  1008. if (!buf) {
  1009. return;
  1010. }
  1011. payload = ao2_alloc(sizeof(*payload), test_suite_message_payload_dtor);
  1012. if (!payload) {
  1013. return;
  1014. }
  1015. va_start(ap, fmt);
  1016. ast_str_set_va(&buf, 0, fmt, ap);
  1017. va_end(ap);
  1018. payload->blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: i, s: s}",
  1019. "type", "testevent",
  1020. "state", state,
  1021. "appfile", file,
  1022. "appfunction", func,
  1023. "line", line,
  1024. "data", ast_str_buffer(buf));
  1025. if (!payload->blob) {
  1026. return;
  1027. }
  1028. msg = stasis_message_create(ast_test_suite_message_type(), payload);
  1029. if (!msg) {
  1030. return;
  1031. }
  1032. stasis_publish(ast_test_suite_topic(), msg);
  1033. }
  1034. AST_TEST_DEFINE(test_registrations)
  1035. {
  1036. switch (cmd) {
  1037. case TEST_INIT:
  1038. info->name = "registrations";
  1039. info->category = "/main/test/";
  1040. info->summary = "Validate Test Registration Data.";
  1041. info->description = "Validate Test Registration Data.";
  1042. return AST_TEST_NOT_RUN;
  1043. case TEST_EXECUTE:
  1044. break;
  1045. }
  1046. if (registration_errors) {
  1047. ast_test_status_update(test,
  1048. "%d test registration error%s occurred. See startup logs for details.\n",
  1049. registration_errors, registration_errors > 1 ? "s" : "");
  1050. return AST_TEST_FAIL;
  1051. }
  1052. return AST_TEST_PASS;
  1053. }
  1054. static void test_cleanup(void)
  1055. {
  1056. AST_TEST_UNREGISTER(test_registrations);
  1057. ast_cli_unregister_multiple(test_cli, ARRAY_LEN(test_cli));
  1058. ao2_cleanup(test_suite_topic);
  1059. test_suite_topic = NULL;
  1060. STASIS_MESSAGE_TYPE_CLEANUP(ast_test_suite_message_type);
  1061. }
  1062. #endif /* TEST_FRAMEWORK */
  1063. int ast_test_init(void)
  1064. {
  1065. #ifdef TEST_FRAMEWORK
  1066. ast_register_cleanup(test_cleanup);
  1067. /* Create stasis topic */
  1068. test_suite_topic = stasis_topic_create("test_suite_topic");
  1069. if (!test_suite_topic) {
  1070. return -1;
  1071. }
  1072. if (STASIS_MESSAGE_TYPE_INIT(ast_test_suite_message_type) != 0) {
  1073. return -1;
  1074. }
  1075. AST_TEST_REGISTER(test_registrations);
  1076. /* Register cli commands */
  1077. ast_cli_register_multiple(test_cli, ARRAY_LEN(test_cli));
  1078. #endif
  1079. return 0;
  1080. }