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

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
...@@ -13,6 +13,28 @@ ...@@ -13,6 +13,28 @@
#include <execinfo.h> /* backtrace */ #include <execinfo.h> /* backtrace */
#include <linux/membarrier.h> #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_NO_TEST 2
#define EXIT_ERR_SETUP_INFRA 3 #define EXIT_ERR_SETUP_INFRA 3
...@@ -55,12 +77,12 @@ static bool should_run(struct test_selector *sel, int num, const char *name) ...@@ -55,12 +77,12 @@ static bool should_run(struct test_selector *sel, int num, const char *name)
int i; int i;
for (i = 0; i < sel->blacklist.cnt; 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; return false;
} }
for (i = 0; i < sel->whitelist.cnt; i++) { 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; return true;
} }
...@@ -148,18 +170,18 @@ void test__end_subtest() ...@@ -148,18 +170,18 @@ void test__end_subtest()
struct prog_test_def *test = env.test; struct prog_test_def *test = env.test;
int sub_error_cnt = test->error_cnt - test->old_error_cnt; 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) if (sub_error_cnt)
env.fail_cnt++; env.fail_cnt++;
else if (test->skip_cnt == 0) else if (test->skip_cnt == 0)
env.sub_succ_cnt++; env.sub_succ_cnt++;
skip_account(); 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); free(test->subtest_name);
test->subtest_name = NULL; test->subtest_name = NULL;
} }
...@@ -450,6 +472,8 @@ enum ARG_KEYS { ...@@ -450,6 +472,8 @@ enum ARG_KEYS {
ARG_VERBOSE = 'v', ARG_VERBOSE = 'v',
ARG_GET_TEST_CNT = 'c', ARG_GET_TEST_CNT = 'c',
ARG_LIST_TEST_NAMES = 'l', ARG_LIST_TEST_NAMES = 'l',
ARG_TEST_NAME_GLOB_ALLOWLIST = 'a',
ARG_TEST_NAME_GLOB_DENYLIST = 'd',
}; };
static const struct argp_option opts[] = { static const struct argp_option opts[] = {
...@@ -467,6 +491,10 @@ static const struct argp_option opts[] = { ...@@ -467,6 +491,10 @@ static const struct argp_option opts[] = {
"Get number of selected top-level tests " }, "Get number of selected top-level tests " },
{ "list", ARG_LIST_TEST_NAMES, NULL, 0, { "list", ARG_LIST_TEST_NAMES, NULL, 0,
"List test names that would run (without running them) " }, "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) ...@@ -491,36 +519,48 @@ static void free_str_set(const struct str_set *set)
free(set->strs); 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; char *input, *state = NULL, *next, **tmp, **strs = NULL;
int cnt = 0; int i, cnt = 0;
input = strdup(s); input = strdup(s);
if (!input) if (!input)
return -ENOMEM; return -ENOMEM;
set->cnt = 0;
set->strs = NULL;
while ((next = strtok_r(state ? NULL : input, ",", &state))) { while ((next = strtok_r(state ? NULL : input, ",", &state))) {
tmp = realloc(strs, sizeof(*strs) * (cnt + 1)); tmp = realloc(strs, sizeof(*strs) * (cnt + 1));
if (!tmp) if (!tmp)
goto err; goto err;
strs = tmp; strs = tmp;
if (is_glob_pattern) {
strs[cnt] = strdup(next); strs[cnt] = strdup(next);
if (!strs[cnt]) if (!strs[cnt])
goto err; goto err;
} else {
strs[cnt] = malloc(strlen(next) + 2 + 1);
if (!strs[cnt])
goto err;
sprintf(strs[cnt], "*%s*", next);
}
cnt++; cnt++;
} }
set->cnt = cnt; tmp = realloc(set->strs, sizeof(*strs) * (cnt + set->cnt));
set->strs = (const char **)strs; if (!tmp)
goto err;
memcpy(tmp + set->cnt, strs, sizeof(*strs) * cnt);
set->strs = (const char **)tmp;
set->cnt += cnt;
free(input); free(input);
free(strs);
return 0; return 0;
err: err:
for (i = 0; i < cnt; i++)
free(strs[i]);
free(strs); free(strs);
free(input); free(input);
return -ENOMEM; return -ENOMEM;
...@@ -553,29 +593,35 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) ...@@ -553,29 +593,35 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
} }
break; break;
} }
case ARG_TEST_NAME_GLOB_ALLOWLIST:
case ARG_TEST_NAME: { case ARG_TEST_NAME: {
char *subtest_str = strchr(arg, '/'); char *subtest_str = strchr(arg, '/');
if (subtest_str) { if (subtest_str) {
*subtest_str = '\0'; *subtest_str = '\0';
if (parse_str_list(subtest_str + 1, if (parse_str_list(subtest_str + 1,
&env->subtest_selector.whitelist)) &env->subtest_selector.whitelist,
key == ARG_TEST_NAME_GLOB_ALLOWLIST))
return -ENOMEM; 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; return -ENOMEM;
break; break;
} }
case ARG_TEST_NAME_GLOB_DENYLIST:
case ARG_TEST_NAME_BLACKLIST: { case ARG_TEST_NAME_BLACKLIST: {
char *subtest_str = strchr(arg, '/'); char *subtest_str = strchr(arg, '/');
if (subtest_str) { if (subtest_str) {
*subtest_str = '\0'; *subtest_str = '\0';
if (parse_str_list(subtest_str + 1, if (parse_str_list(subtest_str + 1,
&env->subtest_selector.blacklist)) &env->subtest_selector.blacklist,
key == ARG_TEST_NAME_GLOB_DENYLIST))
return -ENOMEM; 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; return -ENOMEM;
break; break;
} }
...@@ -755,7 +801,7 @@ int main(int argc, char **argv) ...@@ -755,7 +801,7 @@ int main(int argc, char **argv)
save_netns(); save_netns();
stdio_hijack(); stdio_hijack();
env.has_testmod = true; 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"); fprintf(env.stderr, "WARNING! Selftests relying on bpf_testmod.ko will be skipped.\n");
env.has_testmod = false; env.has_testmod = false;
} }
...@@ -786,24 +832,25 @@ int main(int argc, char **argv) ...@@ -786,24 +832,25 @@ int main(int argc, char **argv)
test__end_subtest(); test__end_subtest();
test->tested = true; test->tested = true;
if (test->error_cnt)
env.fail_cnt++;
else
env.succ_cnt++;
skip_account();
dump_test_log(test, test->error_cnt); dump_test_log(test, test->error_cnt);
fprintf(env.stdout, "#%d %s:%s\n", fprintf(env.stdout, "#%d %s:%s\n",
test->test_num, test->test_name, 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(); reset_affinity();
restore_netns(); restore_netns();
if (test->need_cgroup_cleanup) if (test->need_cgroup_cleanup)
cleanup_cgroup_environment(); cleanup_cgroup_environment();
} }
if (env.has_testmod) if (!env.list_test_names && env.has_testmod)
unload_bpf_testmod(); unload_bpf_testmod();
stdio_restore(); stdio_restore();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment