mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
selftests/bpf: Test_progs can read test lists from file
Improve test selection logic when using -a/-b/-d/-t options. The list of tests to include or exclude can now be read from a file, specified as @<filename>. The file contains one name (or wildcard pattern) per line, and comments beginning with # are ignored. These options can be passed multiple times to read more than one file. Signed-off-by: Stephen Veiss <sveiss@meta.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Acked-by: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/bpf/20230427225333.3506052-3-sveiss@meta.com
This commit is contained in:
committed by
Andrii Nakryiko
parent
0a5c0de8b6
commit
64276f01dc
@@ -113,8 +113,63 @@ error:
|
|||||||
free_test_filter_set(&set);
|
free_test_filter_set(&set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_parse_test_list_file(void)
|
||||||
|
{
|
||||||
|
struct test_filter_set set;
|
||||||
|
char tmpfile[80];
|
||||||
|
FILE *fp;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
snprintf(tmpfile, sizeof(tmpfile), "/tmp/bpf_arg_parsing_test.XXXXXX");
|
||||||
|
fd = mkstemp(tmpfile);
|
||||||
|
if (!ASSERT_GE(fd, 0, "create tmp"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
fp = fdopen(fd, "w");
|
||||||
|
if (!ASSERT_NEQ(fp, NULL, "fdopen tmp")) {
|
||||||
|
close(fd);
|
||||||
|
goto out_remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "# comment\n");
|
||||||
|
fprintf(fp, " test_with_spaces \n");
|
||||||
|
fprintf(fp, "testA/subtest # comment\n");
|
||||||
|
fprintf(fp, "testB#comment with no space\n");
|
||||||
|
fprintf(fp, "testB # duplicate\n");
|
||||||
|
fprintf(fp, "testA/subtest # subtest duplicate\n");
|
||||||
|
fprintf(fp, "testA/subtest2\n");
|
||||||
|
fprintf(fp, "testC_no_eof_newline");
|
||||||
|
fflush(fp);
|
||||||
|
|
||||||
|
if (!ASSERT_OK(ferror(fp), "prepare tmp"))
|
||||||
|
goto out_fclose;
|
||||||
|
|
||||||
|
init_test_filter_set(&set);
|
||||||
|
|
||||||
|
ASSERT_OK(parse_test_list_file(tmpfile, &set, true), "parse file");
|
||||||
|
|
||||||
|
ASSERT_EQ(set.cnt, 4, "test count");
|
||||||
|
ASSERT_OK(strcmp("test_with_spaces", set.tests[0].name), "test 0 name");
|
||||||
|
ASSERT_EQ(set.tests[0].subtest_cnt, 0, "test 0 subtest count");
|
||||||
|
ASSERT_OK(strcmp("testA", set.tests[1].name), "test 1 name");
|
||||||
|
ASSERT_EQ(set.tests[1].subtest_cnt, 2, "test 1 subtest count");
|
||||||
|
ASSERT_OK(strcmp("subtest", set.tests[1].subtests[0]), "test 1 subtest 0");
|
||||||
|
ASSERT_OK(strcmp("subtest2", set.tests[1].subtests[1]), "test 1 subtest 1");
|
||||||
|
ASSERT_OK(strcmp("testB", set.tests[2].name), "test 2 name");
|
||||||
|
ASSERT_OK(strcmp("testC_no_eof_newline", set.tests[3].name), "test 3 name");
|
||||||
|
|
||||||
|
free_test_filter_set(&set);
|
||||||
|
|
||||||
|
out_fclose:
|
||||||
|
fclose(fp);
|
||||||
|
out_remove:
|
||||||
|
remove(tmpfile);
|
||||||
|
}
|
||||||
|
|
||||||
void test_arg_parsing(void)
|
void test_arg_parsing(void)
|
||||||
{
|
{
|
||||||
if (test__start_subtest("test_parse_test_list"))
|
if (test__start_subtest("test_parse_test_list"))
|
||||||
test_parse_test_list();
|
test_parse_test_list();
|
||||||
|
if (test__start_subtest("test_parse_test_list_file"))
|
||||||
|
test_parse_test_list_file();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -714,7 +714,13 @@ static struct test_state test_states[ARRAY_SIZE(prog_test_defs)];
|
|||||||
|
|
||||||
const char *argp_program_version = "test_progs 0.1";
|
const char *argp_program_version = "test_progs 0.1";
|
||||||
const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
|
const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
|
||||||
static const char argp_program_doc[] = "BPF selftests test runner";
|
static const char argp_program_doc[] =
|
||||||
|
"BPF selftests test runner\v"
|
||||||
|
"Options accepting the NAMES parameter take either a comma-separated list\n"
|
||||||
|
"of test names, or a filename prefixed with @. The file contains one name\n"
|
||||||
|
"(or wildcard pattern) per line, and comments beginning with # are ignored.\n"
|
||||||
|
"\n"
|
||||||
|
"These options can be passed repeatedly to read multiple files.\n";
|
||||||
|
|
||||||
enum ARG_KEYS {
|
enum ARG_KEYS {
|
||||||
ARG_TEST_NUM = 'n',
|
ARG_TEST_NUM = 'n',
|
||||||
@@ -797,6 +803,7 @@ extern int extra_prog_load_log_flags;
|
|||||||
static error_t parse_arg(int key, char *arg, struct argp_state *state)
|
static error_t parse_arg(int key, char *arg, struct argp_state *state)
|
||||||
{
|
{
|
||||||
struct test_env *env = state->input;
|
struct test_env *env = state->input;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ARG_TEST_NUM: {
|
case ARG_TEST_NUM: {
|
||||||
@@ -821,18 +828,28 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
|
|||||||
}
|
}
|
||||||
case ARG_TEST_NAME_GLOB_ALLOWLIST:
|
case ARG_TEST_NAME_GLOB_ALLOWLIST:
|
||||||
case ARG_TEST_NAME: {
|
case ARG_TEST_NAME: {
|
||||||
if (parse_test_list(arg,
|
if (arg[0] == '@')
|
||||||
|
err = parse_test_list_file(arg + 1,
|
||||||
&env->test_selector.whitelist,
|
&env->test_selector.whitelist,
|
||||||
key == ARG_TEST_NAME_GLOB_ALLOWLIST))
|
key == ARG_TEST_NAME_GLOB_ALLOWLIST);
|
||||||
return -ENOMEM;
|
else
|
||||||
|
err = parse_test_list(arg,
|
||||||
|
&env->test_selector.whitelist,
|
||||||
|
key == ARG_TEST_NAME_GLOB_ALLOWLIST);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ARG_TEST_NAME_GLOB_DENYLIST:
|
case ARG_TEST_NAME_GLOB_DENYLIST:
|
||||||
case ARG_TEST_NAME_BLACKLIST: {
|
case ARG_TEST_NAME_BLACKLIST: {
|
||||||
if (parse_test_list(arg,
|
if (arg[0] == '@')
|
||||||
|
err = parse_test_list_file(arg + 1,
|
||||||
&env->test_selector.blacklist,
|
&env->test_selector.blacklist,
|
||||||
key == ARG_TEST_NAME_GLOB_DENYLIST))
|
key == ARG_TEST_NAME_GLOB_DENYLIST);
|
||||||
return -ENOMEM;
|
else
|
||||||
|
err = parse_test_list(arg,
|
||||||
|
&env->test_selector.blacklist,
|
||||||
|
key == ARG_TEST_NAME_GLOB_DENYLIST);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ARG_VERIFIER_STATS:
|
case ARG_VERIFIER_STATS:
|
||||||
@@ -900,7 +917,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
|
|||||||
default:
|
default:
|
||||||
return ARGP_ERR_UNKNOWN;
|
return ARGP_ERR_UNKNOWN;
|
||||||
}
|
}
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||||
/* Copyright (C) 2019 Netronome Systems, Inc. */
|
/* Copyright (C) 2019 Netronome Systems, Inc. */
|
||||||
/* Copyright (C) 2020 Facebook, Inc. */
|
/* Copyright (C) 2020 Facebook, Inc. */
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -167,6 +168,52 @@ err:
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_test_list_file(const char *path,
|
||||||
|
struct test_filter_set *set,
|
||||||
|
bool is_glob_pattern)
|
||||||
|
{
|
||||||
|
char *buf = NULL, *capture_start, *capture_end, *scan_end;
|
||||||
|
size_t buflen = 0;
|
||||||
|
int err = 0;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
f = fopen(path, "r");
|
||||||
|
if (!f) {
|
||||||
|
err = -errno;
|
||||||
|
fprintf(stderr, "Failed to open '%s': %d\n", path, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (getline(&buf, &buflen, f) != -1) {
|
||||||
|
capture_start = buf;
|
||||||
|
|
||||||
|
while (isspace(*capture_start))
|
||||||
|
++capture_start;
|
||||||
|
|
||||||
|
capture_end = capture_start;
|
||||||
|
scan_end = capture_start;
|
||||||
|
|
||||||
|
while (*scan_end && *scan_end != '#') {
|
||||||
|
if (!isspace(*scan_end))
|
||||||
|
capture_end = scan_end;
|
||||||
|
|
||||||
|
++scan_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capture_end == capture_start)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*(++capture_end) = '\0';
|
||||||
|
|
||||||
|
err = insert_test(set, capture_start, is_glob_pattern);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int parse_test_list(const char *s,
|
int parse_test_list(const char *s,
|
||||||
struct test_filter_set *set,
|
struct test_filter_set *set,
|
||||||
bool is_glob_pattern)
|
bool is_glob_pattern)
|
||||||
|
|||||||
@@ -20,5 +20,8 @@ struct test_filter_set;
|
|||||||
int parse_test_list(const char *s,
|
int parse_test_list(const char *s,
|
||||||
struct test_filter_set *test_set,
|
struct test_filter_set *test_set,
|
||||||
bool is_glob_pattern);
|
bool is_glob_pattern);
|
||||||
|
int parse_test_list_file(const char *path,
|
||||||
|
struct test_filter_set *test_set,
|
||||||
|
bool is_glob_pattern);
|
||||||
|
|
||||||
__u64 read_perf_max_sample_freq(void);
|
__u64 read_perf_max_sample_freq(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user