Commit 87bb11cc authored by Andrii Nakryiko's avatar Andrii Nakryiko
Browse files

Merge branch 'selftests/bpf: Improve the usability of test_progs'



Yucong Sun says:

====================

This short series adds two new switches to test_progs, "-a" and "-d",
adding support for both exact string matching, as well as '*' wildcards.
It also cleans up the output to make it possible to generate
allowlist/denylist using common cli tools.
====================

Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents 857f75ea 74339a8f
Loading
Loading
Loading
Loading
+77 −30
Original line number Diff line number Diff line
@@ -13,6 +13,28 @@
#include <execinfo.h> /* backtrace */
#include <linux/membarrier.h>

/* Adapted from perf/util/string.c */
static bool glob_match(const char *str, const char *pat)
{
	while (*str && *pat && *pat != '*') {
		if (*str != *pat)
			return false;
		str++;
		pat++;
	}
	/* Check wild card */
	if (*pat == '*') {
		while (*pat == '*')
			pat++;
		if (!*pat) /* Tail wild card matches all */
			return true;
		while (*str)
			if (glob_match(str++, pat))
				return true;
	}
	return !*str && !*pat;
}

#define EXIT_NO_TEST		2
#define EXIT_ERR_SETUP_INFRA	3

@@ -55,12 +77,12 @@ static bool should_run(struct test_selector *sel, int num, const char *name)
	int i;

	for (i = 0; i < sel->blacklist.cnt; i++) {
		if (strstr(name, sel->blacklist.strs[i]))
		if (glob_match(name, sel->blacklist.strs[i]))
			return false;
	}

	for (i = 0; i < sel->whitelist.cnt; i++) {
		if (strstr(name, sel->whitelist.strs[i]))
		if (glob_match(name, sel->whitelist.strs[i]))
			return true;
	}

@@ -148,18 +170,18 @@ void test__end_subtest()
	struct prog_test_def *test = env.test;
	int sub_error_cnt = test->error_cnt - test->old_error_cnt;

	dump_test_log(test, sub_error_cnt);

	fprintf(env.stdout, "#%d/%d %s/%s:%s\n",
	       test->test_num, test->subtest_num, test->test_name, test->subtest_name,
	       sub_error_cnt ? "FAIL" : (test->skip_cnt ? "SKIP" : "OK"));

	if (sub_error_cnt)
		env.fail_cnt++;
	else if (test->skip_cnt == 0)
		env.sub_succ_cnt++;
	skip_account();

	dump_test_log(test, sub_error_cnt);

	fprintf(env.stdout, "#%d/%d %s:%s\n",
	       test->test_num, test->subtest_num, test->subtest_name,
	       sub_error_cnt ? "FAIL" : (test->skip_cnt ? "SKIP" : "OK"));

	free(test->subtest_name);
	test->subtest_name = NULL;
}
@@ -450,6 +472,8 @@ enum ARG_KEYS {
	ARG_VERBOSE = 'v',
	ARG_GET_TEST_CNT = 'c',
	ARG_LIST_TEST_NAMES = 'l',
	ARG_TEST_NAME_GLOB_ALLOWLIST = 'a',
	ARG_TEST_NAME_GLOB_DENYLIST = 'd',
};

static const struct argp_option opts[] = {
@@ -467,6 +491,10 @@ static const struct argp_option opts[] = {
	  "Get number of selected top-level tests " },
	{ "list", ARG_LIST_TEST_NAMES, NULL, 0,
	  "List test names that would run (without running them) " },
	{ "allow", ARG_TEST_NAME_GLOB_ALLOWLIST, "NAMES", 0,
	  "Run tests with name matching the pattern (supports '*' wildcard)." },
	{ "deny", ARG_TEST_NAME_GLOB_DENYLIST, "NAMES", 0,
	  "Don't run tests with name matching the pattern (supports '*' wildcard)." },
	{},
};

@@ -491,36 +519,48 @@ static void free_str_set(const struct str_set *set)
	free(set->strs);
}

static int parse_str_list(const char *s, struct str_set *set)
static int parse_str_list(const char *s, struct str_set *set, bool is_glob_pattern)
{
	char *input, *state = NULL, *next, **tmp, **strs = NULL;
	int cnt = 0;
	int i, cnt = 0;

	input = strdup(s);
	if (!input)
		return -ENOMEM;

	set->cnt = 0;
	set->strs = NULL;

	while ((next = strtok_r(state ? NULL : input, ",", &state))) {
		tmp = realloc(strs, sizeof(*strs) * (cnt + 1));
		if (!tmp)
			goto err;
		strs = tmp;

		if (is_glob_pattern) {
			strs[cnt] = strdup(next);
			if (!strs[cnt])
				goto err;
		} else {
			strs[cnt] = malloc(strlen(next) + 2 + 1);
			if (!strs[cnt])
				goto err;
			sprintf(strs[cnt], "*%s*", next);
		}

		cnt++;
	}

	set->cnt = cnt;
	set->strs = (const char **)strs;
	tmp = realloc(set->strs, sizeof(*strs) * (cnt + set->cnt));
	if (!tmp)
		goto err;
	memcpy(tmp + set->cnt, strs, sizeof(*strs) * cnt);
	set->strs = (const char **)tmp;
	set->cnt += cnt;

	free(input);
	free(strs);
	return 0;
err:
	for (i = 0; i < cnt; i++)
		free(strs[i]);
	free(strs);
	free(input);
	return -ENOMEM;
@@ -553,29 +593,35 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
		}
		break;
	}
	case ARG_TEST_NAME_GLOB_ALLOWLIST:
	case ARG_TEST_NAME: {
		char *subtest_str = strchr(arg, '/');

		if (subtest_str) {
			*subtest_str = '\0';
			if (parse_str_list(subtest_str + 1,
					   &env->subtest_selector.whitelist))
					   &env->subtest_selector.whitelist,
					   key == ARG_TEST_NAME_GLOB_ALLOWLIST))
				return -ENOMEM;
		}
		if (parse_str_list(arg, &env->test_selector.whitelist))
		if (parse_str_list(arg, &env->test_selector.whitelist,
				   key == ARG_TEST_NAME_GLOB_ALLOWLIST))
			return -ENOMEM;
		break;
	}
	case ARG_TEST_NAME_GLOB_DENYLIST:
	case ARG_TEST_NAME_BLACKLIST: {
		char *subtest_str = strchr(arg, '/');

		if (subtest_str) {
			*subtest_str = '\0';
			if (parse_str_list(subtest_str + 1,
					   &env->subtest_selector.blacklist))
					   &env->subtest_selector.blacklist,
					   key == ARG_TEST_NAME_GLOB_DENYLIST))
				return -ENOMEM;
		}
		if (parse_str_list(arg, &env->test_selector.blacklist))
		if (parse_str_list(arg, &env->test_selector.blacklist,
				   key == ARG_TEST_NAME_GLOB_DENYLIST))
			return -ENOMEM;
		break;
	}
@@ -755,7 +801,7 @@ int main(int argc, char **argv)
	save_netns();
	stdio_hijack();
	env.has_testmod = true;
	if (load_bpf_testmod()) {
	if (!env.list_test_names && load_bpf_testmod()) {
		fprintf(env.stderr, "WARNING! Selftests relying on bpf_testmod.ko will be skipped.\n");
		env.has_testmod = false;
	}
@@ -786,24 +832,25 @@ int main(int argc, char **argv)
			test__end_subtest();

		test->tested = true;
		if (test->error_cnt)
			env.fail_cnt++;
		else
			env.succ_cnt++;
		skip_account();

		dump_test_log(test, test->error_cnt);

		fprintf(env.stdout, "#%d %s:%s\n",
			test->test_num, test->test_name,
			test->error_cnt ? "FAIL" : "OK");
			test->error_cnt ? "FAIL" : (test->skip_cnt ? "SKIP" : "OK"));

		if (test->error_cnt)
			env.fail_cnt++;
		else
			env.succ_cnt++;
		skip_account();

		reset_affinity();
		restore_netns();
		if (test->need_cgroup_cleanup)
			cleanup_cgroup_environment();
	}
	if (env.has_testmod)
	if (!env.list_test_names && env.has_testmod)
		unload_bpf_testmod();
	stdio_restore();