| /* |
| * Copyright 2014, NICTA |
| * |
| * This software may be distributed and modified according to the terms of |
| * the BSD 2-Clause license. Note that NO WARRANTY is provided. |
| * See "LICENSE_BSD2.txt" for details. |
| * |
| * @TAG(NICTA_BSD) |
| */ |
| |
| #include <autoconf.h> |
| |
| #include <regex.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include <sel4test/prototype.h> |
| #include <sel4test/test.h> |
| #include <utils/util.h> |
| |
| #define MAX_NAME_SIZE 100 |
| #define STDOUT_CACHE 50000 |
| |
| #define BUFFERING_DISABLED (-1) |
| |
| #ifdef CONFIG_BUFFER_OUTPUT |
| static char sel4test_name[MAX_NAME_SIZE]; |
| static char current_stdout_bank[STDOUT_CACHE]; |
| static int buf_index = BUFFERING_DISABLED; |
| #endif |
| |
| static int num_tests = 0; |
| static int num_tests_passed = 0; |
| static int current_test_passed = true; |
| |
| #ifdef CONFIG_BUFFER_OUTPUT |
| #undef printf |
| void |
| sel4test_printf(const char *string) { |
| if(buf_index == BUFFERING_DISABLED) { |
| printf("%s", string); |
| } else if (buf_index < STDOUT_CACHE && buf_index >= 0) { |
| size_t len = STDOUT_CACHE - buf_index; |
| snprintf(current_stdout_bank + buf_index, len, "%s", string); |
| /* NULL terminate the destination in case 'string' was too long. */ |
| current_stdout_bank[STDOUT_CACHE - 1] = '\0'; |
| buf_index += strlen(string); |
| } |
| } |
| #endif /* CONFIG_BUFFER_OUTPUT */ |
| |
| |
| void |
| sel4test_start_suite(char *name) { |
| |
| #ifdef CONFIG_PRINT_XML |
| buf_index = 0; |
| printf("<testsuite>\n"); |
| strncpy(sel4test_name, name, MAX_NAME_SIZE); |
| /* NULL terminate sel4test_name in case name was too long. */ |
| sel4test_name[MAX_NAME_SIZE - 1] = '\0'; |
| #else |
| printf("Starting test suite %s\n", name); |
| #endif /* CONFIG_PRINT_XML */ |
| |
| num_tests = 0; |
| num_tests_passed = 0; |
| current_test_passed = true; |
| } |
| |
| void |
| sel4test_end_suite(void) { |
| #ifdef CONFIG_PRINT_XML |
| printf("</testsuite>\n"); |
| buf_index = BUFFERING_DISABLED; |
| #else |
| if (num_tests_passed != num_tests) { |
| printf("Test suite failed. %d/%d tests passed.\n", num_tests_passed, num_tests); |
| } else { |
| printf("Test suite passed. %d tests passed.\n", num_tests); |
| } |
| #endif /* CONFIG_PRINT_XML */ |
| } |
| |
| void |
| sel4test_start_test(const char *name) { |
| #ifdef CONFIG_BUFFER_OUTPUT |
| buf_index = 0; |
| memset(current_stdout_bank, 0, STDOUT_CACHE); |
| #endif /* CONFIG_BUFFER_OUTPUT */ |
| #ifdef CONFIG_PRINT_XML |
| printf("\t<testcase classname=\"%s\" name=\"%s\">\n", sel4test_name, name); |
| #else |
| printf("Starting test %d: %s\n", num_tests, name); |
| #endif /* CONFIG_PRINT_XML */ |
| num_tests++; |
| current_test_passed = true; |
| } |
| |
| void |
| _sel4test_report_error(char *error, char *file, int line) { |
| #ifdef CONFIG_PRINT_XML |
| printf("\t\t<error>%s at line %d of file %s</error>\n", error, line, file); |
| #else |
| printf("\tError: %s at line %d of file %s\n", error, line, file); |
| #endif /* CONFIG_PRINT_XML */ |
| current_test_passed = false; |
| } |
| |
| |
| void |
| _sel4test_failure(char *failure, char *file, int line) { |
| #ifdef CONFIG_PRINT_XML |
| printf("\t\t<failure type=\"failure\">%s at line %d of file %s</failure>\n", failure, line, file); |
| #else |
| printf("\tFailure: %s at line %d of file %s\n", failure, line, file); |
| #endif /* CONFIG_PRINT_XML */ |
| current_test_passed = false; |
| } |
| |
| void |
| sel4test_end_test(void) { |
| if (current_test_passed) { |
| num_tests_passed++; |
| } else { |
| _sel4test_failure("Test check failure (see log).", "", 0); |
| } |
| #ifdef CONFIG_BUFFER_OUTPUT |
| if (buf_index != 0) { |
| printf("\t\t<system-out>%s</system-out>\n", current_stdout_bank); |
| buf_index = 0; |
| } |
| #endif /* CONFIG_BUFFER_OUTPUT */ |
| #ifdef CONFIG_PRINT_XML |
| printf("\t</testcase>\n"); |
| #endif /* CONFIG_PRINT_XML */ |
| } |
| |
| bool |
| sel4test_get_result(void) |
| { |
| return current_test_passed; |
| } |
| |
| |
| void sel4_test_get_suite_results(int *out_num_tests, int *out_passed, int *out_failed) { |
| if (out_num_tests) (*out_num_tests) = num_tests; |
| if (out_passed) (*out_passed) = num_tests_passed; |
| if (out_failed) (*out_failed) = num_tests - num_tests_passed; |
| } |
| |
| /* Definitions so that we can find the test cases */ |
| extern struct testcase __start__test_case[]; |
| extern struct testcase __stop__test_case[]; |
| |
| /* Force the _test_case section to be created even if no tests are defined. */ |
| static USED SECTION("_test_case") struct {} dummy; |
| |
| testcase_t* |
| sel4test_get_test(char *name) |
| { |
| |
| for (testcase_t *t = __start__test_case; t < __stop__test_case; t++) { |
| if (strcmp(name, t->name) == 0) { |
| return t; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| |
| void |
| sel4test_run_tests(char *name, int (*run_test)(struct testcase *t)) { |
| |
| /* Count how many tests actually exist and allocate space for them */ |
| int max_tests = (int)(__stop__test_case - __start__test_case); |
| struct testcase *tests[max_tests]; |
| |
| /* Extract and filter the tests based on the regex */ |
| regex_t reg; |
| int error = regcomp(®, CONFIG_TESTPRINTER_REGEX, REG_EXTENDED | REG_NOSUB); |
| if (error != 0) { |
| printf("Error compiling regex \"%s\"\n", CONFIG_TESTPRINTER_REGEX); |
| return; |
| } |
| |
| int num_tests = 0; |
| for (struct testcase *i = __start__test_case; i < __stop__test_case; i++) { |
| if (regexec(®, i->name, 0, NULL, 0) == 0) { |
| tests[num_tests] = i; |
| num_tests++; |
| } |
| } |
| |
| regfree(®); |
| |
| /* Sort the tests to remove any non determinism in test ordering */ |
| qsort(tests, num_tests, sizeof(struct testcase*), test_comparator); |
| |
| /* Now that they are sorted we can easily ensure there are no duplicate tests. |
| * this just ensures some sanity as if there are duplicates, they could have some |
| * arbitrary ordering, which might result in difficulty reproducing test failures */ |
| for (int i = 1; i < num_tests; i++) { |
| test_assert_fatal(strcmp(tests[i]->name, tests[i - 1]->name) != 0); |
| } |
| |
| sel4test_start_suite(name); |
| /* Run tests */ |
| for (int i = 0; i < num_tests; i++) { |
| sel4test_start_test(tests[i]->name); |
| int result = run_test(tests[i]); |
| if (result != SUCCESS) { |
| current_test_passed = false; |
| } |
| sel4test_end_test(); |
| } |
| |
| /* Run the tests backwards... */ |
| for (int i = num_tests - 1; i >= 0; i--) { |
| assert(tests[i]->name != NULL); |
| /* junit doesn't like duplicate test names */ |
| int length = strlen(tests[i]->name); |
| char name_copy[length + 2]; |
| strncpy(name_copy, tests[i]->name, length); |
| name_copy[length] = '2'; |
| name_copy[length + 1] = '\0'; |
| sel4test_start_test(name_copy); |
| int result = run_test(tests[i]); |
| if (result != SUCCESS) { |
| current_test_passed = false; |
| } |
| sel4test_end_test(); |
| } |
| |
| sel4test_end_suite(); |
| |
| /* Print closing banner. */ |
| printf("\n"); |
| int tests_run, passes, failures; |
| sel4_test_get_suite_results(&tests_run, &passes, &failures); |
| printf("%d/%d tests passed.\n", tests_run - failures, tests_run); |
| if (failures > 0) { |
| printf("*** FAILURES DETECTED ***\n"); |
| } else { |
| printf("All is well in the universe.\n"); |
| } |
| printf("\n\n"); |
| } |
| |