Asterisk - The Open Source Telephony Project  18.5.0
test.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009-2010, Digium, Inc.
5  *
6  * David Vossel <[email protected]>
7  * Russell Bryant <[email protected]>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*!
21  * \file
22  * \brief Unit Test Framework
23  *
24  * \author David Vossel <[email protected]>
25  * \author Russell Bryant <[email protected]>
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/_private.h"
35 
36 #ifdef TEST_FRAMEWORK
37 #include "asterisk/test.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/linkedlists.h"
40 #include "asterisk/utils.h"
41 #include "asterisk/cli.h"
42 #include "asterisk/term.h"
43 #include "asterisk/ast_version.h"
44 #include "asterisk/paths.h"
45 #include "asterisk/time.h"
46 #include "asterisk/stasis.h"
47 #include "asterisk/json.h"
48 #include "asterisk/astobj2.h"
49 #include "asterisk/stasis.h"
50 #include "asterisk/json.h"
51 
52 /*! \since 12
53  * \brief The topic for test suite messages
54  */
56 
57 /*! This array corresponds to the values defined in the ast_test_state enum */
58 static const char * const test_result2str[] = {
59  [AST_TEST_NOT_RUN] = "NOT RUN",
60  [AST_TEST_PASS] = "PASS",
61  [AST_TEST_FAIL] = "FAIL",
62 };
63 
64 /*! holds all the information pertaining to a single defined test */
65 struct ast_test {
66  struct ast_test_info info; /*!< holds test callback information */
67  /*!
68  * \brief Test defined status output from last execution
69  */
71  /*!
72  * \brief CLI arguments, if tests being run from the CLI
73  *
74  * If this is set, status updates from the tests will be sent to the
75  * CLI in addition to being saved off in status_str.
76  */
77  struct ast_cli_args *cli;
78  enum ast_test_result_state state; /*!< current test state */
79  unsigned int time; /*!< time in ms test took */
80  ast_test_cb_t *cb; /*!< test callback function */
81  ast_test_init_cb_t *init_cb; /*!< test init function */
82  ast_test_cleanup_cb_t *cleanup_cb; /*!< test cleanup function */
84 };
85 
86 /*! global structure containing both total and last test execution results */
87 static struct ast_test_execute_results {
88  unsigned int total_tests; /*!< total number of tests, regardless if they have been executed or not */
89  unsigned int total_passed; /*!< total number of executed tests passed */
90  unsigned int total_failed; /*!< total number of executed tests failed */
91  unsigned int total_time; /*!< total time of all executed tests */
92  unsigned int last_passed; /*!< number of passed tests during last execution */
93  unsigned int last_failed; /*!< number of failed tests during last execution */
94  unsigned int last_time; /*!< total time of the last test execution */
95 } last_results;
96 
97 enum test_mode {
98  TEST_ALL = 0,
101 };
102 
103 /*! List of registered test definitions */
105 
106 static struct ast_test *test_alloc(ast_test_cb_t *cb);
107 static struct ast_test *test_free(struct ast_test *test);
108 static int test_insert(struct ast_test *test);
109 static struct ast_test *test_remove(ast_test_cb_t *cb);
110 static int test_cat_cmp(const char *cat1, const char *cat2);
111 static int registration_errors = 0;
112 
113 void ast_test_debug(struct ast_test *test, const char *fmt, ...)
114 {
115  struct ast_str *buf = NULL;
116  va_list ap;
117 
118  buf = ast_str_create(128);
119  if (!buf) {
120  return;
121  }
122 
123  va_start(ap, fmt);
124  ast_str_set_va(&buf, 0, fmt, ap);
125  va_end(ap);
126 
127  if (test->cli) {
128  ast_cli(test->cli->fd, "%s", ast_str_buffer(buf));
129  }
130 
131  ast_free(buf);
132 }
133 
134 int __ast_test_status_update(const char *file, const char *func, int line, struct ast_test *test, const char *fmt, ...)
135 {
136  struct ast_str *buf = NULL;
137  va_list ap;
138 
139  if (!(buf = ast_str_create(128))) {
140  return -1;
141  }
142 
143  va_start(ap, fmt);
144  ast_str_set_va(&buf, 0, fmt, ap);
145  va_end(ap);
146 
147  if (test->cli) {
148  ast_cli(test->cli->fd, "[%s:%s:%d]: %s",
149  file, func, line, ast_str_buffer(buf));
150  }
151 
152  ast_str_append(&test->status_str, 0, "[%s:%s:%d]: %s",
153  file, func, line, ast_str_buffer(buf));
154 
155  ast_free(buf);
156 
157  return 0;
158 }
159 
160 int ast_test_register_init(const char *category, ast_test_init_cb_t *cb)
161 {
162  struct ast_test *test;
163  int registered = 1;
164 
166  AST_LIST_TRAVERSE(&tests, test, entry) {
167  if (!(test_cat_cmp(test->info.category, category))) {
168  test->init_cb = cb;
169  registered = 0;
170  }
171  }
173 
174  return registered;
175 }
176 
177 int ast_test_register_cleanup(const char *category, ast_test_cleanup_cb_t *cb)
178 {
179  struct ast_test *test;
180  int registered = 1;
181 
183  AST_LIST_TRAVERSE(&tests, test, entry) {
184  if (!(test_cat_cmp(test->info.category, category))) {
185  test->cleanup_cb = cb;
186  registered = 0;
187  }
188  }
190 
191  return registered;
192 }
193 
194 int ast_test_register(ast_test_cb_t *cb)
195 {
196  struct ast_test *test;
197 
198  if (!cb) {
199  ast_log(LOG_ERROR, "Attempted to register test without all required information\n");
201  return -1;
202  }
203 
204  if (!(test = test_alloc(cb))) {
206  return -1;
207  }
208 
209  if (test_insert(test)) {
210  test_free(test);
212  return -1;
213  }
214 
215  return 0;
216 }
217 
218 int ast_test_unregister(ast_test_cb_t *cb)
219 {
220  struct ast_test *test;
221 
222  if (!(test = test_remove(cb))) {
223  return -1; /* not found */
224  }
225 
226  test_free(test);
227 
228  return 0;
229 }
230 
231 /*!
232  * \internal
233  * \brief executes a single test, storing the results in the test->result structure.
234  *
235  * \note The last_results structure which contains global statistics about test execution
236  * must be updated when using this function. See use in test_execute_multiple().
237  */
238 static void test_execute(struct ast_test *test)
239 {
240  struct timeval begin;
242 
243  ast_str_reset(test->status_str);
244 
245  begin = ast_tvnow();
246  if (test->init_cb && test->init_cb(&test->info, test)) {
247  test->state = AST_TEST_FAIL;
248  goto exit;
249  }
250  test->state = AST_TEST_NOT_RUN;
251  result = test->cb(&test->info, TEST_EXECUTE, test);
252  if (test->state != AST_TEST_FAIL) {
253  test->state = result;
254  }
255  if (test->cleanup_cb && test->cleanup_cb(&test->info, test)) {
256  test->state = AST_TEST_FAIL;
257  }
258 exit:
259  test->time = ast_tvdiff_ms(ast_tvnow(), begin);
260 }
261 
263 {
264  if (test->state == AST_TEST_FAIL || state == AST_TEST_NOT_RUN) {
265  return;
266  }
267  test->state = state;
268 }
269 
270 /*
271  * These are the Java reserved words we need to munge so Jenkins
272  * doesn't barf on them.
273  */
274 static char *reserved_words[] = {
275  "abstract", "arguments", "as", "assert", "await",
276  "boolean", "break", "byte", "case", "catch", "char", "class",
277  "const", "continue", "debugger", "def", "default", "delete", "do",
278  "double", "else", "enum", "eval", "export", "extends", "false",
279  "final", "finally", "float", "for", "function", "goto", "if",
280  "implements", "import", "in", "instanceof", "int", "interface",
281  "let", "long", "native", "new", "null", "package", "private",
282  "protected", "public", "return", "short", "static", "strictfp",
283  "string", "super", "switch", "synchronized", "this", "throw", "throws",
284  "trait", "transient", "true", "try", "typeof", "var", "void",
285  "volatile", "while", "with", "yield" };
286 
287 static int is_reserved_word(const char *word)
288 {
289  int i;
290 
291  for (i = 0; i < ARRAY_LEN(reserved_words); i++) {
292  if (strcmp(word, reserved_words[i]) == 0) {
293  return 1;
294  }
295  }
296 
297  return 0;
298 }
299 
300 static void test_xml_entry(struct ast_test *test, FILE *f)
301 {
302  /* We need a copy of the category skipping past the initial '/' */
303  char *test_cat = ast_strdupa(test->info.category + 1);
304  char *next_cat;
305  char *test_name = (char *)test->info.name;
306  struct ast_str *category = ast_str_create(strlen(test->info.category) + 32);
307 
308  if (!category || test->state == AST_TEST_NOT_RUN) {
309  ast_free(category);
310 
311  return;
312  }
313 
314  while ((next_cat = ast_strsep(&test_cat, '/', AST_STRSEP_TRIM))) {
315  char *prefix = "";
316 
317  if (is_reserved_word(next_cat)) {
318  prefix = "_";
319  }
320  ast_str_append(&category, 0, ".%s%s", prefix, next_cat);
321  }
322  test_cat = ast_str_buffer(category);
323  /* Skip past the initial '.' */
324  test_cat++;
325 
326  if (is_reserved_word(test->info.name)) {
327  size_t name_length = strlen(test->info.name) + 2;
328 
329  test_name = ast_alloca(name_length);
330  snprintf(test_name, name_length, "_%s", test->info.name);
331  }
332 
333  fprintf(f, "\t\t<testcase time=\"%u.%u\" classname=\"%s\" name=\"%s\"%s>\n",
334  test->time / 1000, test->time % 1000,
335  test_cat, test_name,
336  test->state == AST_TEST_PASS ? "/" : "");
337 
338  ast_free(category);
339 
340  if (test->state == AST_TEST_FAIL) {
341  fprintf(f, "\t\t\t<failure><![CDATA[\n%s\n\t\t]]></failure>\n",
342  S_OR(ast_str_buffer(test->status_str), "NA"));
343  fprintf(f, "\t\t</testcase>\n");
344  }
345 
346 }
347 
348 static void test_txt_entry(struct ast_test *test, FILE *f)
349 {
350  if (!f || !test) {
351  return;
352  }
353 
354  fprintf(f, "\nName: %s\n", test->info.name);
355  fprintf(f, "Category: %s\n", test->info.category);
356  fprintf(f, "Summary: %s\n", test->info.summary);
357  fprintf(f, "Description: %s\n", test->info.description);
358  fprintf(f, "Result: %s\n", test_result2str[test->state]);
359  if (test->state != AST_TEST_NOT_RUN) {
360  fprintf(f, "Time: %u\n", test->time);
361  }
362  if (test->state == AST_TEST_FAIL) {
363  fprintf(f, "Error Description: %s\n\n", S_OR(ast_str_buffer(test->status_str), "NA"));
364  }
365 }
366 
367 /*!
368  * \internal
369  * \brief Executes registered unit tests
370  *
371  * \param name of test to run (optional)
372  * \param test category to run (optional)
373  * \param cli args for cli test updates (optional)
374  *
375  * \return number of tests executed.
376  *
377  * \note This function has three modes of operation
378  * -# When given a name and category, a matching individual test will execute if found.
379  * -# When given only a category all matching tests within that category will execute.
380  * -# If given no name or category all registered tests will execute.
381  */
382 static int test_execute_multiple(const char *name, const char *category, struct ast_cli_args *cli)
383 {
384  char result_buf[32] = { 0 };
385  struct ast_test *test = NULL;
386  enum test_mode mode = TEST_ALL; /* 3 modes, 0 = run all, 1 = only by category, 2 = only by name and category */
387  int execute = 0;
388  int res = 0;
389 
390  if (!ast_strlen_zero(category)) {
391  if (!ast_strlen_zero(name)) {
392  mode = TEST_NAME_CATEGORY;
393  } else {
394  mode = TEST_CATEGORY;
395  }
396  }
397 
399  /* clear previous execution results */
400  memset(&last_results, 0, sizeof(last_results));
401  AST_LIST_TRAVERSE(&tests, test, entry) {
402 
403  execute = 0;
404  switch (mode) {
405  case TEST_CATEGORY:
406  if (!test_cat_cmp(test->info.category, category) && !test->info.explicit_only) {
407  execute = 1;
408  }
409  break;
410  case TEST_NAME_CATEGORY:
411  if (!(test_cat_cmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
412  execute = 1;
413  }
414  break;
415  case TEST_ALL:
416  execute = !test->info.explicit_only;
417  }
418 
419  if (execute) {
420  if (cli) {
421  ast_cli(cli->fd, "START %s - %s \n", test->info.category, test->info.name);
422  }
423 
424  /* set the test status update argument. it is ok if cli is NULL */
425  test->cli = cli;
426 
427  /* execute the test and save results */
428  test_execute(test);
429 
430  test->cli = NULL;
431 
432  /* update execution specific counts here */
433  last_results.last_time += test->time;
434  if (test->state == AST_TEST_PASS) {
436  } else if (test->state == AST_TEST_FAIL) {
438  }
439 
440  if (cli) {
441  term_color(result_buf,
442  test_result2str[test->state],
443  (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
444  0,
445  sizeof(result_buf));
446  ast_cli(cli->fd, "END %s - %s Time: %s%ums Result: %s\n",
447  test->info.category,
448  test->info.name,
449  test->time ? "" : "<",
450  test->time ? test->time : 1,
451  result_buf);
452  }
453  }
454 
455  /* update total counts as well during this iteration
456  * even if the current test did not execute this time */
457  last_results.total_time += test->time;
459  if (test->state != AST_TEST_NOT_RUN) {
460  if (test->state == AST_TEST_PASS) {
462  } else {
464  }
465  }
466  }
469 
470  return res;
471 }
472 
473 /*!
474  * \internal
475  * \brief Generate test results.
476  *
477  * \param name of test result to generate (optional)
478  * \param test category to generate (optional)
479  * \param path to xml file to generate. (optional)
480  * \param path to txt file to generate, (optional)
481  *
482  * \retval 0 success
483  * \retval -1 failure
484  *
485  * \note This function has three modes of operation.
486  * -# When given both a name and category, results will be generated for that single test.
487  * -# When given only a category, results for every test within the category will be generated.
488  * -# When given no name or category, results for every registered test will be generated.
489  *
490  * In order for the results to be generated, an xml and or txt file path must be provided.
491  */
492 static int test_generate_results(const char *name, const char *category, const char *xml_path, const char *txt_path)
493 {
494  enum test_mode mode = TEST_ALL; /* 0 generate all, 1 generate by category only, 2 generate by name and category */
495  FILE *f_xml = NULL, *f_txt = NULL;
496  int res = 0;
497  struct ast_test *test = NULL;
498 
499  /* verify at least one output file was given */
500  if (ast_strlen_zero(xml_path) && ast_strlen_zero(txt_path)) {
501  return -1;
502  }
503 
504  /* define what mode is to be used */
505  if (!ast_strlen_zero(category)) {
506  if (!ast_strlen_zero(name)) {
507  mode = TEST_NAME_CATEGORY;
508  } else {
509  mode = TEST_CATEGORY;
510  }
511  }
512  /* open files for writing */
513  if (!ast_strlen_zero(xml_path)) {
514  if (!(f_xml = fopen(xml_path, "w"))) {
515  ast_log(LOG_WARNING, "Could not open file %s for xml test results\n", xml_path);
516  res = -1;
517  goto done;
518  }
519  }
520  if (!ast_strlen_zero(txt_path)) {
521  if (!(f_txt = fopen(txt_path, "w"))) {
522  ast_log(LOG_WARNING, "Could not open file %s for text output of test results\n", txt_path);
523  res = -1;
524  goto done;
525  }
526  }
527 
529  /* xml header information */
530  if (f_xml) {
531  /*
532  * http://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo
533  */
534  fprintf(f_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
535  fprintf(f_xml, "<testsuites>\n");
536  fprintf(f_xml, "\t<testsuite errors=\"0\" time=\"%u.%u\" tests=\"%u\" "
537  "name=\"AsteriskUnitTests\">\n",
540  fprintf(f_xml, "\t\t<properties>\n");
541  fprintf(f_xml, "\t\t\t<property name=\"version\" value=\"%s\"/>\n", ast_get_version());
542  fprintf(f_xml, "\t\t</properties>\n");
543  }
544 
545  /* txt header information */
546  if (f_txt) {
547  fprintf(f_txt, "Asterisk Version: %s\n", ast_get_version());
548  fprintf(f_txt, "Asterisk Version Number: %s\n", ast_get_version_num());
549  fprintf(f_txt, "Number of Tests: %u\n", last_results.total_tests);
550  fprintf(f_txt, "Number of Tests Executed: %u\n", (last_results.total_passed + last_results.total_failed));
551  fprintf(f_txt, "Passed Tests: %u\n", last_results.total_passed);
552  fprintf(f_txt, "Failed Tests: %u\n", last_results.total_failed);
553  fprintf(f_txt, "Total Execution Time: %u\n", last_results.total_time);
554  }
555 
556  /* export each individual test */
557  AST_LIST_TRAVERSE(&tests, test, entry) {
558  switch (mode) {
559  case TEST_CATEGORY:
560  if (!test_cat_cmp(test->info.category, category)) {
561  test_xml_entry(test, f_xml);
562  test_txt_entry(test, f_txt);
563  }
564  break;
565  case TEST_NAME_CATEGORY:
566  if (!(strcmp(test->info.category, category)) && !(strcmp(test->info.name, name))) {
567  test_xml_entry(test, f_xml);
568  test_txt_entry(test, f_txt);
569  }
570  break;
571  case TEST_ALL:
572  test_xml_entry(test, f_xml);
573  test_txt_entry(test, f_txt);
574  }
575  }
577 
578 done:
579  if (f_xml) {
580  fprintf(f_xml, "\t</testsuite>\n");
581  fprintf(f_xml, "</testsuites>\n");
582  fclose(f_xml);
583  }
584  if (f_txt) {
585  fclose(f_txt);
586  }
587 
588  return res;
589 }
590 
591 /*!
592  * \internal
593  * \brief adds test to container sorted first by category then by name
594  *
595  * \retval 0 success
596  * \retval -1 failure
597  */
598 static int test_insert(struct ast_test *test)
599 {
600  /* This is a slow operation that may need to be optimized in the future
601  * as the test framework expands. At the moment we are doing string
602  * comparisons on every item within the list to insert in sorted order. */
603 
607 
608  return 0;
609 }
610 
611 /*!
612  * \internal
613  * \brief removes test from container
614  *
615  * \return ast_test removed from list on success, or NULL on failure
616  */
617 static struct ast_test *test_remove(ast_test_cb_t *cb)
618 {
619  struct ast_test *cur = NULL;
620 
623  if (cur->cb == cb) {
625  break;
626  }
627  }
630 
631  return cur;
632 }
633 
634 /*!
635  * \brief compares two test categories to determine if cat1 resides in cat2
636  * \internal
637  *
638  * \retval 0 true
639  * \retval non-zero false
640  */
641 
642 static int test_cat_cmp(const char *cat1, const char *cat2)
643 {
644  int len1 = 0;
645  int len2 = 0;
646 
647  if (!cat1 || !cat2) {
648  return -1;
649  }
650 
651  len1 = strlen(cat1);
652  len2 = strlen(cat2);
653 
654  if (len2 > len1) {
655  return -1;
656  }
657 
658  return strncmp(cat1, cat2, len2) ? 1 : 0;
659 }
660 
661 /*!
662  * \internal
663  * \brief free an ast_test object and all it's data members
664  */
665 static struct ast_test *test_free(struct ast_test *test)
666 {
667  if (!test) {
668  return NULL;
669  }
670 
671  ast_free(test->status_str);
672  ast_free(test);
673 
674  return NULL;
675 }
676 
677 /*!
678  * \internal
679  * \brief allocate an ast_test object.
680  */
681 static struct ast_test *test_alloc(ast_test_cb_t *cb)
682 {
683  struct ast_test *test;
684 
685  test = ast_calloc(1, sizeof(*test));
686  if (!test) {
687  ast_log(LOG_ERROR, "Failed to allocate test, registration failed.\n");
688  return NULL;
689  }
690 
691  test->cb = cb;
692 
693  test->cb(&test->info, TEST_INIT, test);
694 
695  if (ast_strlen_zero(test->info.name)) {
696  ast_log(LOG_ERROR, "Test has no name, test registration refused.\n");
697  return test_free(test);
698  }
699 
700  if (ast_strlen_zero(test->info.category)) {
701  ast_log(LOG_ERROR, "Test %s has no category, test registration refused.\n",
702  test->info.name);
703  return test_free(test);
704  }
705 
706  if (test->info.category[0] != '/' || test->info.category[strlen(test->info.category) - 1] != '/') {
707  ast_log(LOG_WARNING, "Test category '%s' for test '%s' is missing a leading or trailing slash.\n",
708  test->info.category, test->info.name);
709  /*
710  * Flag an error anyways so test_registrations fails but allow the
711  * test to be registered.
712  */
714  }
715 
716  if (ast_strlen_zero(test->info.summary)) {
717  ast_log(LOG_ERROR, "Test %s%s has no summary, test registration refused.\n",
718  test->info.category, test->info.name);
719  return test_free(test);
720  }
721  if (test->info.summary[strlen(test->info.summary) - 1] == '\n') {
722  ast_log(LOG_WARNING, "Test %s%s summary has a trailing newline.\n",
723  test->info.category, test->info.name);
724  /*
725  * Flag an error anyways so test_registrations fails but allow the
726  * test to be registered.
727  */
729  }
730 
731  if (ast_strlen_zero(test->info.description)) {
732  ast_log(LOG_ERROR, "Test %s%s has no description, test registration refused.\n",
733  test->info.category, test->info.name);
734  return test_free(test);
735  }
736  if (test->info.description[strlen(test->info.description) - 1] == '\n') {
737  ast_log(LOG_WARNING, "Test %s%s description has a trailing newline.\n",
738  test->info.category, test->info.name);
739  /*
740  * Flag an error anyways so test_registrations fails but allow the
741  * test to be registered.
742  */
744  }
745 
746  if (!(test->status_str = ast_str_create(128))) {
747  ast_log(LOG_ERROR, "Failed to allocate status_str for %s%s, test registration failed.\n",
748  test->info.category, test->info.name);
749  return test_free(test);
750  }
751 
752  return test;
753 }
754 
755 static char *complete_test_category(const char *word)
756 {
757  int wordlen = strlen(word);
758  struct ast_test *test;
759 
761  AST_LIST_TRAVERSE(&tests, test, entry) {
762  if (!strncasecmp(word, test->info.category, wordlen)) {
764  break;
765  }
766  }
767  }
769 
770  return NULL;
771 }
772 
773 static char *complete_test_name(const char *word, const char *category)
774 {
775  int wordlen = strlen(word);
776  struct ast_test *test;
777 
779  AST_LIST_TRAVERSE(&tests, test, entry) {
780  if (!test_cat_cmp(test->info.category, category) && !strncasecmp(word, test->info.name, wordlen)) {
782  break;
783  }
784  }
785  }
787 
788  return NULL;
789 }
790 
791 /* CLI commands */
792 static char *test_cli_show_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
793 {
794 #define FORMAT "%-25.25s %-30.30s %-40.40s %-13.13s\n"
795  static const char * const option1[] = { "all", "category", NULL };
796  static const char * const option2[] = { "name", NULL };
797  struct ast_test *test = NULL;
798  int count = 0;
799  switch (cmd) {
800  case CLI_INIT:
801  e->command = "test show registered";
802 
803  e->usage =
804  "Usage: 'test show registered' can be used in three ways.\n"
805  " 1. 'test show registered all' shows all registered tests\n"
806  " 2. 'test show registered category [test category]' shows all tests in the given\n"
807  " category.\n"
808  " 3. 'test show registered category [test category] name [test name]' shows all\n"
809  " tests in a given category matching a given name\n";
810  return NULL;
811  case CLI_GENERATE:
812  if (a->pos == 3) {
813  return ast_cli_complete(a->word, option1, -1);
814  }
815  if (a->pos == 4 && !strcasecmp(a->argv[3], "category")) {
816  return complete_test_category(a->word);
817  }
818  if (a->pos == 5) {
819  return ast_cli_complete(a->word, option2, -1);
820  }
821  if (a->pos == 6) {
822  return complete_test_name(a->word, a->argv[4]);
823  }
824  return NULL;
825  case CLI_HANDLER:
826  if ((a->argc < 4) || (a->argc == 6) || (a->argc > 7) ||
827  ((a->argc == 4) && strcasecmp(a->argv[3], "all")) ||
828  ((a->argc == 7) && strcasecmp(a->argv[5], "name"))) {
829  return CLI_SHOWUSAGE;
830  }
831  ast_cli(a->fd, FORMAT, "Category", "Name", "Summary", "Test Result");
832  ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
834  AST_LIST_TRAVERSE(&tests, test, entry) {
835  if ((a->argc == 4) ||
836  ((a->argc == 5) && !test_cat_cmp(test->info.category, a->argv[4])) ||
837  ((a->argc == 7) && !strcmp(test->info.category, a->argv[4]) && !strcmp(test->info.name, a->argv[6]))) {
838 
839  ast_cli(a->fd, FORMAT, test->info.category, test->info.name,
840  test->info.summary, test_result2str[test->state]);
841  count++;
842  }
843  }
845  ast_cli(a->fd, FORMAT, "--------", "----", "-------", "-----------");
846  ast_cli(a->fd, "\n%d Registered Tests Matched\n", count);
847  default:
848  return NULL;
849  }
850 
851  return CLI_SUCCESS;
852 }
853 
854 static char *test_cli_execute_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
855 {
856  static const char * const option1[] = { "all", "category", NULL };
857  static const char * const option2[] = { "name", NULL };
858 
859  switch (cmd) {
860  case CLI_INIT:
861  e->command = "test execute";
862  e->usage =
863  "Usage: test execute can be used in three ways.\n"
864  " 1. 'test execute all' runs all registered tests\n"
865  " 2. 'test execute category [test category]' runs all tests in the given\n"
866  " category.\n"
867  " 3. 'test execute category [test category] name [test name]' runs all\n"
868  " tests in a given category matching a given name\n";
869  return NULL;
870  case CLI_GENERATE:
871  if (a->pos == 2) {
872  return ast_cli_complete(a->word, option1, -1);
873  }
874  if (a->pos == 3 && !strcasecmp(a->argv[2], "category")) {
875  return complete_test_category(a->word);
876  }
877  if (a->pos == 4) {
878  return ast_cli_complete(a->word, option2, -1);
879  }
880  if (a->pos == 5) {
881  return complete_test_name(a->word, a->argv[3]);
882  }
883  return NULL;
884  case CLI_HANDLER:
885 
886  if (a->argc < 3|| a->argc > 6) {
887  return CLI_SHOWUSAGE;
888  }
889 
890  if ((a->argc == 3) && !strcasecmp(a->argv[2], "all")) { /* run all registered tests */
891  ast_cli(a->fd, "Running all available tests...\n\n");
893  } else if (a->argc == 4) { /* run only tests within a category */
894  ast_cli(a->fd, "Running all available tests matching category %s\n\n", a->argv[3]);
895  test_execute_multiple(NULL, a->argv[3], a);
896  } else if (a->argc == 6) { /* run only a single test matching the category and name */
897  ast_cli(a->fd, "Running all available tests matching category %s and name %s\n\n", a->argv[3], a->argv[5]);
898  test_execute_multiple(a->argv[5], a->argv[3], a);
899  } else {
900  return CLI_SHOWUSAGE;
901  }
902 
905  ast_cli(a->fd, "--- No Tests Found! ---\n");
906  }
907  ast_cli(a->fd, "\n%u Test(s) Executed %u Passed %u Failed\n",
912  default:
913  return NULL;
914  }
915 
916  return CLI_SUCCESS;
917 }
918 
919 static char *test_cli_show_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
920 {
921 #define FORMAT_RES_ALL1 "%s%s %-30.30s %-25.25s %-10.10s\n"
922 #define FORMAT_RES_ALL2 "%s%s %-30.30s %-25.25s %s%ums\n"
923  static const char * const option1[] = { "all", "failed", "passed", NULL };
924  char result_buf[32] = { 0 };
925  struct ast_test *test = NULL;
926  int failed = 0;
927  int passed = 0;
928  int mode; /* 0 for show all, 1 for show fail, 2 for show passed */
929 
930  switch (cmd) {
931  case CLI_INIT:
932  e->command = "test show results";
933  e->usage =
934  "Usage: test show results can be used in three ways\n"
935  " 1. 'test show results all' Displays results for all executed tests.\n"
936  " 2. 'test show results passed' Displays results for all passed tests.\n"
937  " 3. 'test show results failed' Displays results for all failed tests.\n";
938  return NULL;
939  case CLI_GENERATE:
940  if (a->pos == 3) {
941  return ast_cli_complete(a->word, option1, -1);
942  }
943  return NULL;
944  case CLI_HANDLER:
945 
946  /* verify input */
947  if (a->argc != 4) {
948  return CLI_SHOWUSAGE;
949  } else if (!strcasecmp(a->argv[3], "passed")) {
950  mode = 2;
951  } else if (!strcasecmp(a->argv[3], "failed")) {
952  mode = 1;
953  } else if (!strcasecmp(a->argv[3], "all")) {
954  mode = 0;
955  } else {
956  return CLI_SHOWUSAGE;
957  }
958 
959  ast_cli(a->fd, FORMAT_RES_ALL1, "Result", "", "Name", "Category", "Time");
961  AST_LIST_TRAVERSE(&tests, test, entry) {
962  if (test->state == AST_TEST_NOT_RUN) {
963  continue;
964  }
965  test->state == AST_TEST_FAIL ? failed++ : passed++;
966  if (!mode || ((mode == 1) && (test->state == AST_TEST_FAIL)) || ((mode == 2) && (test->state == AST_TEST_PASS))) {
967  /* give our results pretty colors */
968  term_color(result_buf, test_result2str[test->state],
969  (test->state == AST_TEST_FAIL) ? COLOR_RED : COLOR_GREEN,
970  0, sizeof(result_buf));
971 
973  result_buf,
974  " ",
975  test->info.name,
976  test->info.category,
977  test->time ? " " : "<",
978  test->time ? test->time : 1);
979  }
980  }
982 
983  ast_cli(a->fd, "%d Test(s) Executed %d Passed %d Failed\n", (failed + passed), passed, failed);
984  default:
985  return NULL;
986  }
987  return CLI_SUCCESS;
988 }
989 
990 static char *test_cli_generate_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
991 {
992  static const char * const option[] = { "xml", "txt", NULL };
993  const char *file = NULL;
994  const char *type = "";
995  int isxml = 0;
996  int res = 0;
997  struct ast_str *buf = NULL;
998  struct timeval time = ast_tvnow();
999 
1000  switch (cmd) {
1001  case CLI_INIT:
1002  e->command = "test generate results";
1003  e->usage =
1004  "Usage: 'test generate results'\n"
1005  " Generates test results in either xml or txt format. An optional \n"
1006  " file path may be provided to specify the location of the xml or\n"
1007  " txt file\n"
1008  " \nExample usage:\n"
1009  " 'test generate results xml' this writes to a default file\n"
1010  " 'test generate results xml /path/to/file.xml' writes to specified file\n";
1011  return NULL;
1012  case CLI_GENERATE:
1013  if (a->pos == 3) {
1014  return ast_cli_complete(a->word, option, -1);
1015  }
1016  return NULL;
1017  case CLI_HANDLER:
1018 
1019  /* verify input */
1020  if (a->argc < 4 || a->argc > 5) {
1021  return CLI_SHOWUSAGE;
1022  } else if (!strcasecmp(a->argv[3], "xml")) {
1023  type = "xml";
1024  isxml = 1;
1025  } else if (!strcasecmp(a->argv[3], "txt")) {
1026  type = "txt";
1027  } else {
1028  return CLI_SHOWUSAGE;
1029  }
1030 
1031  if (a->argc == 5) {
1032  file = a->argv[4];
1033  } else {
1034  if (!(buf = ast_str_create(256))) {
1035  return NULL;
1036  }
1037  ast_str_set(&buf, 0, "%s/asterisk_test_results-%ld.%s", ast_config_AST_LOG_DIR, (long) time.tv_sec, type);
1038 
1039  file = ast_str_buffer(buf);
1040  }
1041 
1042  if (isxml) {
1043  res = test_generate_results(NULL, NULL, file, NULL);
1044  } else {
1045  res = test_generate_results(NULL, NULL, NULL, file);
1046  }
1047 
1048  if (!res) {
1049  ast_cli(a->fd, "Results Generated Successfully: %s\n", S_OR(file, ""));
1050  } else {
1051  ast_cli(a->fd, "Results Could Not Be Generated: %s\n", S_OR(file, ""));
1052  }
1053 
1054  ast_free(buf);
1055  default:
1056  return NULL;
1057  }
1058 
1059  return CLI_SUCCESS;
1060 }
1061 
1062 static struct ast_cli_entry test_cli[] = {
1063  AST_CLI_DEFINE(test_cli_show_registered, "show registered tests"),
1064  AST_CLI_DEFINE(test_cli_execute_registered, "execute registered tests"),
1065  AST_CLI_DEFINE(test_cli_show_results, "show last test results"),
1066  AST_CLI_DEFINE(test_cli_generate_results, "generate test results to file"),
1067 };
1068 
1070 {
1071  return test_suite_topic;
1072 }
1073 
1074 /*!
1075  * \since 12
1076  * \brief A wrapper object that can be ao2 ref counted around an \ref ast_json blob
1077  */
1079  struct ast_json *blob; /*!< The actual blob that we want to deliver */
1080 };
1081 
1082 /*! \internal
1083  * \since 12
1084  * \brief Destructor for \ref ast_test_suite_message_payload
1085  */
1086 static void test_suite_message_payload_dtor(void *obj)
1087 {
1088  struct ast_test_suite_message_payload *payload = obj;
1089 
1090  if (payload->blob) {
1091  ast_json_unref(payload->blob);
1092  }
1093 }
1094 
1096 {
1097  return payload->blob;
1098 }
1099 
1101 {
1102  RAII_VAR(struct ast_str *, packet_string, ast_str_create(128), ast_free);
1103  struct ast_test_suite_message_payload *payload;
1104  struct ast_json *blob;
1105  const char *type;
1106 
1107  payload = stasis_message_data(msg);
1108  if (!payload) {
1109  return NULL;
1110  }
1111  blob = ast_test_suite_get_blob(payload);
1112  if (!blob) {
1113  return NULL;
1114  }
1115 
1116  type = ast_json_string_get(ast_json_object_get(blob, "type"));
1117  if (ast_strlen_zero(type) || strcmp("testevent", type)) {
1118  return NULL;
1119  }
1120 
1121  ast_str_append(&packet_string, 0, "Type: StateChange\r\n");
1122  ast_str_append(&packet_string, 0, "State: %s\r\n",
1123  ast_json_string_get(ast_json_object_get(blob, "state")));
1124  ast_str_append(&packet_string, 0, "AppFile: %s\r\n",
1125  ast_json_string_get(ast_json_object_get(blob, "appfile")));
1126  ast_str_append(&packet_string, 0, "AppFunction: %s\r\n",
1127  ast_json_string_get(ast_json_object_get(blob, "appfunction")));
1128  ast_str_append(&packet_string, 0, "AppLine: %jd\r\n",
1129  ast_json_integer_get(ast_json_object_get(blob, "line")));
1130  ast_str_append(&packet_string, 0, "%s\r\n",
1131  ast_json_string_get(ast_json_object_get(blob, "data")));
1132 
1134  "TestEvent",
1135  "%s",
1136  ast_str_buffer(packet_string));
1137 }
1138 
1139 /*! \since 12
1140  * \brief The message type for test suite messages
1141  */
1142 STASIS_MESSAGE_TYPE_DEFN(ast_test_suite_message_type,
1144 
1145 void __ast_test_suite_event_notify(const char *file, const char *func, int line, const char *state, const char *fmt, ...)
1146 {
1147  RAII_VAR(struct ast_test_suite_message_payload *, payload,
1148  NULL,
1149  ao2_cleanup);
1150  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
1151  RAII_VAR(struct ast_str *, buf, NULL, ast_free);
1152  va_list ap;
1153 
1154  if (!ast_test_suite_message_type()) {
1155  return;
1156  }
1157 
1158  buf = ast_str_create(128);
1159  if (!buf) {
1160  return;
1161  }
1162 
1163  payload = ao2_alloc(sizeof(*payload), test_suite_message_payload_dtor);
1164  if (!payload) {
1165  return;
1166  }
1167 
1168  va_start(ap, fmt);
1169  ast_str_set_va(&buf, 0, fmt, ap);
1170  va_end(ap);
1171  payload->blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: i, s: s}",
1172  "type", "testevent",
1173  "state", state,
1174  "appfile", file,
1175  "appfunction", func,
1176  "line", line,
1177  "data", ast_str_buffer(buf));
1178  if (!payload->blob) {
1179  return;
1180  }
1181  msg = stasis_message_create(ast_test_suite_message_type(), payload);
1182  if (!msg) {
1183  return;
1184  }
1186 }
1187 
1188 AST_TEST_DEFINE(test_registrations)
1189 {
1190  switch (cmd) {
1191  case TEST_INIT:
1192  info->name = "registrations";
1193  info->category = "/main/test/";
1194  info->summary = "Validate Test Registration Data.";
1195  info->description = "Validate Test Registration Data.";
1196  return AST_TEST_NOT_RUN;
1197  case TEST_EXECUTE:
1198  break;
1199  }
1200 
1201  if (registration_errors) {
1203  "%d test registration error%s occurred. See startup logs for details.\n",
1204  registration_errors, registration_errors > 1 ? "s" : "");
1205  return AST_TEST_FAIL;
1206  }
1207 
1208  return AST_TEST_PASS;
1209 }
1210 
1211 static void test_cleanup(void)
1212 {
1213  AST_TEST_UNREGISTER(test_registrations);
1214  ast_cli_unregister_multiple(test_cli, ARRAY_LEN(test_cli));
1215  ao2_cleanup(test_suite_topic);
1216  test_suite_topic = NULL;
1217  STASIS_MESSAGE_TYPE_CLEANUP(ast_test_suite_message_type);
1218 }
1219 #endif /* TEST_FRAMEWORK */
1220 
1221 int ast_test_init(void)
1222 {
1223 #ifdef TEST_FRAMEWORK
1225 
1226  /* Create stasis topic */
1227  test_suite_topic = stasis_topic_create("testsuite:all");
1228  if (!test_suite_topic) {
1229  return -1;
1230  }
1231 
1232  if (STASIS_MESSAGE_TYPE_INIT(ast_test_suite_message_type) != 0) {
1233  return -1;
1234  }
1235 
1236  AST_TEST_REGISTER(test_registrations);
1237 
1238  /* Register cli commands */
1239  ast_cli_register_multiple(test_cli, ARRAY_LEN(test_cli));
1240 #endif
1241 
1242  return 0;
1243 }
unsigned int total_time
Definition: test.c:91
static char * complete_test_name(const char *word, const char *category)
Definition: test.c:773
Contains all the initialization information required to store a new test definition.
Definition: test.h:221
static const char type[]
Definition: chan_ooh323.c:109
Struct containing info for an AMI event to send out.
Definition: manager.h:491
const char * summary
Short summary of test.
Definition: test.h:237
void ast_test_debug(struct ast_test *test, const char *fmt,...)
Definition: test.c:113
unsigned int last_failed
Definition: test.c:93
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
static char * reserved_words[]
Definition: test.c:274
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk main include file. File version handling, generic pbx functions.
static void test_execute(struct ast_test *test)
Definition: test.c:238
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Common execution function for SQL queries.
Definition: func_odbc.c:454
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
void __ast_test_suite_event_notify(const char *file, const char *func, int line, const char *state, const char *fmt,...)
Definition: test.c:1145
unsigned int last_time
Definition: test.c:94
const char cat1[]
Definition: test_config.c:68
int ast_test_unregister(ast_test_cb_t *cb)
Definition: test.c:218
static char * test_cli_generate_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: test.c:990
Asterisk version information.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
test_mode
Definition: test.c:97
int ast_test_register_init(const char *category, ast_test_init_cb_t *cb)
Definition: test.c:160
Time-related functions and macros.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
const char * ast_get_version(void)
Retrieve the Asterisk version string.
Definition: version.c:16
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1501
static int registration_errors
Definition: test.c:111
static struct ast_test * test_remove(ast_test_cb_t *cb)
Definition: test.c:617
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static char * complete_test_category(const char *word)
Definition: test.c:755
static struct ast_manager_event_blob * test_suite_event_to_ami(struct stasis_message *msg)
Definition: test.c:1100
int ast_str_set_va(struct ast_str **buf, ssize_t max_len, const char *fmt, va_list ap)
Set a dynamic string from a va_list.
Definition: strings.h:982
Test Framework API.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1523
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
Definition: cli.h:152
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define COLOR_GREEN
Definition: term.h:51
static struct ast_test * test_free(struct ast_test *test)
Definition: test.c:665
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
static int test_generate_results(const char *name, const char *category, const char *xml_path, const char *txt_path)
Definition: test.c:492
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
struct ast_str * status_str
Test defined status output from last execution.
Definition: test.c:70
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
Definition: manager.c:9727
static void test_xml_entry(struct ast_test *test, FILE *f)
Definition: test.c:300
static char * test_cli_show_results(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: test.c:919
Utility functions.
#define ast_strlen_zero(foo)
Definition: strings.h:52
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
Definition: main/cli.c:1811
struct stasis_topic * test_suite_topic
The topic for test suite messages.
Definition: test.c:55
int done
Definition: test_amihooks.c:48
#define AST_LIST_INSERT_SORTALPHA(head, elm, field, sortfield)
Inserts a list entry into a alphabetically sorted list.
Definition: linkedlists.h:750
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
int ast_test_register(ast_test_cb_t *cb)
Definition: test.c:194
ast_test_cleanup_cb_t * cleanup_cb
Definition: test.c:82
struct ast_json * ast_test_suite_get_blob(struct ast_test_suite_message_payload *payload)
Definition: test.c:1095
#define ast_log
Definition: astobj2.c:42
int ast_test_init(void)
Definition: test.c:1221
Asterisk JSON abstraction layer.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
Asterisk file paths, configured in asterisk.conf.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
const int fd
Definition: cli.h:159
struct ast_cli_args * cli
CLI arguments, if tests being run from the CLI.
Definition: test.c:77
unsigned int last_passed
Definition: test.c:92
unsigned int total_passed
Definition: test.c:89
static char * test_cli_execute_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: test.c:854
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
unsigned int time
Definition: test.c:79
int __ast_test_status_update(const char *file, const char *func, int line, struct ast_test *test, const char *fmt,...)
Definition: test.c:134
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
unsigned int total_failed
Definition: test.c:90
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: main/utils.c:1656
A set of macros to manage forward-linked lists.
static void test_suite_message_payload_dtor(void *obj)
Definition: test.c:1086
const char * description
More detailed description of test.
Definition: test.h:243
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
Definition: stasis.c:618
const char * name
name of test, unique to category
Definition: test.h:223
int ast_test_register_cleanup(const char *category, ast_test_cleanup_cb_t *cb)
Definition: test.c:177
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
#define COLOR_RED
Definition: term.h:49
char * term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
Colorize a specified string by adding terminal color codes.
Definition: term.c:184
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char *const * argv
Definition: cli.h:161
#define LOG_ERROR
Definition: logger.h:285
AST_TEST_DEFINE(test_registrations)
Definition: test.c:1188
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define CLI_SHOWUSAGE
Definition: cli.h:45
STASIS_MESSAGE_TYPE_DEFN(ast_test_suite_message_type,.to_ami=test_suite_event_to_ami)
The message type for test suite messages.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
static int is_reserved_word(const char *word)
Definition: test.c:287
static int test_cat_cmp(const char *cat1, const char *cat2)
compares two test categories to determine if cat1 resides in cat2
Definition: test.c:642
static int test_execute_multiple(const char *name, const char *category, struct ast_cli_args *cli)
Definition: test.c:382
struct stasis_topic * ast_test_suite_topic(void)
Definition: test.c:1069
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
const char * category
test category
Definition: test.h:231
const char * ast_config_AST_LOG_DIR
Definition: options.c:159
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic&#39;s subscribers.
Definition: stasis.c:1511
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
static void test_cleanup(void)
Definition: test.c:1211
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static int test_insert(struct ast_test *test)
Definition: test.c:598
void ast_test_set_result(struct ast_test *test, enum ast_test_result_state state)
Definition: test.c:262
static void to_ami(struct ast_sip_subscription *sub, struct ast_str **buf)
const char * word
Definition: cli.h:163
unsigned int explicit_only
Only run if explicitly named.
Definition: test.h:251
Prototypes for public functions only of internal interest,.
A wrapper object that can be ao2 ref counted around an ast_json blob.
Definition: test.c:1078
#define FORMAT_RES_ALL1
ast_test_cb_t * cb
Definition: test.c:80
#define FORMAT
Support for logging to various files, console and syslog Configuration in file logger.conf.
const char * ast_get_version_num(void)
Retrieve the numeric Asterisk version.
Definition: version.c:21
static struct ast_test_execute_results last_results
const char * usage
Definition: cli.h:177
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:653
#define EVENT_FLAG_REPORTING
Definition: manager.h:80
#define CLI_SUCCESS
Definition: cli.h:44
Definition: test.c:65
Definition: test.c:104
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
static struct ast_threadstorage result_buf
Definition: func_sprintf.c:42
struct ast_test_info info
Definition: test.c:66
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
const int pos
Definition: cli.h:164
static PGresult * result
Definition: cel_pgsql.c:88
const char cat2[]
Definition: test_config.c:76
static void test_txt_entry(struct ast_test *test, FILE *f)
Definition: test.c:348
Definition: test.c:98
struct ast_json * blob
Definition: test.c:1079
static struct ast_test * test_alloc(ast_test_cb_t *cb)
Definition: test.c:681
Abstract JSON element (object, array, string, int, ...).
Definition: search.h:40
Handy terminal functions for vt* terms.
static struct ast_cli_entry test_cli[]
Definition: test.c:1062
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
struct ast_test::@430 entry
#define FORMAT_RES_ALL2
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2726
static char * test_cli_show_registered(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: test.c:792
unsigned int total_tests
Definition: test.c:88
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:322
ast_test_init_cb_t * init_cb
Definition: test.c:81
enum ast_test_result_state state
Definition: test.c:78
ast_test_result_state
Definition: test.h:200
static struct ast_test * test
Definition: localtime.c:115
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
short word
static char prefix[MAX_PREFIX]
Definition: http.c:141
static const char *const test_result2str[]
Definition: test.c:58
static struct test_val a