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
usage(struct ut_config * config)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
parse_args(int argc,char ** argv,struct ut_config * config)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
run_tests(const struct ut_config * config)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
list_tests(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
spdk_ut_run_tests(int argc,char ** argv,const struct spdk_ut_opts * opts)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