2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2009-2010, Digium, Inc.
6 * David Vossel <dvossel@digium.com>
7 * Russell Bryant <russell@digium.com>
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.
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.
22 * \brief Unit Test Framework
24 * \author David Vossel <dvossel@digium.com>
25 * \author Russell Bryant <russell@digium.com>
29 <support_level>core</support_level>
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
36 #include "asterisk/_private.h"
39 #include "asterisk/test.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/linkedlists.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/term.h"
45 #include "asterisk/ast_version.h"
46 #include "asterisk/paths.h"
47 #include "asterisk/time.h"
48 #include "asterisk/manager.h"
50 /*! This array corresponds to the values defined in the ast_test_state enum */
51 static const char * const test_result2str[] = {
52 [AST_TEST_NOT_RUN] = "NOT RUN",
53 [AST_TEST_PASS] = "PASS",
54 [AST_TEST_FAIL] = "FAIL",
57 /*! holds all the information pertaining to a single defined test */
59 struct ast_test_info info; /*!< holds test callback information */
61 * \brief Test defined status output from last execution
63 struct ast_str *status_str;
65 * \brief CLI arguments, if tests being run from the CLI
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.
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_LIST_ENTRY(ast_test) entry;
77 /*! global structure containing both total and last test execution results */
78 static struct ast_test_execute_results {
79 unsigned int total_tests; /*!< total number of tests, regardless if they have been executed or not */
80 unsigned int total_passed; /*!< total number of executed tests passed */
81 unsigned int total_failed; /*!< total number of executed tests failed */
82 unsigned int total_time; /*!< total time of all executed tests */
83 unsigned int last_passed; /*!< number of passed tests during last execution */
84 unsigned int last_failed; /*!< number of failed tests during last execution */
85 unsigned int last_time; /*!< total time of the last test execution */
91 TEST_NAME_CATEGORY = 2,
94 /*! List of registered test definitions */
95 static AST_LIST_HEAD_STATIC(tests, ast_test);
97 static struct ast_test *test_alloc(ast_test_cb_t *cb);
98 static struct ast_test *test_free(struct ast_test *test);
99 static int test_insert(struct ast_test *test);
100 static struct ast_test *test_remove(ast_test_cb_t *cb);
101 static int test_cat_cmp(const char *cat1, const char *cat2);
103 void ast_test_debug(struct ast_test *test, const char *fmt, ...)
105 struct ast_str *buf = NULL;
108 buf = ast_str_create(128);
114 ast_str_set_va(&buf, 0, fmt, ap);
118 ast_cli(test->cli->fd, "%s", ast_str_buffer(buf));
124 int __ast_test_status_update(const char *file, const char *func, int line, struct ast_test *test, const char *fmt, ...)
126 struct ast_str *buf = NULL;
129 if (!(buf = ast_str_create(128))) {
134 ast_str_set_va(&buf, 0, fmt, ap);
138 ast_cli(test->cli->fd, "[%s:%s:%d]: %s",
139 file, func, line, ast_str_buffer(buf));
142 ast_str_append(&test->status_str, 0, "[%s:%s:%d]: %s",
143 file, func, line, ast_str_buffer(buf));
150 int ast_test_register(ast_test_cb_t *cb)
152 struct ast_test *test;
155 ast_log(LOG_WARNING, "Attempted to register test without all required information\n");
159 if (!(test = test_alloc(cb))) {
163 if (test_insert(test)) {
171 int ast_test_unregister(ast_test_cb_t *cb)
173 struct ast_test *test;
175 if (!(test = test_remove(cb))) {
176 return -1; /* not found */
186 * \brief executes a single test, storing the results in the test->result structure.
188 * \note The last_results structure which contains global statistics about test execution
189 * must be updated when using this function. See use in test_execute_multiple().
191 static void test_execute(struct ast_test *test)
193 struct timeval begin;
195 ast_str_reset(test->status_str);
198 test->state = test->cb(&test->info, TEST_EXECUTE, test);
199 test->time = ast_tvdiff_ms(ast_tvnow(), begin);
202 static void test_xml_entry(struct ast_test *test, FILE *f)
204 if (!f || !test || test->state == AST_TEST_NOT_RUN) {
208 fprintf(f, "\t<testcase time=\"%d.%d\" name=\"%s%s\"%s>\n",
209 test->time / 1000, test->time % 1000,
210 test->info.category, test->info.name,
211 test->state == AST_TEST_PASS ? "/" : "");
213 if (test->state == AST_TEST_FAIL) {
214 fprintf(f, "\t\t<failure><![CDATA[\n%s\n\t\t]]></failure>\n",
215 S_OR(ast_str_buffer(test->status_str), "NA"));
216 fprintf(f, "\t</testcase>\n");
221 static void test_txt_entry(struct ast_test *test, FILE *f)
227 fprintf(f, "\nName: %s\n", test->info.name);
228 fprintf(f, "Category: %s\n", test->info.category);
229 fprintf(f, "Summary: %s\n", test->info.summary);
230 fprintf(f, "Description: %s\n", test->info.description);
231 fprintf(f, "Result: %s\n", test_result2str[test->state]);
232 if (test->state != AST_TEST_NOT_RUN) {
233 fprintf(f, "Time: %d\n", test->time);
235 if (test->state == AST_TEST_FAIL) {
236 fprintf(f, "Error Description: %s\n\n", S_OR(ast_str_buffer(test->status_str), "NA"));
242 * \brief Executes registered unit tests
244 * \param name of test to run (optional)
245 * \param test category to run (optional)
246 * \param cli args for cli test updates (optional)
248 * \return number of tests executed.
250 * \note This function has three modes of operation
251 * -# When given a name and category, a matching individual test will execute if found.
252 * -# When given only a category all matching tests within that category will execute.
253 * -# If given no name or category all registered tests will execute.
255 static int test_execute_multiple(const char *name, const char *category, struct ast_cli_args *cli)
257 char result_buf[32] = { 0 };
258 struct ast_test *test = NULL;
259 enum test_mode mode = TEST_ALL; /* 3 modes, 0 = run all, 1 = only by category, 2 = only by name and category */
263 if (!ast_strlen_zero(category)) {
264 if (!ast_strlen_zero(name)) {
265 mode = TEST_NAME_CATEGORY;
267 mode = TEST_CATEGORY;
271 AST_LIST_LOCK(&tests);
272 /* clear previous execution results */
273 memset(&last_results, 0, sizeof(last_results));
274 AST_LIST_TRAVERSE(&tests, test, entry) {
279 if (!test_cat_cmp(test->info.category, category)) {
283 case TEST_NAME_CATEGORY:
284 if (!(test_cat_cmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
294 ast_cli(cli->fd, "START %s - %s \n", test->info.category, test->info.name);
297 /* set the test status update argument. it is ok if cli is NULL */
300 /* execute the test and save results */
305 /* update execution specific counts here */
306 last_results.last_time += test->time;
307 if (test->state == AST_TEST_PASS) {
308 last_results.last_passed++;
309 } else if (test->state == AST_TEST_FAIL) {
310 last_results.last_failed++;
314 term_color(result_buf,
315 test_result2str[test->state],
316 (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
319 ast_cli(cli->fd, "END %s - %s Time: %s%dms Result: %s\n",
322 test->time ? "" : "<",
323 test->time ? test->time : 1,
328 /* update total counts as well during this iteration
329 * even if the current test did not execute this time */
330 last_results.total_time += test->time;
331 last_results.total_tests++;
332 if (test->state != AST_TEST_NOT_RUN) {
333 if (test->state == AST_TEST_PASS) {
334 last_results.total_passed++;
336 last_results.total_failed++;
340 res = last_results.last_passed + last_results.last_failed;
341 AST_LIST_UNLOCK(&tests);
348 * \brief Generate test results.
350 * \param name of test result to generate (optional)
351 * \param test category to generate (optional)
352 * \param path to xml file to generate. (optional)
353 * \param path to txt file to generate, (optional)
358 * \note This function has three modes of operation.
359 * -# When given both a name and category, results will be generated for that single test.
360 * -# When given only a category, results for every test within the category will be generated.
361 * -# When given no name or category, results for every registered test will be generated.
363 * In order for the results to be generated, an xml and or txt file path must be provided.
365 static int test_generate_results(const char *name, const char *category, const char *xml_path, const char *txt_path)
367 enum test_mode mode = TEST_ALL; /* 0 generate all, 1 generate by category only, 2 generate by name and category */
368 FILE *f_xml = NULL, *f_txt = NULL;
370 struct ast_test *test = NULL;
372 /* verify at least one output file was given */
373 if (ast_strlen_zero(xml_path) && ast_strlen_zero(txt_path)) {
377 /* define what mode is to be used */
378 if (!ast_strlen_zero(category)) {
379 if (!ast_strlen_zero(name)) {
380 mode = TEST_NAME_CATEGORY;
382 mode = TEST_CATEGORY;
385 /* open files for writing */
386 if (!ast_strlen_zero(xml_path)) {
387 if (!(f_xml = fopen(xml_path, "w"))) {
388 ast_log(LOG_WARNING, "Could not open file %s for xml test results\n", xml_path);
393 if (!ast_strlen_zero(txt_path)) {
394 if (!(f_txt = fopen(txt_path, "w"))) {
395 ast_log(LOG_WARNING, "Could not open file %s for text output of test results\n", txt_path);
401 AST_LIST_LOCK(&tests);
402 /* xml header information */
405 * http://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo
407 fprintf(f_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
408 fprintf(f_xml, "<testsuite errors=\"0\" time=\"%d.%d\" tests=\"%d\" "
409 "name=\"AsteriskUnitTests\">\n",
410 last_results.total_time / 1000, last_results.total_time % 1000,
411 last_results.total_tests);
412 fprintf(f_xml, "\t<properties>\n");
413 fprintf(f_xml, "\t\t<property name=\"version\" value=\"%s\"/>\n", ast_get_version());
414 fprintf(f_xml, "\t</properties>\n");
417 /* txt header information */
419 fprintf(f_txt, "Asterisk Version: %s\n", ast_get_version());
420 fprintf(f_txt, "Asterisk Version Number: %s\n", ast_get_version_num());
421 fprintf(f_txt, "Number of Tests: %d\n", last_results.total_tests);
422 fprintf(f_txt, "Number of Tests Executed: %d\n", (last_results.total_passed + last_results.total_failed));
423 fprintf(f_txt, "Passed Tests: %d\n", last_results.total_passed);
424 fprintf(f_txt, "Failed Tests: %d\n", last_results.total_failed);
425 fprintf(f_txt, "Total Execution Time: %d\n", last_results.total_time);
428 /* export each individual test */
429 AST_LIST_TRAVERSE(&tests, test, entry) {
432 if (!test_cat_cmp(test->info.category, category)) {
433 test_xml_entry(test, f_xml);
434 test_txt_entry(test, f_txt);
437 case TEST_NAME_CATEGORY:
438 if (!(strcmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
439 test_xml_entry(test, f_xml);
440 test_txt_entry(test, f_txt);
444 test_xml_entry(test, f_xml);
445 test_txt_entry(test, f_txt);
448 AST_LIST_UNLOCK(&tests);
452 fprintf(f_xml, "</testsuite>\n");
464 * \brief adds test to container sorted first by category then by name
469 static int test_insert(struct ast_test *test)
471 /* This is a slow operation that may need to be optimized in the future
472 * as the test framework expands. At the moment we are doing string
473 * comparisons on every item within the list to insert in sorted order. */
475 AST_LIST_LOCK(&tests);
476 AST_LIST_INSERT_SORTALPHA(&tests, test, entry, info.category);
477 AST_LIST_UNLOCK(&tests);
484 * \brief removes test from container
486 * \return ast_test removed from list on success, or NULL on failure
488 static struct ast_test *test_remove(ast_test_cb_t *cb)
490 struct ast_test *cur = NULL;
492 AST_LIST_LOCK(&tests);
493 AST_LIST_TRAVERSE_SAFE_BEGIN(&tests, cur, entry) {
495 AST_LIST_REMOVE_CURRENT(entry);
499 AST_LIST_TRAVERSE_SAFE_END;
500 AST_LIST_UNLOCK(&tests);
506 * \brief compares two test categories to determine if cat1 resides in cat2
510 * \retval non-zero false
513 static int test_cat_cmp(const char *cat1, const char *cat2)
518 if (!cat1 || !cat2) {
529 return strncmp(cat1, cat2, len2) ? 1 : 0;
534 * \brief free an ast_test object and all it's data members
536 static struct ast_test *test_free(struct ast_test *test)
542 ast_free(test->status_str);
550 * \brief allocate an ast_test object.
552 static struct ast_test *test_alloc(ast_test_cb_t *cb)
554 struct ast_test *test;
556 if (!cb || !(test = ast_calloc(1, sizeof(*test)))) {
562 test->cb(&test->info, TEST_INIT, test);
564 if (ast_strlen_zero(test->info.name)) {
565 ast_log(LOG_WARNING, "Test has no name, test registration refused.\n");
566 return test_free(test);
569 if (ast_strlen_zero(test->info.category)) {
570 ast_log(LOG_WARNING, "Test %s has no category, test registration refused.\n",
572 return test_free(test);
575 if (test->info.category[0] != '/' || test->info.category[strlen(test->info.category) - 1] != '/') {
576 ast_log(LOG_WARNING, "Test category '%s' for test '%s' is missing a leading or trailing slash.\n",
577 test->info.category, test->info.name);
580 if (ast_strlen_zero(test->info.summary)) {
581 ast_log(LOG_WARNING, "Test %s%s has no summary, test registration refused.\n",
582 test->info.category, test->info.name);
583 return test_free(test);
586 if (ast_strlen_zero(test->info.description)) {
587 ast_log(LOG_WARNING, "Test %s%s has no description, test registration refused.\n",
588 test->info.category, test->info.name);
589 return test_free(test);
592 if (!(test->status_str = ast_str_create(128))) {
593 return test_free(test);
599 static char *complete_test_category(const char *line, const char *word, int pos, int state)
602 int wordlen = strlen(word);
604 struct ast_test *test;
606 AST_LIST_LOCK(&tests);
607 AST_LIST_TRAVERSE(&tests, test, entry) {
608 if (!strncasecmp(word, test->info.category, wordlen) && ++which > state) {
609 ret = ast_strdup(test->info.category);
613 AST_LIST_UNLOCK(&tests);
617 static char *complete_test_name(const char *line, const char *word, int pos, int state, const char *category)
620 int wordlen = strlen(word);
622 struct ast_test *test;
624 AST_LIST_LOCK(&tests);
625 AST_LIST_TRAVERSE(&tests, test, entry) {
626 if (!test_cat_cmp(test->info.category, category) && (!strncasecmp(word, test->info.name, wordlen) && ++which > state)) {
627 ret = ast_strdup(test->info.name);
631 AST_LIST_UNLOCK(&tests);
636 static char *test_cli_show_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
638 #define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n"
639 static const char * const option1[] = { "all", "category", NULL };
640 static const char * const option2[] = { "name", NULL };
641 struct ast_test *test = NULL;
645 e->command = "test show registered";
648 "Usage: 'test show registered' can be used in three ways.\n"
649 " 1. 'test show registered all' shows all registered tests\n"
650 " 2. 'test show registered category [test category]' shows all tests in the given\n"
652 " 3. 'test show registered category [test category] name [test name]' shows all\n"
653 " tests in a given category matching a given name\n";
657 return ast_cli_complete(a->word, option1, a->n);
660 return complete_test_category(a->line, a->word, a->pos, a->n);
663 return ast_cli_complete(a->word, option2, a->n);
666 return complete_test_name(a->line, a->word, a->pos, a->n, a->argv[3]);
670 if ((a->argc < 4) || (a->argc == 6) || (a->argc > 7) ||
671 ((a->argc == 4) && strcmp(a->argv[3], "all")) ||
672 ((a->argc == 7) && strcmp(a->argv[5], "name"))) {
673 return CLI_SHOWUSAGE;
675 ast_cli(a->fd, FORMAT, "Category", "Name", "Summary", "Test Result");
676 ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
677 AST_LIST_LOCK(&tests);
678 AST_LIST_TRAVERSE(&tests, test, entry) {
679 if ((a->argc == 4) ||
680 ((a->argc == 5) && !test_cat_cmp(test->info.category, a->argv[4])) ||
681 ((a->argc == 7) && !strcmp(test->info.category, a->argv[4]) && !strcmp(test->info.name, a->argv[6]))) {
683 ast_cli(a->fd, FORMAT, test->info.category, test->info.name,
684 test->info.summary, test_result2str[test->state]);
688 AST_LIST_UNLOCK(&tests);
689 ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
690 ast_cli(a->fd, "\n%d Registered Tests Matched\n", count);
698 static char *test_cli_execute_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
700 static const char * const option1[] = { "all", "category", NULL };
701 static const char * const option2[] = { "name", NULL };
705 e->command = "test execute";
707 "Usage: test execute can be used in three ways.\n"
708 " 1. 'test execute all' runs all registered tests\n"
709 " 2. 'test execute category [test category]' runs all tests in the given\n"
711 " 3. 'test execute category [test category] name [test name]' runs all\n"
712 " tests in a given category matching a given name\n";
716 return ast_cli_complete(a->word, option1, a->n);
719 return complete_test_category(a->line, a->word, a->pos, a->n);
722 return ast_cli_complete(a->word, option2, a->n);
725 return complete_test_name(a->line, a->word, a->pos, a->n, a->argv[3]);
730 if (a->argc < 3|| a->argc > 6) {
731 return CLI_SHOWUSAGE;
734 if ((a->argc == 3) && !strcmp(a->argv[2], "all")) { /* run all registered tests */
735 ast_cli(a->fd, "Running all available tests...\n\n");
736 test_execute_multiple(NULL, NULL, a);
737 } else if (a->argc == 4) { /* run only tests within a category */
738 ast_cli(a->fd, "Running all available tests matching category %s\n\n", a->argv[3]);
739 test_execute_multiple(NULL, a->argv[3], a);
740 } else if (a->argc == 6) { /* run only a single test matching the category and name */
741 ast_cli(a->fd, "Running all available tests matching category %s and name %s\n\n", a->argv[3], a->argv[5]);
742 test_execute_multiple(a->argv[5], a->argv[3], a);
744 return CLI_SHOWUSAGE;
747 AST_LIST_LOCK(&tests);
748 if (!(last_results.last_passed + last_results.last_failed)) {
749 ast_cli(a->fd, "--- No Tests Found! ---\n");
751 ast_cli(a->fd, "\n%d Test(s) Executed %d Passed %d Failed\n",
752 (last_results.last_passed + last_results.last_failed),
753 last_results.last_passed,
754 last_results.last_failed);
755 AST_LIST_UNLOCK(&tests);
763 static char *test_cli_show_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
765 #define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n"
766 #define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n"
767 static const char * const option1[] = { "all", "failed", "passed", NULL };
768 char result_buf[32] = { 0 };
769 struct ast_test *test = NULL;
772 int mode; /* 0 for show all, 1 for show fail, 2 for show passed */
776 e->command = "test show results";
778 "Usage: test show results can be used in three ways\n"
779 " 1. 'test show results all' Displays results for all executed tests.\n"
780 " 2. 'test show results passed' Displays results for all passed tests.\n"
781 " 3. 'test show results failed' Displays results for all failed tests.\n";
785 return ast_cli_complete(a->word, option1, a->n);
792 return CLI_SHOWUSAGE;
793 } else if (!strcmp(a->argv[3], "passed")) {
795 } else if (!strcmp(a->argv[3], "failed")) {
797 } else if (!strcmp(a->argv[3], "all")) {
800 return CLI_SHOWUSAGE;
803 ast_cli(a->fd, FORMAT_RES_ALL1, "Result", "", "Name", "Category", "Time");
804 AST_LIST_LOCK(&tests);
805 AST_LIST_TRAVERSE(&tests, test, entry) {
806 if (test->state == AST_TEST_NOT_RUN) {
809 test->state == AST_TEST_FAIL ? failed++ : passed++;
810 if (!mode || ((mode == 1) && (test->state == AST_TEST_FAIL)) || ((mode == 2) && (test->state == AST_TEST_PASS))) {
811 /* give our results pretty colors */
812 term_color(result_buf, test_result2str[test->state],
813 (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
814 0, sizeof(result_buf));
816 ast_cli(a->fd, FORMAT_RES_ALL2,
821 test->time ? " " : "<",
822 test->time ? test->time : 1);
825 AST_LIST_UNLOCK(&tests);
827 ast_cli(a->fd, "%d Test(s) Executed %d Passed %d Failed\n", (failed + passed), passed, failed);
834 static char *test_cli_generate_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
836 static const char * const option[] = { "xml", "txt", NULL };
837 const char *file = NULL;
838 const char *type = "";
841 struct ast_str *buf = NULL;
842 struct timeval time = ast_tvnow();
846 e->command = "test generate results";
848 "Usage: 'test generate results'\n"
849 " Generates test results in either xml or txt format. An optional \n"
850 " file path may be provided to specify the location of the xml or\n"
852 " \nExample usage:\n"
853 " 'test generate results xml' this writes to a default file\n"
854 " 'test generate results xml /path/to/file.xml' writes to specified file\n";
858 return ast_cli_complete(a->word, option, a->n);
864 if (a->argc < 4 || a->argc > 5) {
865 return CLI_SHOWUSAGE;
866 } else if (!strcmp(a->argv[3], "xml")) {
869 } else if (!strcmp(a->argv[3], "txt")) {
872 return CLI_SHOWUSAGE;
878 if (!(buf = ast_str_create(256))) {
881 ast_str_set(&buf, 0, "%s/asterisk_test_results-%ld.%s", ast_config_AST_LOG_DIR, (long) time.tv_sec, type);
883 file = ast_str_buffer(buf);
887 res = test_generate_results(NULL, NULL, file, NULL);
889 res = test_generate_results(NULL, NULL, NULL, file);
893 ast_cli(a->fd, "Results Generated Successfully: %s\n", S_OR(file, ""));
895 ast_cli(a->fd, "Results Could Not Be Generated: %s\n", S_OR(file, ""));
906 static struct ast_cli_entry test_cli[] = {
907 AST_CLI_DEFINE(test_cli_show_registered, "show registered tests"),
908 AST_CLI_DEFINE(test_cli_execute_registered, "execute registered tests"),
909 AST_CLI_DEFINE(test_cli_show_results, "show last test results"),
910 AST_CLI_DEFINE(test_cli_generate_results, "generate test results to file"),
913 void __ast_test_suite_event_notify(const char *file, const char *func, int line, const char *state, const char *fmt, ...)
915 struct ast_str *buf = NULL;
918 if (!(buf = ast_str_create(128))) {
923 ast_str_set_va(&buf, 0, fmt, ap);
926 manager_event(EVENT_FLAG_TEST, "TestEvent",
927 "Type: StateChange\r\n"
930 "AppFunction: %s\r\n"
933 state, file, func, line, ast_str_buffer(buf));
938 void __ast_test_suite_assert_notify(const char *file, const char *func, int line, const char *exp)
940 manager_event(EVENT_FLAG_TEST, "TestEvent",
943 "AppFunction: %s\r\n"
945 "Expression: %s\r\n",
946 file, func, line, exp);
949 #endif /* TEST_FRAMEWORK */
951 int ast_test_init(void)
953 #ifdef TEST_FRAMEWORK
954 /* Register cli commands */
955 ast_cli_register_multiple(test_cli, ARRAY_LEN(test_cli));