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: Andrii Nakryiko <andrii@kernel.org>
This commit is contained in:
commit
87bb11ccfe
@ -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)
|
|||||||
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()
|
|||||||
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 {
|
|||||||
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[] = {
|
|||||||
"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)
|
|||||||
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;
|
||||||
|
|
||||||
strs[cnt] = strdup(next);
|
if (is_glob_pattern) {
|
||||||
if (!strs[cnt])
|
strs[cnt] = strdup(next);
|
||||||
goto err;
|
if (!strs[cnt])
|
||||||
|
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)
|
|||||||
}
|
}
|
||||||
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)
|
|||||||
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)
|
|||||||
test__end_subtest();
|
test__end_subtest();
|
||||||
|
|
||||||
test->tested = true;
|
test->tested = true;
|
||||||
|
|
||||||
|
dump_test_log(test, test->error_cnt);
|
||||||
|
|
||||||
|
fprintf(env.stdout, "#%d %s:%s\n",
|
||||||
|
test->test_num, test->test_name,
|
||||||
|
test->error_cnt ? "FAIL" : (test->skip_cnt ? "SKIP" : "OK"));
|
||||||
|
|
||||||
if (test->error_cnt)
|
if (test->error_cnt)
|
||||||
env.fail_cnt++;
|
env.fail_cnt++;
|
||||||
else
|
else
|
||||||
env.succ_cnt++;
|
env.succ_cnt++;
|
||||||
skip_account();
|
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");
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user