Re: [igt-dev] [PATCH] runner: add --list-all and --blacklist

Petri Latvala
2019-06-14T14:28:41Z

On Fri, Jun 14, 2019 at 04:24:29PM +0300, Oleg Vasilev wrote:
> Currently, runner already collects all subtest names into job_list.
> --list-all allows to output it to stdout.
>
> --blacklist option takes a filename as an argument and adds all regexes
> from that file to the exclusion list.
>
> Cc: Petri Latvala <petri.latvala@intel.com>
> Signed-off-by: Oleg Vasilev <oleg.vasilev@intel.com>
> ---
> runner/job_list.c | 18 +++++++
> runner/job_list.h | 1 +
> runner/runner.c | 5 ++
> runner/runner_tests.c | 14 ++++-
> runner/settings.c | 118 ++++++++++++++++++++++++++++++++++--------
> runner/settings.h | 1 +
> 6 files changed, 135 insertions(+), 22 deletions(-)
>
> diff --git a/runner/job_list.c b/runner/job_list.c
> index 4a16742f..2a50dc5c 100644
> --- a/runner/job_list.c
> +++ b/runner/job_list.c
> @@ -291,6 +291,24 @@ static bool job_list_from_test_list(struct job_list *job_list,
> return any;
> }
>
> +void list_all_tests(struct job_list* lst)
> +{
> + for (size_t test_idx = 0; test_idx < lst->size; ++test_idx){
> + struct job_list_entry* current_entry = lst->entries+test_idx;
> + if (current_entry->subtest_count==0) {
> + printf("igt@%s\n", current_entry->binary);
> + continue;
> + }
> + for (size_t subtest_idx = 0;
> + subtest_idx < current_entry->subtest_count;
> + ++subtest_idx) {
> + char *subtest_name = current_entry->subtests[subtest_idx];
> + printf("igt@%s@%s\n", current_entry->binary, subtest_name);
> + }
> + }
> +}
> +
> +
> static char *lowercase(const char *str)
> {
> char *ret = malloc(strlen(str) + 1);
> diff --git a/runner/job_list.h b/runner/job_list.h
> index f8bbbddc..cee4bff6 100644
> --- a/runner/job_list.h
> +++ b/runner/job_list.h
> @@ -36,5 +36,6 @@ bool create_job_list(struct job_list *job_list, struct settings *settings);
>
> bool serialize_job_list(struct job_list *job_list, struct settings *settings);
> bool read_job_list(struct job_list *job_list, int dirfd);
> +void list_all_tests(struct job_list* lst);
>
> #endif
> diff --git a/runner/runner.c b/runner/runner.c
> index e1a6ccba..909ec861 100644
> --- a/runner/runner.c
> +++ b/runner/runner.c
> @@ -24,6 +24,11 @@ int main(int argc, char **argv)
> return 1;
> }
>
> + if(settings.list_all) {

Add space after if

> + list_all_tests(&job_list);
> + return 0;
> + }
> +
> if (!initialize_execute_state(&state, &settings, &job_list)) {
> return 1;
> }
> diff --git a/runner/runner_tests.c b/runner/runner_tests.c
> index c09cda70..00bb9de3 100644
> --- a/runner/runner_tests.c
> +++ b/runner/runner_tests.c
> @@ -388,6 +388,8 @@ igt_main
> "-t", "pattern2",
> "-x", "xpattern1",
> "-x", "xpattern2",
> + "-b", "../runner/testdata/test-blacklist.txt",
> + "--blacklist", "../runner/testdata/test-blacklist2.txt",

Don't use relative paths, you don't know what the CWD is.

Use the variable testdatadir to find runner/testdata. And remember to git add those files.

> "-s",
> "-l", "verbose",
> "--overwrite",
> @@ -410,9 +412,11 @@ igt_main
> igt_assert_eq(settings->include_regexes.size, 2);
> igt_assert_eqstr(settings->include_regexes.regex_strings[0], "pattern1");
> igt_assert_eqstr(settings->include_regexes.regex_strings[1], "pattern2");
> - igt_assert_eq(settings->exclude_regexes.size, 2);
> + igt_assert_eq(settings->exclude_regexes.size, 4);
> igt_assert_eqstr(settings->exclude_regexes.regex_strings[0], "xpattern1");
> igt_assert_eqstr(settings->exclude_regexes.regex_strings[1], "xpattern2");
> + igt_assert_eqstr(settings->exclude_regexes.regex_strings[2], "xpattern3"); /* From blacklist */
> + igt_assert_eqstr(settings->exclude_regexes.regex_strings[3], "xpattern4"); /* From blacklist2 */
> igt_assert(settings->sync);
> igt_assert_eq(settings->log_level, LOG_LEVEL_VERBOSE);
> igt_assert(settings->overwrite);
> @@ -426,6 +430,14 @@ igt_main
> igt_assert(settings->piglit_style_dmesg);
> igt_assert_eq(settings->dmesg_warn_level, 3);
> }
> + igt_subtest("parse-list-all") {
> + const char *argv[] = { "runner",
> + "--list-all",
> + "test-root-dir"};
> +
> + igt_assert(parse_options(ARRAY_SIZE(argv), (char**)argv, settings));
> + igt_assert_eq(settings->list_all, 1);
> + }
>
> igt_subtest("dmesg-warn-level-inferred") {
> const char *argv[] = { "runner",
> diff --git a/runner/settings.c b/runner/settings.c
> index ad38ae8d..2692370f 100644
> --- a/runner/settings.c
> +++ b/runner/settings.c
> @@ -1,5 +1,6 @@
> #include "settings.h"
>
> +#include <ctype.h>
> #include <errno.h>
> #include <fcntl.h>
> #include <getopt.h>
> @@ -30,6 +31,8 @@ enum {
> OPT_MULTIPLE = 'm',
> OPT_TIMEOUT = 'c',
> OPT_WATCHDOG = 'g',
> + OPT_BLACKLIST = 'b',
> + OPT_LIST_ALL = 'L',
> };
>
> static struct {
> @@ -117,7 +120,8 @@ static bool parse_abort_conditions(struct settings *settings, const char *optarg
> }
>
> static const char *usage_str =
> - "usage: runner [options] [test_root] results-path\n\n"
> + "usage: runner [options] [test_root] results-path\n"
> + " or: runner --list-all [options] [test_root] \n\n"
> "Options:\n"
> " Piglit compatible:\n"
> " -h, --help Show this help message and exit\n"
> @@ -172,6 +176,10 @@ static const char *usage_str =
> " (longer) filter list means the test result should\n"
> " change. KERN_NOTICE dmesg level is treated as warn,\n"
> " unless overridden with --dmesg-warn-level.\n"
> + " -b, --blacklist FILENAME\n"
> + " Exclude all test matching to regexes from FILENAME\n"
> + " (can be used more than once)\n"
> + " -L, --list-all List all matching subtests instead of running\n"
> " [test_root] Directory that contains the IGT tests. The environment\n"
> " variable IGT_TEST_ROOT will be used if set, overriding\n"
> " this option if given.\n"
> @@ -213,6 +221,51 @@ static bool add_regex(struct regex_list *list, char *new)
> return true;
> }
>
> +static bool parse_blacklist(struct regex_list *exclude_regexes, char *blacklist_filename)
> +{
> + FILE *f;
> + char *line = NULL;
> + size_t line_len = 0;
> + bool status;
> +
> +

You have an extra newline here.

> + if ((f = fopen(blacklist_filename, "r")) == NULL) {
> + fprintf(stderr, "Cannot open blacklist file %s\n", blacklist_filename);
> + return false;
> + }
> + while (1) {
> + size_t str_size = 0, idx = 0;
> +
> + if (getline(&line, &line_len, f) == -1) {
> + if (errno == EINTR)
> + continue;
> + else
> + break;
> + }
> +
> + while (true) {
> + if(line[idx]=='\n' ||
> + line[idx]=='#' || /* # starts a comment */
> + line[idx]=='\0')
> + break;
> + idx++;
> + if (!isspace(line[str_size]))
> + str_size = idx;
> + }
> + if (str_size > 0) {
> + char * test_regex = strndup(line, str_size);
> + status = add_regex(exclude_regexes, test_regex);
> + if(!status){
> + break;
> + }
> + }

There's something in this parsing code that feels funky but as long as
it works (more on that later)...

> + }
> +
> + free(line);
> + fclose(f);
> + return status;
> +}
> +
> static void free_regexes(struct regex_list *regexes)
> {
> size_t i;
> @@ -272,6 +325,8 @@ bool parse_options(int argc, char **argv,
> {"use-watchdog", no_argument, NULL, OPT_WATCHDOG},
> {"piglit-style-dmesg", no_argument, NULL, OPT_PIGLIT_DMESG},
> {"dmesg-warn-level", required_argument, NULL, OPT_DMESG_WARN_LEVEL},
> + {"blacklist", required_argument, NULL, OPT_BLACKLIST},
> + {"list-all", no_argument, NULL, OPT_LIST_ALL},
> { 0, 0, 0, 0},
> };
>
> @@ -281,7 +336,7 @@ bool parse_options(int argc, char **argv,
>
> settings->dmesg_warn_level = -1;
>
> - while ((c = getopt_long(argc, argv, "hn:dt:x:sl:om", long_options, NULL)) != -1) {
> + while ((c = getopt_long(argc, argv, "hn:dt:x:sl:omb:L", long_options, NULL)) != -1) {
> switch (c) {
> case OPT_HELP:
> usage(NULL, stdout);
> @@ -342,6 +397,13 @@ bool parse_options(int argc, char **argv,
> case OPT_DMESG_WARN_LEVEL:
> settings->dmesg_warn_level = atoi(optarg);
> break;
> + case OPT_BLACKLIST:
> + if (!parse_blacklist(&settings->exclude_regexes, absolute_path(optarg)))
> + goto error;
> + break;
> + case OPT_LIST_ALL:
> + settings->list_all = true;
> + break;
> case '?':
> usage(NULL, stderr);
> goto error;
> @@ -354,20 +416,39 @@ bool parse_options(int argc, char **argv,
> if (settings->dmesg_warn_level < 0)
> settings->dmesg_warn_level = 4; /* KERN_WARN */
>
> - switch (argc - optind) {
> - case 2:
> - settings->test_root = absolute_path(argv[optind]);
> - ++optind;
> - /* fallthrough */
> - case 1:
> - settings->results_path = absolute_path(argv[optind]);
> - break;
> - case 0:
> - usage("Results-path missing", stderr);
> - goto error;
> - default:
> - usage("Extra arguments after results-path", stderr);
> - goto error;
> + if (settings->list_all) { /* --list-all doesn't requrie results path */

Typo, require

> + switch (argc - optind) {
> + case 1:
> + settings->test_root = absolute_path(argv[optind]);
> + ++optind;
> + /* fallthrough */
> + case 0:
> + break;
> + default:
> + usage("Too many arguments for --list-all", stderr);
> + goto error;
> + }
> + } else {
> + switch (argc - optind) {
> + case 2:
> + settings->test_root = absolute_path(argv[optind]);
> + ++optind;
> + /* fallthrough */
> + case 1:
> + settings->results_path = absolute_path(argv[optind]);
> + break;
> + case 0:
> + usage("Results-path missing", stderr);
> + goto error;
> + default:
> + usage("Extra arguments after results-path", stderr);
> + goto error;
> + }
> + if (!settings->name) {
> + char *name = strdup(settings->results_path);
> + settings->name = strdup(basename(name));
> + free(name);
> + }

TODO for later: Refactor this duplicated switch.

Looks good otherwise, but nack for merging as of yet, because:

While performance is better:

# time ./scripts/run-tests.sh -l
.......
real 0m5.188s
user 0m3.890s
sys 0m1.354s

# time build/runner/igt_runner -L build/tests
.......
real 0m3.065s
user 0m2.222s
sys 0m0.895s

(I won't compare listing with blacklist processing, that's a horrible
shell kludge)

the results are different:

original-kludge.txt - blacklist-processed testlist with horrible shell
kludge

runner.txt - igt_runner -L -b tests/intel-ci/blacklist.txt
build/tests, sorted and downcased to compare apples to apples

diff -u original-kludge.txt runner.txt

+igt@gem_exec_lut_handle
+igt@gem_fd_exhaustion
+igt@gem_gtt_hog
+igt@gem_gtt_speed
+igt@gem_lut_handle
+igt@gem_ring_sync_loop

Those are blacklisted with e.g.

igt@gem_exec_lut_handle(@.*)?

Please check whether the fault is glib regex being unable to process
that or our usage of it.

--
Petri Latvala