From: Cyril Hrubis <chrubis@suse.cz>
To: Andrea Cervesato <andrea.cervesato@suse.de>
Cc: Linux Test Project <ltp@lists.linux.it>
Subject: Re: [LTP] [PATCH v2 1/2] metadata: add tests grouping support
Date: Fri, 19 Jun 2026 10:40:28 +0200 [thread overview]
Message-ID: <ajUAfFOaQgGJejCW@yuki.lan> (raw)
In-Reply-To: <20260618-metadata_groups-v2-1-6ab298932233@suse.com>
Hi!
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> metadata/metaparse.c | 159 +++++++++++++++++++++++++++++++++++--------
> metadata/tests/groups.c | 11 +++
> metadata/tests/groups.c.json | 12 ++++
> 3 files changed, 152 insertions(+), 30 deletions(-)
>
> diff --git a/metadata/metaparse.c b/metadata/metaparse.c
> index 561cbb9d2d54689988c9aa49d591628696bcf847..94e795c275aebda41737558d06f7d9c8c1a2f4f7 100644
> --- a/metadata/metaparse.c
> +++ b/metadata/metaparse.c
> @@ -17,6 +17,7 @@
> #include "data_storage.h"
>
> #define INCLUDE_PATH_MAX 5
> +#define GROUPS_TAG "groups"
>
> static int verbose;
> static char *cmdline_includepath[INCLUDE_PATH_MAX];
> @@ -50,7 +51,49 @@ static const char *eat_asterisk_space(const char *c)
> return c;
> }
>
> -static void multiline_comment(FILE *f, struct data_node *doc)
> +/*
> + * Add a group to the groups array, skipping 'kernel' as it's too generic.
> + * Returns 0 if no group was added, 1 otherwise.
> + */
> +static int add_group(struct data_node *groups, const char *name)
> +{
> + if (name && strcmp(name, "kernel")) {
> + data_node_array_add(groups, data_node_string(name));
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Parse a '@groups foo bar baz' doc comment line, adding each
> + * whitespace-separated name to the groups array. Returns 1 if the line
> + * was a @groups tag (and should be consumed), 0 otherwise.
> + */
> +static int parse_groups(struct data_node *groups, char *line)
> +{
> + char *s;
> + char *name;
> +
> + if (!groups)
> + return 0;
> +
> + s = (char *)eat_asterisk_space(line);
It's way cleaner to drop the const from the eat_asterisk_space() fuction
than this ugly cast.
> + if (strncmp(s, GROUPS_TAG, sizeof(GROUPS_TAG) - 1))
> + return 0;
> +
> + s += sizeof(GROUPS_TAG) - 1;
> + if (*s && *s != ' ' && *s != '\t')
> + return 0;
I guess that we should WARN() here about empty groups tag and return 1
so that it's "consumed".
> + for (name = strtok(s, " \t"); name; name = strtok(NULL, " \t"))
> + add_group(groups, name);
> +
> + return 1;
> +}
> +
> +static void multiline_comment(FILE *f, struct data_node *doc,
> + struct data_node *groups)
> {
> int c;
> int state = 0;
> @@ -62,12 +105,14 @@ static void multiline_comment(FILE *f, struct data_node *doc)
>
> if (doc) {
> if (c == '\n') {
> - struct data_node *line;
> + char *str;
> buf[bufp] = 0;
> - line = data_node_string(eat_asterisk_space(buf));
> - if (data_node_array_add(doc, line))
> - WARN("doc string comment truncated");
> + str = (char *)eat_asterisk_space(buf);
> bufp = 0;
> + if (parse_groups(groups, str))
> + continue;
> + if (data_node_array_add(doc, data_node_string(str)))
> + WARN("doc string comment truncated");
> continue;
> }
>
> @@ -100,7 +145,8 @@ static void multiline_comment(FILE *f, struct data_node *doc)
>
> static const char doc_prefix[] = "\\\n";
>
> -static void maybe_doc_comment(FILE *f, struct data_node *doc)
> +static void maybe_doc_comment(FILE *f, struct data_node *doc,
> + struct data_node *groups)
> {
> int c, i;
>
> @@ -113,14 +159,15 @@ static void maybe_doc_comment(FILE *f, struct data_node *doc)
> if (c == '*')
> ungetc(c, f);
>
> - multiline_comment(f, NULL);
> + multiline_comment(f, NULL, NULL);
> return;
> }
>
> - multiline_comment(f, doc);
> + multiline_comment(f, doc, groups);
> }
>
> -static void maybe_comment(FILE *f, struct data_node *doc)
> +static void maybe_comment(FILE *f, struct data_node *doc,
> + struct data_node *groups)
> {
> int c = getc(f);
>
> @@ -129,7 +176,7 @@ static void maybe_comment(FILE *f, struct data_node *doc)
> remove_to_newline(f);
> break;
> case '*':
> - maybe_doc_comment(f, doc);
> + maybe_doc_comment(f, doc, groups);
> break;
> default:
> ungetc(c, f);
> @@ -137,7 +184,8 @@ static void maybe_comment(FILE *f, struct data_node *doc)
> }
> }
>
> -static char *next_token2(FILE *f, char *buf, size_t buf_len, struct data_node *doc)
> +static char *next_token2(FILE *f, char *buf, size_t buf_len,
> + struct data_node *doc, struct data_node *groups)
> {
> size_t i = 0;
> int c;
> @@ -194,7 +242,7 @@ static char *next_token2(FILE *f, char *buf, size_t buf_len, struct data_node *d
> buf[i++] = c;
> break;
> case '/':
> - maybe_comment(f, doc);
> + maybe_comment(f, doc, groups);
> break;
> case '"':
> in_str = 1;
> @@ -216,11 +264,11 @@ exit:
> return buf;
> }
>
> -static char *next_token(FILE *f, struct data_node *doc)
> +static char *next_token(FILE *f, struct data_node *doc, struct data_node *groups)
> {
> static char buf[4096];
>
> - return next_token2(f, buf, sizeof(buf), doc);
> + return next_token2(f, buf, sizeof(buf), doc, groups);
> }
>
> static FILE *open_file(const char *dir, const char *fname)
> @@ -383,7 +431,7 @@ static int array_is_hash(FILE *f)
> int in_id = 1;
> char *token;
>
> - while ((token = next_token(f, NULL))) {
> + while ((token = next_token(f, NULL, NULL))) {
>
> if (!strcmp(token, "}")) {
> if (in_id && !comma_last)
> @@ -402,7 +450,7 @@ static int array_is_hash(FILE *f)
> int level = 1;
>
> for (;;) {
> - token = next_token(f, NULL);
> + token = next_token(f, NULL, NULL);
>
> if (!token)
> goto ret;
> @@ -453,7 +501,7 @@ static int parse_array(FILE *f, const char *arr_id, struct data_node **ret)
> *ret = data_node_array();
>
> for (;;) {
> - if (!(token = next_token(f, NULL)))
> + if (!(token = next_token(f, NULL, NULL)))
> return 1;
>
> if (!strcmp(token, "{")) {
> @@ -529,14 +577,14 @@ static int parse_get_array_len(FILE *f)
> const char *token;
> int cnt = 0, depth = 0, prev_comma = 0;
>
> - if (!(token = next_token(f, NULL)))
> + if (!(token = next_token(f, NULL, NULL)))
> return 0;
>
> if (strcmp(token, "{"))
> return 0;
>
> for (;;) {
> - if (!(token = next_token(f, NULL)))
> + if (!(token = next_token(f, NULL, NULL)))
> return 0;
>
> if (!strcmp(token, "{"))
> @@ -565,7 +613,7 @@ static void look_for_array_size(FILE *f, const char *arr_id, struct data_node **
> int prev_buf = 1;
>
> for (;;) {
> - if (!(token = next_token2(f, buf[cur_buf], sizeof(buf[cur_buf]), NULL)))
> + if (!(token = next_token2(f, buf[cur_buf], sizeof(buf[cur_buf]), NULL, NULL)))
> break;
>
> if (!strcmp(token, "=") && !strcmp(buf[prev_buf], arr_id)) {
> @@ -595,13 +643,13 @@ static int parse_array_size(FILE *f, struct data_node **res)
>
> *res = NULL;
>
> - if (!(token = next_token(f, NULL)))
> + if (!(token = next_token(f, NULL, NULL)))
> return 1;
>
> if (strcmp(token, "("))
> return 1;
>
> - if (!(token = next_token(f, NULL)))
> + if (!(token = next_token(f, NULL, NULL)))
> return 1;
>
> arr_id = strdup(token);
> @@ -621,7 +669,7 @@ static int parse_array_size(FILE *f, struct data_node **res)
> rewind(f);
>
> for (;;) {
> - if (!(token = next_token(f, NULL)))
> + if (!(token = next_token(f, NULL, NULL)))
> break;
>
> if (token[0] == '#') {
> @@ -654,7 +702,8 @@ static int parse_array_size(FILE *f, struct data_node **res)
> return 0;
> }
>
> -static int parse_test_struct(FILE *f, struct data_node *doc, struct data_node *node)
> +static int parse_test_struct(FILE *f, struct data_node *doc,
> + struct data_node *groups, struct data_node *node)
> {
> char *token;
> char *id = NULL;
> @@ -662,7 +711,7 @@ static int parse_test_struct(FILE *f, struct data_node *doc, struct data_node *n
> struct data_node *ret;
>
> for (;;) {
> - if (!(token = next_token(f, doc)))
> + if (!(token = next_token(f, doc, groups)))
> return 1;
>
> if (!strcmp(token, "}"))
> @@ -842,7 +891,7 @@ static void parse_include_macros(FILE *f, int level)
> if (!inc)
> return;
>
> - while ((token = next_token(inc, NULL))) {
> + while ((token = next_token(inc, NULL, NULL))) {
> if (token[0] == '#') {
> hash = 1;
> continue;
> @@ -907,6 +956,47 @@ static void load_internal_macros(void)
> fprintf(stderr, "END PREDEFINED MACROS\n");
> }
>
> +/*
> + * Add groups derived from the source file path.
> + *
> + * Groups are the two nearest parent directories (immediate parent
> + * first), skipping 'kernel' as it's too generic:
> + *
> + * testcases/kernel/syscalls/clone/clone01.c -> clone, syscalls
> + * testcases/kernel/kvm/kvm_pagefault01.c -> kvm
> + * testcases/cve/cve-2017-16939.c -> cve
> + */
> +static void add_path_groups(struct data_node *groups, const char *fname)
> +{
> + char *buf;
> + char *dirs[8];
> + int ndirs = 0;
> + char *p;
> +
> + if (strncmp(fname, "testcases/", 10))
> + return;
> +
> + buf = strdup(fname + 10);
> + if (!buf)
> + return;
We should do fprintf(stderr, "Allocation failed!\n"); exit(1); here
instead of silently dropping the tags if the strdup failed.
Maybe we should add ERR() macro that does print a message and calls
exit(1) since we have that pattern in many places, but that is a
completely unrelated refactor.
> + p = strtok(buf, "/");
> + while (p && ndirs < 8) {
> + dirs[ndirs++] = p;
> + p = strtok(NULL, "/");
> + }
> +
> + /* Last element is the filename, skip it */
> + ndirs--;
> +
> + if (ndirs >= 1)
> + add_group(groups, dirs[ndirs - 1]);
> + if (ndirs >= 2)
> + add_group(groups, dirs[ndirs - 2]);
> +
> + free(buf);
> +}
> +
> static struct data_node *parse_file(const char *fname)
> {
> int state = 0, found = 0;
> @@ -923,15 +1013,18 @@ static struct data_node *parse_file(const char *fname)
>
> struct data_node *res = data_node_hash();
> struct data_node *doc = data_node_array();
> + struct data_node *groups = data_node_array();
> +
> + add_path_groups(groups, fname);
>
> load_internal_macros();
>
> - while ((token = next_token(f, doc))) {
> + while ((token = next_token(f, doc, groups))) {
> if (state < 6 && !strcmp(tokens[state], token)) {
> state++;
> } else {
> if (token[0] == '#') {
> - token = next_token(f, doc);
> + token = next_token(f, doc, groups);
> if (token) {
> if (!strcmp(token, "define"))
> parse_macro(f);
> @@ -948,7 +1041,7 @@ static struct data_node *parse_file(const char *fname)
> continue;
>
> found = 1;
> - parse_test_struct(f, doc, res);
> + parse_test_struct(f, doc, groups, res);
> }
>
> if (data_node_array_len(doc)) {
> @@ -958,6 +1051,11 @@ static struct data_node *parse_file(const char *fname)
> data_node_free(doc);
> }
>
> + if (data_node_array_len(groups))
> + data_node_hash_add(res, "groups", groups);
> + else
> + data_node_free(groups);
The fuction that adds groups from path will add at least one group, so
this can be just data_node_hash_add(res, "groups", groups).
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-06-19 8:40 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-18 14:10 [LTP] [PATCH v2 0/2] Support metadata groups Andrea Cervesato
2026-06-18 14:10 ` [LTP] [PATCH v2 1/2] metadata: add tests grouping support Andrea Cervesato
2026-06-18 16:40 ` [LTP] " linuxtestproject.agent
2026-06-19 8:40 ` Cyril Hrubis [this message]
2026-06-18 14:10 ` [LTP] [PATCH v2 2/2] doc: conf.py: Show groups in test catalog Andrea Cervesato
2026-06-19 8:44 ` Cyril Hrubis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ajUAfFOaQgGJejCW@yuki.lan \
--to=chrubis@suse.cz \
--cc=andrea.cervesato@suse.de \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.