13c6897d0SKonrad Sztyber /* SPDX-License-Identifier: BSD-3-Clause
23c6897d0SKonrad Sztyber * Copyright (C) 2023 Intel Corporation. All rights reserved.
33c6897d0SKonrad Sztyber */
43c6897d0SKonrad Sztyber
53c6897d0SKonrad Sztyber #include "spdk/stdinc.h"
6*f1167cb5SKonrad Sztyber #include "spdk/util.h"
73c6897d0SKonrad Sztyber #include "spdk_internal/cunit.h"
83c6897d0SKonrad Sztyber
93c6897d0SKonrad Sztyber enum ut_action {
103c6897d0SKonrad Sztyber UT_ACTION_RUN_TESTS,
113c6897d0SKonrad Sztyber UT_ACTION_PRINT_HELP,
128fec33aeSKonrad Sztyber UT_ACTION_LIST_TESTS,
133c6897d0SKonrad Sztyber };
143c6897d0SKonrad Sztyber
153c6897d0SKonrad Sztyber struct ut_config {
163c6897d0SKonrad Sztyber const char *app;
173c6897d0SKonrad Sztyber const char *test;
183c6897d0SKonrad Sztyber const char *suite;
193c6897d0SKonrad Sztyber enum ut_action action;
20*f1167cb5SKonrad Sztyber const struct spdk_ut_opts *opts;
213c6897d0SKonrad Sztyber };
223c6897d0SKonrad Sztyber
238fec33aeSKonrad Sztyber #define OPTION_STRING "hls:t:"
243c6897d0SKonrad Sztyber
253c6897d0SKonrad Sztyber static const struct option g_ut_options[] = {
263c6897d0SKonrad Sztyber #define OPTION_TEST_CASE 't'
273c6897d0SKonrad Sztyber {"test", required_argument, NULL, OPTION_TEST_CASE},
283c6897d0SKonrad Sztyber #define OPTION_TEST_SUITE 's'
293c6897d0SKonrad Sztyber {"suite", required_argument, NULL, OPTION_TEST_SUITE},
308fec33aeSKonrad Sztyber #define OPTION_LIST 'l'
318fec33aeSKonrad Sztyber {"list", no_argument, NULL, OPTION_LIST},
323c6897d0SKonrad Sztyber #define OPTION_HELP 'h'
333c6897d0SKonrad Sztyber {"help", no_argument, NULL, OPTION_HELP},
343c6897d0SKonrad Sztyber {},
353c6897d0SKonrad Sztyber };
363c6897d0SKonrad Sztyber
373c6897d0SKonrad Sztyber static void
usage(struct ut_config * config)383c6897d0SKonrad Sztyber usage(struct ut_config *config)
393c6897d0SKonrad Sztyber {
40*f1167cb5SKonrad Sztyber const struct spdk_ut_opts *opts = config->opts;
41*f1167cb5SKonrad Sztyber
423c6897d0SKonrad Sztyber printf("Usage: %s [OPTIONS]\n", config->app);
433c6897d0SKonrad Sztyber printf(" -t, --test run single test case\n");
443c6897d0SKonrad Sztyber printf(" -s, --suite run all tests in a given suite\n");
458fec33aeSKonrad Sztyber printf(" -l, --list list registered test suites and test cases\n");
463c6897d0SKonrad Sztyber printf(" -h, --help print this help\n");
47*f1167cb5SKonrad Sztyber
48*f1167cb5SKonrad Sztyber if (opts != NULL && opts->usage_cb_fn != NULL) {
49*f1167cb5SKonrad Sztyber opts->usage_cb_fn(opts->cb_arg);
50*f1167cb5SKonrad Sztyber }
513c6897d0SKonrad Sztyber }
523c6897d0SKonrad Sztyber
533c6897d0SKonrad Sztyber static int
parse_args(int argc,char ** argv,struct ut_config * config)543c6897d0SKonrad Sztyber parse_args(int argc, char **argv, struct ut_config *config)
553c6897d0SKonrad Sztyber {
56*f1167cb5SKonrad Sztyber const struct spdk_ut_opts *opts = config->opts;
57*f1167cb5SKonrad Sztyber #define MAX_OPTSTRING_LEN 4096
58*f1167cb5SKonrad Sztyber char optstring[MAX_OPTSTRING_LEN] = {};
59*f1167cb5SKonrad Sztyber #define MAX_OPT_COUNT 128
60*f1167cb5SKonrad Sztyber struct option options[MAX_OPT_COUNT] = {};
61*f1167cb5SKonrad Sztyber size_t optlen;
62*f1167cb5SKonrad Sztyber int op, rc;
633c6897d0SKonrad Sztyber
643c6897d0SKonrad Sztyber /* Run the tests by default */
653c6897d0SKonrad Sztyber config->action = UT_ACTION_RUN_TESTS;
663c6897d0SKonrad Sztyber config->app = argv[0];
673c6897d0SKonrad Sztyber
68*f1167cb5SKonrad Sztyber if (opts != NULL && opts->opts != NULL) {
69*f1167cb5SKonrad Sztyber optlen = SPDK_COUNTOF(g_ut_options) + opts->optlen;
70*f1167cb5SKonrad Sztyber if (optlen > MAX_OPT_COUNT) {
71*f1167cb5SKonrad Sztyber fprintf(stderr, "%s: unsupported number of options: %zu\n",
72*f1167cb5SKonrad Sztyber config->app, optlen);
73*f1167cb5SKonrad Sztyber return -EINVAL;
74*f1167cb5SKonrad Sztyber }
75*f1167cb5SKonrad Sztyber
76*f1167cb5SKonrad Sztyber memcpy(&options[0], opts->opts, sizeof(*opts->opts) * opts->optlen);
77*f1167cb5SKonrad Sztyber memcpy(&options[opts->optlen], g_ut_options, sizeof(g_ut_options));
78*f1167cb5SKonrad Sztyber
79*f1167cb5SKonrad Sztyber rc = snprintf(optstring, MAX_OPTSTRING_LEN, "%s%s", OPTION_STRING,
80*f1167cb5SKonrad Sztyber opts->optstring);
81*f1167cb5SKonrad Sztyber if (rc < 0 || rc >= MAX_OPTSTRING_LEN) {
82*f1167cb5SKonrad Sztyber fprintf(stderr, "%s: bad optstring\n", config->app);
83*f1167cb5SKonrad Sztyber return -EINVAL;
84*f1167cb5SKonrad Sztyber }
85*f1167cb5SKonrad Sztyber } else {
86*f1167cb5SKonrad Sztyber snprintf(optstring, sizeof(optstring), "%s", OPTION_STRING);
87*f1167cb5SKonrad Sztyber memcpy(options, g_ut_options, sizeof(g_ut_options));
88*f1167cb5SKonrad Sztyber }
89*f1167cb5SKonrad Sztyber
90*f1167cb5SKonrad Sztyber while ((op = getopt_long(argc, argv, optstring, options, NULL)) != -1) {
913c6897d0SKonrad Sztyber switch (op) {
923c6897d0SKonrad Sztyber case OPTION_TEST_CASE:
933c6897d0SKonrad Sztyber config->test = optarg;
943c6897d0SKonrad Sztyber break;
953c6897d0SKonrad Sztyber case OPTION_TEST_SUITE:
963c6897d0SKonrad Sztyber config->suite = optarg;
973c6897d0SKonrad Sztyber break;
983c6897d0SKonrad Sztyber case OPTION_HELP:
993c6897d0SKonrad Sztyber config->action = UT_ACTION_PRINT_HELP;
1003c6897d0SKonrad Sztyber break;
1018fec33aeSKonrad Sztyber case OPTION_LIST:
1028fec33aeSKonrad Sztyber config->action = UT_ACTION_LIST_TESTS;
1038fec33aeSKonrad Sztyber break;
104*f1167cb5SKonrad Sztyber case '?':
1053c6897d0SKonrad Sztyber return -EINVAL;
106*f1167cb5SKonrad Sztyber default:
107*f1167cb5SKonrad Sztyber if (opts != NULL && opts->option_cb_fn != NULL) {
108*f1167cb5SKonrad Sztyber rc = opts->option_cb_fn(op, optarg, opts->cb_arg);
109*f1167cb5SKonrad Sztyber if (rc != 0) {
110*f1167cb5SKonrad Sztyber return rc;
111*f1167cb5SKonrad Sztyber }
112*f1167cb5SKonrad Sztyber } else {
113*f1167cb5SKonrad Sztyber return -EINVAL;
114*f1167cb5SKonrad Sztyber }
1153c6897d0SKonrad Sztyber }
1163c6897d0SKonrad Sztyber }
1173c6897d0SKonrad Sztyber
1183c6897d0SKonrad Sztyber return 0;
1193c6897d0SKonrad Sztyber }
1203c6897d0SKonrad Sztyber
1213c6897d0SKonrad Sztyber static int
run_tests(const struct ut_config * config)1223c6897d0SKonrad Sztyber run_tests(const struct ut_config *config)
1233c6897d0SKonrad Sztyber {
1243c6897d0SKonrad Sztyber CU_pSuite suite = NULL;
1253c6897d0SKonrad Sztyber CU_pTest test = NULL;
1263c6897d0SKonrad Sztyber
1273c6897d0SKonrad Sztyber if (config->suite != NULL) {
1283c6897d0SKonrad Sztyber suite = CU_get_suite(config->suite);
1293c6897d0SKonrad Sztyber if (suite == NULL) {
1303c6897d0SKonrad Sztyber fprintf(stderr, "%s: invalid test suite: '%s'\n",
1313c6897d0SKonrad Sztyber config->app, config->suite);
1323c6897d0SKonrad Sztyber return 1;
1333c6897d0SKonrad Sztyber }
1343c6897d0SKonrad Sztyber }
1353c6897d0SKonrad Sztyber
1363c6897d0SKonrad Sztyber if (config->test != NULL) {
1373c6897d0SKonrad Sztyber if (suite == NULL) {
1383c6897d0SKonrad Sztyber /* Allow users to skip test suite if there's only a single test suite
1393c6897d0SKonrad Sztyber * registered (CUnit starts indexing from 1). */
1403c6897d0SKonrad Sztyber if (CU_get_suite_at_pos(2) != NULL) {
1413c6897d0SKonrad Sztyber fprintf(stderr, "%s: there are multiple test suites registered, "
1423c6897d0SKonrad Sztyber "select one using the -s option\n", config->app);
1433c6897d0SKonrad Sztyber return 1;
1443c6897d0SKonrad Sztyber }
1453c6897d0SKonrad Sztyber
1463c6897d0SKonrad Sztyber suite = CU_get_suite_at_pos(1);
1473c6897d0SKonrad Sztyber if (suite == NULL) {
1483c6897d0SKonrad Sztyber fprintf(stderr, "%s: there are no tests registered\n", config->app);
1493c6897d0SKonrad Sztyber return 1;
1503c6897d0SKonrad Sztyber }
1513c6897d0SKonrad Sztyber }
1523c6897d0SKonrad Sztyber
1533c6897d0SKonrad Sztyber test = CU_get_test(suite, config->test);
1543c6897d0SKonrad Sztyber if (test == NULL) {
1553c6897d0SKonrad Sztyber fprintf(stderr, "%s: invalid test case: '%s'\n", config->app, config->test);
1563c6897d0SKonrad Sztyber return 1;
1573c6897d0SKonrad Sztyber }
1583c6897d0SKonrad Sztyber }
1593c6897d0SKonrad Sztyber
1603c6897d0SKonrad Sztyber CU_set_error_action(CUEA_ABORT);
1613c6897d0SKonrad Sztyber CU_basic_set_mode(CU_BRM_VERBOSE);
1623c6897d0SKonrad Sztyber
1633c6897d0SKonrad Sztyber /* Either run a single test, all tests in a given test suite, or all registered tests */
1643c6897d0SKonrad Sztyber if (test != NULL) {
1653c6897d0SKonrad Sztyber CU_basic_run_test(suite, test);
1663c6897d0SKonrad Sztyber } else if (suite != NULL) {
1673c6897d0SKonrad Sztyber CU_basic_run_suite(suite);
1683c6897d0SKonrad Sztyber } else {
1693c6897d0SKonrad Sztyber CU_basic_run_tests();
1703c6897d0SKonrad Sztyber }
1713c6897d0SKonrad Sztyber
1723c6897d0SKonrad Sztyber return CU_get_number_of_failures();
1733c6897d0SKonrad Sztyber }
1743c6897d0SKonrad Sztyber
1758fec33aeSKonrad Sztyber static void
list_tests(void)1768fec33aeSKonrad Sztyber list_tests(void)
1778fec33aeSKonrad Sztyber {
1788fec33aeSKonrad Sztyber CU_pSuite suite;
1798fec33aeSKonrad Sztyber CU_pTest test;
1808fec33aeSKonrad Sztyber int sid, tid;
1818fec33aeSKonrad Sztyber
1828fec33aeSKonrad Sztyber for (sid = 1;; ++sid) {
1838fec33aeSKonrad Sztyber suite = CU_get_suite_at_pos(sid);
1848fec33aeSKonrad Sztyber if (suite == NULL) {
1858fec33aeSKonrad Sztyber break;
1868fec33aeSKonrad Sztyber }
1878fec33aeSKonrad Sztyber
1888fec33aeSKonrad Sztyber printf("%s:\n", suite->pName);
1898fec33aeSKonrad Sztyber for (tid = 1;; ++tid) {
1908fec33aeSKonrad Sztyber test = CU_get_test_at_pos(suite, tid);
1918fec33aeSKonrad Sztyber if (test == NULL) {
1928fec33aeSKonrad Sztyber break;
1938fec33aeSKonrad Sztyber }
1948fec33aeSKonrad Sztyber
1958fec33aeSKonrad Sztyber printf(" %s\n", test->pName);
1968fec33aeSKonrad Sztyber }
1978fec33aeSKonrad Sztyber }
1988fec33aeSKonrad Sztyber }
1998fec33aeSKonrad Sztyber
2003c6897d0SKonrad Sztyber int
spdk_ut_run_tests(int argc,char ** argv,const struct spdk_ut_opts * opts)2013c6897d0SKonrad Sztyber spdk_ut_run_tests(int argc, char **argv, const struct spdk_ut_opts *opts)
2023c6897d0SKonrad Sztyber {
203*f1167cb5SKonrad Sztyber struct ut_config config = {.opts = opts};
2043c6897d0SKonrad Sztyber int rc;
2053c6897d0SKonrad Sztyber
2063c6897d0SKonrad Sztyber rc = parse_args(argc, argv, &config);
2073c6897d0SKonrad Sztyber if (rc != 0) {
2083c6897d0SKonrad Sztyber usage(&config);
2093c6897d0SKonrad Sztyber return 1;
2103c6897d0SKonrad Sztyber }
2113c6897d0SKonrad Sztyber
2123c6897d0SKonrad Sztyber switch (config.action) {
2133c6897d0SKonrad Sztyber case UT_ACTION_PRINT_HELP:
2143c6897d0SKonrad Sztyber usage(&config);
2153c6897d0SKonrad Sztyber break;
2163c6897d0SKonrad Sztyber case UT_ACTION_RUN_TESTS:
217*f1167cb5SKonrad Sztyber if (opts != NULL && opts->init_cb_fn != NULL) {
218*f1167cb5SKonrad Sztyber rc = opts->init_cb_fn(opts->cb_arg);
219*f1167cb5SKonrad Sztyber if (rc != 0) {
220*f1167cb5SKonrad Sztyber usage(&config);
221*f1167cb5SKonrad Sztyber return 1;
222*f1167cb5SKonrad Sztyber }
223*f1167cb5SKonrad Sztyber }
224*f1167cb5SKonrad Sztyber
2253c6897d0SKonrad Sztyber rc = run_tests(&config);
2263c6897d0SKonrad Sztyber break;
2278fec33aeSKonrad Sztyber case UT_ACTION_LIST_TESTS:
2288fec33aeSKonrad Sztyber list_tests();
2298fec33aeSKonrad Sztyber break;
2303c6897d0SKonrad Sztyber default:
2313c6897d0SKonrad Sztyber assert(0);
2323c6897d0SKonrad Sztyber }
2333c6897d0SKonrad Sztyber
2343c6897d0SKonrad Sztyber return rc;
2353c6897d0SKonrad Sztyber }
236