xref: /spdk/lib/ut/ut.c (revision 8afdeef3becfe9409cc9e7372bd0bc10e8b7d46d)
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