1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2023 Intel Corporation. All rights reserved. 3 */ 4 5 #include "spdk/stdinc.h" 6 #include "spdk/util.h" 7 #include "spdk_internal/cunit.h" 8 9 enum ut_action { 10 UT_ACTION_RUN_TESTS, 11 UT_ACTION_PRINT_HELP, 12 UT_ACTION_LIST_TESTS, 13 }; 14 15 struct ut_config { 16 const char *app; 17 const char *test; 18 const char *suite; 19 enum ut_action action; 20 const struct spdk_ut_opts *opts; 21 }; 22 23 #define OPTION_STRING "hls:t:" 24 25 static const struct option g_ut_options[] = { 26 #define OPTION_TEST_CASE 't' 27 {"test", required_argument, NULL, OPTION_TEST_CASE}, 28 #define OPTION_TEST_SUITE 's' 29 {"suite", required_argument, NULL, OPTION_TEST_SUITE}, 30 #define OPTION_LIST 'l' 31 {"list", no_argument, NULL, OPTION_LIST}, 32 #define OPTION_HELP 'h' 33 {"help", no_argument, NULL, OPTION_HELP}, 34 {}, 35 }; 36 37 static void 38 usage(struct ut_config *config) 39 { 40 const struct spdk_ut_opts *opts = config->opts; 41 42 printf("Usage: %s [OPTIONS]\n", config->app); 43 printf(" -t, --test run single test case\n"); 44 printf(" -s, --suite run all tests in a given suite\n"); 45 printf(" -l, --list list registered test suites and test cases\n"); 46 printf(" -h, --help print this help\n"); 47 48 if (opts != NULL && opts->usage_cb_fn != NULL) { 49 opts->usage_cb_fn(opts->cb_arg); 50 } 51 } 52 53 static int 54 parse_args(int argc, char **argv, struct ut_config *config) 55 { 56 const struct spdk_ut_opts *opts = config->opts; 57 #define MAX_OPTSTRING_LEN 4096 58 char optstring[MAX_OPTSTRING_LEN] = {}; 59 #define MAX_OPT_COUNT 128 60 struct option options[MAX_OPT_COUNT] = {}; 61 size_t optlen; 62 int op, rc; 63 64 /* Run the tests by default */ 65 config->action = UT_ACTION_RUN_TESTS; 66 config->app = argv[0]; 67 68 if (opts != NULL && opts->opts != NULL) { 69 optlen = SPDK_COUNTOF(g_ut_options) + opts->optlen; 70 if (optlen > MAX_OPT_COUNT) { 71 fprintf(stderr, "%s: unsupported number of options: %zu\n", 72 config->app, optlen); 73 return -EINVAL; 74 } 75 76 memcpy(&options[0], opts->opts, sizeof(*opts->opts) * opts->optlen); 77 memcpy(&options[opts->optlen], g_ut_options, sizeof(g_ut_options)); 78 79 rc = snprintf(optstring, MAX_OPTSTRING_LEN, "%s%s", OPTION_STRING, 80 opts->optstring); 81 if (rc < 0 || rc >= MAX_OPTSTRING_LEN) { 82 fprintf(stderr, "%s: bad optstring\n", config->app); 83 return -EINVAL; 84 } 85 } else { 86 snprintf(optstring, sizeof(optstring), "%s", OPTION_STRING); 87 memcpy(options, g_ut_options, sizeof(g_ut_options)); 88 } 89 90 while ((op = getopt_long(argc, argv, optstring, options, NULL)) != -1) { 91 switch (op) { 92 case OPTION_TEST_CASE: 93 config->test = optarg; 94 break; 95 case OPTION_TEST_SUITE: 96 config->suite = optarg; 97 break; 98 case OPTION_HELP: 99 config->action = UT_ACTION_PRINT_HELP; 100 break; 101 case OPTION_LIST: 102 config->action = UT_ACTION_LIST_TESTS; 103 break; 104 case '?': 105 return -EINVAL; 106 default: 107 if (opts != NULL && opts->option_cb_fn != NULL) { 108 rc = opts->option_cb_fn(op, optarg, opts->cb_arg); 109 if (rc != 0) { 110 return rc; 111 } 112 } else { 113 return -EINVAL; 114 } 115 } 116 } 117 118 return 0; 119 } 120 121 static int 122 run_tests(const struct ut_config *config) 123 { 124 CU_pSuite suite = NULL; 125 CU_pTest test = NULL; 126 127 if (config->suite != NULL) { 128 suite = CU_get_suite(config->suite); 129 if (suite == NULL) { 130 fprintf(stderr, "%s: invalid test suite: '%s'\n", 131 config->app, config->suite); 132 return 1; 133 } 134 } 135 136 if (config->test != NULL) { 137 if (suite == NULL) { 138 /* Allow users to skip test suite if there's only a single test suite 139 * registered (CUnit starts indexing from 1). */ 140 if (CU_get_suite_at_pos(2) != NULL) { 141 fprintf(stderr, "%s: there are multiple test suites registered, " 142 "select one using the -s option\n", config->app); 143 return 1; 144 } 145 146 suite = CU_get_suite_at_pos(1); 147 if (suite == NULL) { 148 fprintf(stderr, "%s: there are no tests registered\n", config->app); 149 return 1; 150 } 151 } 152 153 test = CU_get_test(suite, config->test); 154 if (test == NULL) { 155 fprintf(stderr, "%s: invalid test case: '%s'\n", config->app, config->test); 156 return 1; 157 } 158 } 159 160 CU_set_error_action(CUEA_ABORT); 161 CU_basic_set_mode(CU_BRM_VERBOSE); 162 163 /* Either run a single test, all tests in a given test suite, or all registered tests */ 164 if (test != NULL) { 165 CU_basic_run_test(suite, test); 166 } else if (suite != NULL) { 167 CU_basic_run_suite(suite); 168 } else { 169 CU_basic_run_tests(); 170 } 171 172 return CU_get_number_of_failures(); 173 } 174 175 static void 176 list_tests(void) 177 { 178 CU_pSuite suite; 179 CU_pTest test; 180 int sid, tid; 181 182 for (sid = 1;; ++sid) { 183 suite = CU_get_suite_at_pos(sid); 184 if (suite == NULL) { 185 break; 186 } 187 188 printf("%s:\n", suite->pName); 189 for (tid = 1;; ++tid) { 190 test = CU_get_test_at_pos(suite, tid); 191 if (test == NULL) { 192 break; 193 } 194 195 printf(" %s\n", test->pName); 196 } 197 } 198 } 199 200 int 201 spdk_ut_run_tests(int argc, char **argv, const struct spdk_ut_opts *opts) 202 { 203 struct ut_config config = {.opts = opts}; 204 int rc; 205 206 rc = parse_args(argc, argv, &config); 207 if (rc != 0) { 208 usage(&config); 209 return 1; 210 } 211 212 switch (config.action) { 213 case UT_ACTION_PRINT_HELP: 214 usage(&config); 215 break; 216 case UT_ACTION_RUN_TESTS: 217 if (opts != NULL && opts->init_cb_fn != NULL) { 218 rc = opts->init_cb_fn(opts->cb_arg); 219 if (rc != 0) { 220 usage(&config); 221 return 1; 222 } 223 } 224 225 rc = run_tests(&config); 226 break; 227 case UT_ACTION_LIST_TESTS: 228 list_tests(); 229 break; 230 default: 231 assert(0); 232 } 233 234 return rc; 235 } 236