Wed Aug 7 17:15:45 2019

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

Generated on 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1