Commit 3c6897d0 authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Tomasz Zawadzki
Browse files

lib: add ut library



The purpose of this new library is to provide helpers functions for
common unit test tasks.  In the first iteration, it'll provide an
interface for running the tests based on the command-line parameters.
To use it, the calls to CU_basic_run_tests() need to be replaced with
spdk_ut_run_tests().

When working on a feature, it's often useful to run a single test case,
as it makes easier to read the logs.  So, the command-line interface
provides parameters to select the test case (-t, --test) or test suite
(-s, --suite) to execute.  By default, if no arguments are specified,
all tests in all test suites are executed.  If there's only a single
test suite registered (like in most unit tests), users can omit the test
suite argument.

Signed-off-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Ia8fd4bc047e7c6adf21a71215a0fd3d4f06bc834
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/19285


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarAleksey Marchuk <alexeymar@nvidia.com>
Reviewed-by: default avatarTomasz Zawadzki <tomasz.zawadzki@intel.com>
Community-CI: Mellanox Build Bot
parent ae431e31
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -25,4 +25,19 @@
		}				\
	} while (0)

struct spdk_ut_opts {
	/* Empty for now */
};

/**
 * Execute unit tests registered using CUnit.
 *
 * \param argc Size of the `argv` array.
 * \param argv Arguments to the test app.
 * \param opts Options.
 *
 * \return Number of test failures.
 */
int spdk_ut_run_tests(int argc, char **argv, const struct spdk_ut_opts *opts);

#endif /* SPDK_CUNIT_H */
+6 −0
Original line number Diff line number Diff line
@@ -17,6 +17,12 @@ DIRS-y += ublk
endif
endif

# The ut library is used by unit tests and some functional tests, so build it only when either of
# these configs is enabled
ifeq ($(filter $(CONFIG_TESTS) $(CONFIG_UNIT_TESTS),y),y)
DIRS-y += ut
endif

DIRS-$(CONFIG_OCF) += env_ocf
DIRS-$(CONFIG_IDXD) += idxd
DIRS-$(CONFIG_VHOST) += vhost

lib/ut/Makefile

0 → 100644
+18 −0
Original line number Diff line number Diff line
#  SPDX-License-Identifier: BSD-3-Clause
#  Copyright (C) 2023 Intel Corporation. All rights reserved.
#

SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk

SO_VER := 1
SO_MINOR := 0

C_SRCS = ut.c
LIBNAME = ut

LOCAL_SYS_LIBS = -lcunit

SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ut.map)

include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk

lib/ut/spdk_ut.map

0 → 100644
+6 −0
Original line number Diff line number Diff line
{
	global:
	spdk_ut_run_tests;

	local: *;
};

lib/ut/ut.c

0 → 100644
+147 −0
Original line number Diff line number Diff line
/*   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright (C) 2023 Intel Corporation. All rights reserved.
 */

#include "spdk/stdinc.h"
#include "spdk_internal/cunit.h"

enum ut_action {
	UT_ACTION_RUN_TESTS,
	UT_ACTION_PRINT_HELP,
};

struct ut_config {
	const char	*app;
	const char	*test;
	const char	*suite;
	enum ut_action	action;
};

#define OPTION_STRING "hs:t:"

static const struct option g_ut_options[] = {
#define OPTION_TEST_CASE 't'
	{"test", required_argument, NULL, OPTION_TEST_CASE},
#define OPTION_TEST_SUITE 's'
	{"suite", required_argument, NULL, OPTION_TEST_SUITE},
#define OPTION_HELP 'h'
	{"help", no_argument, NULL, OPTION_HELP},
	{},
};

static void
usage(struct ut_config *config)
{
	printf("Usage: %s [OPTIONS]\n", config->app);
	printf("  -t, --test                       run single test case\n");
	printf("  -s, --suite                      run all tests in a given suite\n");
	printf("  -h, --help                       print this help\n");
}

static int
parse_args(int argc, char **argv, struct ut_config *config)
{
	int op;

	/* Run the tests by default */
	config->action = UT_ACTION_RUN_TESTS;
	config->app = argv[0];

	while ((op = getopt_long(argc, argv, OPTION_STRING, g_ut_options, NULL)) != -1) {
		switch (op) {
		case OPTION_TEST_CASE:
			config->test = optarg;
			break;
		case OPTION_TEST_SUITE:
			config->suite = optarg;
			break;
		case OPTION_HELP:
			config->action = UT_ACTION_PRINT_HELP;
			break;
		default:
			return -EINVAL;
		}
	}

	return 0;
}

static int
run_tests(const struct ut_config *config)
{
	CU_pSuite suite = NULL;
	CU_pTest test = NULL;

	if (config->suite != NULL) {
		suite = CU_get_suite(config->suite);
		if (suite == NULL) {
			fprintf(stderr, "%s: invalid test suite: '%s'\n",
				config->app, config->suite);
			return 1;
		}
	}

	if (config->test != NULL) {
		if (suite == NULL) {
			/* Allow users to skip test suite if there's only a single test suite
			 * registered (CUnit starts indexing from 1). */
			if (CU_get_suite_at_pos(2) != NULL) {
				fprintf(stderr, "%s: there are multiple test suites registered, "
					"select one using the -s option\n", config->app);
				return 1;
			}

			suite = CU_get_suite_at_pos(1);
			if (suite == NULL) {
				fprintf(stderr, "%s: there are no tests registered\n", config->app);
				return 1;
			}
		}

		test = CU_get_test(suite, config->test);
		if (test == NULL) {
			fprintf(stderr, "%s: invalid test case: '%s'\n", config->app, config->test);
			return 1;
		}
	}

	CU_set_error_action(CUEA_ABORT);
	CU_basic_set_mode(CU_BRM_VERBOSE);

	/* Either run a single test, all tests in a given test suite, or all registered tests */
	if (test != NULL) {
		CU_basic_run_test(suite, test);
	} else if (suite != NULL) {
		CU_basic_run_suite(suite);
	} else {
		CU_basic_run_tests();
	}

	return CU_get_number_of_failures();
}

int
spdk_ut_run_tests(int argc, char **argv, const struct spdk_ut_opts *opts)
{
	struct ut_config config = {};
	int rc;

	rc = parse_args(argc, argv, &config);
	if (rc != 0) {
		usage(&config);
		return 1;
	}

	switch (config.action) {
	case UT_ACTION_PRINT_HELP:
		usage(&config);
		break;
	case UT_ACTION_RUN_TESTS:
		rc = run_tests(&config);
		break;
	default:
		assert(0);
	}

	return rc;
}