selftests/bpf: Support glob matching for test selector.
authorYucong Sun <fallentree@fb.com>
Tue, 17 Aug 2021 04:47:32 +0000 (21:47 -0700)
committerAndrii Nakryiko <andrii@kernel.org>
Tue, 17 Aug 2021 18:31:29 +0000 (11:31 -0700)
This patch adds '-a' and '-d' arguments supporting both exact string match as
well as using '*' wildcard in test/subtests selection. '-a' and '-t' can
co-exists, same as '-d' and '-b', in which case they just add to the list of
allowed or denied test selectors.

Caveat: Same as the current substring matching mechanism, test and subtest
selector applies independently, 'a*/b*' will execute all tests matching "a*",
and with subtest name matching "b*", but tests matching "a*" that has no
subtests will also be executed.

Signed-off-by: Yucong Sun <fallentree@fb.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20210817044732.3263066-5-fallentree@fb.com
tools/testing/selftests/bpf/test_progs.c

index 90539b1..cc1cd24 100644 (file)
 #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;
        }
 
@@ -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;
 
-               strs[cnt] = strdup(next);
-               if (!strs[cnt])
-                       goto err;
+               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;
        }