All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH v3 0/2] Support metadata groups
@ 2026-06-22  8:53 Andrea Cervesato
  2026-06-22  8:53 ` [LTP] [PATCH v3 1/2] metadata: add tests grouping support Andrea Cervesato
  2026-06-22  8:53 ` [LTP] [PATCH v3 2/2] doc: conf.py: Show groups in test catalog Andrea Cervesato
  0 siblings, 2 replies; 11+ messages in thread
From: Andrea Cervesato @ 2026-06-22  8:53 UTC (permalink / raw)
  To: Linux Test Project

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
Changes in v3:
- match @groups not groups
- drop const qualifier in eat_asterisk_space()
- WARN() when empty groups is provided
- error on strdup() failure
- we always add 1 group, so always add items to the hash
- Link to v2: https://lore.kernel.org/r/20260618-metadata_groups-v2-0-6ab298932233@suse.com

Changes in v2:
- switch from @group to @groups
- use Cyril snippet to folter out groups from description
- optimize code
- Link to v1: https://lore.kernel.org/r/20260612-metadata_groups-v1-0-d7a24a7ac7a8@suse.com

---
Andrea Cervesato (2):
      metadata: add tests grouping support
      doc: conf.py: Show groups in test catalog

 doc/conf.py                           |  10 +++
 metadata/metaparse.c                  | 163 +++++++++++++++++++++++++++-------
 metadata/tests/array_size01.c.json    |   2 +
 metadata/tests/array_size02.c.json    |   2 +
 metadata/tests/array_size03.c.json    |   2 +
 metadata/tests/array_size04.c.json    |   2 +
 metadata/tests/empty_struct.c.json    |   2 +
 metadata/tests/expand_flags.c.json    |   2 +
 metadata/tests/groups.c               |  11 +++
 metadata/tests/groups.c.json          |  12 +++
 metadata/tests/include.c.json         |   2 +
 metadata/tests/macro.c.json           |   2 +
 metadata/tests/macro_str.c.json       |   2 +
 metadata/tests/multiline_macro.c.json |   2 +
 metadata/tests/tags.c.json            |   2 +
 15 files changed, 187 insertions(+), 31 deletions(-)
---
base-commit: bc9cfca5c7b6599e501d580bb42b9538390fd9a6
change-id: 20260612-metadata_groups-dd2430e21901

Best regards,
-- 
Andrea Cervesato <andrea.cervesato@suse.com>


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 11+ messages in thread
* [LTP] [PATCH v2 1/2] metadata: add tests grouping support
@ 2026-06-18 14:10 Andrea Cervesato
  2026-06-18 16:40 ` [LTP] " linuxtestproject.agent
  0 siblings, 1 reply; 11+ messages in thread
From: Andrea Cervesato @ 2026-06-18 14:10 UTC (permalink / raw)
  To: Linux Test Project

From: Andrea Cervesato <andrea.cervesato@suse.com>

Add groups field to metaparse JSON output, so we can filter out tests
in kirk. Groups are derived from:

1. Source file path - the two nearest parent directories (immediate
   parent first), skipping 'kernel' as too generic. For example:
   - testcases/kernel/syscalls/clone/clone01.c -> clone, syscalls
   - testcases/kernel/kvm/kvm_pagefault01.c -> kvm
   - testcases/cve/cve-2017-16939.c -> cve

2. @groups tags in the doc comment block, e.g.:
   /*\
    * Test description.
    *
    * @groups stress syscalls
    * @groups memory
    */

Add test case for @groups tag parsing.

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);
+	if (strncmp(s, GROUPS_TAG, sizeof(GROUPS_TAG) - 1))
+		return 0;
+
+	s += sizeof(GROUPS_TAG) - 1;
+	if (*s && *s != ' ' && *s != '\t')
+		return 0;
+
+	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;
+
+	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);
+
 	fclose(f);
 
 	if (!found) {
@@ -985,7 +1083,7 @@ static void parse_must_files(void)
 			if (!f)
 				continue;
 
-			while ((token = next_token(f, NULL))) {
+			while ((token = next_token(f, NULL, NULL))) {
 				if (!strcmp(token, "define"))
 					parse_macro(f);
 			}
@@ -1238,6 +1336,7 @@ int main(int argc, char *argv[])
 	}
 
 	data_node_hash_add(res, "fname", data_node_string(argv[optind]));
+
 	printf("  \"%s\": ", strip_name(argv[optind]));
 	data_to_json(res, stdout, 2);
 	data_node_free(res);
diff --git a/metadata/tests/groups.c b/metadata/tests/groups.c
new file mode 100644
index 0000000000000000000000000000000000000000..40032a7ef60e398c50690995fc68025575d7f684
--- /dev/null
+++ b/metadata/tests/groups.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*\
+ * Test for @groups tag parsing.
+ *
+ * @groups stress regression
+ * @groups smoke
+ */
+
+static struct tst_test test = {
+};
diff --git a/metadata/tests/groups.c.json b/metadata/tests/groups.c.json
new file mode 100644
index 0000000000000000000000000000000000000000..2ca8b5d21d980cdffae4bdaeea20b659a52bad8a
--- /dev/null
+++ b/metadata/tests/groups.c.json
@@ -0,0 +1,12 @@
+  "groups": {
+   "doc": [
+    "Test for @groups tag parsing.",
+    ""
+   ],
+   "groups": [
+    "stress",
+    "regression",
+    "smoke"
+   ],
+   "fname": "groups.c"
+  }
\ No newline at end of file

-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 11+ messages in thread
* [LTP] [PATCH 1/2] metadata: add tests grouping support
@ 2026-06-12 13:54 Andrea Cervesato
  2026-06-12 19:10 ` [LTP] " linuxtestproject.agent
  0 siblings, 1 reply; 11+ messages in thread
From: Andrea Cervesato @ 2026-06-12 13:54 UTC (permalink / raw)
  To: Linux Test Project

From: Andrea Cervesato <andrea.cervesato@suse.com>

Add groups field to metaparse JSON output, so we can filter out tests
in kirk. Groups are derived from:

1. Source file path - the two nearest parent directories (immediate
   parent first), skipping 'kernel' as too generic. For example:
   - testcases/kernel/syscalls/clone/clone01.c -> clone, syscalls
   - testcases/kernel/kvm/kvm_pagefault01.c -> kvm
   - testcases/cve/cve-2017-16939.c -> cve

2. @group tags in the doc comment block, e.g.:
   /*    * Test description.
    *
    * @group stress
    */

Add test case for @group tag parsing.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
 metadata/metaparse.c         | 88 ++++++++++++++++++++++++++++++++++++++++++++
 metadata/tests/groups.c      | 11 ++++++
 metadata/tests/groups.c.json | 13 +++++++
 3 files changed, 112 insertions(+)

diff --git a/metadata/metaparse.c b/metadata/metaparse.c
index 561cbb9d2d54689988c9aa49d591628696bcf847..6bc4b7af60c7449d4b60a1252fa58fed77e03066 100644
--- a/metadata/metaparse.c
+++ b/metadata/metaparse.c
@@ -1168,6 +1168,92 @@ static void print_help(const char *prgname)
 	exit(0);
 }
 
+/*
+ * 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[256];
+	int offsets[8];
+	int ndirs = 0;
+	int ngroups = 0;
+	char *p;
+
+	if (strncmp(fname, "testcases/", 10))
+		return;
+
+	snprintf(buf, sizeof(buf), "%s", fname + 10);
+
+	p = strtok(buf, "/");
+	while (p && ndirs < 8) {
+		offsets[ndirs++] = p - buf;
+		p = strtok(NULL, "/");
+	}
+
+	/* Last element is the filename, skip it */
+	ndirs--;
+
+	for (int j = ndirs - 1; j >= 0 && ngroups < 2; j--) {
+		if (!strcmp(buf + offsets[j], "kernel"))
+			continue;
+
+		data_node_array_add(groups, data_node_string(buf + offsets[j]));
+		ngroups++;
+	}
+}
+
+/*
+ * Add groups from @group tags in the doc comment block.
+ */
+static void add_doc_groups(struct data_node *groups, struct data_node *doc)
+{
+	if (!doc || doc->type != DATA_ARRAY)
+		return;
+
+	for (unsigned int i = 0; i < data_node_array_len(doc); i++) {
+		struct data_node *line = doc->array.array[i];
+		const char *s;
+
+		if (line->type != DATA_STRING)
+			continue;
+
+		s = line->string.val;
+
+		while (*s && (*s == ' ' || *s == '\t'))
+			s++;
+
+		if (strncmp(s, "@group ", 7))
+			continue;
+
+		s += 7;
+		while (*s && (*s == ' ' || *s == '\t'))
+			s++;
+
+		if (*s)
+			data_node_array_add(groups, data_node_string(s));
+	}
+}
+
+static void build_groups(struct data_node *res, const char *fname)
+{
+	struct data_node *groups = data_node_array();
+
+	add_path_groups(groups, fname);
+	add_doc_groups(groups, data_node_hash_get(res, "doc"));
+
+	if (data_node_array_len(groups))
+		data_node_hash_add(res, "groups", groups);
+	else
+		data_node_free(groups);
+}
+
 int main(int argc, char *argv[])
 {
 	unsigned int i, j;
@@ -1238,6 +1324,8 @@ int main(int argc, char *argv[])
 	}
 
 	data_node_hash_add(res, "fname", data_node_string(argv[optind]));
+	build_groups(res, argv[optind]);
+
 	printf("  \"%s\": ", strip_name(argv[optind]));
 	data_to_json(res, stdout, 2);
 	data_node_free(res);
diff --git a/metadata/tests/groups.c b/metadata/tests/groups.c
new file mode 100644
index 0000000000000000000000000000000000000000..82f07111c1506c634f13822ee6aa95f574eb19a5
--- /dev/null
+++ b/metadata/tests/groups.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*\
+ * Test for @group tag parsing.
+ *
+ * @group stress
+ * @group regression
+ */
+
+static struct tst_test test = {
+};
diff --git a/metadata/tests/groups.c.json b/metadata/tests/groups.c.json
new file mode 100644
index 0000000000000000000000000000000000000000..4683e6cf07eeebc60faefb9aead9370dc3f631aa
--- /dev/null
+++ b/metadata/tests/groups.c.json
@@ -0,0 +1,13 @@
+  "groups": {
+   "doc": [
+    "Test for @group tag parsing.",
+    "",
+    "@group stress",
+    "@group regression"
+   ],
+   "fname": "groups.c",
+   "groups": [
+    "stress",
+    "regression"
+   ]
+  }
\ No newline at end of file

-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-06-22 12:08 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-22  8:53 [LTP] [PATCH v3 0/2] Support metadata groups Andrea Cervesato
2026-06-22  8:53 ` [LTP] [PATCH v3 1/2] metadata: add tests grouping support Andrea Cervesato
2026-06-22  9:57   ` [LTP] " linuxtestproject.agent
2026-06-22  9:59   ` [LTP] [PATCH v3 1/2] " Cyril Hrubis
2026-06-22 10:48     ` Andrea Cervesato via ltp
2026-06-22 11:15     ` Cyril Hrubis
2026-06-22 11:59       ` Andrea Cervesato via ltp
2026-06-22 12:08         ` Cyril Hrubis
2026-06-22  8:53 ` [LTP] [PATCH v3 2/2] doc: conf.py: Show groups in test catalog Andrea Cervesato
  -- strict thread matches above, loose matches on Subject: below --
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-12 13:54 [LTP] [PATCH 1/2] " Andrea Cervesato
2026-06-12 19:10 ` [LTP] " linuxtestproject.agent

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.