68static const char *
const test_result2str[] = {
91 ast_test_init_cb_t *init_cb;
97static struct ast_test_execute_results {
98 unsigned int total_tests;
99 unsigned int total_passed;
100 unsigned int total_failed;
101 unsigned int total_time;
102 unsigned int last_passed;
103 unsigned int last_failed;
104 unsigned int last_time;
110 TEST_NAME_CATEGORY = 2,
114 ({ if (fp != NULL) { \
129#define movefd(oldfd, newfd) \
130 ({ if (oldfd != newfd) { \
131 dup2(oldfd, newfd); \
138#define lowerfd(oldfd) \
139 ({ int newfd = dup(oldfd); \
152static struct ast_test *
test_alloc(ast_test_cb_t *cb);
153static struct ast_test *test_free(
struct ast_test *
test);
154static int test_insert(
struct ast_test *
test);
155static struct ast_test *test_remove(ast_test_cb_t *cb);
156static int test_cat_cmp(
const char *
cat1,
const char *
cat2);
157static int registration_errors = 0;
180int __ast_test_status_update(
const char *
file,
const char *func,
int line,
struct ast_test *
test,
const char *fmt, ...)
206int ast_test_register_init(
const char *category, ast_test_init_cb_t *cb)
208 struct ast_test *
test;
213 if (!(test_cat_cmp(
test->info.category, category))) {
223int ast_test_register_cleanup(
const char *category, ast_test_cleanup_cb_t *cb)
225 struct ast_test *
test;
230 if (!(test_cat_cmp(
test->info.category, category))) {
231 test->cleanup_cb = cb;
240int ast_test_register(ast_test_cb_t *cb)
242 struct ast_test *
test;
245 ast_log(
LOG_ERROR,
"Attempted to register test without all required information\n");
246 registration_errors++;
251 registration_errors++;
255 if (test_insert(
test)) {
257 registration_errors++;
264int ast_test_unregister(ast_test_cb_t *cb)
266 struct ast_test *
test;
268 if (!(
test = test_remove(cb))) {
284static void test_execute(
struct ast_test *
test)
286 struct timeval begin;
338int ast_test_capture_command(
struct ast_test_capture *capture,
const char *
file,
char *
const argv[],
const char *data,
unsigned datalen)
340 int fd0[2] = { -1, -1 }, fd1[2] = { -1, -1 }, fd2[2] = { -1, -1 };
345 ast_test_capture_init(capture);
347 if (data !=
NULL && datalen > 0) {
348 if (pipe(fd0) == -1) {
352 fcntl(fd0[1], F_SETFL, fcntl(fd0[1], F_GETFL, 0) | O_NONBLOCK);
354 if ((fd0[0] = open(
"/dev/null", O_RDONLY)) == -1) {
360 if (pipe(fd1) == -1) {
365 if (pipe(fd2) == -1) {
373 if ((pid = fork()) == -1) {
377 }
else if (pid == 0) {
393 int wstatus, n, nfds;
394 fd_set readfds, writefds;
405 if ((cmd = fmemopen(
buf,
sizeof(
buf),
"w")) ==
NULL) {
410 for (i = 0; argv[i] !=
NULL; ++i) {
433 n = waitpid(pid, &wstatus, WNOHANG);
454 nfds =
MAX(fd0[1],
MAX(fd1[0], fd2[0])) + 1;
460 if (data !=
NULL && datalen > 0)
461 FD_SET(fd0[1], &writefds);
473 n = select(nfds, &readfds, &writefds,
NULL,
NULL);
476#define SAFE_FD_ISSET(fd, setptr) ((fd) != -1 && FD_ISSET((fd), setptr))
478 if (SAFE_FD_ISSET(fd0[1], &writefds)) {
479 n = write(fd0[1], data, datalen);
482 datalen -=
MIN(datalen, n);
491 if (SAFE_FD_ISSET(fd1[0], &readfds)) {
492 n = read(fd1[0],
buf,
sizeof(
buf));
494 fwrite(
buf,
sizeof(
char), n,
out);
500 if (SAFE_FD_ISSET(fd2[0], &readfds)) {
501 n = read(fd2[0],
buf,
sizeof(
buf));
503 fwrite(
buf,
sizeof(
char), n, err);
534static char *reserved_words[] = {
535 "abstract",
"arguments",
"as",
"assert",
"await",
536 "boolean",
"break",
"byte",
"case",
"catch",
"char",
"class",
537 "const",
"continue",
"debugger",
"def",
"default",
"delete",
"do",
538 "double",
"else",
"enum",
"eval",
"export",
"extends",
"false",
539 "final",
"finally",
"float",
"for",
"function",
"goto",
"if",
540 "implements",
"import",
"in",
"instanceof",
"int",
"interface",
541 "let",
"long",
"native",
"new",
"null",
"package",
"private",
542 "protected",
"public",
"return",
"short",
"static",
"strictfp",
543 "string",
"super",
"switch",
"synchronized",
"this",
"throw",
"throws",
544 "trait",
"transient",
"true",
"try",
"typeof",
"var",
"void",
545 "volatile",
"while",
"with",
"yield" };
547static int is_reserved_word(
const char *
word)
551 for (i = 0; i <
ARRAY_LEN(reserved_words); i++) {
552 if (strcmp(
word, reserved_words[i]) == 0) {
560static void test_xml_entry(
struct ast_test *
test, FILE *f)
565 char *test_name = (
char *)
test->info.
name;
577 if (is_reserved_word(next_cat)) {
586 if (is_reserved_word(
test->info.
name)) {
587 size_t name_length = strlen(
test->info.
name) + 2;
590 snprintf(test_name, name_length,
"_%s",
test->info.
name);
593 fprintf(f,
"\t\t<testcase time=\"%u.%u\" classname=\"%s\" name=\"%s\"%s>\n",
594 test->time / 1000,
test->time % 1000,
601 fprintf(f,
"\t\t\t<failure><![CDATA[\n%s\n\t\t]]></failure>\n",
603 fprintf(f,
"\t\t</testcase>\n");
608static void test_txt_entry(
struct ast_test *
test, FILE *f)
614 fprintf(f,
"\nName: %s\n",
test->info.
name);
615 fprintf(f,
"Category: %s\n",
test->info.category);
616 fprintf(f,
"Summary: %s\n",
test->info.summary);
617 fprintf(f,
"Description: %s\n",
test->info.description);
618 fprintf(f,
"Result: %s\n", test_result2str[
test->state]);
620 fprintf(f,
"Time: %u\n",
test->time);
642static int test_execute_multiple(
const char *
name,
const char *category,
struct ast_cli_args *
cli)
646 enum test_mode mode = TEST_ALL;
652 mode = TEST_NAME_CATEGORY;
660 memset(&last_results, 0,
sizeof(last_results));
666 if (!test_cat_cmp(
test->info.category, category) && !
test->info.explicit_only) {
670 case TEST_NAME_CATEGORY:
671 if (!(test_cat_cmp(
test->info.category, category)) && !(strcmp(
test->info.
name,
name))) {
693 last_results.last_time +=
test->time;
695 last_results.last_passed++;
697 last_results.last_failed++;
702 test_result2str[
test->state],
706 ast_cli(
cli->fd,
"END %s - %s Time: %s%ums Result: %s\n",
709 test->time ?
"" :
"<",
717 last_results.total_time +=
test->time;
719 last_results.total_tests++;
721 last_results.total_passed++;
723 last_results.total_failed++;
727 res = last_results.last_passed + last_results.last_failed;
752static int test_generate_results(
const char *
name,
const char *category,
const char *xml_path,
const char *txt_path)
754 enum test_mode mode = TEST_ALL;
767 mode = TEST_NAME_CATEGORY;
774 if (!(f_xml = fopen(xml_path,
"w"))) {
781 if (!(f_txt = fopen(txt_path,
"w"))) {
782 ast_log(
LOG_WARNING,
"Could not open file %s for text output of test results\n", txt_path);
794 fprintf(f_xml,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
795 fprintf(f_xml,
"<testsuites>\n");
796 fprintf(f_xml,
"\t<testsuite errors=\"0\" time=\"%u.%u\" tests=\"%u\" failures=\"%u\" "
797 "name=\"AsteriskUnitTests\">\n",
798 last_results.total_time / 1000, last_results.total_time % 1000,
799 last_results.total_tests, last_results.total_failed);
800 fprintf(f_xml,
"\t\t<properties>\n");
801 fprintf(f_xml,
"\t\t\t<property name=\"version\" value=\"%s\"/>\n",
ast_get_version());
802 fprintf(f_xml,
"\t\t</properties>\n");
809 fprintf(f_txt,
"Number of Tests: %u\n", last_results.total_tests);
810 fprintf(f_txt,
"Number of Tests Executed: %u\n", (last_results.total_passed + last_results.total_failed));
811 fprintf(f_txt,
"Passed Tests: %u\n", last_results.total_passed);
812 fprintf(f_txt,
"Failed Tests: %u\n", last_results.total_failed);
813 fprintf(f_txt,
"Total Execution Time: %u\n", last_results.total_time);
820 if (!test_cat_cmp(
test->info.category, category)) {
821 test_xml_entry(
test, f_xml);
822 test_txt_entry(
test, f_txt);
825 case TEST_NAME_CATEGORY:
826 if (!(strcmp(
test->info.category, category)) && !(strcmp(
test->info.
name,
name))) {
827 test_xml_entry(
test, f_xml);
828 test_txt_entry(
test, f_txt);
832 test_xml_entry(
test, f_xml);
833 test_txt_entry(
test, f_txt);
840 fprintf(f_xml,
"\t</testsuite>\n");
841 fprintf(f_xml,
"</testsuites>\n");
858static int test_insert(
struct ast_test *
test)
878static struct ast_test *test_remove(ast_test_cb_t *cb)
880 struct ast_test *cur =
NULL;
903static int test_cat_cmp(
const char *
cat1,
const char *
cat2)
919 return strncmp(
cat1,
cat2, len2) ? 1 : 0;
926static struct ast_test *test_free(
struct ast_test *
test)
942static struct ast_test *
test_alloc(ast_test_cb_t *cb)
944 struct ast_test *
test;
958 return test_free(
test);
964 return test_free(
test);
967 if (
test->info.category[0] !=
'/' ||
test->info.category[strlen(
test->info.category) - 1] !=
'/') {
968 ast_log(
LOG_WARNING,
"Test category '%s' for test '%s' is missing a leading or trailing slash.\n",
974 ++registration_errors;
978 ast_log(
LOG_ERROR,
"Test %s%s has no summary, test registration refused.\n",
980 return test_free(
test);
982 if (
test->info.summary[strlen(
test->info.summary) - 1] ==
'\n') {
989 ++registration_errors;
993 ast_log(
LOG_ERROR,
"Test %s%s has no description, test registration refused.\n",
995 return test_free(
test);
997 if (
test->info.description[strlen(
test->info.description) - 1] ==
'\n') {
1004 ++registration_errors;
1008 ast_log(
LOG_ERROR,
"Failed to allocate status_str for %s%s, test registration failed.\n",
1010 return test_free(
test);
1016static char *complete_test_category(
const char *
word)
1018 int wordlen = strlen(
word);
1019 struct ast_test *
test;
1023 if (!strncasecmp(
word,
test->info.category, wordlen)) {
1034static char *complete_test_name(
const char *
word,
const char *category)
1036 int wordlen = strlen(
word);
1037 struct ast_test *
test;
1041 if (!test_cat_cmp(
test->info.category, category) && !strncasecmp(
word,
test->info.
name, wordlen)) {
1055#define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n"
1056 static const char *
const option1[] = {
"all",
"category",
NULL };
1057 static const char *
const option2[] = {
"name",
NULL };
1062 e->
command =
"test show registered";
1065 "Usage: 'test show registered' can be used in three ways.\n"
1066 " 1. 'test show registered all' shows all registered tests\n"
1067 " 2. 'test show registered category [test category]' shows all tests in the given\n"
1069 " 3. 'test show registered category [test category] name [test name]' shows all\n"
1070 " tests in a given category matching a given name\n";
1076 if (
a->pos == 4 && !strcasecmp(
a->argv[3],
"category")) {
1077 return complete_test_category(
a->word);
1083 return complete_test_name(
a->word,
a->argv[4]);
1087 if ((
a->argc < 4) || (
a->argc == 6) || (
a->argc > 7) ||
1088 ((
a->argc == 4) && strcasecmp(
a->argv[3],
"all")) ||
1089 ((
a->argc == 7) && strcasecmp(
a->argv[5],
"name"))) {
1092 ast_cli(
a->fd,
FORMAT,
"Category",
"Name",
"Summary",
"Test Result");
1093 ast_cli(
a->fd,
FORMAT,
"--------",
"----",
"-------",
"-----------");
1096 if ((
a->argc == 4) ||
1097 ((
a->argc == 5) && !test_cat_cmp(
test->info.category,
a->argv[4])) ||
1098 ((
a->argc == 7) && !strcmp(
test->info.category,
a->argv[4]) && !strcmp(
test->info.
name,
a->argv[6]))) {
1101 test->info.summary, test_result2str[
test->state]);
1106 ast_cli(
a->fd,
FORMAT,
"--------",
"----",
"-------",
"-----------");
1107 ast_cli(
a->fd,
"\n%d Registered Tests Matched\n", count);
1117 static const char *
const option1[] = {
"all",
"category",
NULL };
1118 static const char *
const option2[] = {
"name",
NULL };
1124 "Usage: test execute can be used in three ways.\n"
1125 " 1. 'test execute all' runs all registered tests\n"
1126 " 2. 'test execute category [test category]' runs all tests in the given\n"
1128 " 3. 'test execute category [test category] name [test name]' runs all\n"
1129 " tests in a given category matching a given name\n";
1135 if (
a->pos == 3 && !strcasecmp(
a->argv[2],
"category")) {
1136 return complete_test_category(
a->word);
1142 return complete_test_name(
a->word,
a->argv[3]);
1147 if (
a->argc < 3||
a->argc > 6) {
1151 if ((
a->argc == 3) && !strcasecmp(
a->argv[2],
"all")) {
1152 ast_cli(
a->fd,
"Running all available tests...\n\n");
1154 }
else if (
a->argc == 4) {
1155 ast_cli(
a->fd,
"Running all available tests matching category %s\n\n",
a->argv[3]);
1156 test_execute_multiple(
NULL,
a->argv[3],
a);
1157 }
else if (
a->argc == 6) {
1158 ast_cli(
a->fd,
"Running all available tests matching category %s and name %s\n\n",
a->argv[3],
a->argv[5]);
1159 test_execute_multiple(
a->argv[5],
a->argv[3],
a);
1165 if (!(last_results.last_passed + last_results.last_failed)) {
1166 ast_cli(
a->fd,
"--- No Tests Found! ---\n");
1168 ast_cli(
a->fd,
"\n%u Test(s) Executed %u Passed %u Failed\n",
1169 (last_results.last_passed + last_results.last_failed),
1170 last_results.last_passed,
1171 last_results.last_failed);
1182#define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n"
1183#define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n"
1184 static const char *
const option1[] = {
"all",
"failed",
"passed",
NULL };
1193 e->
command =
"test show results";
1195 "Usage: test show results can be used in three ways\n"
1196 " 1. 'test show results all' Displays results for all executed tests.\n"
1197 " 2. 'test show results passed' Displays results for all passed tests.\n"
1198 " 3. 'test show results failed' Displays results for all failed tests.\n";
1210 }
else if (!strcasecmp(
a->argv[3],
"passed")) {
1212 }
else if (!strcasecmp(
a->argv[3],
"failed")) {
1214 }
else if (!strcasecmp(
a->argv[3],
"all")) {
1220 ast_cli(
a->fd, FORMAT_RES_ALL1,
"Result",
"",
"Name",
"Category",
"Time");
1237 test->info.category,
1238 test->time ?
" " :
"<",
1244 ast_cli(
a->fd,
"%d Test(s) Executed %d Passed %d Failed\n", (failed + passed), passed, failed);
1253 static const char *
const option[] = {
"xml",
"txt",
NULL };
1255 const char *
type =
"";
1263 e->
command =
"test generate results";
1265 "Usage: 'test generate results'\n"
1266 " Generates test results in either xml or txt format. An optional \n"
1267 " file path may be provided to specify the location of the xml or\n"
1269 " \nExample usage:\n"
1270 " 'test generate results xml' this writes to a default file\n"
1271 " 'test generate results xml /path/to/file.xml' writes to specified file\n";
1281 if (
a->argc < 4 ||
a->argc > 5) {
1283 }
else if (!strcasecmp(
a->argv[3],
"xml")) {
1286 }
else if (!strcasecmp(
a->argv[3],
"txt")) {
1324 AST_CLI_DEFINE(test_cli_show_registered,
"show registered tests"),
1325 AST_CLI_DEFINE(test_cli_execute_registered,
"execute registered tests"),
1327 AST_CLI_DEFINE(test_cli_generate_results,
"generate test results to file"),
1332 return test_suite_topic;
1339struct ast_test_suite_message_payload {
1347static void test_suite_message_payload_dtor(
void *obj)
1349 struct ast_test_suite_message_payload *payload = obj;
1351 if (payload->blob) {
1356struct ast_json *ast_test_suite_get_blob(
struct ast_test_suite_message_payload *payload)
1358 return payload->blob;
1364 struct ast_test_suite_message_payload *payload;
1372 blob = ast_test_suite_get_blob(payload);
1404 .
to_ami = test_suite_event_to_ami);
1406void __ast_test_suite_event_notify(
const char *
file,
const char *func,
int line,
const char *
state,
const char *fmt, ...)
1408 RAII_VAR(
struct ast_test_suite_message_payload *, payload,
1415 if (!ast_test_suite_message_type()) {
1424 payload =
ao2_alloc(
sizeof(*payload), test_suite_message_payload_dtor);
1432 payload->blob =
ast_json_pack(
"{s: s, s: s, s: s, s: s, s: i, s: s}",
1433 "type",
"testevent",
1436 "appfunction", func,
1439 if (!payload->blob) {
1453 info->name =
"registrations";
1454 info->category =
"/main/test/";
1455 info->summary =
"Validate Test Registration Data.";
1456 info->description =
"Validate Test Registration Data.";
1462 if (registration_errors) {
1464 "%d test registration error%s occurred. See startup logs for details.\n",
1465 registration_errors, registration_errors > 1 ?
"s" :
"");
1472static void test_cleanup(
void)
1477 test_suite_topic =
NULL;
1484#ifdef TEST_FRAMEWORK
1489 if (!test_suite_topic) {
Prototypes for public functions only of internal interest,.
Asterisk version information.
const char * ast_get_version(void)
Retrieve the Asterisk version string.
const char * ast_get_version_num(void)
Retrieve the numeric Asterisk version.
Asterisk main include file. File version handling, generic pbx functions.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
void ast_std_free(void *ptr)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
#define ast_strdup(str)
A wrapper for strdup()
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ast_calloc(num, len)
A wrapper for calloc()
#define ao2_alloc(data_size, destructor_fn)
Standard Command Line Interface.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
#define AST_CLI_DEFINE(fn, txt,...)
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
void ast_cli(int fd, const char *fmt,...)
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
#define ast_cli_register_multiple(e, len)
Register multiple commands.
static struct ast_cli_entry cli[]
static struct ast_threadstorage result_buf
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Common execution function for SQL queries.
static char prefix[MAX_PREFIX]
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
void ast_replace_sigchld(void)
Replace the SIGCHLD handler.
void ast_unreplace_sigchld(void)
Restore the SIGCHLD handler.
Support for logging to various files, console and syslog Configuration in file logger....
Asterisk JSON abstraction layer.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
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.
#define AST_LIST_INSERT_SORTALPHA(head, elm, field, sortfield)
Inserts a list entry into a alphabetically sorted list.
#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.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define AST_LIST_LOCK(head)
Locks a list.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
#define WEXITSTATUS(status)
#define WIFEXITED(status)
#define EVENT_FLAG_REPORTING
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_LOG_DIR
static void * cleanup(void *unused)
static void to_ami(struct ast_sip_subscription *sub, struct ast_str **buf)
static int cleanup_cb(void *obj, void *arg, int flags)
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#define STASIS_MESSAGE_TYPE_DEFN(name,...)
Boiler-plate messaging macro for defining public message types.
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
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.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
descriptor for a cli entry.
Abstract JSON element (object, array, string, int, ...).
Struct containing info for an AMI event to send out.
Support for dynamic strings.
A capture of running an external process.
char * outbuf
buffer holding stdout
char * errbuf
buffer holding stderr
size_t errlen
length of buffer holding stderr
pid_t pid
process id of child
size_t outlen
length of buffer holding stdout
int exitcode
exit code of child
Contains all the initialization information required to store a new test definition.
Handy terminal functions for vt* terms.
char * term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
Colorize a specified string by adding terminal color codes.
#define ast_test_debug(test, fmt,...)
#define AST_TEST_REGISTER(cb)
#define ast_test_status_update(a, b, c...)
#define AST_TEST_UNREGISTER(cb)
#define AST_TEST_DEFINE(hdr)
static struct test_listener_data * test_alloc(void)
Time-related functions and macros.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.