Line data Source code
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 0 : usage(struct ut_config *config)
39 : {
40 0 : const struct spdk_ut_opts *opts = config->opts;
41 :
42 0 : printf("Usage: %s [OPTIONS]\n", config->app);
43 0 : printf(" -t, --test run single test case\n");
44 0 : printf(" -s, --suite run all tests in a given suite\n");
45 0 : printf(" -l, --list list registered test suites and test cases\n");
46 0 : printf(" -h, --help print this help\n");
47 :
48 0 : if (opts != NULL && opts->usage_cb_fn != NULL) {
49 0 : opts->usage_cb_fn(opts->cb_arg);
50 0 : }
51 0 : }
52 :
53 : static int
54 89 : parse_args(int argc, char **argv, struct ut_config *config)
55 : {
56 89 : const struct spdk_ut_opts *opts = config->opts;
57 : #define MAX_OPTSTRING_LEN 4096
58 89 : char optstring[MAX_OPTSTRING_LEN] = {};
59 : #define MAX_OPT_COUNT 128
60 89 : struct option options[MAX_OPT_COUNT] = {};
61 : size_t optlen;
62 : int op, rc;
63 :
64 : /* Run the tests by default */
65 89 : config->action = UT_ACTION_RUN_TESTS;
66 89 : config->app = argv[0];
67 :
68 89 : if (opts != NULL && opts->opts != NULL) {
69 0 : optlen = SPDK_COUNTOF(g_ut_options) + opts->optlen;
70 0 : if (optlen > MAX_OPT_COUNT) {
71 0 : fprintf(stderr, "%s: unsupported number of options: %zu\n",
72 0 : config->app, optlen);
73 0 : return -EINVAL;
74 : }
75 :
76 0 : memcpy(&options[0], opts->opts, sizeof(*opts->opts) * opts->optlen);
77 0 : memcpy(&options[opts->optlen], g_ut_options, sizeof(g_ut_options));
78 :
79 0 : rc = snprintf(optstring, MAX_OPTSTRING_LEN, "%s%s", OPTION_STRING,
80 0 : opts->optstring);
81 0 : if (rc < 0 || rc >= MAX_OPTSTRING_LEN) {
82 0 : fprintf(stderr, "%s: bad optstring\n", config->app);
83 0 : return -EINVAL;
84 : }
85 0 : } else {
86 89 : snprintf(optstring, sizeof(optstring), "%s", OPTION_STRING);
87 89 : memcpy(options, g_ut_options, sizeof(g_ut_options));
88 : }
89 :
90 89 : while ((op = getopt_long(argc, argv, optstring, options, NULL)) != -1) {
91 0 : switch (op) {
92 : case OPTION_TEST_CASE:
93 0 : config->test = optarg;
94 0 : break;
95 : case OPTION_TEST_SUITE:
96 0 : config->suite = optarg;
97 0 : break;
98 : case OPTION_HELP:
99 0 : config->action = UT_ACTION_PRINT_HELP;
100 0 : break;
101 : case OPTION_LIST:
102 0 : config->action = UT_ACTION_LIST_TESTS;
103 0 : break;
104 : case '?':
105 0 : return -EINVAL;
106 : default:
107 0 : if (opts != NULL && opts->option_cb_fn != NULL) {
108 0 : rc = opts->option_cb_fn(op, optarg, opts->cb_arg);
109 0 : if (rc != 0) {
110 0 : return rc;
111 : }
112 0 : } else {
113 0 : return -EINVAL;
114 : }
115 0 : }
116 : }
117 :
118 89 : return 0;
119 89 : }
120 :
121 : static int
122 89 : run_tests(const struct ut_config *config)
123 : {
124 89 : CU_pSuite suite = NULL;
125 89 : CU_pTest test = NULL;
126 :
127 89 : if (config->suite != NULL) {
128 0 : suite = CU_get_suite(config->suite);
129 0 : if (suite == NULL) {
130 0 : fprintf(stderr, "%s: invalid test suite: '%s'\n",
131 0 : config->app, config->suite);
132 0 : return 1;
133 : }
134 0 : }
135 :
136 89 : if (config->test != NULL) {
137 0 : 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 0 : if (CU_get_suite_at_pos(2) != NULL) {
141 0 : fprintf(stderr, "%s: there are multiple test suites registered, "
142 0 : "select one using the -s option\n", config->app);
143 0 : return 1;
144 : }
145 :
146 0 : suite = CU_get_suite_at_pos(1);
147 0 : if (suite == NULL) {
148 0 : fprintf(stderr, "%s: there are no tests registered\n", config->app);
149 0 : return 1;
150 : }
151 0 : }
152 :
153 0 : test = CU_get_test(suite, config->test);
154 0 : if (test == NULL) {
155 0 : fprintf(stderr, "%s: invalid test case: '%s'\n", config->app, config->test);
156 0 : return 1;
157 : }
158 0 : }
159 :
160 89 : CU_set_error_action(CUEA_ABORT);
161 89 : 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 89 : if (test != NULL) {
165 0 : CU_basic_run_test(suite, test);
166 89 : } else if (suite != NULL) {
167 0 : CU_basic_run_suite(suite);
168 0 : } else {
169 89 : CU_basic_run_tests();
170 : }
171 :
172 89 : return CU_get_number_of_failures();
173 89 : }
174 :
175 : static void
176 0 : list_tests(void)
177 : {
178 : CU_pSuite suite;
179 : CU_pTest test;
180 : int sid, tid;
181 :
182 0 : for (sid = 1;; ++sid) {
183 0 : suite = CU_get_suite_at_pos(sid);
184 0 : if (suite == NULL) {
185 0 : break;
186 : }
187 :
188 0 : printf("%s:\n", suite->pName);
189 0 : for (tid = 1;; ++tid) {
190 0 : test = CU_get_test_at_pos(suite, tid);
191 0 : if (test == NULL) {
192 0 : break;
193 : }
194 :
195 0 : printf(" %s\n", test->pName);
196 0 : }
197 0 : }
198 0 : }
199 :
200 : int
201 89 : spdk_ut_run_tests(int argc, char **argv, const struct spdk_ut_opts *opts)
202 : {
203 89 : struct ut_config config = {.opts = opts};
204 : int rc;
205 :
206 89 : rc = parse_args(argc, argv, &config);
207 89 : if (rc != 0) {
208 0 : usage(&config);
209 0 : return 1;
210 : }
211 :
212 89 : switch (config.action) {
213 : case UT_ACTION_PRINT_HELP:
214 0 : usage(&config);
215 0 : break;
216 : case UT_ACTION_RUN_TESTS:
217 89 : if (opts != NULL && opts->init_cb_fn != NULL) {
218 0 : rc = opts->init_cb_fn(opts->cb_arg);
219 0 : if (rc != 0) {
220 0 : usage(&config);
221 0 : return 1;
222 : }
223 0 : }
224 :
225 89 : rc = run_tests(&config);
226 89 : break;
227 : case UT_ACTION_LIST_TESTS:
228 0 : list_tests();
229 0 : break;
230 : default:
231 0 : assert(0);
232 : }
233 :
234 89 : return rc;
235 89 : }
|