1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <libgen.h> 6#include <limits.h> 7#include <stdlib.h> 8#include <unittest/unittest.h> 9 10#include "watchdog.h" 11 12static test_case_element* test_case_list = nullptr; 13static test_case_element* failed_test_case_list = nullptr; 14 15static unittest_help_printer_type* print_test_help = nullptr; 16 17// Registers a test case with the unit test framework. 18void unittest_register_test_case(test_case_element* elem) { 19 elem->next = test_case_list; 20 test_case_list = elem; 21} 22 23bool unittest_run_one_test(test_case_element* elem, test_type_t type) { 24 utest_test_type = type; 25 return elem->test_case(false, nullptr); 26} 27 28void unittest_register_test_help_printer(unittest_help_printer_type* func) { 29 print_test_help = func; 30} 31 32// Case name and test name are optional parameters that will cause only the 33// test[case]s matching the given name to run. If null, all test[case]s will 34// run. 35static bool unittest_run_all_tests_etc(const char* test_binary_name, test_type_t type, 36 const char* case_name, const char* test_name, 37 bool list_only) { 38 unsigned int n_tests = 0; 39 unsigned int n_failed = 0; 40 41 utest_test_type = type; 42 43 test_case_element* current = test_case_list; 44 while (current) { 45 if (!case_name || strcmp(current->name, case_name) == 0) { 46 if (!current->test_case(list_only, test_name)) { 47 current->failed_next = failed_test_case_list; 48 failed_test_case_list = current; 49 n_failed++; 50 } 51 n_tests++; 52 } 53 current = current->next; 54 } 55 56 // Don't print test results in list mode. 57 if (list_only) 58 return true; 59 60 unittest_printf_critical("====================================================\n"); 61 if (test_binary_name != nullptr && test_binary_name[0] != '\0') { 62 unittest_printf_critical("Results for test binary \"%s\":\n", test_binary_name); 63 } else { 64 // argv[0] can be null for binaries that run as userboot, 65 // like core-tests. 66 unittest_printf_critical("Results:\n"); 67 } 68 if (n_failed == 0) { 69 unittest_printf_critical(" SUCCESS! All test cases passed!\n"); 70 } else { 71 unittest_printf_critical("\n"); 72 unittest_printf_critical(" The following test cases failed:\n"); 73 test_case_element* failed = failed_test_case_list; 74 while (failed) { 75 unittest_printf_critical(" %s\n", failed->name); 76 test_case_element* failed_next = failed->failed_next; 77 failed->failed_next = nullptr; 78 failed = failed_next; 79 } 80 failed_test_case_list = nullptr; 81 unittest_printf_critical("\n"); 82 } 83 unittest_printf_critical(" CASES: %d SUCCESS: %d FAILED: %d \n", n_tests, 84 n_tests - n_failed, n_failed); 85 unittest_printf_critical("====================================================\n"); 86 return n_failed == 0; 87} 88 89static void print_help(const char* prog_name, FILE* f) { 90 fprintf(f, "Usage: %s [OPTIONS]\n", prog_name); 91 fprintf(f, "\nOptions:\n" 92 " -h | --help\n" 93 " Prints this text and exits.\n" 94 "\n" 95 " --list\n" 96 " Prints the test names instead of running them.\n" 97 "\n" 98 " --case <test_case>\n" 99 " Only the tests from the matching test case will be run.\n" 100 " <test_case> is case-sensitive; regex is not supported\n" 101 "\n" 102 " --test <test>\n" 103 " Only the tests from the matching test will be run\n" 104 " <test> is case-sensitive; regex is not supported\n" 105 "\n" 106 " v=<level>\n" 107 " Set the unit test verbosity level to <level>\n" 108 ); 109 if (print_test_help) { 110 fprintf(f, "\nTest-specific options:\n"); 111 print_test_help(f); 112 } 113 fprintf(f, "\n" 114 "Environment variables:\n" 115 " %s=<types-mask>\n" 116 " Specifies the types of tests to run.\n" 117 " Must be the OR of the following values, in base 10:\n" 118 " 0x01 = small\n" 119 " 0x02 = medium\n" 120 " 0x04 = large\n" 121 " 0x08 = performance\n" 122 " If unspecified then all tests are run.\n" 123 "\n" 124 " %s=<base-timeout-in-seconds>\n" 125 " Specifies the base timeout which is the timeout of\n" 126 " small tests. Other test types have a timeout that is a\n" 127 " multiple of this amount. If unspecified the default base\n" 128 " timeout is %d seconds.\n", 129 TEST_ENV_NAME, WATCHDOG_ENV_NAME, 130 DEFAULT_BASE_TIMEOUT_SECONDS); 131 fprintf(f, 132 " A scaling factor is applied to the base timeout:\n" 133 " Small - x %d\n" 134 " Medium - x %d\n" 135 " Large - x %d\n" 136 " Performance - x %d\n", 137 TEST_TIMEOUT_FACTOR_SMALL, TEST_TIMEOUT_FACTOR_MEDIUM, 138 TEST_TIMEOUT_FACTOR_LARGE, TEST_TIMEOUT_FACTOR_PERFORMANCE); 139} 140 141/* 142 * Runs all registered test cases. 143 */ 144bool unittest_run_all_tests(int argc, char** argv) { 145 const char* prog_name = basename(argv[0]); 146 bool list_tests_only = false; 147 const char* case_matcher = nullptr; 148 const char* test_matcher = nullptr; 149 150 int i = 1; 151 while (i < argc) { 152 const char* arg = argv[i]; 153 if (arg[0] == '-') { 154 // Got a switch. 155 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { 156 // Specifying --help at any point prints the help and exits. 157 print_help(prog_name, stdout); 158 return true; 159 } else if (strcmp(arg, "--list") == 0) { 160 list_tests_only = true; 161 } else if (strcmp(arg, "--case") == 0) { 162 if (i + 1 >= argc) { 163 fprintf(stderr, "Error: missing arg to %s\n", arg); 164 return false; 165 } 166 case_matcher = argv[++i]; 167 } else if (strcmp(arg, "--test") == 0) { 168 if (i + 1 >= argc) { 169 fprintf(stderr, "Error: missing arg to %s\n", arg); 170 return false; 171 } 172 test_matcher = argv[++i]; 173 } 174 } else if ((strlen(arg) == 3) && (arg[0] == 'v') && (arg[1] == '=')) { 175 unittest_set_verbosity_level(arg[2] - '0'); 176 } // Ignore other parameters 177 i++; 178 } 179 180 // Rely on the TEST_ENV_NAME environment variable to tell us which 181 // classes of tests we should execute. 182 const char* test_type_str = getenv(TEST_ENV_NAME); 183 test_type_t test_type; 184 if (test_type_str == nullptr) { 185 // If we cannot access the environment variable, run all tests 186 test_type = TEST_ALL; 187 } else { 188 test_type = static_cast<test_type_t>(atoi(test_type_str)); 189 } 190 191 // Rely on the WATCHDOG_ENV_NAME environment variable to tell us 192 // the timeout to use. 193 const char* watchdog_timeout_str = getenv(WATCHDOG_ENV_NAME); 194 if (watchdog_timeout_str != nullptr) { 195 char* end; 196 long timeout = strtol(watchdog_timeout_str, &end, 0); 197 if (*watchdog_timeout_str == '\0' || *end != '\0' || 198 timeout < 0 || timeout > INT_MAX) { 199 fprintf(stderr, "Error: bad watchdog timeout\n"); 200 return false; 201 } 202 watchdog_set_base_timeout(static_cast<int>(timeout)); 203 } 204 205 watchdog_initialize(); 206 207 auto result = unittest_run_all_tests_etc(argv[0], test_type, case_matcher, test_matcher, 208 list_tests_only); 209 210 watchdog_terminate(); 211 return result; 212} 213