From 6ed96a542a3e5c2f23b9e84ff1bcee136ba95c22 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Fri, 25 Jun 2010 18:58:37 +0000 Subject: [PATCH] Backport unit test API from trunk. Also, update existing test modules that were already in this branch but had been converted to the unit test API in trunk. Review: https://reviewboard.asterisk.org/r/748/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.6.2@272531 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- build_tools/cflags-devmode.xml | 2 + include/asterisk/_private.h | 1 + include/asterisk/test.h | 215 ++++++++ main/Makefile | 2 +- main/asterisk.c | 7 + main/test.c | 892 +++++++++++++++++++++++++++++++++ tests/test_heap.c | 193 ++++--- tests/test_sched.c | 81 +-- tests/test_skel.c | 44 +- 9 files changed, 1290 insertions(+), 147 deletions(-) create mode 100644 include/asterisk/test.h create mode 100644 main/test.c diff --git a/build_tools/cflags-devmode.xml b/build_tools/cflags-devmode.xml index 0b0f5e9078..e47e7160b4 100644 --- a/build_tools/cflags-devmode.xml +++ b/build_tools/cflags-devmode.xml @@ -20,4 +20,6 @@ + + diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index b630375fcd..eb3950a6b2 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -42,6 +42,7 @@ int ast_timing_init(void); /*!< Provided by timing.c */ int ast_indications_init(void); /*!< Provided by indications.c */ int ast_indications_reload(void);/*!< Provided by indications.c */ int ast_ssl_init(void); /*!< Porvided by ssl.c */ +int ast_test_init(void); /*!< Provided by test.c */ /*! * \brief Reload asterisk modules. diff --git a/include/asterisk/test.h b/include/asterisk/test.h new file mode 100644 index 0000000000..f97df80d75 --- /dev/null +++ b/include/asterisk/test.h @@ -0,0 +1,215 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009-2010, Digium, Inc. + * + * David Vossel + * Russell Bryant + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Test Framework API + * + * For an overview on how to use the test API, see \ref AstUnitTestAPI + * + * \author David Vossel + * \author Russell Bryant + */ + +#ifndef _AST_TEST_H_ +#define _AST_TEST_H_ + +#ifdef TEST_FRAMEWORK +#include "asterisk/cli.h" +#include "asterisk/strings.h" +#endif + +/*! + +\page AstUnitTestAPI Asterisk Unit Test API + +\section UnitTestAPIUsage How to Use the Unit Test API + +\subsection DefineTest Define a Test + + Create a callback function for the test using the AST_TEST_DEFINE macro. + + Each defined test has three arguments avaliable to it's test code. + \param struct ast_test_info *info + \param enum ast_test_command cmd + \param struct ast_test *test + + While these arguments are not visible they are passed to every test function + defined using the AST_TEST_DEFINE macro. + + Below is an example of how to define and write a test function. + +\code + AST_TEST_DEFINE(sample_test_cb) \\The name of the callback function + { \\The the function's body + switch (cmd) { + case TEST_INIT: + info->name = "sample_test"; + info->category = "main/test/"; + info->summary = "sample test for example purpose"; + info->description = "This demonstrates how to initialize a test function"; + + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + \test code + . + . + . + if (fail) { \\ the following is just some example logic + ast_test_status_update(test, "an error occured because..."); + res = AST_RESULT_FAIL; + } else { + res = AST_RESULT_PASS + } + return res; \\ result must be of type enum ast_test_result_state + } +\endcode + + Details of the test execution, especially failure details, should be provided + by using the ast_test_status_update() function. + +\subsection RegisterTest Register a Test + + Register the test using the AST_TEST_REGISTER macro. + + AST_TEST_REGISTER uses the callback function to retrieve all the information + pertaining to a test, so the callback function is the only argument required + for registering a test. + + AST_TEST_REGISTER(sample_test_cb); \\ Test callback function defined by AST_TEST_DEFINE + + Tests are unregestered by using the AST_TEST_UNREGISTER macro. + + AST_TEST_UNREGISTER(sample_test_cb); \\ Remove a registered test by callback function + +\subsection ExecuteTest Execute a Test + + Execute and generate test results via CLI commands + + CLI Examples: +\code + 'test show registered all' will show every registered test. + 'test execute all' will execute every registered test. + 'test show results all' will show detailed results for ever executed test + 'test generate results xml' will generate a test report in xml format + 'test generate results txt' will generate a test report in txt format +\endcode +*/ + +/*! Macros used for defining and registering a test */ +#ifdef TEST_FRAMEWORK + +#define AST_TEST_DEFINE(hdr) static enum ast_test_result_state hdr(struct ast_test_info *info, enum ast_test_command cmd, struct ast_test *test) +#define AST_TEST_REGISTER(cb) ast_test_register(cb) +#define AST_TEST_UNREGISTER(cb) ast_test_unregister(cb) + +#else + +#define AST_TEST_DEFINE(hdr) static enum ast_test_result_state attribute_unused hdr(struct ast_test_info *info, enum ast_test_command cmd, struct ast_test *test) +#define AST_TEST_REGISTER(cb) +#define AST_TEST_UNREGISTER(cb) +#define ast_test_status_update(a,b,c...) + +#endif + +enum ast_test_result_state { + AST_TEST_NOT_RUN, + AST_TEST_PASS, + AST_TEST_FAIL, +}; + +enum ast_test_command { + TEST_INIT, + TEST_EXECUTE, +}; + +/*! + * \brief An Asterisk unit test. + * + * This is an opaque type. + */ +struct ast_test; + +/*! + * \brief Contains all the initialization information required to store a new test definition + */ +struct ast_test_info { + /*! \brief name of test, unique to category */ + const char *name; + /*! \brief test category */ + const char *category; + /*! \brief optional short summary of test */ + const char *summary; + /*! \brief optional brief detailed description of test */ + const char *description; +}; + +#ifdef TEST_FRAMEWORK +/*! + * \brief Generic test callback function + * + * \param error buffer string for failure results + * + * \retval AST_TEST_PASS for pass + * \retval AST_TEST_FAIL for failure + */ +typedef enum ast_test_result_state (ast_test_cb_t)(struct ast_test_info *info, + enum ast_test_command cmd, struct ast_test *test); + +/*! + * \brief unregisters a test with the test framework + * + * \param test callback function (required) + * + * \retval 0 success + * \retval -1 failure + */ +int ast_test_unregister(ast_test_cb_t *cb); + +/*! + * \brief registers a test with the test framework + * + * \param test callback function (required) + * + * \retval 0 success + * \retval -1 failure + */ +int ast_test_register(ast_test_cb_t *cb); + +/*! + * \brief update test's status during testing. + * + * \param test currently executing test + * + * \retval 0 success + * \retval -1 failure + */ +int __ast_test_status_update(const char *file, const char *func, int line, + struct ast_test *test, const char *fmt, ...) + __attribute__((format(printf, 5, 6))); + +/*! + * \ref __ast_test_status_update() + */ +#define ast_test_status_update(t, f, ...) __ast_test_status_update(__FILE__, __PRETTY_FUNCTION__, __LINE__, (t), (f), ## __VA_ARGS__) + +#endif /* TEST_FRAMEWORK */ +#endif /* _AST_TEST_H */ diff --git a/main/Makefile b/main/Makefile index f5ef788a87..6e618087ce 100644 --- a/main/Makefile +++ b/main/Makefile @@ -29,7 +29,7 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \ strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \ astobj2.o hashtab.o global_datastores.o version.o \ features.o taskprocessor.o timing.o datastore.o xml.o xmldoc.o \ - strings.o bridging.o poll.o ssl.o + strings.o bridging.o poll.o ssl.o test.o # we need to link in the objects statically, not as a library, because # otherwise modules will not have them available if none of the static diff --git a/main/asterisk.c b/main/asterisk.c index dba1a7224a..47a9cb53e4 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -3568,6 +3568,13 @@ int main(int argc, char *argv[]) exit(1); } +#ifdef TEST_FRAMEWORK + if (ast_test_init()) { + printf("%s", term_quit()); + exit(1); + } +#endif + ast_makesocket(); sigemptyset(&sigs); sigaddset(&sigs, SIGHUP); diff --git a/main/test.c b/main/test.c new file mode 100644 index 0000000000..f1436827d0 --- /dev/null +++ b/main/test.c @@ -0,0 +1,892 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009-2010, Digium, Inc. + * + * David Vossel + * Russell Bryant + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Unit Test Framework + * + * \author David Vossel + * \author Russell Bryant + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); + +#include "asterisk/_private.h" + +#ifdef TEST_FRAMEWORK +#include "asterisk/test.h" +#include "asterisk/logger.h" +#include "asterisk/linkedlists.h" +#include "asterisk/utils.h" +#include "asterisk/cli.h" +#include "asterisk/term.h" +#include "asterisk/version.h" +#include "asterisk/paths.h" +#include "asterisk/time.h" + +/*! This array corresponds to the values defined in the ast_test_state enum */ +static const char * const test_result2str[] = { + [AST_TEST_NOT_RUN] = "NOT RUN", + [AST_TEST_PASS] = "PASS", + [AST_TEST_FAIL] = "FAIL", +}; + +/*! holds all the information pertaining to a single defined test */ +struct ast_test { + struct ast_test_info info; /*!< holds test callback information */ + /*! + * \brief Test defined status output from last execution + */ + struct ast_str *status_str; + /*! + * \brief CLI arguments, if tests being run from the CLI + * + * If this is set, status updates from the tests will be sent to the + * CLI in addition to being saved off in status_str. + */ + struct ast_cli_args *cli; + enum ast_test_result_state state; /*!< current test state */ + unsigned int time; /*!< time in ms test took */ + ast_test_cb_t *cb; /*!< test callback function */ + AST_LIST_ENTRY(ast_test) entry; +}; + +/*! global structure containing both total and last test execution results */ +static struct ast_test_execute_results { + unsigned int total_tests; /*!< total number of tests, regardless if they have been executed or not */ + unsigned int total_passed; /*!< total number of executed tests passed */ + unsigned int total_failed; /*!< total number of executed tests failed */ + unsigned int total_time; /*!< total time of all executed tests */ + unsigned int last_passed; /*!< number of passed tests during last execution */ + unsigned int last_failed; /*!< number of failed tests during last execution */ + unsigned int last_time; /*!< total time of the last test execution */ +} last_results; + +enum test_mode { + TEST_ALL = 0, + TEST_CATEGORY = 1, + TEST_NAME_CATEGORY = 2, +}; + +/*! List of registered test definitions */ +static AST_LIST_HEAD_STATIC(tests, ast_test); + +static struct ast_test *test_alloc(ast_test_cb_t *cb); +static struct ast_test *test_free(struct ast_test *test); +static int test_insert(struct ast_test *test); +static struct ast_test *test_remove(ast_test_cb_t *cb); +static int test_cat_cmp(const char *cat1, const char *cat2); + +int __ast_test_status_update(const char *file, const char *func, int line, + struct ast_test *test, const char *fmt, ...) +{ + struct ast_str *buf = NULL; + va_list ap; + + if (!(buf = ast_str_create(128))) { + return -1; + } + + va_start(ap, fmt); + ast_str_set_va(&buf, 0, fmt, ap); + va_end(ap); + + if (test->cli) { + ast_cli(test->cli->fd, "[%s:%s:%d]: %s", + file, func, line, ast_str_buffer(buf)); + } + + ast_str_append(&test->status_str, 0, "[%s:%s:%d]: %s", + file, func, line, ast_str_buffer(buf)); + + ast_free(buf); + + return 0; +} + +int ast_test_register(ast_test_cb_t *cb) +{ + struct ast_test *test; + + if (!cb) { + ast_log(LOG_WARNING, "Attempted to register test without all required information\n"); + return -1; + } + + if (!(test = test_alloc(cb))) { + return -1; + } + + if (test_insert(test)) { + test_free(test); + return -1; + } + + return 0; +} + +int ast_test_unregister(ast_test_cb_t *cb) +{ + struct ast_test *test; + + if (!(test = test_remove(cb))) { + return -1; /* not found */ + } + + test_free(test); + + return 0; +} + +/*! + * \internal + * \brief executes a single test, storing the results in the test->result structure. + * + * \note The last_results structure which contains global statistics about test execution + * must be updated when using this function. See use in test_execute_multiple(). + */ +static void test_execute(struct ast_test *test) +{ + struct timeval begin; + + ast_str_reset(test->status_str); + + begin = ast_tvnow(); + test->state = test->cb(&test->info, TEST_EXECUTE, test); + test->time = ast_tvdiff_ms(ast_tvnow(), begin); +} + +static void test_xml_entry(struct ast_test *test, FILE *f) +{ + if (!f || !test || test->state == AST_TEST_NOT_RUN) { + return; + } + + fprintf(f, "\t\n", + test->time / 1000, test->time % 1000, + test->info.category, test->info.name, + test->state == AST_TEST_PASS ? "/" : ""); + + if (test->state == AST_TEST_FAIL) { + fprintf(f, "\t\t\n", + S_OR(ast_str_buffer(test->status_str), "NA")); + fprintf(f, "\t\n"); + } + +} + +static void test_txt_entry(struct ast_test *test, FILE *f) +{ + if (!f || !test) { + return; + } + + fprintf(f, "\nName: %s\n", test->info.name); + fprintf(f, "Category: %s\n", test->info.category); + fprintf(f, "Summary: %s\n", test->info.summary); + fprintf(f, "Description: %s\n", test->info.description); + fprintf(f, "Result: %s\n", test_result2str[test->state]); + if (test->state != AST_TEST_NOT_RUN) { + fprintf(f, "Time: %d\n", test->time); + } + if (test->state == AST_TEST_FAIL) { + fprintf(f, "Error Description: %s\n\n", S_OR(ast_str_buffer(test->status_str), "NA")); + } +} + +/*! + * \internal + * \brief Executes registered unit tests + * + * \param name of test to run (optional) + * \param test category to run (optional) + * \param cli args for cli test updates (optional) + * + * \return number of tests executed. + * + * \note This function has three modes of operation + * -# When given a name and category, a matching individual test will execute if found. + * -# When given only a category all matching tests within that category will execute. + * -# If given no name or category all registered tests will execute. + */ +static int test_execute_multiple(const char *name, const char *category, struct ast_cli_args *cli) +{ + char result_buf[32] = { 0 }; + struct ast_test *test = NULL; + enum test_mode mode = TEST_ALL; /* 3 modes, 0 = run all, 1 = only by category, 2 = only by name and category */ + int execute = 0; + int res = 0; + + if (!ast_strlen_zero(category)) { + if (!ast_strlen_zero(name)) { + mode = TEST_NAME_CATEGORY; + } else { + mode = TEST_CATEGORY; + } + } + + AST_LIST_LOCK(&tests); + /* clear previous execution results */ + memset(&last_results, 0, sizeof(last_results)); + AST_LIST_TRAVERSE(&tests, test, entry) { + + execute = 0; + switch (mode) { + case TEST_CATEGORY: + if (!test_cat_cmp(test->info.category, category)) { + execute = 1; + } + break; + case TEST_NAME_CATEGORY: + if (!(test_cat_cmp(test->info.category, category)) && !(strcmp(test->info.name, name))) { + execute = 1; + } + break; + case TEST_ALL: + execute = 1; + } + + if (execute) { + if (cli) { + ast_cli(cli->fd, "START %s - %s \n", test->info.category, test->info.name); + } + + /* set the test status update argument. it is ok if cli is NULL */ + test->cli = cli; + + /* execute the test and save results */ + test_execute(test); + + test->cli = NULL; + + /* update execution specific counts here */ + last_results.last_time += test->time; + if (test->state == AST_TEST_PASS) { + last_results.last_passed++; + } else if (test->state == AST_TEST_FAIL) { + last_results.last_failed++; + } + + if (cli) { + term_color(result_buf, + test_result2str[test->state], + (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN, + 0, + sizeof(result_buf)); + ast_cli(cli->fd, "END %s - %s Time: %s%dms Result: %s\n", + test->info.category, + test->info.name, + test->time ? "" : "<", + test->time ? test->time : 1, + result_buf); + } + } + + /* update total counts as well during this iteration + * even if the current test did not execute this time */ + last_results.total_time += test->time; + last_results.total_tests++; + if (test->state != AST_TEST_NOT_RUN) { + if (test->state == AST_TEST_PASS) { + last_results.total_passed++; + } else { + last_results.total_failed++; + } + } + } + res = last_results.last_passed + last_results.last_failed; + AST_LIST_UNLOCK(&tests); + + return res; +} + +/*! + * \internal + * \brief Generate test results. + * + * \param name of test result to generate (optional) + * \param test category to generate (optional) + * \param path to xml file to generate. (optional) + * \param path to txt file to generate, (optional) + * + * \retval 0 success + * \retval -1 failure + * + * \note This function has three modes of operation. + * -# When given both a name and category, results will be generated for that single test. + * -# When given only a category, results for every test within the category will be generated. + * -# When given no name or category, results for every registered test will be generated. + * + * In order for the results to be generated, an xml and or txt file path must be provided. + */ +static int test_generate_results(const char *name, const char *category, const char *xml_path, const char *txt_path) +{ + enum test_mode mode = TEST_ALL; /* 0 generate all, 1 generate by category only, 2 generate by name and category */ + FILE *f_xml = NULL, *f_txt = NULL; + int res = 0; + struct ast_test *test = NULL; + + /* verify at least one output file was given */ + if (ast_strlen_zero(xml_path) && ast_strlen_zero(txt_path)) { + return -1; + } + + /* define what mode is to be used */ + if (!ast_strlen_zero(category)) { + if (!ast_strlen_zero(name)) { + mode = TEST_NAME_CATEGORY; + } else { + mode = TEST_CATEGORY; + } + } + /* open files for writing */ + if (!ast_strlen_zero(xml_path)) { + if (!(f_xml = fopen(xml_path, "w"))) { + ast_log(LOG_WARNING, "Could not open file %s for xml test results\n", xml_path); + res = -1; + goto done; + } + } + if (!ast_strlen_zero(txt_path)) { + if (!(f_txt = fopen(txt_path, "w"))) { + ast_log(LOG_WARNING, "Could not open file %s for text output of test results\n", txt_path); + res = -1; + goto done; + } + } + + AST_LIST_LOCK(&tests); + /* xml header information */ + if (f_xml) { + /* + * http://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo + */ + fprintf(f_xml, "\n"); + fprintf(f_xml, "\n", + last_results.total_time / 1000, last_results.total_time % 1000, + last_results.total_tests); + fprintf(f_xml, "\t\n"); + fprintf(f_xml, "\t\t\n", ASTERISK_VERSION); + fprintf(f_xml, "\t\n"); + } + + /* txt header information */ + if (f_txt) { + fprintf(f_txt, "Asterisk Version: %s\n", ASTERISK_VERSION); + fprintf(f_txt, "Asterisk Version Number: %d\n", ASTERISK_VERSION_NUM); + fprintf(f_txt, "Number of Tests: %d\n", last_results.total_tests); + fprintf(f_txt, "Number of Tests Executed: %d\n", (last_results.total_passed + last_results.total_failed)); + fprintf(f_txt, "Passed Tests: %d\n", last_results.total_passed); + fprintf(f_txt, "Failed Tests: %d\n", last_results.total_failed); + fprintf(f_txt, "Total Execution Time: %d\n", last_results.total_time); + } + + /* export each individual test */ + AST_LIST_TRAVERSE(&tests, test, entry) { + switch (mode) { + case TEST_CATEGORY: + if (!test_cat_cmp(test->info.category, category)) { + test_xml_entry(test, f_xml); + test_txt_entry(test, f_txt); + } + break; + case TEST_NAME_CATEGORY: + if (!(strcmp(test->info.category, category)) && !(strcmp(test->info.name, name))) { + test_xml_entry(test, f_xml); + test_txt_entry(test, f_txt); + } + break; + case TEST_ALL: + test_xml_entry(test, f_xml); + test_txt_entry(test, f_txt); + } + } + AST_LIST_UNLOCK(&tests); + +done: + if (f_xml) { + fprintf(f_xml, "\n"); + fclose(f_xml); + } + if (f_txt) { + fclose(f_txt); + } + + return res; +} + +/*! + * \internal + * \brief adds test to container sorted first by category then by name + * + * \retval 0 success + * \retval -1 failure + */ +static int test_insert(struct ast_test *test) +{ + /* This is a slow operation that may need to be optimized in the future + * as the test framework expands. At the moment we are doing string + * comparisons on every item within the list to insert in sorted order. */ + + AST_LIST_LOCK(&tests); + AST_LIST_INSERT_SORTALPHA(&tests, test, entry, info.category); + AST_LIST_UNLOCK(&tests); + + return 0; +} + +/*! + * \internal + * \brief removes test from container + * + * \return ast_test removed from list on success, or NULL on failure + */ +static struct ast_test *test_remove(ast_test_cb_t *cb) +{ + struct ast_test *cur = NULL; + + AST_LIST_LOCK(&tests); + AST_LIST_TRAVERSE_SAFE_BEGIN(&tests, cur, entry) { + if (cur->cb == cb) { + AST_LIST_REMOVE_CURRENT(entry); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&tests); + + return cur; +} + +/*! + * \brief compares two test categories to determine if cat1 resides in cat2 + * \internal + * + * \retval 0 true + * \retval non-zero false + */ + +static int test_cat_cmp(const char *cat1, const char *cat2) +{ + int len1 = 0; + int len2 = 0; + + if (!cat1 || !cat2) { + return -1; + } + + len1 = strlen(cat1); + len2 = strlen(cat2); + + if (len2 > len1) { + return -1; + } + + return strncmp(cat1, cat2, len2) ? 1 : 0; +} + +/*! + * \internal + * \brief free an ast_test object and all it's data members + */ +static struct ast_test *test_free(struct ast_test *test) +{ + if (!test) { + return NULL; + } + + ast_free(test->status_str); + ast_free(test); + + return NULL; +} + +/*! + * \internal + * \brief allocate an ast_test object. + */ +static struct ast_test *test_alloc(ast_test_cb_t *cb) +{ + struct ast_test *test; + + if (!cb || !(test = ast_calloc(1, sizeof(*test)))) { + return NULL; + } + + test->cb = cb; + + test->cb(&test->info, TEST_INIT, test); + + if (ast_strlen_zero(test->info.name)) { + ast_log(LOG_WARNING, "Test has no name, test registration refused.\n"); + return test_free(test); + } + + if (ast_strlen_zero(test->info.category)) { + ast_log(LOG_WARNING, "Test %s has no category, test registration refused.\n", + test->info.name); + return test_free(test); + } + + if (ast_strlen_zero(test->info.summary)) { + ast_log(LOG_WARNING, "Test %s/%s has no summary, test registration refused.\n", + test->info.category, test->info.name); + return test_free(test); + } + + if (ast_strlen_zero(test->info.description)) { + ast_log(LOG_WARNING, "Test %s/%s has no description, test registration refused.\n", + test->info.category, test->info.name); + return test_free(test); + } + + if (!(test->status_str = ast_str_create(128))) { + return test_free(test); + } + + return test; +} + +static char *complete_test_category(const char *line, const char *word, int pos, int state) +{ + int which = 0; + int wordlen = strlen(word); + char *ret = NULL; + struct ast_test *test; + + AST_LIST_LOCK(&tests); + AST_LIST_TRAVERSE(&tests, test, entry) { + if (!strncasecmp(word, test->info.category, wordlen) && ++which > state) { + ret = ast_strdup(test->info.category); + break; + } + } + AST_LIST_UNLOCK(&tests); + return ret; +} + +static char *complete_test_name(const char *line, const char *word, int pos, int state, const char *category) +{ + int which = 0; + int wordlen = strlen(word); + char *ret = NULL; + struct ast_test *test; + + AST_LIST_LOCK(&tests); + AST_LIST_TRAVERSE(&tests, test, entry) { + if (!test_cat_cmp(test->info.category, category) && (!strncasecmp(word, test->info.name, wordlen) && ++which > state)) { + ret = ast_strdup(test->info.name); + break; + } + } + AST_LIST_UNLOCK(&tests); + return ret; +} + +/* CLI commands */ +static char *test_cli_show_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n" + static char * const option1[] = { "all", "category", NULL }; + static char * const option2[] = { "name", NULL }; + struct ast_test *test = NULL; + int count = 0; + switch (cmd) { + case CLI_INIT: + e->command = "test show registered"; + + e->usage = + "Usage: 'test show registered' can be used in three ways.\n" + " 1. 'test show registered all' shows all registered tests\n" + " 2. 'test show registered category [test category]' shows all tests in the given\n" + " category.\n" + " 3. 'test show registered category [test category] name [test name]' shows all\n" + " tests in a given category matching a given name\n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 3) { + return ast_cli_complete(a->word, option1, a->n); + } + if (a->pos == 4) { + return complete_test_category(a->line, a->word, a->pos, a->n); + } + if (a->pos == 5) { + return ast_cli_complete(a->word, option2, a->n); + } + if (a->pos == 6) { + return complete_test_name(a->line, a->word, a->pos, a->n, a->argv[3]); + } + return NULL; + case CLI_HANDLER: + if ((a->argc < 4) || (a->argc == 6) || (a->argc > 7) || + ((a->argc == 4) && strcmp(a->argv[3], "all")) || + ((a->argc == 7) && strcmp(a->argv[5], "name"))) { + return CLI_SHOWUSAGE; + } + ast_cli(a->fd, FORMAT, "Category", "Name", "Summary", "Test Result"); + ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------"); + AST_LIST_LOCK(&tests); + AST_LIST_TRAVERSE(&tests, test, entry) { + if ((a->argc == 4) || + ((a->argc == 5) && !test_cat_cmp(test->info.category, a->argv[4])) || + ((a->argc == 7) && !strcmp(test->info.category, a->argv[4]) && !strcmp(test->info.name, a->argv[6]))) { + + ast_cli(a->fd, FORMAT, test->info.category, test->info.name, + test->info.summary, test_result2str[test->state]); + count++; + } + } + AST_LIST_UNLOCK(&tests); + ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------"); + ast_cli(a->fd, "\n%d Registered Tests Matched\n", count); + default: + return NULL; + } + + return CLI_SUCCESS; +} + +static char *test_cli_execute_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + static char * const option1[] = { "all", "category", NULL }; + static char * const option2[] = { "name", NULL }; + + switch (cmd) { + case CLI_INIT: + e->command = "test execute"; + e->usage = + "Usage: test execute can be used in three ways.\n" + " 1. 'test execute all' runs all registered tests\n" + " 2. 'test execute category [test category]' runs all tests in the given\n" + " category.\n" + " 3. 'test execute category [test category] name [test name]' runs all\n" + " tests in a given category matching a given name\n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 2) { + return ast_cli_complete(a->word, option1, a->n); + } + if (a->pos == 3) { + return complete_test_category(a->line, a->word, a->pos, a->n); + } + if (a->pos == 4) { + return ast_cli_complete(a->word, option2, a->n); + } + if (a->pos == 5) { + return complete_test_name(a->line, a->word, a->pos, a->n, a->argv[3]); + } + return NULL; + case CLI_HANDLER: + + if (a->argc < 3|| a->argc > 6) { + return CLI_SHOWUSAGE; + } + + if ((a->argc == 3) && !strcmp(a->argv[2], "all")) { /* run all registered tests */ + ast_cli(a->fd, "Running all available tests...\n\n"); + test_execute_multiple(NULL, NULL, a); + } else if (a->argc == 4) { /* run only tests within a category */ + ast_cli(a->fd, "Running all available tests matching category %s\n\n", a->argv[3]); + test_execute_multiple(NULL, a->argv[3], a); + } else if (a->argc == 6) { /* run only a single test matching the category and name */ + ast_cli(a->fd, "Running all available tests matching category %s and name %s\n\n", a->argv[3], a->argv[5]); + test_execute_multiple(a->argv[5], a->argv[3], a); + } else { + return CLI_SHOWUSAGE; + } + + AST_LIST_LOCK(&tests); + if (!(last_results.last_passed + last_results.last_failed)) { + ast_cli(a->fd, "--- No Tests Found! ---\n"); + } + ast_cli(a->fd, "\n%d Test(s) Executed %d Passed %d Failed\n", + (last_results.last_passed + last_results.last_failed), + last_results.last_passed, + last_results.last_failed); + AST_LIST_UNLOCK(&tests); + default: + return NULL; + } + + return CLI_SUCCESS; +} + +static char *test_cli_show_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n" +#define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n" + static char * const option1[] = { "all", "failed", "passed", NULL }; + char result_buf[32] = { 0 }; + struct ast_test *test = NULL; + int failed = 0; + int passed = 0; + int mode; /* 0 for show all, 1 for show fail, 2 for show passed */ + + switch (cmd) { + case CLI_INIT: + e->command = "test show results"; + e->usage = + "Usage: test show results can be used in three ways\n" + " 1. 'test show results all' Displays results for all executed tests.\n" + " 2. 'test show results passed' Displays results for all passed tests.\n" + " 3. 'test show results failed' Displays results for all failed tests.\n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 3) { + return ast_cli_complete(a->word, option1, a->n); + } + return NULL; + case CLI_HANDLER: + + /* verify input */ + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } else if (!strcmp(a->argv[3], "passed")) { + mode = 2; + } else if (!strcmp(a->argv[3], "failed")) { + mode = 1; + } else if (!strcmp(a->argv[3], "all")) { + mode = 0; + } else { + return CLI_SHOWUSAGE; + } + + ast_cli(a->fd, FORMAT_RES_ALL1, "Result", "", "Name", "Category", "Time"); + AST_LIST_LOCK(&tests); + AST_LIST_TRAVERSE(&tests, test, entry) { + if (test->state == AST_TEST_NOT_RUN) { + continue; + } + test->state == AST_TEST_FAIL ? failed++ : passed++; + if (!mode || ((mode == 1) && (test->state == AST_TEST_FAIL)) || ((mode == 2) && (test->state == AST_TEST_PASS))) { + /* give our results pretty colors */ + term_color(result_buf, test_result2str[test->state], + (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN, + 0, sizeof(result_buf)); + + ast_cli(a->fd, FORMAT_RES_ALL2, + result_buf, + " ", + test->info.name, + test->info.category, + test->time ? " " : "<", + test->time ? test->time : 1); + } + } + AST_LIST_UNLOCK(&tests); + + ast_cli(a->fd, "%d Test(s) Executed %d Passed %d Failed\n", (failed + passed), passed, failed); + default: + return NULL; + } + return CLI_SUCCESS; +} + +static char *test_cli_generate_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + static char * const option[] = { "xml", "txt", NULL }; + const char *file = NULL; + const char *type = ""; + int isxml = 0; + int res = 0; + struct ast_str *buf = NULL; + struct timeval time = ast_tvnow(); + + switch (cmd) { + case CLI_INIT: + e->command = "test generate results"; + e->usage = + "Usage: 'test generate results'\n" + " Generates test results in either xml or txt format. An optional \n" + " file path may be provided to specify the location of the xml or\n" + " txt file\n" + " \nExample usage:\n" + " 'test generate results xml' this writes to a default file\n" + " 'test generate results xml /path/to/file.xml' writes to specified file\n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 3) { + return ast_cli_complete(a->word, option, a->n); + } + return NULL; + case CLI_HANDLER: + + /* verify input */ + if (a->argc < 4 || a->argc > 5) { + return CLI_SHOWUSAGE; + } else if (!strcmp(a->argv[3], "xml")) { + type = "xml"; + isxml = 1; + } else if (!strcmp(a->argv[3], "txt")) { + type = "txt"; + } else { + return CLI_SHOWUSAGE; + } + + if (a->argc == 5) { + file = a->argv[4]; + } else { + if (!(buf = ast_str_create(256))) { + return NULL; + } + ast_str_set(&buf, 0, "%s/asterisk_test_results-%ld.%s", ast_config_AST_LOG_DIR, (long) time.tv_sec, type); + + file = ast_str_buffer(buf); + } + + if (isxml) { + res = test_generate_results(NULL, NULL, file, NULL); + } else { + res = test_generate_results(NULL, NULL, NULL, file); + } + + if (!res) { + ast_cli(a->fd, "Results Generated Successfully: %s\n", S_OR(file, "")); + } else { + ast_cli(a->fd, "Results Could Not Be Generated: %s\n", S_OR(file, "")); + } + + ast_free(buf); + default: + return NULL; + } + + return CLI_SUCCESS; +} + +static struct ast_cli_entry test_cli[] = { + AST_CLI_DEFINE(test_cli_show_registered, "show registered tests"), + AST_CLI_DEFINE(test_cli_execute_registered, "execute registered tests"), + AST_CLI_DEFINE(test_cli_show_results, "show last test results"), + AST_CLI_DEFINE(test_cli_generate_results, "generate test results to file"), +}; +#endif /* TEST_FRAMEWORK */ + +int ast_test_init(void) +{ +#ifdef TEST_FRAMEWORK + /* Register cli commands */ + ast_cli_register_multiple(test_cli, ARRAY_LEN(test_cli)); +#endif + + return 0; +} diff --git a/tests/test_heap.c b/tests/test_heap.c index 3ba9abb486..de48f0ae4d 100644 --- a/tests/test_heap.c +++ b/tests/test_heap.c @@ -24,7 +24,7 @@ */ /*** MODULEINFO - no + TEST_FRAMEWORK ***/ #include "asterisk.h" @@ -32,9 +32,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" -#include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/heap.h" +#include "asterisk/test.h" struct node { long val; @@ -55,24 +55,30 @@ static int node_cmp(void *_n1, void *_n2) } } -static int test1(int fd) +AST_TEST_DEFINE(heap_test_1) { struct ast_heap *h; struct node *obj; struct node nodes[3] = { - { 1, }, - { 2, }, - { 3, }, + { 1, } , + { 2, } , + { 3, } , }; - if (!(h = ast_heap_create(8, node_cmp, offsetof(struct node, index)))) { - return -1; + switch (cmd) { + case TEST_INIT: + info->name = "heap_test_1"; + info->category = "main/heap/"; + info->summary = "push and pop elements"; + info->description = "Push a few elements onto a heap and make sure that they come back off in the right order."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; } - /* Pushing 1 2 3, and then popping 3 elements */ - - ast_cli(fd, "Test #1 - Push a few elements onto a heap and make sure that they " - "come back off in the right order.\n"); + if (!(h = ast_heap_create(8, node_cmp, offsetof(struct node, index)))) { + return AST_TEST_FAIL; + } ast_heap_push(h, &nodes[0]); @@ -82,52 +88,66 @@ static int test1(int fd) obj = ast_heap_pop(h); if (obj->val != 3) { - return -2; + ast_test_status_update(test, "expected 3, got %ld\n", obj->val); + return AST_TEST_FAIL; } obj = ast_heap_pop(h); if (obj->val != 2) { - return -3; + ast_test_status_update(test, "expected 2, got %ld\n", obj->val); + return AST_TEST_FAIL; } obj = ast_heap_pop(h); if (obj->val != 1) { - return -4; + ast_test_status_update(test, "expected 1, got %ld\n", obj->val); + return AST_TEST_FAIL; } obj = ast_heap_pop(h); if (obj) { - return -5; + ast_test_status_update(test, "got unexpected object\n"); + return AST_TEST_FAIL; } h = ast_heap_destroy(h); - ast_cli(fd, "Test #1 successful.\n"); - - return 0; + return AST_TEST_PASS; } -static int test2(int fd) +AST_TEST_DEFINE(heap_test_2) { struct ast_heap *h = NULL; - static const unsigned int one_million = 1000000; + static const unsigned int total = 100000; struct node *nodes = NULL; struct node *node; - unsigned int i = one_million; - long last = LONG_MAX, cur; - int res = 0; + unsigned int i = total; + long last = LONG_MAX; + long cur; + enum ast_test_result_state res = AST_TEST_PASS; - ast_cli(fd, "Test #2 - Push a million random elements on to a heap, " - "verify that the heap has been properly constructed, " - "and then ensure that the elements come back off in the proper order\n"); + switch (cmd) { + case TEST_INIT: + info->name = "heap_test_2"; + info->category = "main/heap/"; + info->summary = "load test"; + info->description = + "Push one hundred thousand random elements on to a heap, " + "verify that the heap has been properly constructed, " + "and then ensure that the elements are come back off " + "in the proper order."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } - if (!(nodes = ast_malloc(one_million * sizeof(*node)))) { - res = -1; + if (!(nodes = ast_malloc(total * sizeof(*node)))) { + res = AST_TEST_FAIL; goto return_cleanup; } if (!(h = ast_heap_create(20, node_cmp, offsetof(struct node, index)))) { - res = -2; + res = AST_TEST_FAIL; goto return_cleanup; } @@ -137,7 +157,7 @@ static int test2(int fd) } if (ast_heap_verify(h)) { - res = -3; + res = AST_TEST_FAIL; goto return_cleanup; } @@ -145,22 +165,20 @@ static int test2(int fd) while ((node = ast_heap_pop(h))) { cur = node->val; if (cur > last) { - ast_cli(fd, "i: %u, cur: %ld, last: %ld\n", i, cur, last); - res = -4; + ast_test_status_update(test, "i: %u, cur: %ld, last: %ld\n", i, cur, last); + res = AST_TEST_FAIL; goto return_cleanup; } last = cur; i++; } - if (i != one_million) { - ast_cli(fd, "Stopped popping off after only getting %u nodes\n", i); - res = -5; + if (i != total) { + ast_test_status_update(test, "Stopped popping off after only getting %u nodes\n", i); + res = AST_TEST_FAIL; goto return_cleanup; } - ast_cli(fd, "Test #2 successful.\n"); - return_cleanup: if (h) { h = ast_heap_destroy(h); @@ -172,7 +190,7 @@ return_cleanup: return res; } -static int test3(int fd) +AST_TEST_DEFINE(heap_test_3) { struct ast_heap *h = NULL; struct node *nodes = NULL; @@ -180,20 +198,33 @@ static int test3(int fd) static const unsigned int test_size = 100000; unsigned int i = test_size; long last = LONG_MAX, cur; - int res = 0; int random_index; + enum ast_test_result_state res = AST_TEST_PASS; - ast_cli(fd, "Test #3 - Push a hundred thousand random elements on to a heap, " + switch (cmd) { + case TEST_INIT: + info->name = "heap_test_3"; + info->category = "main/heap/"; + info->summary = "random element removal test"; + info->description = + "Push a hundred thousand random elements on to a heap, " "verify that the heap has been properly constructed, " - "randomly remove and re-add 10000 elements and then ensure that the elements come back off in the proper order\n"); + "randomly remove and re-add 10000 elements, and then " + "ensure that the elements come back off in the proper order."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } if (!(nodes = ast_malloc(test_size * sizeof(*node)))) { - res = -1; + ast_test_status_update(test, "memory allocation failure\n"); + res = AST_TEST_FAIL; goto return_cleanup; } if (!(h = ast_heap_create(20, node_cmp, offsetof(struct node, index)))) { - res = -2; + ast_test_status_update(test, "Failed to allocate heap\n"); + res = AST_TEST_FAIL; goto return_cleanup; } @@ -203,7 +234,8 @@ static int test3(int fd) } if (ast_heap_verify(h)) { - res = -3; + ast_test_status_update(test, "Failed to verify heap after populating it\n"); + res = AST_TEST_FAIL; goto return_cleanup; } @@ -212,15 +244,16 @@ static int test3(int fd) random_index = ast_random() % test_size; node = ast_heap_remove(h, &nodes[random_index]); if (nodes[random_index].val != node->val){ - res = -4; + ast_test_status_update(test, "Failed to remove what we expected to\n"); + res = AST_TEST_FAIL; goto return_cleanup; } - ast_heap_push(h, &nodes[random_index]); - + ast_heap_push(h, &nodes[random_index]); } if (ast_heap_verify(h)) { - res = -5; + ast_test_status_update(test, "Failed to verify after removals\n"); + res = AST_TEST_FAIL; goto return_cleanup; } @@ -228,8 +261,8 @@ static int test3(int fd) while ((node = ast_heap_pop(h))) { cur = node->val; if (cur > last) { - ast_cli(fd, "i: %u, cur: %ld, last: %ld\n", i, cur, last); - res = -6; + ast_test_status_update(test, "i: %u, cur: %ld, last: %ld\n", i, cur, last); + res = AST_TEST_FAIL; goto return_cleanup; } last = cur; @@ -237,13 +270,11 @@ static int test3(int fd) } if (i != test_size) { - ast_cli(fd, "Stopped popping off after only getting %u nodes\n", i); - res = -7; + ast_test_status_update(test, "Stopped popping off after only getting %u nodes\n", i); + res = AST_TEST_FAIL; goto return_cleanup; } - ast_cli(fd, "Test #3 successful.\n"); - return_cleanup: if (h) { h = ast_heap_destroy(h); @@ -255,57 +286,21 @@ return_cleanup: return res; } - -static char *handle_cli_heap_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - int res; - - switch (cmd) { - case CLI_INIT: - e->command = "heap test"; - e->usage = "" - "Usage: heap test\n" - ""; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != e->args) { - return CLI_SHOWUSAGE; - } - - if ((res = test1(a->fd))) { - ast_cli(a->fd, "Test 1 failed! (%d)\n", res); - return CLI_FAILURE; - } - - if ((res = test2(a->fd))) { - ast_cli(a->fd, "Test 2 failed! (%d)\n", res); - return CLI_FAILURE; - } - - if ((res = test3(a->fd))) { - ast_cli(a->fd, "Test 3 failed! (%d)\n", res); - return CLI_FAILURE; - } - - return CLI_SUCCESS; -} - -static struct ast_cli_entry cli_heap[] = { - AST_CLI_DEFINE(handle_cli_heap_test, "Test the heap implementation"), -}; - static int unload_module(void) { - ast_cli_unregister_multiple(cli_heap, ARRAY_LEN(cli_heap)); + AST_TEST_UNREGISTER(heap_test_1); + AST_TEST_UNREGISTER(heap_test_2); + AST_TEST_UNREGISTER(heap_test_3); + return 0; } static int load_module(void) { - ast_cli_register_multiple(cli_heap, ARRAY_LEN(cli_heap)); + AST_TEST_REGISTER(heap_test_1); + AST_TEST_REGISTER(heap_test_2); + AST_TEST_REGISTER(heap_test_3); + return AST_MODULE_LOAD_SUCCESS; } diff --git a/tests/test_sched.c b/tests/test_sched.c index afbb759491..6cb9f61cb0 100644 --- a/tests/test_sched.c +++ b/tests/test_sched.c @@ -24,7 +24,7 @@ */ /*** MODULEINFO - no + TEST_FRAMEWORK ***/ #include "asterisk.h" @@ -34,122 +34,124 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" -#include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/sched.h" +#include "asterisk/test.h" +#include "asterisk/cli.h" static int sched_cb(const void *data) { return 0; } -static char *handle_cli_sched_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +AST_TEST_DEFINE(sched_test_order) { struct sched_context *con; - char *res = CLI_FAILURE; + enum ast_test_result_state res = AST_TEST_FAIL; int id1, id2, id3, wait; switch (cmd) { - case CLI_INIT: - e->command = "sched test"; - e->usage = "" - "Usage: sched test\n" - " Test scheduler entry ordering.\n" - ""; - return NULL; - case CLI_GENERATE: - return NULL; + case TEST_INIT: + info->name = "sched_test_order"; + info->category = "main/sched/"; + info->summary = "Test ordering of events in the scheduler API"; + info->description = + "This test ensures that events are properly ordered by the " + "time they are scheduled to execute in the scheduler API."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; } - if (a->argc != e->args) { - return CLI_SHOWUSAGE; - } - - ast_cli(a->fd, "Testing scheduler entry ordering ...\n"); - if (!(con = sched_context_create())) { - ast_cli(a->fd, "Test failed - could not create scheduler context\n"); - return CLI_FAILURE; + ast_test_status_update(test, + "Test failed - could not create scheduler context\n"); + return AST_TEST_FAIL; } /* Add 3 scheduler entries, and then remove them, ensuring that the result * of ast_sched_wait() looks appropriate at each step along the way. */ if ((wait = ast_sched_wait(con)) != -1) { - ast_cli(a->fd, "ast_sched_wait() should have returned -1, returned '%d'\n", + ast_test_status_update(test, + "ast_sched_wait() should have returned -1, returned '%d'\n", wait); goto return_cleanup; } if ((id1 = ast_sched_add(con, 100000, sched_cb, NULL)) == -1) { - ast_cli(a->fd, "Failed to add scheduler entry\n"); + ast_test_status_update(test, "Failed to add scheduler entry\n"); goto return_cleanup; } if ((wait = ast_sched_wait(con)) > 100000) { - ast_cli(a->fd, "ast_sched_wait() should have returned <= 100000, returned '%d'\n", + ast_test_status_update(test, + "ast_sched_wait() should have returned <= 100000, returned '%d'\n", wait); goto return_cleanup; } if ((id2 = ast_sched_add(con, 10000, sched_cb, NULL)) == -1) { - ast_cli(a->fd, "Failed to add scheduler entry\n"); + ast_test_status_update(test, "Failed to add scheduler entry\n"); goto return_cleanup; } if ((wait = ast_sched_wait(con)) > 10000) { - ast_cli(a->fd, "ast_sched_wait() should have returned <= 10000, returned '%d'\n", + ast_test_status_update(test, + "ast_sched_wait() should have returned <= 10000, returned '%d'\n", wait); goto return_cleanup; } if ((id3 = ast_sched_add(con, 1000, sched_cb, NULL)) == -1) { - ast_cli(a->fd, "Failed to add scheduler entry\n"); + ast_test_status_update(test, "Failed to add scheduler entry\n"); goto return_cleanup; } if ((wait = ast_sched_wait(con)) > 1000) { - ast_cli(a->fd, "ast_sched_wait() should have returned <= 1000, returned '%d'\n", + ast_test_status_update(test, + "ast_sched_wait() should have returned <= 1000, returned '%d'\n", wait); goto return_cleanup; } if (ast_sched_del(con, id3) == -1) { - ast_cli(a->fd, "Failed to remove scheduler entry\n"); + ast_test_status_update(test, "Failed to remove scheduler entry\n"); goto return_cleanup; } if ((wait = ast_sched_wait(con)) <= 1000) { - ast_cli(a->fd, "ast_sched_wait() should have returned > 1000, returned '%d'\n", + ast_test_status_update(test, + "ast_sched_wait() should have returned > 1000, returned '%d'\n", wait); goto return_cleanup; } if (ast_sched_del(con, id2) == -1) { - ast_cli(a->fd, "Failed to remove scheduler entry\n"); + ast_test_status_update(test, "Failed to remove scheduler entry\n"); goto return_cleanup; } if ((wait = ast_sched_wait(con)) <= 10000) { - ast_cli(a->fd, "ast_sched_wait() should have returned > 10000, returned '%d'\n", + ast_test_status_update(test, + "ast_sched_wait() should have returned > 10000, returned '%d'\n", wait); goto return_cleanup; } if (ast_sched_del(con, id1) == -1) { - ast_cli(a->fd, "Failed to remove scheduler entry\n"); + ast_test_status_update(test, "Failed to remove scheduler entry\n"); goto return_cleanup; } if ((wait = ast_sched_wait(con)) != -1) { - ast_cli(a->fd, "ast_sched_wait() should have returned -1, returned '%d'\n", + ast_test_status_update(test, + "ast_sched_wait() should have returned -1, returned '%d'\n", wait); goto return_cleanup; } - res = CLI_SUCCESS; - - ast_cli(a->fd, "Test passed!\n"); + res = AST_TEST_PASS; return_cleanup: sched_context_destroy(con); @@ -168,7 +170,7 @@ static char *handle_cli_sched_bench(struct ast_cli_entry *e, int cmd, struct ast case CLI_INIT: e->command = "sched benchmark"; e->usage = "" - "Usage: sched test \n" + "Usage: sched benchmark \n" ""; return NULL; case CLI_GENERATE: @@ -233,17 +235,18 @@ return_cleanup: static struct ast_cli_entry cli_sched[] = { AST_CLI_DEFINE(handle_cli_sched_bench, "Benchmark ast_sched add/del performance"), - AST_CLI_DEFINE(handle_cli_sched_test, "Test scheduler entry ordering"), }; static int unload_module(void) { + AST_TEST_UNREGISTER(sched_test_order); ast_cli_unregister_multiple(cli_sched, ARRAY_LEN(cli_sched)); return 0; } static int load_module(void) { + AST_TEST_REGISTER(sched_test_order); ast_cli_register_multiple(cli_sched, ARRAY_LEN(cli_sched)); return AST_MODULE_LOAD_SUCCESS; } diff --git a/tests/test_skel.c b/tests/test_skel.c index 9f80086588..c1ce5e844d 100644 --- a/tests/test_skel.c +++ b/tests/test_skel.c @@ -16,8 +16,8 @@ * at the top of the source tree. */ -/*! \file - * +/*! + * \file * \brief Skeleton Test * * \author\verbatim <> \endverbatim @@ -27,27 +27,55 @@ */ /*** MODULEINFO - no + TEST_FRAMEWORK ***/ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include "asterisk/file.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" +#include "asterisk/utils.h" #include "asterisk/module.h" -#include "asterisk/lock.h" -#include "asterisk/app.h" +#include "asterisk/test.h" + +AST_TEST_DEFINE(sample_test) +{ + void *ptr; + + switch (cmd) { + case TEST_INIT: + info->name = "sample_test"; + info->category = "main/sample/"; + info->summary = "sample unit test"; + info->description = + "This demonstrates what is required to implement " + "a unit test."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_test_status_update(test, "Executing sample test...\n"); + + if (!(ptr = ast_malloc(8))) { + ast_test_status_update(test, "ast_malloc() failed\n"); + return AST_TEST_FAIL; + } + + ast_free(ptr); + + return AST_TEST_PASS; +} static int unload_module(void) { + AST_TEST_UNREGISTER(sample_test); return 0; } static int load_module(void) { + AST_TEST_REGISTER(sample_test); return AST_MODULE_LOAD_SUCCESS; }