Sat Aug 6 00:39:32 2011

Asterisk developer's documentation


test.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2009-2010, Digium, Inc.
00005  *
00006  * David Vossel <dvossel@digium.com>
00007  * Russell Bryant <russell@digium.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*!
00021  * \file
00022  * \brief Unit Test Framework
00023  *
00024  * \author David Vossel <dvossel@digium.com>
00025  * \author Russell Bryant <russell@digium.com>
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 286023 $");
00031 
00032 #ifdef TEST_FRAMEWORK
00033 #include "asterisk/test.h"
00034 #include "asterisk/logger.h"
00035 #include "asterisk/linkedlists.h"
00036 #include "asterisk/utils.h"
00037 #include "asterisk/cli.h"
00038 #include "asterisk/term.h"
00039 #include "asterisk/version.h"
00040 #include "asterisk/paths.h"
00041 #include "asterisk/time.h"
00042 #include "asterisk/threadstorage.h"
00043 
00044 /*! This array corresponds to the values defined in the ast_test_state enum */
00045 static const char * const test_result2str[] = {
00046    [AST_TEST_NOT_RUN] = "NOT RUN",
00047    [AST_TEST_PASS]    = "PASS",
00048    [AST_TEST_FAIL]    = "FAIL",
00049 };
00050 
00051 /*! holds all the information pertaining to a single defined test */
00052 struct ast_test {
00053    struct ast_test_info info;        /*!< holds test callback information */
00054    /*!
00055     * \brief Test defined status output from last execution
00056     */
00057    struct ast_dynamic_str *status_str;
00058    /*!
00059     * \brief CLI arguments, if tests being run from the CLI
00060     *
00061     * If this is set, status updates from the tests will be sent to the
00062     * CLI in addition to being saved off in status_str.
00063     */
00064    int cli_fd;
00065    enum ast_test_result_state state; /*!< current test state */
00066    unsigned int time;                /*!< time in ms test took */
00067    ast_test_cb_t *cb;                /*!< test callback function */
00068    AST_LIST_ENTRY(ast_test) entry;
00069 };
00070 
00071 /*! global structure containing both total and last test execution results */
00072 static struct ast_test_execute_results {
00073    unsigned int total_tests;  /*!< total number of tests, regardless if they have been executed or not */
00074    unsigned int total_passed; /*!< total number of executed tests passed */
00075    unsigned int total_failed; /*!< total number of executed tests failed */
00076    unsigned int total_time;   /*!< total time of all executed tests */
00077    unsigned int last_passed;  /*!< number of passed tests during last execution */
00078    unsigned int last_failed;  /*!< number of failed tests during last execution */
00079    unsigned int last_time;    /*!< total time of the last test execution */
00080 } last_results;
00081 
00082 enum test_mode {
00083    TEST_ALL = 0,
00084    TEST_CATEGORY = 1,
00085    TEST_NAME_CATEGORY = 2,
00086 };
00087 
00088 /*! List of registered test definitions */
00089 static AST_LIST_HEAD_STATIC(tests, ast_test);
00090 
00091 static struct ast_test *test_alloc(ast_test_cb_t *cb);
00092 static struct ast_test *test_free(struct ast_test *test);
00093 static int test_insert(struct ast_test *test);
00094 static struct ast_test *test_remove(ast_test_cb_t *cb);
00095 static int test_cat_cmp(const char *cat1, const char *cat2);
00096 
00097 int __ast_test_status_update(const char *file, const char *func, int line,
00098       struct ast_test *test, const char *fmt, ...)
00099 {
00100    struct ast_dynamic_str *buf = NULL;
00101    va_list ap;
00102 
00103    if (!(buf = ast_dynamic_str_create(128))) {
00104       return -1;
00105    }
00106 
00107    va_start(ap, fmt);
00108    ast_dynamic_str_thread_set_va(&buf, 0, NULL, fmt, ap);
00109    va_end(ap);
00110 
00111    if (test->cli_fd > -1) {
00112       ast_cli(test->cli_fd, "[%s:%s:%d]: %s",
00113             file, func, line, buf->str);
00114    }
00115 
00116    ast_dynamic_str_append(&test->status_str, 0, "[%s:%s:%d]: %s",
00117          file, func, line, buf->str);
00118 
00119    ast_free(buf);
00120 
00121    return 0;
00122 }
00123 
00124 int ast_test_register(ast_test_cb_t *cb)
00125 {
00126    struct ast_test *test;
00127 
00128    if (!cb) {
00129       ast_log(LOG_WARNING, "Attempted to register test without all required information\n");
00130       return -1;
00131    }
00132 
00133    if (!(test = test_alloc(cb))) {
00134       return -1;
00135    }
00136 
00137    if (test_insert(test)) {
00138       test_free(test);
00139       return -1;
00140    }
00141 
00142    return 0;
00143 }
00144 
00145 int ast_test_unregister(ast_test_cb_t *cb)
00146 {
00147    struct ast_test *test;
00148 
00149    if (!(test = test_remove(cb))) {
00150       return -1; /* not found */
00151    }
00152 
00153    test_free(test);
00154 
00155    return 0;
00156 }
00157 
00158 /*!
00159  * \internal
00160  * \brief executes a single test, storing the results in the test->result structure.
00161  *
00162  * \note The last_results structure which contains global statistics about test execution
00163  * must be updated when using this function. See use in test_execute_multiple().
00164  */
00165 static void test_execute(struct ast_test *test)
00166 {
00167    struct timeval begin;
00168 
00169    ast_dynamic_str_set(&test->status_str, 0, "%s", "");
00170 
00171    begin = ast_tvnow();
00172    test->state = test->cb(&test->info, TEST_EXECUTE, test);
00173    test->time = ast_tvdiff_ms(ast_tvnow(), begin);
00174 }
00175 
00176 static void test_xml_entry(struct ast_test *test, FILE *f)
00177 {
00178    if (!f || !test || test->state == AST_TEST_NOT_RUN) {
00179       return;
00180    }
00181 
00182    fprintf(f, "\t<testcase time=\"%d.%d\" name=\"%s%s\"%s>\n",
00183          test->time / 1000, test->time % 1000,
00184          test->info.category, test->info.name,
00185          test->state == AST_TEST_PASS ? "/" : "");
00186 
00187    if (test->state == AST_TEST_FAIL) {
00188       fprintf(f, "\t\t<failure><![CDATA[\n%s\n\t\t]]></failure>\n",
00189             S_OR(test->status_str->str, "NA"));
00190       fprintf(f, "\t</testcase>\n");
00191    }
00192 
00193 }
00194 
00195 static void test_txt_entry(struct ast_test *test, FILE *f)
00196 {
00197    if (!f || !test) {
00198       return;
00199    }
00200 
00201    fprintf(f, "\nName:              %s\n", test->info.name);
00202    fprintf(f,   "Category:          %s\n", test->info.category);
00203    fprintf(f,   "Summary:           %s\n", test->info.summary);
00204    fprintf(f,   "Description:       %s\n", test->info.description);
00205    fprintf(f,   "Result:            %s\n", test_result2str[test->state]);
00206    if (test->state != AST_TEST_NOT_RUN) {
00207       fprintf(f,   "Time:              %d\n", test->time);
00208    }
00209    if (test->state == AST_TEST_FAIL) {
00210       fprintf(f,   "Error Description: %s\n\n", S_OR(test->status_str->str, "NA"));
00211    }
00212 }
00213 
00214 /*!
00215  * \internal
00216  * \brief Executes registered unit tests
00217  *
00218  * \param name of test to run (optional)
00219  * \param test category to run (optional)
00220  * \param cli args for cli test updates (optional)
00221  *
00222  * \return number of tests executed.
00223  *
00224  * \note This function has three modes of operation
00225  * -# When given a name and category, a matching individual test will execute if found.
00226  * -# When given only a category all matching tests within that category will execute.
00227  * -# If given no name or category all registered tests will execute.
00228  */
00229 static int test_execute_multiple(const char *name, const char *category, int cli_fd)
00230 {
00231    char result_buf[32] = { 0 };
00232    struct ast_test *test = NULL;
00233    enum test_mode mode = TEST_ALL; /* 3 modes, 0 = run all, 1 = only by category, 2 = only by name and category */
00234    int execute = 0;
00235    int res = 0;
00236 
00237    if (!ast_strlen_zero(category)) {
00238       if (!ast_strlen_zero(name)) {
00239          mode = TEST_NAME_CATEGORY;
00240       } else {
00241          mode = TEST_CATEGORY;
00242       }
00243    }
00244 
00245    AST_LIST_LOCK(&tests);
00246    /* clear previous execution results */
00247    memset(&last_results, 0, sizeof(last_results));
00248    AST_LIST_TRAVERSE(&tests, test, entry) {
00249 
00250       execute = 0;
00251       switch (mode) {
00252       case TEST_CATEGORY:
00253          if (!test_cat_cmp(test->info.category, category)) {
00254             execute = 1;
00255          }
00256          break;
00257       case TEST_NAME_CATEGORY:
00258          if (!(test_cat_cmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
00259             execute = 1;
00260          }
00261          break;
00262       case TEST_ALL:
00263          execute = 1;
00264       }
00265 
00266       if (execute) {
00267          if (cli_fd > -1) {
00268             ast_cli(cli_fd, "START  %s - %s \n", test->info.category, test->info.name);
00269          }
00270 
00271          /* set the test status update argument. it is ok if cli is NULL */
00272          test->cli_fd = cli_fd;
00273 
00274          /* execute the test and save results */
00275          test_execute(test);
00276 
00277          test->cli_fd = -1;
00278 
00279          /* update execution specific counts here */
00280          last_results.last_time += test->time;
00281          if (test->state == AST_TEST_PASS) {
00282             last_results.last_passed++;
00283          } else if (test->state == AST_TEST_FAIL) {
00284             last_results.last_failed++;
00285          }
00286 
00287          if (cli_fd > -1) {
00288             term_color(result_buf,
00289                test_result2str[test->state],
00290                (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
00291                0,
00292                sizeof(result_buf));
00293             ast_cli(cli_fd, "END    %s - %s Time: %s%dms Result: %s\n",
00294                test->info.category,
00295                test->info.name,
00296                test->time ? "" : "<",
00297                test->time ? test->time : 1,
00298                result_buf);
00299          }
00300       }
00301 
00302       /* update total counts as well during this iteration
00303        * even if the current test did not execute this time */
00304       last_results.total_time += test->time;
00305       last_results.total_tests++;
00306       if (test->state != AST_TEST_NOT_RUN) {
00307          if (test->state == AST_TEST_PASS) {
00308             last_results.total_passed++;
00309          } else {
00310             last_results.total_failed++;
00311          }
00312       }
00313    }
00314    res = last_results.last_passed + last_results.last_failed;
00315    AST_LIST_UNLOCK(&tests);
00316 
00317    return res;
00318 }
00319 
00320 /*!
00321  * \internal
00322  * \brief Generate test results.
00323  *
00324  * \param name of test result to generate (optional)
00325  * \param test category to generate (optional)
00326  * \param path to xml file to generate. (optional)
00327  * \param path to txt file to generate, (optional)
00328  *
00329  * \retval 0 success
00330  * \retval -1 failure
00331  *
00332  * \note This function has three modes of operation.
00333  * -# When given both a name and category, results will be generated for that single test.
00334  * -# When given only a category, results for every test within the category will be generated.
00335  * -# When given no name or category, results for every registered test will be generated.
00336  *
00337  * In order for the results to be generated, an xml and or txt file path must be provided.
00338  */
00339 static int test_generate_results(const char *name, const char *category, const char *xml_path, const char *txt_path)
00340 {
00341    enum test_mode mode = TEST_ALL;  /* 0 generate all, 1 generate by category only, 2 generate by name and category */
00342    FILE *f_xml = NULL, *f_txt = NULL;
00343    int res = 0;
00344    struct ast_test *test = NULL;
00345 
00346    /* verify at least one output file was given */
00347    if (ast_strlen_zero(xml_path) && ast_strlen_zero(txt_path)) {
00348       return -1;
00349    }
00350 
00351    /* define what mode is to be used */
00352    if (!ast_strlen_zero(category)) {
00353       if (!ast_strlen_zero(name)) {
00354          mode = TEST_NAME_CATEGORY;
00355       } else {
00356          mode = TEST_CATEGORY;
00357       }
00358    }
00359    /* open files for writing */
00360    if (!ast_strlen_zero(xml_path)) {
00361       if (!(f_xml = fopen(xml_path, "w"))) {
00362          ast_log(LOG_WARNING, "Could not open file %s for xml test results\n", xml_path);
00363          res = -1;
00364          goto done;
00365       }
00366    }
00367    if (!ast_strlen_zero(txt_path)) {
00368       if (!(f_txt = fopen(txt_path, "w"))) {
00369          ast_log(LOG_WARNING, "Could not open file %s for text output of test results\n", txt_path);
00370          res = -1;
00371          goto done;
00372       }
00373    }
00374 
00375    AST_LIST_LOCK(&tests);
00376    /* xml header information */
00377    if (f_xml) {
00378       /*
00379        * http://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo
00380        */
00381       fprintf(f_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
00382       fprintf(f_xml, "<testsuite errors=\"0\" time=\"%d.%d\" tests=\"%d\" "
00383             "name=\"AsteriskUnitTests\">\n",
00384             last_results.total_time / 1000, last_results.total_time % 1000,
00385             last_results.total_tests);
00386       fprintf(f_xml, "\t<properties>\n");
00387       fprintf(f_xml, "\t\t<property name=\"version\" value=\"%s\"/>\n", ASTERISK_VERSION);
00388       fprintf(f_xml, "\t</properties>\n");
00389    }
00390 
00391    /* txt header information */
00392    if (f_txt) {
00393       fprintf(f_txt, "Asterisk Version:         %s\n", ASTERISK_VERSION);
00394       fprintf(f_txt, "Asterisk Version Number:  %d\n", ASTERISK_VERSION_NUM);
00395       fprintf(f_txt, "Number of Tests:          %d\n", last_results.total_tests);
00396       fprintf(f_txt, "Number of Tests Executed: %d\n", (last_results.total_passed + last_results.total_failed));
00397       fprintf(f_txt, "Passed Tests:             %d\n", last_results.total_passed);
00398       fprintf(f_txt, "Failed Tests:             %d\n", last_results.total_failed);
00399       fprintf(f_txt, "Total Execution Time:     %d\n", last_results.total_time);
00400    }
00401 
00402    /* export each individual test */
00403    AST_LIST_TRAVERSE(&tests, test, entry) {
00404       switch (mode) {
00405       case TEST_CATEGORY:
00406          if (!test_cat_cmp(test->info.category, category)) {
00407             test_xml_entry(test, f_xml);
00408             test_txt_entry(test, f_txt);
00409          }
00410          break;
00411       case TEST_NAME_CATEGORY:
00412          if (!(strcmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
00413             test_xml_entry(test, f_xml);
00414             test_txt_entry(test, f_txt);
00415          }
00416          break;
00417       case TEST_ALL:
00418          test_xml_entry(test, f_xml);
00419          test_txt_entry(test, f_txt);
00420       }
00421    }
00422    AST_LIST_UNLOCK(&tests);
00423 
00424 done:
00425    if (f_xml) {
00426       fprintf(f_xml, "</testsuite>\n");
00427       fclose(f_xml);
00428    }
00429    if (f_txt) {
00430       fclose(f_txt);
00431    }
00432 
00433    return res;
00434 }
00435 
00436 /*!
00437  * \internal
00438  * \brief adds test to container sorted first by category then by name
00439  *
00440  * \retval 0 success
00441  * \retval -1 failure
00442  */
00443 static int test_insert(struct ast_test *test)
00444 {
00445    /* This is a slow operation that may need to be optimized in the future
00446     * as the test framework expands.  At the moment we are doing string
00447     * comparisons on every item within the list to insert in sorted order. */
00448 
00449    AST_LIST_LOCK(&tests);
00450    AST_LIST_INSERT_SORTALPHA(&tests, test, entry, info.category);
00451    AST_LIST_UNLOCK(&tests);
00452 
00453    return 0;
00454 }
00455 
00456 /*!
00457  * \internal
00458  * \brief removes test from container
00459  *
00460  * \return ast_test removed from list on success, or NULL on failure
00461  */
00462 static struct ast_test *test_remove(ast_test_cb_t *cb)
00463 {
00464    struct ast_test *cur = NULL;
00465 
00466    AST_LIST_LOCK(&tests);
00467    AST_LIST_TRAVERSE_SAFE_BEGIN(&tests, cur, entry) {
00468       if (cur->cb == cb) {
00469          AST_LIST_REMOVE_CURRENT(&tests, entry);
00470          break;
00471       }
00472    }
00473    AST_LIST_TRAVERSE_SAFE_END;
00474    AST_LIST_UNLOCK(&tests);
00475 
00476    return cur;
00477 }
00478 
00479 /*!
00480  * \brief compares two test categories to determine if cat1 resides in cat2
00481  * \internal
00482  *
00483  * \retval 0 true
00484  * \retval non-zero false
00485  */
00486 
00487 static int test_cat_cmp(const char *cat1, const char *cat2)
00488 {
00489    int len1 = 0;
00490    int len2 = 0;
00491 
00492    if (!cat1 || !cat2) {
00493       return -1;
00494    }
00495 
00496    len1 = strlen(cat1);
00497    len2 = strlen(cat2);
00498 
00499    if (len2 > len1) {
00500       return -1;
00501    }
00502 
00503    return strncmp(cat1, cat2, len2) ? 1 : 0;
00504 }
00505 
00506 /*!
00507  * \internal
00508  * \brief free an ast_test object and all it's data members
00509  */
00510 static struct ast_test *test_free(struct ast_test *test)
00511 {
00512    if (!test) {
00513       return NULL;
00514    }
00515 
00516    ast_free(test->status_str);
00517    ast_free(test);
00518 
00519    return NULL;
00520 }
00521 
00522 /*!
00523  * \internal
00524  * \brief allocate an ast_test object.
00525  */
00526 static struct ast_test *test_alloc(ast_test_cb_t *cb)
00527 {
00528    struct ast_test *test;
00529 
00530    if (!cb || !(test = ast_calloc(1, sizeof(*test)))) {
00531       return NULL;
00532    }
00533 
00534    test->cli_fd = -1;
00535    test->cb = cb;
00536 
00537    test->cb(&test->info, TEST_INIT, test);
00538 
00539    if (ast_strlen_zero(test->info.name)) {
00540       ast_log(LOG_WARNING, "Test has no name, test registration refused.\n");
00541       return test_free(test);
00542    }
00543 
00544    if (ast_strlen_zero(test->info.category)) {
00545       ast_log(LOG_WARNING, "Test %s has no category, test registration refused.\n",
00546             test->info.name);
00547       return test_free(test);
00548    }
00549 
00550    if (test->info.category[0] != '/' || test->info.category[strlen(test->info.category) - 1] != '/') {
00551       ast_log(LOG_WARNING, "Test category is missing a leading or trailing backslash for test %s%s\n",
00552             test->info.category, test->info.name);
00553    }
00554 
00555    if (ast_strlen_zero(test->info.summary)) {
00556       ast_log(LOG_WARNING, "Test %s/%s has no summary, test registration refused.\n",
00557             test->info.category, test->info.name);
00558       return test_free(test);
00559    }
00560 
00561    if (ast_strlen_zero(test->info.description)) {
00562       ast_log(LOG_WARNING, "Test %s/%s has no description, test registration refused.\n",
00563             test->info.category, test->info.name);
00564       return test_free(test);
00565    }
00566 
00567    if (!(test->status_str = ast_dynamic_str_create(128))) {
00568       return test_free(test);
00569    }
00570 
00571    return test;
00572 }
00573 
00574 static char *complete_test_category(const char *line, const char *word, int pos, int state)
00575 {
00576    int which = 0;
00577    int wordlen = strlen(word);
00578    char *ret = NULL;
00579    struct ast_test *test;
00580 
00581    AST_LIST_LOCK(&tests);
00582    AST_LIST_TRAVERSE(&tests, test, entry) {
00583       if (!strncasecmp(word, test->info.category, wordlen) && ++which > state) {
00584          ret = ast_strdup(test->info.category);
00585          break;
00586       }
00587    }
00588    AST_LIST_UNLOCK(&tests);
00589    return ret;
00590 }
00591 
00592 static char *complete_test_name(const char *line, const char *word, int pos, int state, int cat_pos)
00593 {
00594    int which = 0;
00595    int wordlen = strlen(word);
00596    char *ret = NULL;
00597    struct ast_test *test;
00598    char *cat = NULL;
00599    char *tmp = ast_strdupa(line);
00600    int i;
00601 
00602    for (i = 0; i < cat_pos - 1 && tmp; cat = strsep(&tmp, " "), cat_pos--) {
00603       tmp = ast_skip_blanks(tmp);
00604    }
00605 
00606    AST_LIST_LOCK(&tests);
00607    AST_LIST_TRAVERSE(&tests, test, entry) {
00608       if (!test_cat_cmp(test->info.category, cat) && (!strncasecmp(word, test->info.name, wordlen) && ++which > state)) {
00609          ret = ast_strdup(test->info.name);
00610          break;
00611       }
00612    }
00613    AST_LIST_UNLOCK(&tests);
00614    return ret;
00615 }
00616 
00617 static char *complete_show_registered(const char *line, const char *word, int pos, int state)
00618 {
00619    static char * const option1[] = { "all", "category", NULL };
00620    static char * const option2[] = { "name", NULL };
00621 
00622    if (pos == 3) {
00623       return ast_cli_complete(word, option1, state);
00624    }
00625    if (pos == 4) {
00626       return complete_test_category(line, word, pos, state);
00627    }
00628    if (pos == 5) {
00629       return ast_cli_complete(word, option2, state);
00630    }
00631    if (pos == 6) {
00632       return complete_test_name(line, word, pos, state, 6);
00633    }
00634 
00635    return NULL;
00636 }
00637 
00638 /* CLI commands */
00639 static int test_cli_show_registered(int fd, int argc, char *argv[])
00640 {
00641 #define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n"
00642    struct ast_test *test = NULL;
00643    int count = 0;
00644 
00645    if ((argc < 4) || (argc == 6) || (argc > 7) ||
00646       ((argc == 4) && strcmp(argv[3], "all")) ||
00647       ((argc == 7) && strcmp(argv[5], "name"))) {
00648       return RESULT_SHOWUSAGE;
00649    }
00650    ast_cli(fd, FORMAT, "Category", "Name", "Summary", "Test Result");
00651    ast_cli(fd, FORMAT, "--------", "----", "-------", "-----------");
00652    AST_LIST_LOCK(&tests);
00653    AST_LIST_TRAVERSE(&tests, test, entry) {
00654       if ((argc == 4) ||
00655           ((argc == 5) && !test_cat_cmp(test->info.category, argv[4])) ||
00656           ((argc == 7) && !strcmp(test->info.category, argv[4]) && !strcmp(test->info.name, argv[6]))) {
00657 
00658          ast_cli(fd, FORMAT, test->info.category, test->info.name,
00659                test->info.summary, test_result2str[test->state]);
00660          count++;
00661       }
00662    }
00663    AST_LIST_UNLOCK(&tests);
00664    ast_cli(fd, FORMAT, "--------", "----", "-------", "-----------");
00665    ast_cli(fd, "\n%d Registered Tests Matched\n", count);
00666 
00667    return RESULT_SUCCESS;
00668 }
00669 
00670 static char *complete_execute_registered(const char *line, const char *word, int pos, int state)
00671 {
00672    static char * const option1[] = { "all", "category", NULL };
00673    static char * const option2[] = { "name", NULL };
00674 
00675    if (pos == 2) {
00676       return ast_cli_complete(word, option1, state);
00677    }
00678    if (pos == 3) {
00679       return complete_test_category(line, word, pos, state);
00680    }
00681    if (pos == 4) {
00682       return ast_cli_complete(word, option2, state);
00683    }
00684    if (pos == 5) {
00685       return complete_test_name(line, word, pos, state, 5);
00686    }
00687 
00688    return NULL;
00689 }
00690 
00691 static int test_cli_execute_registered(int fd, int argc, char *argv[])
00692 {
00693    if (argc < 3|| argc > 6) {
00694       return RESULT_SHOWUSAGE;
00695    }
00696 
00697    if ((argc == 3) && !strcmp(argv[2], "all")) { /* run all registered tests */
00698       ast_cli(fd, "Running all available tests...\n\n");
00699       test_execute_multiple(NULL, NULL, fd);
00700    } else if (argc == 4) { /* run only tests within a category */
00701       ast_cli(fd, "Running all available tests matching category %s\n\n", argv[3]);
00702       test_execute_multiple(NULL, argv[3], fd);
00703    } else if (argc == 6) { /* run only a single test matching the category and name */
00704       ast_cli(fd, "Running all available tests matching category %s and name %s\n\n", argv[3], argv[5]);
00705       test_execute_multiple(argv[5], argv[3], fd);
00706    } else {
00707       return RESULT_SHOWUSAGE;
00708    }
00709 
00710    AST_LIST_LOCK(&tests);
00711    if (!(last_results.last_passed + last_results.last_failed)) {
00712       ast_cli(fd, "--- No Tests Found! ---\n");
00713    }
00714    ast_cli(fd, "\n%d Test(s) Executed  %d Passed  %d Failed\n",
00715       (last_results.last_passed + last_results.last_failed),
00716       last_results.last_passed,
00717       last_results.last_failed);
00718    AST_LIST_UNLOCK(&tests);
00719 
00720    return RESULT_SUCCESS;
00721 }
00722 
00723 static char *complete_show_results(const char *line, const char *word, int pos, int state)
00724 {
00725    static char * const option1[] = { "all", "failed", "passed", NULL };
00726 
00727    if (pos == 3) {
00728       return ast_cli_complete(word, option1, state);
00729    }
00730 
00731    return NULL;
00732 }
00733 
00734 static int test_cli_show_results(int fd, int argc, char *argv[])
00735 {
00736 #define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n"
00737 #define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n"
00738    char result_buf[32] = { 0 };
00739    struct ast_test *test = NULL;
00740    int failed = 0;
00741    int passed = 0;
00742    int mode;  /* 0 for show all, 1 for show fail, 2 for show passed */
00743 
00744    /* verify input */
00745    if (argc != 4) {
00746       return RESULT_SHOWUSAGE;
00747    } else if (!strcmp(argv[3], "passed")) {
00748       mode = 2;
00749    } else if (!strcmp(argv[3], "failed")) {
00750       mode = 1;
00751    } else if (!strcmp(argv[3], "all")) {
00752       mode = 0;
00753    } else {
00754       return RESULT_SHOWUSAGE;
00755    }
00756 
00757    ast_cli(fd, FORMAT_RES_ALL1, "Result", "", "Name", "Category", "Time");
00758    AST_LIST_LOCK(&tests);
00759    AST_LIST_TRAVERSE(&tests, test, entry) {
00760       if (test->state == AST_TEST_NOT_RUN) {
00761          continue;
00762       }
00763       test->state == AST_TEST_FAIL ? failed++ : passed++;
00764       if (!mode || ((mode == 1) && (test->state == AST_TEST_FAIL)) || ((mode == 2) && (test->state == AST_TEST_PASS))) {
00765          /* give our results pretty colors */
00766          term_color(result_buf, test_result2str[test->state],
00767             (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
00768             0, sizeof(result_buf));
00769 
00770          ast_cli(fd, FORMAT_RES_ALL2,
00771             result_buf,
00772             "  ",
00773             test->info.name,
00774             test->info.category,
00775             test->time ? " " : "<",
00776             test->time ? test->time : 1);
00777       }
00778    }
00779    AST_LIST_UNLOCK(&tests);
00780 
00781    ast_cli(fd, "%d Test(s) Executed  %d Passed  %d Failed\n", (failed + passed), passed, failed);
00782 
00783    return RESULT_SUCCESS;
00784 }
00785 
00786 static char *complete_generate_results(const char *line, const char *word, int pos, int state)
00787 {
00788    static char * const option[] = { "xml", "txt", NULL };
00789 
00790    if (pos == 3) {
00791       return ast_cli_complete(word, option, state);
00792    }
00793 
00794    return NULL;
00795 }
00796 
00797 static int test_cli_generate_results(int fd, int argc, char *argv[])
00798 {
00799    const char *file = NULL;
00800    const char *type = "";
00801    int isxml = 0;
00802    int res = 0;
00803    struct ast_dynamic_str *buf = NULL;
00804    struct timeval time = ast_tvnow();
00805 
00806    /* verify input */
00807    if (argc < 4 || argc > 5) {
00808       return RESULT_SHOWUSAGE;
00809    } else if (!strcmp(argv[3], "xml")) {
00810       type = "xml";
00811       isxml = 1;
00812    } else if (!strcmp(argv[3], "txt")) {
00813       type = "txt";
00814    } else {
00815       return RESULT_SHOWUSAGE;
00816    }
00817 
00818    if (argc == 5) {
00819       file = argv[4];
00820    } else {
00821       if (!(buf = ast_dynamic_str_create(256))) {
00822          return RESULT_FAILURE;
00823       }
00824       ast_dynamic_str_set(&buf, 0, "%s/asterisk_test_results-%ld.%s", ast_config_AST_LOG_DIR, (long) time.tv_sec, type);
00825 
00826       file = buf->str;
00827    }
00828 
00829    if (isxml) {
00830       res = test_generate_results(NULL, NULL, file, NULL);
00831    } else {
00832       res = test_generate_results(NULL, NULL, NULL, file);
00833    }
00834 
00835    if (!res) {
00836       ast_cli(fd, "Results Generated Successfully: %s\n", S_OR(file, ""));
00837    } else {
00838       ast_cli(fd, "Results Could Not Be Generated: %s\n", S_OR(file, ""));
00839    }
00840 
00841    ast_free(buf);
00842 
00843    return RESULT_SUCCESS;
00844 }
00845 
00846 static const char show_registered_help[] = ""
00847    "Usage: 'test show registered' can be used in three ways.\n"
00848    "       1. 'test show registered all' shows all registered tests\n"
00849    "       2. 'test show registered category [test category]' shows all tests in the given\n"
00850    "          category.\n"
00851    "       3. 'test show registered category [test category] name [test name]' shows all\n"
00852    "           tests in a given category matching a given name\n";
00853 
00854 static const char execute_registered_help[] = ""
00855    "Usage: test execute can be used in three ways.\n"
00856    "       1. 'test execute all' runs all registered tests\n"
00857    "       2. 'test execute category [test category]' runs all tests in the given\n"
00858    "          category.\n"
00859    "       3. 'test execute category [test category] name [test name]' runs all\n"
00860    "           tests in a given category matching a given name\n";
00861 
00862 static const char show_results_help[] = ""
00863    "Usage: test show results can be used in three ways\n"
00864    "       1. 'test show results all' Displays results for all executed tests.\n"
00865    "       2. 'test show results passed' Displays results for all passed tests.\n"
00866    "       3. 'test show results failed' Displays results for all failed tests.\n";
00867 
00868 static const char generate_results_help[] = ""
00869    "Usage: 'test generate results'\n"
00870    "       Generates test results in either xml or txt format. An optional \n"
00871    "       file path may be provided to specify the location of the xml or\n"
00872    "       txt file\n"
00873    "       \nExample usage:\n"
00874    "       'test generate results xml' this writes to a default file\n"
00875    "       'test generate results xml /path/to/file.xml' writes to specified file\n";
00876 
00877 static struct ast_cli_entry test_cli[] = {
00878    { { "test", "show", "registered", NULL },
00879    test_cli_show_registered, "Show registered tests",
00880    show_registered_help, complete_show_registered, },
00881 
00882    { { "test", "execute", NULL },
00883    test_cli_execute_registered, "Execute registered tests",
00884    execute_registered_help, complete_execute_registered, },
00885 
00886    { { "test", "show", "results", NULL },
00887    test_cli_show_results, "Show last test results",
00888    show_results_help, complete_show_results, },
00889 
00890    { { "test", "generate", "results", NULL },
00891    test_cli_generate_results, "Generate test results to a file",
00892    generate_results_help, complete_generate_results, },
00893 };
00894 #endif /* TEST_FRAMEWORK */
00895 
00896 int ast_test_init()
00897 {
00898 #ifdef TEST_FRAMEWORK
00899    /* Register cli commands */
00900    ast_cli_register_multiple(test_cli, ARRAY_LEN(test_cli));
00901 #endif
00902 
00903    return 0;
00904 }

Generated on Sat Aug 6 00:39:32 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7