51 static const char *
const test_result2str[] = {
78 static struct ast_test_execute_results {
79 unsigned int total_tests;
80 unsigned int total_passed;
81 unsigned int total_failed;
82 unsigned int total_time;
83 unsigned int last_passed;
84 unsigned int last_failed;
85 unsigned int last_time;
91 TEST_NAME_CATEGORY = 2,
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 int __ast_test_status_update(
const char *file,
const char *func,
int line,
104 struct ast_test *test,
const char *fmt, ...)
118 ast_cli(test->cli->fd,
"[%s:%s:%d]: %s",
130 int ast_test_register(ast_test_cb_t *cb)
132 struct ast_test *test;
135 ast_log(
LOG_WARNING,
"Attempted to register test without all required information\n");
139 if (!(test = test_alloc(cb))) {
143 if (test_insert(test)) {
151 int ast_test_unregister(ast_test_cb_t *cb)
153 struct ast_test *test;
155 if (!(test = test_remove(cb))) {
171 static void test_execute(
struct ast_test *test)
173 struct timeval begin;
178 test->state = test->cb(&test->info,
TEST_EXECUTE, test);
182 static void test_xml_entry(
struct ast_test *test, FILE *
f)
188 fprintf(f,
"\t<testcase time=\"%u.%u\" name=\"%s%s\"%s>\n",
189 test->time / 1000, test->time % 1000,
190 test->info.category, test->info.name,
194 fprintf(f,
"\t\t<failure><![CDATA[\n%s\n\t\t]]></failure>\n",
196 fprintf(f,
"\t</testcase>\n");
201 static void test_txt_entry(
struct ast_test *test, FILE *f)
207 fprintf(f,
"\nName: %s\n", test->info.name);
208 fprintf(f,
"Category: %s\n", test->info.category);
209 fprintf(f,
"Summary: %s\n", test->info.summary);
210 fprintf(f,
"Description: %s\n", test->info.description);
211 fprintf(f,
"Result: %s\n", test_result2str[test->state]);
213 fprintf(f,
"Time: %u\n", test->time);
216 fprintf(f,
"Error Description: %s\n\n",
S_OR(
ast_str_buffer(test->status_str),
"NA"));
235 static int test_execute_multiple(
const char *
name,
const char *category,
struct ast_cli_args *
cli)
238 struct ast_test *test = NULL;
239 enum test_mode mode = TEST_ALL;
245 mode = TEST_NAME_CATEGORY;
247 mode = TEST_CATEGORY;
253 memset(&last_results, 0,
sizeof(last_results));
259 if (!test_cat_cmp(test->info.category, category)) {
263 case TEST_NAME_CATEGORY:
264 if (!(test_cat_cmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
274 ast_cli(cli->
fd,
"START %s - %s \n", test->info.category, test->info.name);
286 last_results.last_time += test->time;
288 last_results.last_passed++;
290 last_results.last_failed++;
295 test_result2str[test->state],
299 ast_cli(cli->
fd,
"END %s - %s Time: %s%ums Result: %s\n",
302 test->time ?
"" :
"<",
303 test->time ? test->time : 1,
310 last_results.total_time += test->time;
311 last_results.total_tests++;
314 last_results.total_passed++;
316 last_results.total_failed++;
320 res = last_results.last_passed + last_results.last_failed;
345 static int test_generate_results(
const char *name,
const char *category,
const char *xml_path,
const char *txt_path)
347 enum test_mode mode = TEST_ALL;
348 FILE *f_xml = NULL, *f_txt = NULL;
350 struct ast_test *test = NULL;
360 mode = TEST_NAME_CATEGORY;
362 mode = TEST_CATEGORY;
367 if (!(f_xml = fopen(xml_path,
"w"))) {
374 if (!(f_txt = fopen(txt_path,
"w"))) {
375 ast_log(
LOG_WARNING,
"Could not open file %s for text output of test results\n", txt_path);
387 fprintf(f_xml,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
388 fprintf(f_xml,
"<testsuite errors=\"0\" time=\"%u.%u\" tests=\"%u\" "
389 "name=\"AsteriskUnitTests\">\n",
390 last_results.total_time / 1000, last_results.total_time % 1000,
391 last_results.total_tests);
392 fprintf(f_xml,
"\t<properties>\n");
393 fprintf(f_xml,
"\t\t<property name=\"version\" value=\"%s\"/>\n",
ast_get_version());
394 fprintf(f_xml,
"\t</properties>\n");
401 fprintf(f_txt,
"Number of Tests: %u\n", last_results.total_tests);
402 fprintf(f_txt,
"Number of Tests Executed: %u\n", (last_results.total_passed + last_results.total_failed));
403 fprintf(f_txt,
"Passed Tests: %u\n", last_results.total_passed);
404 fprintf(f_txt,
"Failed Tests: %u\n", last_results.total_failed);
405 fprintf(f_txt,
"Total Execution Time: %u\n", last_results.total_time);
412 if (!test_cat_cmp(test->info.category, category)) {
413 test_xml_entry(test, f_xml);
414 test_txt_entry(test, f_txt);
417 case TEST_NAME_CATEGORY:
418 if (!(strcmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
419 test_xml_entry(test, f_xml);
420 test_txt_entry(test, f_txt);
424 test_xml_entry(test, f_xml);
425 test_txt_entry(test, f_txt);
432 fprintf(f_xml,
"</testsuite>\n");
449 static int test_insert(
struct ast_test *test)
468 static struct ast_test *test_remove(ast_test_cb_t *cb)
470 struct ast_test *cur = NULL;
493 static int test_cat_cmp(
const char *cat1,
const char *cat2)
498 if (!cat1 || !cat2) {
509 return strncmp(cat1, cat2, len2) ? 1 : 0;
516 static struct ast_test *test_free(
struct ast_test *test)
532 static struct ast_test *test_alloc(ast_test_cb_t *cb)
534 struct ast_test *test;
536 if (!cb || !(test =
ast_calloc(1,
sizeof(*test)))) {
546 return test_free(test);
552 return test_free(test);
555 if (test->info.category[0] !=
'/' || test->info.category[strlen(test->info.category) - 1] !=
'/') {
556 ast_log(
LOG_WARNING,
"Test category is missing a leading or trailing backslash for test %s%s\n",
557 test->info.category, test->info.name);
562 test->info.category, test->info.name);
563 return test_free(test);
568 test->info.category, test->info.name);
569 return test_free(test);
573 return test_free(test);
579 static char *complete_test_category(
const char *line,
const char *
word,
int pos,
int state)
582 int wordlen = strlen(word);
584 struct ast_test *test;
588 if (!strncasecmp(word, test->info.category, wordlen) && ++which > state) {
597 static char *complete_test_name(
const char *line,
const char *word,
int pos,
int state,
const char *category)
600 int wordlen = strlen(word);
602 struct ast_test *test;
606 if (!test_cat_cmp(test->info.category, category) && (!strncasecmp(word, test->info.name, wordlen) && ++which > state)) {
618 #define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n"
619 static const char *
const option1[] = {
"all",
"category", NULL };
620 static const char *
const option2[] = {
"name", NULL };
621 struct ast_test *test = NULL;
625 e->
command =
"test show registered";
628 "Usage: 'test show registered' can be used in three ways.\n"
629 " 1. 'test show registered all' shows all registered tests\n"
630 " 2. 'test show registered category [test category]' shows all tests in the given\n"
632 " 3. 'test show registered category [test category] name [test name]' shows all\n"
633 " tests in a given category matching a given name\n";
640 return complete_test_category(a->
line, a->
word, a->
pos, a->
n);
651 ((a->
argc == 4) && strcmp(a->
argv[3],
"all")) ||
652 ((a->
argc == 7) && strcmp(a->
argv[5],
"name"))) {
659 if ((a->
argc == 4) ||
660 ((a->
argc == 5) && !test_cat_cmp(test->info.category, a->
argv[4])) ||
661 ((a->
argc == 7) && !strcmp(test->info.category, a->
argv[4]) && !strcmp(test->info.name, a->
argv[6]))) {
664 test->info.summary, test_result2str[test->state]);
670 ast_cli(a->
fd,
"\n%d Registered Tests Matched\n", count);
680 static const char *
const option1[] = {
"all",
"category", NULL };
681 static const char *
const option2[] = {
"name", NULL };
687 "Usage: test execute can be used in three ways.\n"
688 " 1. 'test execute all' runs all registered tests\n"
689 " 2. 'test execute category [test category]' runs all tests in the given\n"
691 " 3. 'test execute category [test category] name [test name]' runs all\n"
692 " tests in a given category matching a given name\n";
699 return complete_test_category(a->
line, a->
word, a->
pos, a->
n);
714 if ((a->
argc == 3) && !strcmp(a->
argv[2],
"all")) {
715 ast_cli(a->
fd,
"Running all available tests...\n\n");
716 test_execute_multiple(NULL, NULL, a);
717 }
else if (a->
argc == 4) {
718 ast_cli(a->
fd,
"Running all available tests matching category %s\n\n", a->
argv[3]);
719 test_execute_multiple(NULL, a->
argv[3], a);
720 }
else if (a->
argc == 6) {
721 ast_cli(a->
fd,
"Running all available tests matching category %s and name %s\n\n", a->
argv[3], a->
argv[5]);
722 test_execute_multiple(a->
argv[5], a->
argv[3], a);
728 if (!(last_results.last_passed + last_results.last_failed)) {
729 ast_cli(a->
fd,
"--- No Tests Found! ---\n");
731 ast_cli(a->
fd,
"\n%u Test(s) Executed %u Passed %u Failed\n",
732 (last_results.last_passed + last_results.last_failed),
733 last_results.last_passed,
734 last_results.last_failed);
745 #define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n"
746 #define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n"
747 static const char *
const option1[] = {
"all",
"failed",
"passed", NULL };
748 char result_buf[32] = { 0 };
749 struct ast_test *test = NULL;
756 e->
command =
"test show results";
758 "Usage: test show results can be used in three ways\n"
759 " 1. 'test show results all' Displays results for all executed tests.\n"
760 " 2. 'test show results passed' Displays results for all passed tests.\n"
761 " 3. 'test show results failed' Displays results for all failed tests.\n";
773 }
else if (!strcmp(a->
argv[3],
"passed")) {
775 }
else if (!strcmp(a->
argv[3],
"failed")) {
777 }
else if (!strcmp(a->
argv[3],
"all")) {
783 ast_cli(a->
fd, FORMAT_RES_ALL1,
"Result",
"",
"Name",
"Category",
"Time");
792 term_color(result_buf, test_result2str[test->state],
794 0,
sizeof(result_buf));
801 test->time ?
" " :
"<",
802 test->time ? test->time : 1);
807 ast_cli(a->
fd,
"%d Test(s) Executed %d Passed %d Failed\n", (failed + passed), passed, failed);
816 static const char *
const option[] = {
"xml",
"txt", NULL };
817 const char *file = NULL;
818 const char *
type =
"";
826 e->
command =
"test generate results";
828 "Usage: 'test generate results'\n"
829 " Generates test results in either xml or txt format. An optional \n"
830 " file path may be provided to specify the location of the xml or\n"
832 " \nExample usage:\n"
833 " 'test generate results xml' this writes to a default file\n"
834 " 'test generate results xml /path/to/file.xml' writes to specified file\n";
846 }
else if (!strcmp(a->
argv[3],
"xml")) {
849 }
else if (!strcmp(a->
argv[3],
"txt")) {
867 res = test_generate_results(NULL, NULL, file, NULL);
869 res = test_generate_results(NULL, NULL, NULL, file);
873 ast_cli(a->
fd,
"Results Generated Successfully: %s\n",
S_OR(file,
""));
875 ast_cli(a->
fd,
"Results Could Not Be Generated: %s\n",
S_OR(file,
""));
887 AST_CLI_DEFINE(test_cli_show_registered,
"show registered tests"),
888 AST_CLI_DEFINE(test_cli_execute_registered,
"execute registered tests"),
890 AST_CLI_DEFINE(test_cli_generate_results,
"generate test results to file"),
893 int __ast_test_suite_event_notify(
const char *file,
const char *func,
int line,
894 const char *state,
const char *fmt, ...)
908 "Type: StateChange\r\n"
911 "AppFunction: %s\r\n"
912 "AppLine: %d\r\n%s\r\n",
920 int __ast_test_suite_assert_notify(
const char *file,
const char *func,
int line,
926 "AppFunction: %s\r\n"
928 "Expression: %s\r\n",
929 file, func, line, exp);
934 static void test_shutdown(
void)
943 #ifdef TEST_FRAMEWORK
Contains all the initialization information required to store a new test definition.
enum sip_cc_notify_state state
#define AST_CLI_DEFINE(fn, txt,...)
#define AST_LIST_LOCK(head)
Locks a list.
Asterisk main include file. File version handling, generic pbx functions.
Asterisk version information.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Time-related functions and macros.
const char * ast_get_version(void)
Retrieve the Asterisk version string.
descriptor for a cli entry.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
int ast_str_set_va(struct ast_str **buf, ssize_t max_len, const char *fmt, va_list ap)
Set a dynamic string from a va_list.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
struct ast_str * ast_str_create(size_t init_len)
Create a malloc'ed dynamic length string.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
void ast_cli(int fd, const char *fmt,...)
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
#define AST_LIST_INSERT_SORTALPHA(head, elm, field, sortfield)
Inserts a list entry into a alphabetically sorted list.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Asterisk file paths, configured in asterisk.conf.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
char * term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
static struct ast_cli_entry cli[]
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
const char * ast_config_AST_LOG_DIR
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Prototypes for public functions only of internal interest,.
Support for logging to various files, console and syslog Configuration in file logger.conf.
const char * ast_get_version_num(void)
Retrieve the numeric Asterisk version.
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Standard Command Line Interface.
static struct ast_threadstorage result_buf
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Handy terminal functions for vt* terms.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.