test.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  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/manager.h"
  43. /*! This array corresponds to the values defined in the ast_test_state enum */
  44. static const char * const test_result2str[] = {
  45. [AST_TEST_NOT_RUN] = "NOT RUN",
  46. [AST_TEST_PASS] = "PASS",
  47. [AST_TEST_FAIL] = "FAIL",
  48. };
  49. /*! holds all the information pertaining to a single defined test */
  50. struct ast_test {
  51. struct ast_test_info info; /*!< holds test callback information */
  52. /*!
  53. * \brief Test defined status output from last execution
  54. */
  55. struct ast_str *status_str;
  56. /*!
  57. * \brief CLI arguments, if tests being run from the CLI
  58. *
  59. * If this is set, status updates from the tests will be sent to the
  60. * CLI in addition to being saved off in status_str.
  61. */
  62. struct ast_cli_args *cli;
  63. enum ast_test_result_state state; /*!< current test state */
  64. unsigned int time; /*!< time in ms test took */
  65. ast_test_cb_t *cb; /*!< test callback function */
  66. AST_LIST_ENTRY(ast_test) entry;
  67. };
  68. /*! global structure containing both total and last test execution results */
  69. static struct ast_test_execute_results {
  70. unsigned int total_tests; /*!< total number of tests, regardless if they have been executed or not */
  71. unsigned int total_passed; /*!< total number of executed tests passed */
  72. unsigned int total_failed; /*!< total number of executed tests failed */
  73. unsigned int total_time; /*!< total time of all executed tests */
  74. unsigned int last_passed; /*!< number of passed tests during last execution */
  75. unsigned int last_failed; /*!< number of failed tests during last execution */
  76. unsigned int last_time; /*!< total time of the last test execution */
  77. } last_results;
  78. enum test_mode {
  79. TEST_ALL = 0,
  80. TEST_CATEGORY = 1,
  81. TEST_NAME_CATEGORY = 2,
  82. };
  83. /*! List of registered test definitions */
  84. static AST_LIST_HEAD_STATIC(tests, ast_test);
  85. static struct ast_test *test_alloc(ast_test_cb_t *cb);
  86. static struct ast_test *test_free(struct ast_test *test);
  87. static int test_insert(struct ast_test *test);
  88. static struct ast_test *test_remove(ast_test_cb_t *cb);
  89. static int test_cat_cmp(const char *cat1, const char *cat2);
  90. int __ast_test_status_update(const char *file, const char *func, int line,
  91. struct ast_test *test, const char *fmt, ...)
  92. {
  93. struct ast_str *buf = NULL;
  94. va_list ap;
  95. if (!(buf = ast_str_create(128))) {
  96. return -1;
  97. }
  98. va_start(ap, fmt);
  99. ast_str_set_va(&buf, 0, fmt, ap);
  100. va_end(ap);
  101. if (test->cli) {
  102. ast_cli(test->cli->fd, "[%s:%s:%d]: %s",
  103. file, func, line, ast_str_buffer(buf));
  104. }
  105. ast_str_append(&test->status_str, 0, "[%s:%s:%d]: %s",
  106. file, func, line, ast_str_buffer(buf));
  107. ast_free(buf);
  108. return 0;
  109. }
  110. int ast_test_register(ast_test_cb_t *cb)
  111. {
  112. struct ast_test *test;
  113. if (!cb) {
  114. ast_log(LOG_WARNING, "Attempted to register test without all required information\n");
  115. return -1;
  116. }
  117. if (!(test = test_alloc(cb))) {
  118. return -1;
  119. }
  120. if (test_insert(test)) {
  121. test_free(test);
  122. return -1;
  123. }
  124. return 0;
  125. }
  126. int ast_test_unregister(ast_test_cb_t *cb)
  127. {
  128. struct ast_test *test;
  129. if (!(test = test_remove(cb))) {
  130. return -1; /* not found */
  131. }
  132. test_free(test);
  133. return 0;
  134. }
  135. /*!
  136. * \internal
  137. * \brief executes a single test, storing the results in the test->result structure.
  138. *
  139. * \note The last_results structure which contains global statistics about test execution
  140. * must be updated when using this function. See use in test_execute_multiple().
  141. */
  142. static void test_execute(struct ast_test *test)
  143. {
  144. struct timeval begin;
  145. ast_str_reset(test->status_str);
  146. begin = ast_tvnow();
  147. test->state = test->cb(&test->info, TEST_EXECUTE, test);
  148. test->time = ast_tvdiff_ms(ast_tvnow(), begin);
  149. }
  150. static void test_xml_entry(struct ast_test *test, FILE *f)
  151. {
  152. if (!f || !test || test->state == AST_TEST_NOT_RUN) {
  153. return;
  154. }
  155. fprintf(f, "\t<testcase time=\"%u.%u\" name=\"%s%s\"%s>\n",
  156. test->time / 1000, test->time % 1000,
  157. test->info.category, test->info.name,
  158. test->state == AST_TEST_PASS ? "/" : "");
  159. if (test->state == AST_TEST_FAIL) {
  160. fprintf(f, "\t\t<failure><![CDATA[\n%s\n\t\t]]></failure>\n",
  161. S_OR(ast_str_buffer(test->status_str), "NA"));
  162. fprintf(f, "\t</testcase>\n");
  163. }
  164. }
  165. static void test_txt_entry(struct ast_test *test, FILE *f)
  166. {
  167. if (!f || !test) {
  168. return;
  169. }
  170. fprintf(f, "\nName: %s\n", test->info.name);
  171. fprintf(f, "Category: %s\n", test->info.category);
  172. fprintf(f, "Summary: %s\n", test->info.summary);
  173. fprintf(f, "Description: %s\n", test->info.description);
  174. fprintf(f, "Result: %s\n", test_result2str[test->state]);
  175. if (test->state != AST_TEST_NOT_RUN) {
  176. fprintf(f, "Time: %u\n", test->time);
  177. }
  178. if (test->state == AST_TEST_FAIL) {
  179. fprintf(f, "Error Description: %s\n\n", S_OR(ast_str_buffer(test->status_str), "NA"));
  180. }
  181. }
  182. /*!
  183. * \internal
  184. * \brief Executes registered unit tests
  185. *
  186. * \param name of test to run (optional)
  187. * \param test category to run (optional)
  188. * \param cli args for cli test updates (optional)
  189. *
  190. * \return number of tests executed.
  191. *
  192. * \note This function has three modes of operation
  193. * -# When given a name and category, a matching individual test will execute if found.
  194. * -# When given only a category all matching tests within that category will execute.
  195. * -# If given no name or category all registered tests will execute.
  196. */
  197. static int test_execute_multiple(const char *name, const char *category, struct ast_cli_args *cli)
  198. {
  199. char result_buf[32] = { 0 };
  200. struct ast_test *test = NULL;
  201. enum test_mode mode = TEST_ALL; /* 3 modes, 0 = run all, 1 = only by category, 2 = only by name and category */
  202. int execute = 0;
  203. int res = 0;
  204. if (!ast_strlen_zero(category)) {
  205. if (!ast_strlen_zero(name)) {
  206. mode = TEST_NAME_CATEGORY;
  207. } else {
  208. mode = TEST_CATEGORY;
  209. }
  210. }
  211. AST_LIST_LOCK(&tests);
  212. /* clear previous execution results */
  213. memset(&last_results, 0, sizeof(last_results));
  214. AST_LIST_TRAVERSE(&tests, test, entry) {
  215. execute = 0;
  216. switch (mode) {
  217. case TEST_CATEGORY:
  218. if (!test_cat_cmp(test->info.category, category)) {
  219. execute = 1;
  220. }
  221. break;
  222. case TEST_NAME_CATEGORY:
  223. if (!(test_cat_cmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
  224. execute = 1;
  225. }
  226. break;
  227. case TEST_ALL:
  228. execute = 1;
  229. }
  230. if (execute) {
  231. if (cli) {
  232. ast_cli(cli->fd, "START %s - %s \n", test->info.category, test->info.name);
  233. }
  234. /* set the test status update argument. it is ok if cli is NULL */
  235. test->cli = cli;
  236. /* execute the test and save results */
  237. test_execute(test);
  238. test->cli = NULL;
  239. /* update execution specific counts here */
  240. last_results.last_time += test->time;
  241. if (test->state == AST_TEST_PASS) {
  242. last_results.last_passed++;
  243. } else if (test->state == AST_TEST_FAIL) {
  244. last_results.last_failed++;
  245. }
  246. if (cli) {
  247. term_color(result_buf,
  248. test_result2str[test->state],
  249. (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
  250. 0,
  251. sizeof(result_buf));
  252. ast_cli(cli->fd, "END %s - %s Time: %s%ums Result: %s\n",
  253. test->info.category,
  254. test->info.name,
  255. test->time ? "" : "<",
  256. test->time ? test->time : 1,
  257. result_buf);
  258. }
  259. }
  260. /* update total counts as well during this iteration
  261. * even if the current test did not execute this time */
  262. last_results.total_time += test->time;
  263. last_results.total_tests++;
  264. if (test->state != AST_TEST_NOT_RUN) {
  265. if (test->state == AST_TEST_PASS) {
  266. last_results.total_passed++;
  267. } else {
  268. last_results.total_failed++;
  269. }
  270. }
  271. }
  272. res = last_results.last_passed + last_results.last_failed;
  273. AST_LIST_UNLOCK(&tests);
  274. return res;
  275. }
  276. /*!
  277. * \internal
  278. * \brief Generate test results.
  279. *
  280. * \param name of test result to generate (optional)
  281. * \param test category to generate (optional)
  282. * \param path to xml file to generate. (optional)
  283. * \param path to txt file to generate, (optional)
  284. *
  285. * \retval 0 success
  286. * \retval -1 failure
  287. *
  288. * \note This function has three modes of operation.
  289. * -# When given both a name and category, results will be generated for that single test.
  290. * -# When given only a category, results for every test within the category will be generated.
  291. * -# When given no name or category, results for every registered test will be generated.
  292. *
  293. * In order for the results to be generated, an xml and or txt file path must be provided.
  294. */
  295. static int test_generate_results(const char *name, const char *category, const char *xml_path, const char *txt_path)
  296. {
  297. enum test_mode mode = TEST_ALL; /* 0 generate all, 1 generate by category only, 2 generate by name and category */
  298. FILE *f_xml = NULL, *f_txt = NULL;
  299. int res = 0;
  300. struct ast_test *test = NULL;
  301. /* verify at least one output file was given */
  302. if (ast_strlen_zero(xml_path) && ast_strlen_zero(txt_path)) {
  303. return -1;
  304. }
  305. /* define what mode is to be used */
  306. if (!ast_strlen_zero(category)) {
  307. if (!ast_strlen_zero(name)) {
  308. mode = TEST_NAME_CATEGORY;
  309. } else {
  310. mode = TEST_CATEGORY;
  311. }
  312. }
  313. /* open files for writing */
  314. if (!ast_strlen_zero(xml_path)) {
  315. if (!(f_xml = fopen(xml_path, "w"))) {
  316. ast_log(LOG_WARNING, "Could not open file %s for xml test results\n", xml_path);
  317. res = -1;
  318. goto done;
  319. }
  320. }
  321. if (!ast_strlen_zero(txt_path)) {
  322. if (!(f_txt = fopen(txt_path, "w"))) {
  323. ast_log(LOG_WARNING, "Could not open file %s for text output of test results\n", txt_path);
  324. res = -1;
  325. goto done;
  326. }
  327. }
  328. AST_LIST_LOCK(&tests);
  329. /* xml header information */
  330. if (f_xml) {
  331. /*
  332. * http://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo
  333. */
  334. fprintf(f_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  335. fprintf(f_xml, "<testsuite errors=\"0\" time=\"%u.%u\" tests=\"%u\" "
  336. "name=\"AsteriskUnitTests\">\n",
  337. last_results.total_time / 1000, last_results.total_time % 1000,
  338. last_results.total_tests);
  339. fprintf(f_xml, "\t<properties>\n");
  340. fprintf(f_xml, "\t\t<property name=\"version\" value=\"%s\"/>\n", ast_get_version());
  341. fprintf(f_xml, "\t</properties>\n");
  342. }
  343. /* txt header information */
  344. if (f_txt) {
  345. fprintf(f_txt, "Asterisk Version: %s\n", ast_get_version());
  346. fprintf(f_txt, "Asterisk Version Number: %s\n", ast_get_version_num());
  347. fprintf(f_txt, "Number of Tests: %u\n", last_results.total_tests);
  348. fprintf(f_txt, "Number of Tests Executed: %u\n", (last_results.total_passed + last_results.total_failed));
  349. fprintf(f_txt, "Passed Tests: %u\n", last_results.total_passed);
  350. fprintf(f_txt, "Failed Tests: %u\n", last_results.total_failed);
  351. fprintf(f_txt, "Total Execution Time: %u\n", last_results.total_time);
  352. }
  353. /* export each individual test */
  354. AST_LIST_TRAVERSE(&tests, test, entry) {
  355. switch (mode) {
  356. case TEST_CATEGORY:
  357. if (!test_cat_cmp(test->info.category, category)) {
  358. test_xml_entry(test, f_xml);
  359. test_txt_entry(test, f_txt);
  360. }
  361. break;
  362. case TEST_NAME_CATEGORY:
  363. if (!(strcmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
  364. test_xml_entry(test, f_xml);
  365. test_txt_entry(test, f_txt);
  366. }
  367. break;
  368. case TEST_ALL:
  369. test_xml_entry(test, f_xml);
  370. test_txt_entry(test, f_txt);
  371. }
  372. }
  373. AST_LIST_UNLOCK(&tests);
  374. done:
  375. if (f_xml) {
  376. fprintf(f_xml, "</testsuite>\n");
  377. fclose(f_xml);
  378. }
  379. if (f_txt) {
  380. fclose(f_txt);
  381. }
  382. return res;
  383. }
  384. /*!
  385. * \internal
  386. * \brief adds test to container sorted first by category then by name
  387. *
  388. * \retval 0 success
  389. * \retval -1 failure
  390. */
  391. static int test_insert(struct ast_test *test)
  392. {
  393. /* This is a slow operation that may need to be optimized in the future
  394. * as the test framework expands. At the moment we are doing string
  395. * comparisons on every item within the list to insert in sorted order. */
  396. AST_LIST_LOCK(&tests);
  397. AST_LIST_INSERT_SORTALPHA(&tests, test, entry, info.category);
  398. AST_LIST_UNLOCK(&tests);
  399. return 0;
  400. }
  401. /*!
  402. * \internal
  403. * \brief removes test from container
  404. *
  405. * \return ast_test removed from list on success, or NULL on failure
  406. */
  407. static struct ast_test *test_remove(ast_test_cb_t *cb)
  408. {
  409. struct ast_test *cur = NULL;
  410. AST_LIST_LOCK(&tests);
  411. AST_LIST_TRAVERSE_SAFE_BEGIN(&tests, cur, entry) {
  412. if (cur->cb == cb) {
  413. AST_LIST_REMOVE_CURRENT(entry);
  414. break;
  415. }
  416. }
  417. AST_LIST_TRAVERSE_SAFE_END;
  418. AST_LIST_UNLOCK(&tests);
  419. return cur;
  420. }
  421. /*!
  422. * \brief compares two test categories to determine if cat1 resides in cat2
  423. * \internal
  424. *
  425. * \retval 0 true
  426. * \retval non-zero false
  427. */
  428. static int test_cat_cmp(const char *cat1, const char *cat2)
  429. {
  430. int len1 = 0;
  431. int len2 = 0;
  432. if (!cat1 || !cat2) {
  433. return -1;
  434. }
  435. len1 = strlen(cat1);
  436. len2 = strlen(cat2);
  437. if (len2 > len1) {
  438. return -1;
  439. }
  440. return strncmp(cat1, cat2, len2) ? 1 : 0;
  441. }
  442. /*!
  443. * \internal
  444. * \brief free an ast_test object and all it's data members
  445. */
  446. static struct ast_test *test_free(struct ast_test *test)
  447. {
  448. if (!test) {
  449. return NULL;
  450. }
  451. ast_free(test->status_str);
  452. ast_free(test);
  453. return NULL;
  454. }
  455. /*!
  456. * \internal
  457. * \brief allocate an ast_test object.
  458. */
  459. static struct ast_test *test_alloc(ast_test_cb_t *cb)
  460. {
  461. struct ast_test *test;
  462. if (!cb || !(test = ast_calloc(1, sizeof(*test)))) {
  463. return NULL;
  464. }
  465. test->cb = cb;
  466. test->cb(&test->info, TEST_INIT, test);
  467. if (ast_strlen_zero(test->info.name)) {
  468. ast_log(LOG_WARNING, "Test has no name, test registration refused.\n");
  469. return test_free(test);
  470. }
  471. if (ast_strlen_zero(test->info.category)) {
  472. ast_log(LOG_WARNING, "Test %s has no category, test registration refused.\n",
  473. test->info.name);
  474. return test_free(test);
  475. }
  476. if (test->info.category[0] != '/' || test->info.category[strlen(test->info.category) - 1] != '/') {
  477. ast_log(LOG_WARNING, "Test category '%s' for test '%s' is missing a leading or trailing slash.\n",
  478. test->info.category, test->info.name);
  479. }
  480. if (ast_strlen_zero(test->info.summary)) {
  481. ast_log(LOG_WARNING, "Test %s%s has no summary, test registration refused.\n",
  482. test->info.category, test->info.name);
  483. return test_free(test);
  484. }
  485. if (ast_strlen_zero(test->info.description)) {
  486. ast_log(LOG_WARNING, "Test %s%s has no description, test registration refused.\n",
  487. test->info.category, test->info.name);
  488. return test_free(test);
  489. }
  490. if (!(test->status_str = ast_str_create(128))) {
  491. return test_free(test);
  492. }
  493. return test;
  494. }
  495. static char *complete_test_category(const char *line, const char *word, int pos, int state)
  496. {
  497. int which = 0;
  498. int wordlen = strlen(word);
  499. char *ret = NULL;
  500. struct ast_test *test;
  501. AST_LIST_LOCK(&tests);
  502. AST_LIST_TRAVERSE(&tests, test, entry) {
  503. if (!strncasecmp(word, test->info.category, wordlen) && ++which > state) {
  504. ret = ast_strdup(test->info.category);
  505. break;
  506. }
  507. }
  508. AST_LIST_UNLOCK(&tests);
  509. return ret;
  510. }
  511. static char *complete_test_name(const char *line, const char *word, int pos, int state, const char *category)
  512. {
  513. int which = 0;
  514. int wordlen = strlen(word);
  515. char *ret = NULL;
  516. struct ast_test *test;
  517. AST_LIST_LOCK(&tests);
  518. AST_LIST_TRAVERSE(&tests, test, entry) {
  519. if (!test_cat_cmp(test->info.category, category) && (!strncasecmp(word, test->info.name, wordlen) && ++which > state)) {
  520. ret = ast_strdup(test->info.name);
  521. break;
  522. }
  523. }
  524. AST_LIST_UNLOCK(&tests);
  525. return ret;
  526. }
  527. /* CLI commands */
  528. static char *test_cli_show_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  529. {
  530. #define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n"
  531. static const char * const option1[] = { "all", "category", NULL };
  532. static const char * const option2[] = { "name", NULL };
  533. struct ast_test *test = NULL;
  534. int count = 0;
  535. switch (cmd) {
  536. case CLI_INIT:
  537. e->command = "test show registered";
  538. e->usage =
  539. "Usage: 'test show registered' can be used in three ways.\n"
  540. " 1. 'test show registered all' shows all registered tests\n"
  541. " 2. 'test show registered category [test category]' shows all tests in the given\n"
  542. " category.\n"
  543. " 3. 'test show registered category [test category] name [test name]' shows all\n"
  544. " tests in a given category matching a given name\n";
  545. return NULL;
  546. case CLI_GENERATE:
  547. if (a->pos == 3) {
  548. return ast_cli_complete(a->word, option1, a->n);
  549. }
  550. if (a->pos == 4) {
  551. return complete_test_category(a->line, a->word, a->pos, a->n);
  552. }
  553. if (a->pos == 5) {
  554. return ast_cli_complete(a->word, option2, a->n);
  555. }
  556. if (a->pos == 6) {
  557. return complete_test_name(a->line, a->word, a->pos, a->n, a->argv[3]);
  558. }
  559. return NULL;
  560. case CLI_HANDLER:
  561. if ((a->argc < 4) || (a->argc == 6) || (a->argc > 7) ||
  562. ((a->argc == 4) && strcmp(a->argv[3], "all")) ||
  563. ((a->argc == 7) && strcmp(a->argv[5], "name"))) {
  564. return CLI_SHOWUSAGE;
  565. }
  566. ast_cli(a->fd, FORMAT, "Category", "Name", "Summary", "Test Result");
  567. ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
  568. AST_LIST_LOCK(&tests);
  569. AST_LIST_TRAVERSE(&tests, test, entry) {
  570. if ((a->argc == 4) ||
  571. ((a->argc == 5) && !test_cat_cmp(test->info.category, a->argv[4])) ||
  572. ((a->argc == 7) && !strcmp(test->info.category, a->argv[4]) && !strcmp(test->info.name, a->argv[6]))) {
  573. ast_cli(a->fd, FORMAT, test->info.category, test->info.name,
  574. test->info.summary, test_result2str[test->state]);
  575. count++;
  576. }
  577. }
  578. AST_LIST_UNLOCK(&tests);
  579. ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
  580. ast_cli(a->fd, "\n%d Registered Tests Matched\n", count);
  581. default:
  582. return NULL;
  583. }
  584. return CLI_SUCCESS;
  585. }
  586. static char *test_cli_execute_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  587. {
  588. static const char * const option1[] = { "all", "category", NULL };
  589. static const char * const option2[] = { "name", NULL };
  590. switch (cmd) {
  591. case CLI_INIT:
  592. e->command = "test execute";
  593. e->usage =
  594. "Usage: test execute can be used in three ways.\n"
  595. " 1. 'test execute all' runs all registered tests\n"
  596. " 2. 'test execute category [test category]' runs all tests in the given\n"
  597. " category.\n"
  598. " 3. 'test execute category [test category] name [test name]' runs all\n"
  599. " tests in a given category matching a given name\n";
  600. return NULL;
  601. case CLI_GENERATE:
  602. if (a->pos == 2) {
  603. return ast_cli_complete(a->word, option1, a->n);
  604. }
  605. if (a->pos == 3) {
  606. return complete_test_category(a->line, a->word, a->pos, a->n);
  607. }
  608. if (a->pos == 4) {
  609. return ast_cli_complete(a->word, option2, a->n);
  610. }
  611. if (a->pos == 5) {
  612. return complete_test_name(a->line, a->word, a->pos, a->n, a->argv[3]);
  613. }
  614. return NULL;
  615. case CLI_HANDLER:
  616. if (a->argc < 3|| a->argc > 6) {
  617. return CLI_SHOWUSAGE;
  618. }
  619. if ((a->argc == 3) && !strcmp(a->argv[2], "all")) { /* run all registered tests */
  620. ast_cli(a->fd, "Running all available tests...\n\n");
  621. test_execute_multiple(NULL, NULL, a);
  622. } else if (a->argc == 4) { /* run only tests within a category */
  623. ast_cli(a->fd, "Running all available tests matching category %s\n\n", a->argv[3]);
  624. test_execute_multiple(NULL, a->argv[3], a);
  625. } else if (a->argc == 6) { /* run only a single test matching the category and name */
  626. ast_cli(a->fd, "Running all available tests matching category %s and name %s\n\n", a->argv[3], a->argv[5]);
  627. test_execute_multiple(a->argv[5], a->argv[3], a);
  628. } else {
  629. return CLI_SHOWUSAGE;
  630. }
  631. AST_LIST_LOCK(&tests);
  632. if (!(last_results.last_passed + last_results.last_failed)) {
  633. ast_cli(a->fd, "--- No Tests Found! ---\n");
  634. }
  635. ast_cli(a->fd, "\n%u Test(s) Executed %u Passed %u Failed\n",
  636. (last_results.last_passed + last_results.last_failed),
  637. last_results.last_passed,
  638. last_results.last_failed);
  639. AST_LIST_UNLOCK(&tests);
  640. default:
  641. return NULL;
  642. }
  643. return CLI_SUCCESS;
  644. }
  645. static char *test_cli_show_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  646. {
  647. #define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n"
  648. #define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n"
  649. static const char * const option1[] = { "all", "failed", "passed", NULL };
  650. char result_buf[32] = { 0 };
  651. struct ast_test *test = NULL;
  652. int failed = 0;
  653. int passed = 0;
  654. int mode; /* 0 for show all, 1 for show fail, 2 for show passed */
  655. switch (cmd) {
  656. case CLI_INIT:
  657. e->command = "test show results";
  658. e->usage =
  659. "Usage: test show results can be used in three ways\n"
  660. " 1. 'test show results all' Displays results for all executed tests.\n"
  661. " 2. 'test show results passed' Displays results for all passed tests.\n"
  662. " 3. 'test show results failed' Displays results for all failed tests.\n";
  663. return NULL;
  664. case CLI_GENERATE:
  665. if (a->pos == 3) {
  666. return ast_cli_complete(a->word, option1, a->n);
  667. }
  668. return NULL;
  669. case CLI_HANDLER:
  670. /* verify input */
  671. if (a->argc != 4) {
  672. return CLI_SHOWUSAGE;
  673. } else if (!strcmp(a->argv[3], "passed")) {
  674. mode = 2;
  675. } else if (!strcmp(a->argv[3], "failed")) {
  676. mode = 1;
  677. } else if (!strcmp(a->argv[3], "all")) {
  678. mode = 0;
  679. } else {
  680. return CLI_SHOWUSAGE;
  681. }
  682. ast_cli(a->fd, FORMAT_RES_ALL1, "Result", "", "Name", "Category", "Time");
  683. AST_LIST_LOCK(&tests);
  684. AST_LIST_TRAVERSE(&tests, test, entry) {
  685. if (test->state == AST_TEST_NOT_RUN) {
  686. continue;
  687. }
  688. test->state == AST_TEST_FAIL ? failed++ : passed++;
  689. if (!mode || ((mode == 1) && (test->state == AST_TEST_FAIL)) || ((mode == 2) && (test->state == AST_TEST_PASS))) {
  690. /* give our results pretty colors */
  691. term_color(result_buf, test_result2str[test->state],
  692. (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
  693. 0, sizeof(result_buf));
  694. ast_cli(a->fd, FORMAT_RES_ALL2,
  695. result_buf,
  696. " ",
  697. test->info.name,
  698. test->info.category,
  699. test->time ? " " : "<",
  700. test->time ? test->time : 1);
  701. }
  702. }
  703. AST_LIST_UNLOCK(&tests);
  704. ast_cli(a->fd, "%d Test(s) Executed %d Passed %d Failed\n", (failed + passed), passed, failed);
  705. default:
  706. return NULL;
  707. }
  708. return CLI_SUCCESS;
  709. }
  710. static char *test_cli_generate_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  711. {
  712. static const char * const option[] = { "xml", "txt", NULL };
  713. const char *file = NULL;
  714. const char *type = "";
  715. int isxml = 0;
  716. int res = 0;
  717. struct ast_str *buf = NULL;
  718. struct timeval time = ast_tvnow();
  719. switch (cmd) {
  720. case CLI_INIT:
  721. e->command = "test generate results";
  722. e->usage =
  723. "Usage: 'test generate results'\n"
  724. " Generates test results in either xml or txt format. An optional \n"
  725. " file path may be provided to specify the location of the xml or\n"
  726. " txt file\n"
  727. " \nExample usage:\n"
  728. " 'test generate results xml' this writes to a default file\n"
  729. " 'test generate results xml /path/to/file.xml' writes to specified file\n";
  730. return NULL;
  731. case CLI_GENERATE:
  732. if (a->pos == 3) {
  733. return ast_cli_complete(a->word, option, a->n);
  734. }
  735. return NULL;
  736. case CLI_HANDLER:
  737. /* verify input */
  738. if (a->argc < 4 || a->argc > 5) {
  739. return CLI_SHOWUSAGE;
  740. } else if (!strcmp(a->argv[3], "xml")) {
  741. type = "xml";
  742. isxml = 1;
  743. } else if (!strcmp(a->argv[3], "txt")) {
  744. type = "txt";
  745. } else {
  746. return CLI_SHOWUSAGE;
  747. }
  748. if (a->argc == 5) {
  749. file = a->argv[4];
  750. } else {
  751. if (!(buf = ast_str_create(256))) {
  752. return NULL;
  753. }
  754. ast_str_set(&buf, 0, "%s/asterisk_test_results-%ld.%s", ast_config_AST_LOG_DIR, (long) time.tv_sec, type);
  755. file = ast_str_buffer(buf);
  756. }
  757. if (isxml) {
  758. res = test_generate_results(NULL, NULL, file, NULL);
  759. } else {
  760. res = test_generate_results(NULL, NULL, NULL, file);
  761. }
  762. if (!res) {
  763. ast_cli(a->fd, "Results Generated Successfully: %s\n", S_OR(file, ""));
  764. } else {
  765. ast_cli(a->fd, "Results Could Not Be Generated: %s\n", S_OR(file, ""));
  766. }
  767. ast_free(buf);
  768. default:
  769. return NULL;
  770. }
  771. return CLI_SUCCESS;
  772. }
  773. static struct ast_cli_entry test_cli[] = {
  774. AST_CLI_DEFINE(test_cli_show_registered, "show registered tests"),
  775. AST_CLI_DEFINE(test_cli_execute_registered, "execute registered tests"),
  776. AST_CLI_DEFINE(test_cli_show_results, "show last test results"),
  777. AST_CLI_DEFINE(test_cli_generate_results, "generate test results to file"),
  778. };
  779. int __ast_test_suite_event_notify(const char *file, const char *func, int line,
  780. const char *state, const char *fmt, ...)
  781. {
  782. struct ast_str *buf = NULL;
  783. va_list ap;
  784. if (!(buf = ast_str_create(128))) {
  785. return -1;
  786. }
  787. va_start(ap, fmt);
  788. ast_str_set_va(&buf, 0, fmt, ap);
  789. va_end(ap);
  790. manager_event(EVENT_FLAG_TEST, "TestEvent",
  791. "Type: StateChange\r\n"
  792. "State: %s\r\n"
  793. "AppFile: %s\r\n"
  794. "AppFunction: %s\r\n"
  795. "AppLine: %d\r\n%s\r\n",
  796. state, file, func, line, ast_str_buffer(buf));
  797. ast_free(buf);
  798. return 0;
  799. }
  800. int __ast_test_suite_assert_notify(const char *file, const char *func, int line,
  801. const char *exp)
  802. {
  803. manager_event(EVENT_FLAG_TEST, "TestEvent",
  804. "Type: Assert\r\n"
  805. "AppFile: %s\r\n"
  806. "AppFunction: %s\r\n"
  807. "AppLine: %d\r\n"
  808. "Expression: %s\r\n",
  809. file, func, line, exp);
  810. return 0;
  811. }
  812. static void test_shutdown(void)
  813. {
  814. ast_cli_unregister_multiple(test_cli, ARRAY_LEN(test_cli));
  815. }
  816. #endif /* TEST_FRAMEWORK */
  817. int ast_test_init()
  818. {
  819. #ifdef TEST_FRAMEWORK
  820. /* Register cli commands */
  821. ast_cli_register_multiple(test_cli, ARRAY_LEN(test_cli));
  822. ast_register_cleanup(test_shutdown);
  823. #endif
  824. return 0;
  825. }