Skip to contentSkip to conversation

kunit: Add test attributes API

PATCH In review Rae Moar 5 participants 20 messages Jul 25, 2023 – Aug 30, 2023

Subsystems (6)
lore
Share & tools
lore

Rae Moar

author
#0 lore raw

v3 cover kunit: Add test attributes API

Hello everyone,

This patch series adds a test attributes framework to KUnit.

There has been interest in filtering out "slow" KUnit tests. Most notably, a new config, CONFIG_MEMCPY_SLOW_KUNIT_TEST, has been added to exclude a particularly slow memcpy test
(https://lore.kernel.org/all/[email protected]/).

This attributes framework can be used to save and access test associated data, including whether a test is slow. These attributes are reportable (via KTAP and command line output) and are also filterable.

This framework is designed to allow for the addition of other attributes in the future. These attributes could include whether the test can be run concurrently, test file path, etc.

To try out the framework I suggest running:
"./tools/testing/kunit/kunit.py run --filter speed!=slow"

This patch series was originally sent out as an RFC. Here is a link to the RFC v2:
https://lore.kernel.org/all/[email protected]/

Thanks!
Rae

Rae Moar (9):
kunit: Add test attributes API structure
kunit: Add speed attribute
kunit: Add module attribute
kunit: Add ability to filter attributes
kunit: tool: Add command line interface to filter and report
attributes
kunit: memcpy: Mark tests as slow using test attributes
kunit: time: Mark test as slow using test attributes
kunit: add tests for filtering attributes
kunit: Add documentation of KUnit test attributes

 Documentation/dev-tools/kunit/run_wrapper.rst |  12 +
 .../dev-tools/kunit/running_tips.rst          | 166 +++++++
 include/kunit/attributes.h                    |  50 +++
 include/kunit/test.h                          |  70 ++-
 kernel/time/time_test.c                       |   2 +-
 lib/Kconfig.debug                             |   3 +
 lib/kunit/Makefile                            |   3 +-
 lib/kunit/attributes.c                        | 418 ++++++++++++++++++
 lib/kunit/executor.c                          | 114 ++++-
 lib/kunit/executor_test.c                     | 128 +++++-
 lib/kunit/kunit-example-test.c                |   9 +
 lib/kunit/test.c                              |  27 +-
 lib/memcpy_kunit.c                            |   8 +-
 tools/testing/kunit/kunit.py                  |  70 ++-
 tools/testing/kunit/kunit_kernel.py           |   8 +-
 tools/testing/kunit/kunit_parser.py           |  11 +-
 tools/testing/kunit/kunit_tool_test.py        |  39 +-
 17 files changed, 1062 insertions(+), 76 deletions(-)
 create mode 100644 include/kunit/attributes.h
 create mode 100644 lib/kunit/attributes.c

base-commit: 64bd4641310c41a1ecf07c13c67bc0ed61045dfd

Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#1 lore raw

PATCH v3 1/9 kunit: Add test attributes API structure

Add the basic structure of the test attribute API to KUnit, which can be used to save and access test associated data.

Add attributes.c and attributes.h to hold associated structs and functions for the API.

Create a struct that holds a variety of associated helper functions for each test attribute. These helper functions will be used to get the attribute value, convert the value to a string, and filter based on the value. This struct is flexible by design to allow for attributes of numerous types and contexts.

Add a method to print test attributes in the format of "# [<test_name if not suite>.]<attribute_name>: <attribute_value>".

Example for a suite: "# speed: slow"

Example for a test case: "# test_case.speed: very_slow"

Use this method to report attributes in the KTAP output (KTAP spec:
https://docs.kernel.org/dev-tools/ktap.html) and _list_tests output when kernel's new kunit.action=list_attr option is used. Note this is derivative of the kunit.action=list option.

In test.h, add fields and associated helper functions to test cases and suites to hold user-inputted test attributes.

---

Changes since v2:
- No changes.
Changes since v1:
- No changes.
Changes since RFC v2:
- No major changes.
Changes since RFC v1:
- Add list_attr option to only include attribute in the _list_tests output when this module param is set
- Add printing options for attributes to print always, print only for suites, or print never.

 include/kunit/attributes.h | 19 +++++++++
 include/kunit/test.h       | 33 ++++++++++++++++
 lib/kunit/Makefile         |  3 +-
 lib/kunit/attributes.c     | 80 ++++++++++++++++++++++++++++++++++++++
 lib/kunit/executor.c       | 21 ++++++++--
 lib/kunit/test.c           | 17 ++++----
 6 files changed, 161 insertions(+), 12 deletions(-)
 create mode 100644 include/kunit/attributes.h
 create mode 100644 lib/kunit/attributes.c
include/kunit/attributes.h new file +19 -0
new file mode 100644
index 000000000000..9fcd184cce36
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * KUnit API to save and access test attributes
+ *
+ * Copyright (C) 2023, Google LLC.
+ * Author: Rae Moar <[email protected]>
+ */
+
+#ifndef _KUNIT_ATTRIBUTES_H
+#define _KUNIT_ATTRIBUTES_H
+
+/*
+ * Print all test attributes for a test case or suite.
+ * Output format for test cases: "# <test_name>.<attribute>: <value>"
+ * Output format for test suites: "# <attribute>: <value>"
+ */
+void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level);
+
+#endif /* _KUNIT_ATTRIBUTES_H */
include/kunit/test.h +33 -0
index 23120d50499e..1fc9155988e9 100644
@@ -63,12 +63,16 @@ enum kunit_status {
 	KUNIT_SKIPPED,
 };
 
+/* Holds attributes for each test case and suite */
+struct kunit_attributes {};
+
 /**
  * struct kunit_case - represents an individual test case.
  *
  * @run_case: the function representing the actual test case.
  * @name:     the name of the test case.
  * @generate_params: the generator function for parameterized tests.
+ * @attr:     the attributes associated with the test
  *
  * A test case is a function with the signature,
  * ``void (*)(struct kunit *)``
@@ -104,6 +108,7 @@ struct kunit_case {
 	void (*run_case)(struct kunit *test);
 	const char *name;
 	const void* (*generate_params)(const void *prev, char *desc);
+	struct kunit_attributes attr;
 
 	/* private: internal use only. */
 	enum kunit_status status;
@@ -133,6 +138,18 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
  */
 #define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name }
 
+/**
+ * KUNIT_CASE_ATTR - A helper for creating a &struct kunit_case
+ * with attributes
+ *
+ * @test_name: a reference to a test case function.
+ * @attributes: a reference to a struct kunit_attributes object containing
+ * test attributes
+ */
+#define KUNIT_CASE_ATTR(test_name, attributes)			\
+		{ .run_case = test_name, .name = #test_name,	\
+		  .attr = attributes }
+
 /**
  * KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case
  *
@@ -154,6 +171,20 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
 		{ .run_case = test_name, .name = #test_name,	\
 		  .generate_params = gen_params }
 
+/**
+ * KUNIT_CASE_PARAM_ATTR - A helper for creating a parameterized &struct
+ * kunit_case with attributes
+ *
+ * @test_name: a reference to a test case function.
+ * @gen_params: a reference to a parameter generator function.
+ * @attributes: a reference to a struct kunit_attributes object containing
+ * test attributes
+ */
+#define KUNIT_CASE_PARAM_ATTR(test_name, gen_params, attributes)	\
+		{ .run_case = test_name, .name = #test_name,	\
+		  .generate_params = gen_params,				\
+		  .attr = attributes }
+
 /**
  * struct kunit_suite - describes a related collection of &struct kunit_case
  *
@@ -163,6 +194,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
  * @init:	called before every test case.
  * @exit:	called after every test case.
  * @test_cases:	a null terminated array of test cases.
+ * @attr:	the attributes associated with the test suite
  *
  * A kunit_suite is a collection of related &struct kunit_case s, such that
  * @init is called before every test case and @exit is called after every
@@ -182,6 +214,7 @@ struct kunit_suite {
 	int (*init)(struct kunit *test);
 	void (*exit)(struct kunit *test);
 	struct kunit_case *test_cases;
+	struct kunit_attributes attr;
 
 	/* private: internal use only */
 	char status_comment[KUNIT_STATUS_COMMENT_SIZE];
lib/kunit/Makefile +2 -1
index cb417f504996..46f75f23dfe4 100644
@@ -6,7 +6,8 @@ kunit-objs +=				test.o \
 					string-stream.o \
 					assert.o \
 					try-catch.o \
-					executor.o
+					executor.o \
+					attributes.o
 
 ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
 kunit-objs +=				debugfs.o
lib/kunit/attributes.c new file +80 -0

80 lines not on this page · raw message

lib/kunit/executor.c +17 -4
index 74982b83707c..12e38a48a5cc 100644
@@ -2,6 +2,7 @@
 
 #include <linux/reboot.h>
 #include <kunit/test.h>
+#include <kunit/attributes.h>
 #include <linux/glob.h>
 #include <linux/moduleparam.h>
 
@@ -24,7 +25,8 @@ module_param_named(action, action_param, charp, 0);
 MODULE_PARM_DESC(action,
 		 "Changes KUnit executor behavior, valid values are:\n"
 		 "<none>: run the tests like normal\n"
-		 "'list' to list test names instead of running them.\n");
+		 "'list' to list test names instead of running them.\n"
+		 "'list_attr' to list test names and attributes instead of running them.\n");
 
 /* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */
 struct kunit_test_filter {
@@ -172,7 +174,7 @@ static void kunit_exec_run_tests(struct suite_set *suite_set)
 	__kunit_test_suites_init(suite_set->start, num_suites);
 }
 
-static void kunit_exec_list_tests(struct suite_set *suite_set)
+static void kunit_exec_list_tests(struct suite_set *suite_set, bool include_attr)
 {
 	struct kunit_suite * const *suites;
 	struct kunit_case *test_case;
@@ -180,10 +182,19 @@ static void kunit_exec_list_tests(struct suite_set *suite_set)
 	/* Hack: print a ktap header so kunit.py can find the start of KUnit output. */
 	pr_info("KTAP version 1\n");
 
-	for (suites = suite_set->start; suites < suite_set->end; suites++)
+	for (suites = suite_set->start; suites < suite_set->end; suites++) {
+		/* Print suite name and suite attributes */
+		pr_info("%s\n", (*suites)->name);
+		if (include_attr)
+			kunit_print_attr((void *)(*suites), false, 0);
+
+		/* Print test case name and attributes in suite */
 		kunit_suite_for_each_test_case((*suites), test_case) {
 			pr_info("%s.%s\n", (*suites)->name, test_case->name);
+			if (include_attr)
+				kunit_print_attr((void *)test_case, true, 0);
 		}
+	}
 }
 
 int kunit_run_all_tests(void)
@@ -206,7 +217,9 @@ int kunit_run_all_tests(void)
 	if (!action_param)
 		kunit_exec_run_tests(&suite_set);
 	else if (strcmp(action_param, "list") == 0)
-		kunit_exec_list_tests(&suite_set);
+		kunit_exec_list_tests(&suite_set, false);
+	else if (strcmp(action_param, "list_attr") == 0)
+		kunit_exec_list_tests(&suite_set, true);
 	else
 		pr_err("kunit executor: unknown action '%s'\n", action_param);
 
lib/kunit/test.c +10 -7
index 84e4666555c9..9ee55139ecd1 100644
@@ -9,6 +9,7 @@
 #include <kunit/resource.h>
 #include <kunit/test.h>
 #include <kunit/test-bug.h>
+#include <kunit/attributes.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -168,6 +169,13 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite)
 }
 EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases);
 
+/* Currently supported test levels */
+enum {
+	KUNIT_LEVEL_SUITE = 0,
+	KUNIT_LEVEL_CASE,
+	KUNIT_LEVEL_CASE_PARAM,
+};
+
 static void kunit_print_suite_start(struct kunit_suite *suite)
 {
 	/*
@@ -181,17 +189,11 @@ static void kunit_print_suite_start(struct kunit_suite *suite)
 	pr_info(KUNIT_SUBTEST_INDENT "KTAP version 1\n");
 	pr_info(KUNIT_SUBTEST_INDENT "# Subtest: %s\n",
 		  suite->name);
+	kunit_print_attr((void *)suite, false, KUNIT_LEVEL_CASE);
 	pr_info(KUNIT_SUBTEST_INDENT "1..%zd\n",
 		  kunit_suite_num_test_cases(suite));
 }
 
-/* Currently supported test levels */
-enum {
-	KUNIT_LEVEL_SUITE = 0,
-	KUNIT_LEVEL_CASE,
-	KUNIT_LEVEL_CASE_PARAM,
-};
-
 static void kunit_print_ok_not_ok(struct kunit *test,
 				  unsigned int test_level,
 				  enum kunit_status status,
@@ -651,6 +653,7 @@ int kunit_run_tests(struct kunit_suite *suite)
 			}
 		}
 
+		kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
 
 		kunit_print_test_stats(&test, param_stats);
 
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#2 lore raw

PATCH v3 2/9 kunit: Add speed attribute

In reply to Rae Moar

Add speed attribute to the test attribute API. This attribute will allow users to mark tests with a category of speed.

Currently the categories of speed proposed are: normal, slow, and very_slow (outlined in enum kunit_speed). These are outlined in the enum kunit_speed.

The assumed default speed for tests is "normal". This indicates that the test takes a relatively trivial amount of time (less than 1 second), regardless of the machine it is running on. Any test slower than this could be marked as "slow" or "very_slow".

Add the macro KUNIT_CASE_SLOW to set a test as slow, as this is likely a common use of the attributes API.

Add an example of marking a slow test to kunit-example-test.c.

---

Changes since v2:
- No changes.
Changes since v1:
- Change kunit_attr_list definition to fix compile error Changes since RFC v2:
- Fixup comment.
Changes since RFC v1:
- Remove the "fast" category of speed.

 include/kunit/test.h           | 32 ++++++++++++++++++++++++-
 lib/kunit/attributes.c         | 44 +++++++++++++++++++++++++++++++++-
 lib/kunit/kunit-example-test.c |  9 +++++++
 3 files changed, 83 insertions(+), 2 deletions(-)
include/kunit/test.h +31 -1
index 1fc9155988e9..ed5f5000a095 100644
@@ -63,8 +63,27 @@ enum kunit_status {
 	KUNIT_SKIPPED,
 };
 
+/* Attribute struct/enum definitions */
+
+/*
+ * Speed Attribute is stored as an enum and separated into categories of
+ * speed: very_slowm, slow, and normal. These speeds are relative to
+ * other KUnit tests.
+ *
+ * Note: unset speed attribute acts as default of KUNIT_SPEED_NORMAL.
+ */
+enum kunit_speed {
+	KUNIT_SPEED_UNSET,
+	KUNIT_SPEED_VERY_SLOW,
+	KUNIT_SPEED_SLOW,
+	KUNIT_SPEED_NORMAL,
+	KUNIT_SPEED_MAX = KUNIT_SPEED_NORMAL,
+};
+
 /* Holds attributes for each test case and suite */
-struct kunit_attributes {};
+struct kunit_attributes {
+	enum kunit_speed speed;
+};
 
 /**
  * struct kunit_case - represents an individual test case.
@@ -150,6 +169,17 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
 		{ .run_case = test_name, .name = #test_name,	\
 		  .attr = attributes }
 
+/**
+ * KUNIT_CASE_SLOW - A helper for creating a &struct kunit_case
+ * with the slow attribute
+ *
+ * @test_name: a reference to a test case function.
+ */
+
+#define KUNIT_CASE_SLOW(test_name)			\
+		{ .run_case = test_name, .name = #test_name,	\
+		  .attr.speed = KUNIT_SPEED_SLOW }
+
 /**
  * KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case
  *
lib/kunit/attributes.c +43 -1

52 lines not on this page · raw message

lib/kunit/kunit-example-test.c +9 -0
index b69b689ea850..01a769f35e1d 100644
@@ -220,6 +220,14 @@ static void example_params_test(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, param->value % param->value, 0);
 }
 
+/*
+ * This test should always pass. Can be used to practice filtering attributes.
+ */
+static void example_slow_test(struct kunit *test)
+{
+	KUNIT_EXPECT_EQ(test, 1 + 1, 2);
+}
+
 /*
  * Here we make a list of all the test cases we want to add to the test suite
  * below.
@@ -237,6 +245,7 @@ static struct kunit_case example_test_cases[] = {
 	KUNIT_CASE(example_all_expect_macros_test),
 	KUNIT_CASE(example_static_stub_test),
 	KUNIT_CASE_PARAM(example_params_test, example_gen_params),
+	KUNIT_CASE_SLOW(example_slow_test),
 	{}
 };
 
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#3 lore raw

PATCH v3 3/9 kunit: Add module attribute

In reply to Rae Moar

Add module attribute to the test attribute API. This attribute stores the module name associated with the test using KBUILD_MODNAME.

The name of a test suite and the module name often do not match. A reference to the module name associated with the suite could be extremely helpful in running tests as modules without needing to check the codebase.

This attribute will be printed for each suite.

---

Changes since v2:
- No changes.
Changes since v1:
- Change kunit_attr_list definition to fix compile error Changes since RFC v2:
- No changes.
Changes: since RFC v1:
- This is a new patch.

 include/kunit/test.h   | 13 ++++++++-----
 lib/kunit/attributes.c | 25 +++++++++++++++++++++++++
 2 files changed, 33 insertions(+), 5 deletions(-)
include/kunit/test.h +8 -5
index ed5f5000a095..011e0d6bb506 100644
@@ -131,6 +131,7 @@ struct kunit_case {
 
 	/* private: internal use only. */
 	enum kunit_status status;
+	char *module_name;
 	char *log;
 };
 
@@ -155,7 +156,9 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
  * &struct kunit_case object from it. See the documentation for
  * &struct kunit_case for an example on how to use it.
  */
-#define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name }
+#define KUNIT_CASE(test_name)			\
+		{ .run_case = test_name, .name = #test_name,	\
+		  .module_name = KBUILD_MODNAME}
 
 /**
  * KUNIT_CASE_ATTR - A helper for creating a &struct kunit_case
@@ -167,7 +170,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
  */
 #define KUNIT_CASE_ATTR(test_name, attributes)			\
 		{ .run_case = test_name, .name = #test_name,	\
-		  .attr = attributes }
+		  .attr = attributes, .module_name = KBUILD_MODNAME}
 
 /**
  * KUNIT_CASE_SLOW - A helper for creating a &struct kunit_case
@@ -178,7 +181,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
 
 #define KUNIT_CASE_SLOW(test_name)			\
 		{ .run_case = test_name, .name = #test_name,	\
-		  .attr.speed = KUNIT_SPEED_SLOW }
+		  .attr.speed = KUNIT_SPEED_SLOW, .module_name = KBUILD_MODNAME}
 
 /**
  * KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case
@@ -199,7 +202,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
  */
 #define KUNIT_CASE_PARAM(test_name, gen_params)			\
 		{ .run_case = test_name, .name = #test_name,	\
-		  .generate_params = gen_params }
+		  .generate_params = gen_params, .module_name = KBUILD_MODNAME}
 
 /**
  * KUNIT_CASE_PARAM_ATTR - A helper for creating a parameterized &struct
@@ -213,7 +216,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
 #define KUNIT_CASE_PARAM_ATTR(test_name, gen_params, attributes)	\
 		{ .run_case = test_name, .name = #test_name,	\
 		  .generate_params = gen_params,				\
-		  .attr = attributes }
+		  .attr = attributes, .module_name = KBUILD_MODNAME}
 
 /**
  * struct kunit_suite - describes a related collection of &struct kunit_case
lib/kunit/attributes.c +25 -0
index ffd0d692b334..9dce4f4d726c 100644
@@ -61,6 +61,12 @@ static const char *attr_speed_to_string(void *attr, bool *to_free)
 	return attr_enum_to_string(attr, speed_str_list, to_free);
 }
 
+static const char *attr_string_to_string(void *attr, bool *to_free)
+{
+	*to_free = false;
+	return (char *) attr;
+}
+
 /* Get Attribute Methods */
 
 static void *attr_speed_get(void *test_or_suite, bool is_test)
@@ -74,6 +80,18 @@ static void *attr_speed_get(void *test_or_suite, bool is_test)
 		return ((void *) suite->attr.speed);
 }
 
+static void *attr_module_get(void *test_or_suite, bool is_test)
+{
+	struct kunit_suite *suite = is_test ? NULL : test_or_suite;
+	struct kunit_case *test = is_test ? test_or_suite : NULL;
+
+	// Suites get their module attribute from their first test_case
+	if (test)
+		return ((void *) test->module_name);
+	else
+		return ((void *) suite->test_cases[0].module_name);
+}
+
 /* List of all Test Attributes */
 
 static struct kunit_attr kunit_attr_list[] = {
@@ -84,6 +102,13 @@ static struct kunit_attr kunit_attr_list[] = {
 		.attr_default = (void *)KUNIT_SPEED_NORMAL,
 		.print = PRINT_ALWAYS,
 	},
+	{
+		.name = "module",
+		.get_attr = attr_module_get,
+		.to_string = attr_string_to_string,
+		.attr_default = (void *)"",
+		.print = PRINT_SUITE,
+	}
 };
 
 /* Helper Functions to Access Attributes */
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#4 lore raw

PATCH v3 4/9 kunit: Add ability to filter attributes

In reply to Rae Moar

Add filtering of test attributes. Users can filter tests using the module_param called "filter".

Filters are imputed in the format: <attribute_name><operation><value>

Example: kunit.filter="speed>slow"

Operations include: >, <, >=, <=, !=, and =. These operations will act the same for attributes of the same type but may not between types.

Note multiple filters can be inputted by separating them with a comma.
Example: kunit.filter="speed=slow, module!=example"

Since both suites and test cases can have attributes, there may be conflicts. The process of filtering follows these rules:
- Filtering always operates at a per-test level.
- If a test has an attribute set, then the test's value is filtered on.
- Otherwise, the value falls back to the suite's value.
- If neither are set, the attribute has a global "default" value, which is used.

Filtered tests will not be run or show in output. The tests can instead be skipped using the configurable option "kunit.filter_action=skip".

Note the default settings for running tests remains unfiltered.

Finally, add "filter" methods for the speed and module attributes to parse and compare attribute values.

Note this filtering functionality will be added to kunit.py in the next patch.

---

Changes since v2:
- Remove extra line.
Changes since v1:
- Fix compile warning of use of uninitialized variable Changes since RFC v2:
- Change to output only one error before exiting.
Changes since RFC v1:
- Change method for inputting filters to allow for spaces in filtering values
- Add option to skip filtered tests instead of not run or show them with the- –filter_skip flag

 include/kunit/attributes.h |  31 +++++
 lib/kunit/attributes.c     | 271 +++++++++++++++++++++++++++++++++++++
 lib/kunit/executor.c       |  93 ++++++++++---
 lib/kunit/executor_test.c  |  12 +-
 lib/kunit/test.c           |  10 +-
 5 files changed, 389 insertions(+), 28 deletions(-)
include/kunit/attributes.h +31 -0
index 9fcd184cce36..bc76a0b786d2 100644
@@ -9,6 +9,20 @@
 #ifndef _KUNIT_ATTRIBUTES_H
 #define _KUNIT_ATTRIBUTES_H
 
+/*
+ * struct kunit_attr_filter - representation of attributes filter with the
+ * attribute object and string input
+ */
+struct kunit_attr_filter {
+	struct kunit_attr *attr;
+	char *input;
+};
+
+/*
+ * Returns the name of the filter's attribute.
+ */
+const char *kunit_attr_filter_name(struct kunit_attr_filter filter);
+
 /*
  * Print all test attributes for a test case or suite.
  * Output format for test cases: "# <test_name>.<attribute>: <value>"
@@ -16,4 +30,21 @@
  */
 void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level);
 
+/*
+ * Returns the number of fitlers in input.
+ */
+int kunit_get_filter_count(char *input);
+
+/*
+ * Parse attributes filter input and return an objects containing the
+ * attribute object and the string input of the next filter.
+ */
+struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err);
+
+/*
+ * Returns a copy of the suite containing only tests that pass the filter.
+ */
+struct kunit_suite *kunit_filter_attr_tests(const struct kunit_suite *const suite,
+		struct kunit_attr_filter filter, char *action, int *err);
+
 #endif /* _KUNIT_ATTRIBUTES_H */
lib/kunit/attributes.c +271 -0

298 lines not on this page · raw message

lib/kunit/executor.c +74 -19

154 lines not on this page · raw message

lib/kunit/executor_test.c +6 -6
index ce6749af374d..d7ab069324b5 100644
@@ -24,15 +24,15 @@ static struct kunit_case dummy_test_cases[] = {
 
 static void parse_filter_test(struct kunit *test)
 {
-	struct kunit_test_filter filter = {NULL, NULL};
+	struct kunit_glob_filter filter = {NULL, NULL};
 
-	kunit_parse_filter_glob(&filter, "suite");
+	kunit_parse_glob_filter(&filter, "suite");
 	KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite");
 	KUNIT_EXPECT_FALSE(test, filter.test_glob);
 	kfree(filter.suite_glob);
 	kfree(filter.test_glob);
 
-	kunit_parse_filter_glob(&filter, "suite.test");
+	kunit_parse_glob_filter(&filter, "suite.test");
 	KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite");
 	KUNIT_EXPECT_STREQ(test, filter.test_glob, "test");
 	kfree(filter.suite_glob);
@@ -50,7 +50,7 @@ static void filter_suites_test(struct kunit *test)
 	subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
 
 	/* Want: suite1, suite2, NULL -> suite2, NULL */
-	got = kunit_filter_suites(&suite_set, "suite2", &err);
+	got = kunit_filter_suites(&suite_set, "suite2", NULL, NULL, &err);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
 	KUNIT_ASSERT_EQ(test, err, 0);
 	kfree_at_end(test, got.start);
@@ -74,7 +74,7 @@ static void filter_suites_test_glob_test(struct kunit *test)
 	subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
 
 	/* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */
-	got = kunit_filter_suites(&suite_set, "suite2.test2", &err);
+	got = kunit_filter_suites(&suite_set, "suite2.test2", NULL, NULL, &err);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
 	KUNIT_ASSERT_EQ(test, err, 0);
 	kfree_at_end(test, got.start);
@@ -100,7 +100,7 @@ static void filter_suites_to_empty_test(struct kunit *test)
 	subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
 	subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
 
-	got = kunit_filter_suites(&suite_set, "not_found", &err);
+	got = kunit_filter_suites(&suite_set, "not_found", NULL, NULL, &err);
 	KUNIT_ASSERT_EQ(test, err, 0);
 	kfree_at_end(test, got.start); /* just in case */
 
lib/kunit/test.c +7 -3
index 9ee55139ecd1..cb9797fa6303 100644
@@ -613,18 +613,22 @@ int kunit_run_tests(struct kunit_suite *suite)
 	kunit_suite_for_each_test_case(suite, test_case) {
 		struct kunit test = { .param_value = NULL, .param_index = 0 };
 		struct kunit_result_stats param_stats = { 0 };
-		test_case->status = KUNIT_SKIPPED;
 
 		kunit_init_test(&test, test_case->name, test_case->log);
-
-		if (!test_case->generate_params) {
+		if (test_case->status == KUNIT_SKIPPED) {
+			/* Test marked as skip */
+			test.status = KUNIT_SKIPPED;
+			kunit_update_stats(&param_stats, test.status);
+		} else if (!test_case->generate_params) {
 			/* Non-parameterised test. */
+			test_case->status = KUNIT_SKIPPED;
 			kunit_run_case_catch_errors(suite, test_case, &test);
 			kunit_update_stats(&param_stats, test.status);
 		} else {
 			/* Get initial param. */
 			param_desc[0] = '\0';
 			test.param_value = test_case->generate_params(NULL, param_desc);
+			test_case->status = KUNIT_SKIPPED;
 			kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
 				  "KTAP version 1\n");
 			kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
#5 lore raw

Re: [PATCH v3 4/9] kunit: Add ability to filter attributes

Hi Rae,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Rae-Moar/kunit-Add-test-attributes-API-structure/20230726-052754
base:   64bd4641310c41a1ecf07c13c67bc0ed61045dfd
patch link:    https://lore.kernel.org/r/20230725212522.1622716-5-rmoar%40google.com
patch subject: [PATCH v3 4/9] kunit: Add ability to filter attributes
config: parisc-randconfig-m041-20230726 (https://download.01.org/0day-ci/archive/20230727/[email protected]/config)
compiler: hppa-linux-gcc (GCC) 12.3.0
reproduce: (https://download.01.org/0day-ci/archive/20230727/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags

| Reported-by: kernel test robot <[email protected]>
| Reported-by: Dan Carpenter <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/

New smatch warnings:
lib/kunit/attributes.c:127 attr_enum_filter() error: uninitialized symbol 'input_int'. lib/kunit/attributes.c:262 kunit_get_filter_count() error: uninitialized symbol 'comma_index'. lib/kunit/attributes.c:284 kunit_next_attr_filter() error: uninitialized symbol 'comma_index'. lib/kunit/executor.c:168 kunit_filter_suites() error: uninitialized symbol 'filter_count'. lib/kunit/executor.c:171 kunit_filter_suites() error: uninitialized symbol 'parsed_filters'.

Old smatch warnings:
lib/kunit/attributes.c:267 kunit_get_filter_count() error: uninitialized symbol 'comma_index'. lib/kunit/executor.c:201 kunit_filter_suites() error: uninitialized symbol 'filter_count'. lib/kunit/executor.c:202 kunit_filter_suites() error: uninitialized symbol 'parsed_filters'.

vim +/input_int +127 lib/kunit/attributes.c

793862a3f94d2f Rae Moar 2023-07-25  102  static int attr_enum_filter(void *attr, const char *input, int *err,
793862a3f94d2f Rae Moar 2023-07-25  103  		const char * const str_list[], int max)
793862a3f94d2f Rae Moar 2023-07-25  104  {
793862a3f94d2f Rae Moar 2023-07-25  105  	int i, j, input_int;
                                                          ^^^^^^^^^
793862a3f94d2f Rae Moar 2023-07-25  106  	long test_val = (long)attr;
793862a3f94d2f Rae Moar 2023-07-25  107  	const char *input_val = NULL;
793862a3f94d2f Rae Moar 2023-07-25  108  
793862a3f94d2f Rae Moar 2023-07-25  109  	for (i = 0; input[i]; i++) {
793862a3f94d2f Rae Moar 2023-07-25  110  		if (!strchr(op_list, input[i])) {
793862a3f94d2f Rae Moar 2023-07-25  111  			input_val = input + i;
793862a3f94d2f Rae Moar 2023-07-25  112  			break;
793862a3f94d2f Rae Moar 2023-07-25  113  		}
793862a3f94d2f Rae Moar 2023-07-25  114  	}
793862a3f94d2f Rae Moar 2023-07-25  115  
793862a3f94d2f Rae Moar 2023-07-25  116  	if (!input_val) {
793862a3f94d2f Rae Moar 2023-07-25  117  		*err = -EINVAL;
793862a3f94d2f Rae Moar 2023-07-25  118  		pr_err("kunit executor: filter value not found: %s\n", input);
793862a3f94d2f Rae Moar 2023-07-25  119  		return false;
793862a3f94d2f Rae Moar 2023-07-25  120  	}
793862a3f94d2f Rae Moar 2023-07-25  121  
793862a3f94d2f Rae Moar 2023-07-25  122  	for (j = 0; j <= max; j++) {
793862a3f94d2f Rae Moar 2023-07-25  123  		if (!strcmp(input_val, str_list[j]))
793862a3f94d2f Rae Moar 2023-07-25  124  			input_int = j;
793862a3f94d2f Rae Moar 2023-07-25  125  	}
793862a3f94d2f Rae Moar 2023-07-25  126  

793862a3f94d2f Rae Moar 2023-07-25 @127 if (!input_int) {

This is a bug, but wait, what? Why is the first element invalid? Maybe do:

	int input_int = -1;

	for (j = 0; j <= max; j++) {
		if found
			input_int = j;
	}

	if (input_int == -1) {
793862a3f94d2f Rae Moar 2023-07-25  128  		*err = -EINVAL;
793862a3f94d2f Rae Moar 2023-07-25  129  		pr_err("kunit executor: invalid filter input: %s\n", input);
793862a3f94d2f Rae Moar 2023-07-25  130  		return false;
793862a3f94d2f Rae Moar 2023-07-25  131  	}
793862a3f94d2f Rae Moar 2023-07-25  132  
793862a3f94d2f Rae Moar 2023-07-25  133  	return int_filter(test_val, input, input_int, err);
793862a3f94d2f Rae Moar 2023-07-25  134  }

[ snip ]

793862a3f94d2f Rae Moar 2023-07-25  256  int kunit_get_filter_count(char *input)
793862a3f94d2f Rae Moar 2023-07-25  257  {
793862a3f94d2f Rae Moar 2023-07-25  258  	int i, comma_index, count = 0;
793862a3f94d2f Rae Moar 2023-07-25  259  
793862a3f94d2f Rae Moar 2023-07-25  260  	for (i = 0; input[i]; i++) {
793862a3f94d2f Rae Moar 2023-07-25  261  		if (input[i] == ',') {

793862a3f94d2f Rae Moar 2023-07-25 @262 if ((i - comma_index) > 1)

initialize comma_index = 0

793862a3f94d2f Rae Moar 2023-07-25  263  				count++;
793862a3f94d2f Rae Moar 2023-07-25  264  			comma_index = i;
793862a3f94d2f Rae Moar 2023-07-25  265  		}
793862a3f94d2f Rae Moar 2023-07-25  266  	}
793862a3f94d2f Rae Moar 2023-07-25  267  	if ((i - comma_index) > 0)
793862a3f94d2f Rae Moar 2023-07-25  268  		count++;
793862a3f94d2f Rae Moar 2023-07-25  269  	return count;
793862a3f94d2f Rae Moar 2023-07-25  270  }
793862a3f94d2f Rae Moar 2023-07-25  271  
793862a3f94d2f Rae Moar 2023-07-25  272  struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err)
793862a3f94d2f Rae Moar 2023-07-25  273  {
793862a3f94d2f Rae Moar 2023-07-25  274  	struct kunit_attr_filter filter = {};
793862a3f94d2f Rae Moar 2023-07-25  275  	int i, j, comma_index, new_start_index;
793862a3f94d2f Rae Moar 2023-07-25  276  	int op_index = -1, attr_index = -1;
793862a3f94d2f Rae Moar 2023-07-25  277  	char op;
793862a3f94d2f Rae Moar 2023-07-25  278  	char *input = *filters;
793862a3f94d2f Rae Moar 2023-07-25  279  
793862a3f94d2f Rae Moar 2023-07-25  280  	/* Parse input until operation */
793862a3f94d2f Rae Moar 2023-07-25  281  	for (i = 0; input[i]; i++) {
793862a3f94d2f Rae Moar 2023-07-25  282  		if (op_index < 0 && strchr(op_list, input[i])) {
793862a3f94d2f Rae Moar 2023-07-25  283  			op_index = i;

793862a3f94d2f Rae Moar 2023-07-25 @284 } else if (!comma_index && input[i] == ',') {

same

793862a3f94d2f Rae Moar 2023-07-25  285  			comma_index = i;
793862a3f94d2f Rae Moar 2023-07-25  286  		} else if (comma_index && input[i] != ' ') {
793862a3f94d2f Rae Moar 2023-07-25  287  			new_start_index = i;
793862a3f94d2f Rae Moar 2023-07-25  288  			break;
793862a3f94d2f Rae Moar 2023-07-25  289  		}
793862a3f94d2f Rae Moar 2023-07-25  290  	}
793862a3f94d2f Rae Moar 2023-07-25  291  
Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
author
#6 lore raw

Re: [PATCH v3 4/9] kunit: Add ability to filter attributes

Quoted 5 lines · Dan Carpenter

Hi Rae,

kernel test robot noticed the following build warnings:

Hello!

Sorry for this bug. It looks like I have a few uninitialized variables. I am going to send out a fix for this in the next day or so.

Quoted 14 lines
url:    https://github.com/intel-lab-lkp/linux/commits/Rae-Moar/kunit-Add-test-attributes-API-structure/20230726-052754
base:   64bd4641310c41a1ecf07c13c67bc0ed61045dfd
patch link:    https://lore.kernel.org/r/20230725212522.1622716-5-rmoar%40google.com
patch subject: [PATCH v3 4/9] kunit: Add ability to filter attributes
config: parisc-randconfig-m041-20230726 (https://download.01.org/0day-ci/archive/20230727/[email protected]/config)
compiler: hppa-linux-gcc (GCC) 12.3.0
reproduce: (https://download.01.org/0day-ci/archive/20230727/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags

| Reported-by: kernel test robot <[email protected]>
| Reported-by: Dan Carpenter <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/
Quoted 6 lines

New smatch warnings:
lib/kunit/attributes.c:127 attr_enum_filter() error: uninitialized symbol 'input_int'. lib/kunit/attributes.c:262 kunit_get_filter_count() error: uninitialized symbol 'comma_index'. lib/kunit/attributes.c:284 kunit_next_attr_filter() error: uninitialized symbol 'comma_index'. lib/kunit/executor.c:168 kunit_filter_suites() error: uninitialized symbol 'filter_count'. lib/kunit/executor.c:171 kunit_filter_suites() error: uninitialized symbol 'parsed_filters'.

I'll initialize filter_count as 0. I'll also initialize parsed_filters as NULL and add an extra check for parsed_filters.

Quoted 39 lines

Old smatch warnings:
lib/kunit/attributes.c:267 kunit_get_filter_count() error: uninitialized symbol 'comma_index'. lib/kunit/executor.c:201 kunit_filter_suites() error: uninitialized symbol 'filter_count'. lib/kunit/executor.c:202 kunit_filter_suites() error: uninitialized symbol 'parsed_filters'.

vim +/input_int +127 lib/kunit/attributes.c

793862a3f94d2f Rae Moar 2023-07-25  102  static int attr_enum_filter(void *attr, const char *input, int *err,
793862a3f94d2f Rae Moar 2023-07-25  103                 const char * const str_list[], int max)
793862a3f94d2f Rae Moar 2023-07-25  104  {
793862a3f94d2f Rae Moar 2023-07-25  105         int i, j, input_int;
                                                          ^^^^^^^^^
793862a3f94d2f Rae Moar 2023-07-25  106         long test_val = (long)attr;
793862a3f94d2f Rae Moar 2023-07-25  107         const char *input_val = NULL;
793862a3f94d2f Rae Moar 2023-07-25  108
793862a3f94d2f Rae Moar 2023-07-25  109         for (i = 0; input[i]; i++) {
793862a3f94d2f Rae Moar 2023-07-25  110                 if (!strchr(op_list, input[i])) {
793862a3f94d2f Rae Moar 2023-07-25  111                         input_val = input + i;
793862a3f94d2f Rae Moar 2023-07-25  112                         break;
793862a3f94d2f Rae Moar 2023-07-25  113                 }
793862a3f94d2f Rae Moar 2023-07-25  114         }
793862a3f94d2f Rae Moar 2023-07-25  115
793862a3f94d2f Rae Moar 2023-07-25  116         if (!input_val) {
793862a3f94d2f Rae Moar 2023-07-25  117                 *err = -EINVAL;
793862a3f94d2f Rae Moar 2023-07-25  118                 pr_err("kunit executor: filter value not found: %s\n", input);
793862a3f94d2f Rae Moar 2023-07-25  119                 return false;
793862a3f94d2f Rae Moar 2023-07-25  120         }
793862a3f94d2f Rae Moar 2023-07-25  121
793862a3f94d2f Rae Moar 2023-07-25  122         for (j = 0; j <= max; j++) {
793862a3f94d2f Rae Moar 2023-07-25  123                 if (!strcmp(input_val, str_list[j]))
793862a3f94d2f Rae Moar 2023-07-25  124                         input_int = j;
793862a3f94d2f Rae Moar 2023-07-25  125         }
793862a3f94d2f Rae Moar 2023-07-25  126

793862a3f94d2f Rae Moar 2023-07-25 @127 if (!input_int) {

This is a bug, but wait, what? Why is the first element invalid? Maybe do:

Quoted 9 lines
        int input_int = -1;

        for (j = 0; j <= max; j++) {
                if found
                        input_int = j;
        }

        if (input_int == -1) {

What you have above looks like a great change.

Quoted 16 lines
793862a3f94d2f Rae Moar 2023-07-25  128                 *err = -EINVAL;
793862a3f94d2f Rae Moar 2023-07-25  129                 pr_err("kunit executor: invalid filter input: %s\n", input);
793862a3f94d2f Rae Moar 2023-07-25  130                 return false;
793862a3f94d2f Rae Moar 2023-07-25  131         }
793862a3f94d2f Rae Moar 2023-07-25  132
793862a3f94d2f Rae Moar 2023-07-25  133         return int_filter(test_val, input, input_int, err);
793862a3f94d2f Rae Moar 2023-07-25  134  }

[ snip ]

793862a3f94d2f Rae Moar 2023-07-25  256  int kunit_get_filter_count(char *input)
793862a3f94d2f Rae Moar 2023-07-25  257  {
793862a3f94d2f Rae Moar 2023-07-25  258         int i, comma_index, count = 0;
793862a3f94d2f Rae Moar 2023-07-25  259
793862a3f94d2f Rae Moar 2023-07-25  260         for (i = 0; input[i]; i++) {
793862a3f94d2f Rae Moar 2023-07-25  261                 if (input[i] == ',') {
Quoted 3 lines

793862a3f94d2f Rae Moar 2023-07-25 @262 if ((i - comma_index) > 1)

initialize comma_index = 0

I will change this to be initialized to 0.

Quoted 22 lines
793862a3f94d2f Rae Moar 2023-07-25  263                                 count++;
793862a3f94d2f Rae Moar 2023-07-25  264                         comma_index = i;
793862a3f94d2f Rae Moar 2023-07-25  265                 }
793862a3f94d2f Rae Moar 2023-07-25  266         }
793862a3f94d2f Rae Moar 2023-07-25  267         if ((i - comma_index) > 0)
793862a3f94d2f Rae Moar 2023-07-25  268                 count++;
793862a3f94d2f Rae Moar 2023-07-25  269         return count;
793862a3f94d2f Rae Moar 2023-07-25  270  }
793862a3f94d2f Rae Moar 2023-07-25  271
793862a3f94d2f Rae Moar 2023-07-25  272  struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err)
793862a3f94d2f Rae Moar 2023-07-25  273  {
793862a3f94d2f Rae Moar 2023-07-25  274         struct kunit_attr_filter filter = {};
793862a3f94d2f Rae Moar 2023-07-25  275         int i, j, comma_index, new_start_index;
793862a3f94d2f Rae Moar 2023-07-25  276         int op_index = -1, attr_index = -1;
793862a3f94d2f Rae Moar 2023-07-25  277         char op;
793862a3f94d2f Rae Moar 2023-07-25  278         char *input = *filters;
793862a3f94d2f Rae Moar 2023-07-25  279
793862a3f94d2f Rae Moar 2023-07-25  280         /* Parse input until operation */
793862a3f94d2f Rae Moar 2023-07-25  281         for (i = 0; input[i]; i++) {
793862a3f94d2f Rae Moar 2023-07-25  282                 if (op_index < 0 && strchr(op_list, input[i])) {
793862a3f94d2f Rae Moar 2023-07-25  283                         op_index = i;
Quoted 3 lines

793862a3f94d2f Rae Moar 2023-07-25 @284 } else if (!comma_index && input[i] == ',') {

same

I will also change this here.

Quoted 13 lines
793862a3f94d2f Rae Moar 2023-07-25  285                         comma_index = i;
793862a3f94d2f Rae Moar 2023-07-25  286                 } else if (comma_index && input[i] != ' ') {
793862a3f94d2f Rae Moar 2023-07-25  287                         new_start_index = i;
793862a3f94d2f Rae Moar 2023-07-25  288                         break;
793862a3f94d2f Rae Moar 2023-07-25  289                 }
793862a3f94d2f Rae Moar 2023-07-25  290         }
793862a3f94d2f Rae Moar 2023-07-25  291

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

#7 lore raw

Re: [PATCH v3 4/9] kunit: Add ability to filter attributes

In reply to Rae Moar

Hi,

Quoted 26 lines · Rae Moar

Add filtering of test attributes. Users can filter tests using the module_param called "filter".

Filters are imputed in the format: <attribute_name><operation><value>

Example: kunit.filter="speed>slow"

Operations include: >, <, >=, <=, !=, and =. These operations will act the same for attributes of the same type but may not between types.

Note multiple filters can be inputted by separating them with a comma.
Example: kunit.filter="speed=slow, module!=example"

Since both suites and test cases can have attributes, there may be conflicts. The process of filtering follows these rules:
- Filtering always operates at a per-test level.
- If a test has an attribute set, then the test's value is filtered on.
- Otherwise, the value falls back to the suite's value.
- If neither are set, the attribute has a global "default" value, which is used.

Filtered tests will not be run or show in output. The tests can instead be skipped using the configurable option "kunit.filter_action=skip".

Note the default settings for running tests remains unfiltered.

Quoted 8 lines

Finally, add "filter" methods for the speed and module attributes to parse and compare attribute values.

Note this filtering functionality will be added to kunit.py in the next patch.

This patch causes all my s390 boot tests to crash.

[   17.009188]     # module: kunit
[   17.009239]     1..8
[   17.014967]     ok 1 parse_filter_test
[   17.020755]     ok 2 filter_suites_test
[   17.026350]     ok 3 filter_suites_test_glob_test
[   17.033102]     ok 4 filter_suites_to_empty_test
[   17.035422] Unable to handle kernel pointer dereference in virtual kernel address space
[   17.035546] Failing address: 0000000001501000 TEID: 0000000001501407
[   17.035617] Fault in home space mode while using kernel ASCE.
[   17.035833] AS:0000000002164007 R3:000000001f71c007 S:000000001f711000 P:000000000150131f
[   17.036313] Oops: 0004 ilc:2 [#1] SMP
[   17.036851] Modules linked in:
[   17.037026] CPU: 0 PID: 193 Comm: kunit_try_catch Tainted: G                 N 6.5.0-01207-g1c59d383390f #1
[   17.037106] Hardware name: QEMU 8561 QEMU (KVM/Linux)
[   17.037199] Krnl PSW : 0704e00180000000 0000000000950e18 (kunit_next_attr_filter+0xc8/0x1c8)
[   17.037451]            R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3
[   17.037531] Krnl GPRS: 0000037f00000000 000000000150120a 000000000000003e 000000000000003e
[   17.037583]            0000038000197e04 0000038000197c04 0000038000197df0 000003800000000a
[   17.037629]            0000038000000005 0000000001501203 00000000015011fe 0000037f0000000c
[   17.037675]            0000000005404100 0000038000197e08 0000000000950dda 0000038000197cd8
[   17.038176] Krnl Code: 0000000000950e0a: b9180098		agfr	%r9,%r8
[   17.038176]            0000000000950e0e: a7080000		lhi	%r0,0
[   17.038176]           #0000000000950e12: e32090000090	llgc	%r2,0(%r9)
[   17.038176]           >0000000000950e18: 92009000		mvi	0(%r9),0
[   17.038176]            0000000000950e1c: b904003a		lgr	%r3,%r10
[   17.038176]            0000000000950e20: c418007bd73c	lgrl	%r1,00000000018cbc98
[   17.038176]            0000000000950e26: b25d0013		clst	%r1,%r3
[   17.038176]            0000000000950e2a: a714fffe		brc	1,0000000000950e26
[   17.038625] Call Trace:
[   17.038679]  [<0000000000950e18>] kunit_next_attr_filter+0xc8/0x1c8
[   17.038749]  [<000000000094e5f2>] parse_filter_attr_test+0x92/0x360
[   17.038796]  [<000000000094e39a>] kunit_generic_run_threadfn_adapter+0x32/0x48
[   17.038840]  [<0000000000186702>] kthread+0x142/0x148
[   17.038884]  [<0000000000103138>] __ret_from_fork+0x40/0x58
[   17.038927]  [<000000000102df9a>] ret_from_fork+0xa/0x30
[   17.038984] INFO: lockdep is turned off.
[   17.039027] Last Breaking-Event-Address:
[   17.039062]  [<0000000000950da4>] kunit_next_attr_filter+0x54/0x1c8
[   17.039303] Kernel panic - not syncing: Fatal exception: panic_on_oops

Bisect log is attached.

Guenter

---
# bad: [1c59d383390f970b891b503b7f79b63a02db2ec5] Merge tag 'linux-kselftest-nolibc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
# good: [2dde18cd1d8fac735875f2e4987f11817cc0bc2c] Linux 6.5
git bisect start '1c59d383390f' 'v6.5'
# good: [42a7f6e3ffe06308c1ec43a7dac39a27de101574] Merge tag 'x86_microcode_for_v6.6_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
git bisect good 42a7f6e3ffe06308c1ec43a7dac39a27de101574
# good: [330235e87410349042468b52baff02af7cb7d331] Merge tag 'acpi-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
git bisect good 330235e87410349042468b52baff02af7cb7d331
# good: [5a31cc7297072a7266a910ca5266b640d27803b4] Merge tag 'linux-kselftest-next-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
git bisect good 5a31cc7297072a7266a910ca5266b640d27803b4
# good: [eea70cdac61087fdb7a1c1e6f306c0bf38ec39d4] tools/nolibc: riscv: shrink _start with _start_c
git bisect good eea70cdac61087fdb7a1c1e6f306c0bf38ec39d4
# good: [faeb4e09fe77262f9a6b2f9f874eec0b6850721e] selftests/nolibc: add test support for ppc64
git bisect good faeb4e09fe77262f9a6b2f9f874eec0b6850721e
# bad: [76066f93f1df27657eb937b7c9c091e3a6abf4db] kunit: add tests for filtering attributes
git bisect bad 76066f93f1df27657eb937b7c9c091e3a6abf4db
# good: [64bd4641310c41a1ecf07c13c67bc0ed61045dfd] MAINTAINERS: add Rust KUnit files to the KUnit entry
git bisect good 64bd4641310c41a1ecf07c13c67bc0ed61045dfd
# bad: [529534e8cba3e60f843a682e2a3149612b30d608] kunit: Add ability to filter attributes
git bisect bad 529534e8cba3e60f843a682e2a3149612b30d608
# good: [02c2d0c2a84172c3c7ec0229c61db55d23dd4730] kunit: Add speed attribute
git bisect good 02c2d0c2a84172c3c7ec0229c61db55d23dd4730
# good: [a00a72709175a4d53096301a8792b8171d1223e5] kunit: Add module attribute
git bisect good a00a72709175a4d53096301a8792b8171d1223e5

# first bad commit: [529534e8cba3e60f843a682e2a3149612b30d608] kunit: Add ability to filter attributes
#8 lore raw

Re: [PATCH v3 4/9] kunit: Add ability to filter attributes

Quoted 33 lines · Guenter Roeck

Hi,

Quoted 29 lines · Rae Moar

Add filtering of test attributes. Users can filter tests using the module_param called "filter".

Filters are imputed in the format: <attribute_name><operation><value>

Example: kunit.filter="speed>slow"

Operations include: >, <, >=, <=, !=, and =. These operations will act the same for attributes of the same type but may not between types.

Note multiple filters can be inputted by separating them with a comma.
Example: kunit.filter="speed=slow, module!=example"

Since both suites and test cases can have attributes, there may be conflicts. The process of filtering follows these rules:
- Filtering always operates at a per-test level.
- If a test has an attribute set, then the test's value is filtered on.
- Otherwise, the value falls back to the suite's value.
- If neither are set, the attribute has a global "default" value, which is used.

Filtered tests will not be run or show in output. The tests can instead be skipped using the configurable option "kunit.filter_action=skip".

Note the default settings for running tests remains unfiltered.

Finally, add "filter" methods for the speed and module attributes to parse and compare attribute values.

Quoted 8 lines
Quoted 5 lines

Note this filtering functionality will be added to kunit.py in the next patch.

This patch causes all my s390 boot tests to crash.

Thanks: I can reproduce this here.

Looks like this is an issue with string constants in the tests not being writable. (I'd've thought my testing with KASAN would pick up on this, but I guess string literals are in a writable segment on x86_64?)

Regardless, using char[] for the filters seems to work here. I've sent out a patch to do so:
https://lore.kernel.org/linux-kselftest/[email protected]/

That fixes the issue with the stacktrack below, but if you bisected it to this patch, maybe there's another issue. The tests weren't introduced until 76066f93f1df ("kunit: add tests for filtering attributes"). I don't get any crashes with this patch applied, though, so let me know if something's still broken.

Cheers,
-- David

Quoted 69 lines
[   17.009188]     # module: kunit
[   17.009239]     1..8
[   17.014967]     ok 1 parse_filter_test
[   17.020755]     ok 2 filter_suites_test
[   17.026350]     ok 3 filter_suites_test_glob_test
[   17.033102]     ok 4 filter_suites_to_empty_test
[   17.035422] Unable to handle kernel pointer dereference in virtual kernel address space
[   17.035546] Failing address: 0000000001501000 TEID: 0000000001501407
[   17.035617] Fault in home space mode while using kernel ASCE.
[   17.035833] AS:0000000002164007 R3:000000001f71c007 S:000000001f711000 P:000000000150131f
[   17.036313] Oops: 0004 ilc:2 [#1] SMP
[   17.036851] Modules linked in:
[   17.037026] CPU: 0 PID: 193 Comm: kunit_try_catch Tainted: G                 N 6.5.0-01207-g1c59d383390f #1
[   17.037106] Hardware name: QEMU 8561 QEMU (KVM/Linux)
[   17.037199] Krnl PSW : 0704e00180000000 0000000000950e18 (kunit_next_attr_filter+0xc8/0x1c8)
[   17.037451]            R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3
[   17.037531] Krnl GPRS: 0000037f00000000 000000000150120a 000000000000003e 000000000000003e
[   17.037583]            0000038000197e04 0000038000197c04 0000038000197df0 000003800000000a
[   17.037629]            0000038000000005 0000000001501203 00000000015011fe 0000037f0000000c
[   17.037675]            0000000005404100 0000038000197e08 0000000000950dda 0000038000197cd8
[   17.038176] Krnl Code: 0000000000950e0a: b9180098            agfr    %r9,%r8
[   17.038176]            0000000000950e0e: a7080000            lhi     %r0,0
[   17.038176]           #0000000000950e12: e32090000090        llgc    %r2,0(%r9)
[   17.038176]           >0000000000950e18: 92009000            mvi     0(%r9),0
[   17.038176]            0000000000950e1c: b904003a            lgr     %r3,%r10
[   17.038176]            0000000000950e20: c418007bd73c        lgrl    %r1,00000000018cbc98
[   17.038176]            0000000000950e26: b25d0013            clst    %r1,%r3
[   17.038176]            0000000000950e2a: a714fffe            brc     1,0000000000950e26
[   17.038625] Call Trace:
[   17.038679]  [<0000000000950e18>] kunit_next_attr_filter+0xc8/0x1c8
[   17.038749]  [<000000000094e5f2>] parse_filter_attr_test+0x92/0x360
[   17.038796]  [<000000000094e39a>] kunit_generic_run_threadfn_adapter+0x32/0x48
[   17.038840]  [<0000000000186702>] kthread+0x142/0x148
[   17.038884]  [<0000000000103138>] __ret_from_fork+0x40/0x58
[   17.038927]  [<000000000102df9a>] ret_from_fork+0xa/0x30
[   17.038984] INFO: lockdep is turned off.
[   17.039027] Last Breaking-Event-Address:
[   17.039062]  [<0000000000950da4>] kunit_next_attr_filter+0x54/0x1c8
[   17.039303] Kernel panic - not syncing: Fatal exception: panic_on_oops

Bisect log is attached.

Guenter

---
# bad: [1c59d383390f970b891b503b7f79b63a02db2ec5] Merge tag 'linux-kselftest-nolibc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
# good: [2dde18cd1d8fac735875f2e4987f11817cc0bc2c] Linux 6.5
git bisect start '1c59d383390f' 'v6.5'
# good: [42a7f6e3ffe06308c1ec43a7dac39a27de101574] Merge tag 'x86_microcode_for_v6.6_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
git bisect good 42a7f6e3ffe06308c1ec43a7dac39a27de101574
# good: [330235e87410349042468b52baff02af7cb7d331] Merge tag 'acpi-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
git bisect good 330235e87410349042468b52baff02af7cb7d331
# good: [5a31cc7297072a7266a910ca5266b640d27803b4] Merge tag 'linux-kselftest-next-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
git bisect good 5a31cc7297072a7266a910ca5266b640d27803b4
# good: [eea70cdac61087fdb7a1c1e6f306c0bf38ec39d4] tools/nolibc: riscv: shrink _start with _start_c
git bisect good eea70cdac61087fdb7a1c1e6f306c0bf38ec39d4
# good: [faeb4e09fe77262f9a6b2f9f874eec0b6850721e] selftests/nolibc: add test support for ppc64
git bisect good faeb4e09fe77262f9a6b2f9f874eec0b6850721e
# bad: [76066f93f1df27657eb937b7c9c091e3a6abf4db] kunit: add tests for filtering attributes
git bisect bad 76066f93f1df27657eb937b7c9c091e3a6abf4db
# good: [64bd4641310c41a1ecf07c13c67bc0ed61045dfd] MAINTAINERS: add Rust KUnit files to the KUnit entry
git bisect good 64bd4641310c41a1ecf07c13c67bc0ed61045dfd
# bad: [529534e8cba3e60f843a682e2a3149612b30d608] kunit: Add ability to filter attributes
git bisect bad 529534e8cba3e60f843a682e2a3149612b30d608
# good: [02c2d0c2a84172c3c7ec0229c61db55d23dd4730] kunit: Add speed attribute
git bisect good 02c2d0c2a84172c3c7ec0229c61db55d23dd4730
# good: [a00a72709175a4d53096301a8792b8171d1223e5] kunit: Add module attribute
git bisect good a00a72709175a4d53096301a8792b8171d1223e5

# first bad commit: [529534e8cba3e60f843a682e2a3149612b30d608] kunit: Add ability to filter attributes
#9 lore raw

Re: [PATCH v3 4/9] kunit: Add ability to filter attributes

Quoted 54 lines · David Gow
Quoted 33 lines · Guenter Roeck

Hi,

Quoted 29 lines · Rae Moar

Add filtering of test attributes. Users can filter tests using the module_param called "filter".

Filters are imputed in the format: <attribute_name><operation><value>

Example: kunit.filter="speed>slow"

Operations include: >, <, >=, <=, !=, and =. These operations will act the same for attributes of the same type but may not between types.

Note multiple filters can be inputted by separating them with a comma.
Example: kunit.filter="speed=slow, module!=example"

Since both suites and test cases can have attributes, there may be conflicts. The process of filtering follows these rules:
- Filtering always operates at a per-test level.
- If a test has an attribute set, then the test's value is filtered on.
- Otherwise, the value falls back to the suite's value.
- If neither are set, the attribute has a global "default" value, which is used.

Filtered tests will not be run or show in output. The tests can instead be skipped using the configurable option "kunit.filter_action=skip".

Note the default settings for running tests remains unfiltered.

Finally, add "filter" methods for the speed and module attributes to parse and compare attribute values.

Quoted 8 lines
Quoted 5 lines

Note this filtering functionality will be added to kunit.py in the next patch.

This patch causes all my s390 boot tests to crash.

Thanks: I can reproduce this here.

Looks like this is an issue with string constants in the tests not being writable. (I'd've thought my testing with KASAN would pick up on this, but I guess string literals are in a writable segment on x86_64?)

Regardless, using char[] for the filters seems to work here. I've sent out a patch to do so:
https://lore.kernel.org/linux-kselftest/[email protected]/

Quoted 6 lines

That fixes the issue with the stacktrack below, but if you bisected it to this patch, maybe there's another issue. The tests weren't introduced until 76066f93f1df ("kunit: add tests for filtering attributes"). I don't get any crashes with this patch applied, though, so let me know if something's still broken.

Your patch works just fine. Maybe bisect acted up ? I don't recall seeing this happen, but who knows.

Thanks a lot for the quick fix!

Guenter

author
#10 lore raw

PATCH v3 5/9 kunit: tool: Add command line interface to filter and report attributes

In reply to Rae Moar

Add ability to kunit.py to filter attributes and report a list of tests including attributes without running tests.

Add flag "--filter" to input filters on test attributes. Tests will be filtered out if they do not match all inputted filters.

Example: --filter speed=slow (This filter would run only the tests that are marked as slow)

Filters have operations: <, >, <=, >=, !=, and =. But note that the characters < and > are often interpreted by the shell, so they may need to be quoted or escaped.

Example: --filter "speed>slow" or --filter speed\>slow (This filter would run only the tests that have the speed faster than slow.

Additionally, multiple filters can be used.

Example: --filter "speed=slow, module!=example" (This filter would run only the tests that have the speed slow and are not in the "example" module)

Note if the user wants to skip filtered tests instead of not running/showing them use the "--filter_action=skip" flag instead.

Expose the output of kunit.action=list option with flag "--list_tests" to output a list of tests. Additionally, add flag "--list_tests_attr" to output a list of tests and their attributes. These flags are useful to see tests and test attributes without needing to run tests.

Example of the output of "--list_tests_attr":
example
example.test_1
example.test_2
# example.test_2.speed: slow

This output includes a suite, example, with two test cases, test_1 and test_2. And in this instance test_2 has been marked as slow.

---

Changes since v2:
- No changes.
Changes since v1:
- No major changes
Changes since RFC v2:
- Remove -–filter_skip flag and replace with -–filter_action
- Make KUnit executor errors visible in kunit.py and raw_output
- Fix up help comments
Changes since RFC v1:
- Change method for inputting filters to allow for spaces in filtering values
- Add option to skip filtered tests instead of not run or show them with the –-filter_skip flag
- Separate the new feature to list tests and their attributes into both –-list_tests (lists just tests) and –-list_tests_attr (lists all)

 tools/testing/kunit/kunit.py           | 70 ++++++++++++++++++++++++--
 tools/testing/kunit/kunit_kernel.py    |  8 ++-
 tools/testing/kunit/kunit_parser.py    | 11 +++-
 tools/testing/kunit/kunit_tool_test.py | 39 +++++++-------
 4 files changed, 99 insertions(+), 29 deletions(-)
tools/testing/kunit/kunit.py +65 -5
index 3905c43369c3..bc74088c458a 100755
@@ -55,8 +55,12 @@ class KunitExecRequest(KunitParseRequest):
 	build_dir: str
 	timeout: int
 	filter_glob: str
+	filter: str
+	filter_action: Optional[str]
 	kernel_args: Optional[List[str]]
 	run_isolated: Optional[str]
+	list_tests: bool
+	list_tests_attr: bool
 
 @dataclass
 class KunitRequest(KunitExecRequest, KunitBuildRequest):
@@ -102,19 +106,41 @@ def config_and_build_tests(linux: kunit_kernel.LinuxSourceTree,
 
 def _list_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> List[str]:
 	args = ['kunit.action=list']
+
+	if request.kernel_args:
+		args.extend(request.kernel_args)
+
+	output = linux.run_kernel(args=args,
+			   timeout=request.timeout,
+			   filter_glob=request.filter_glob,
+			   filter=request.filter,
+			   filter_action=request.filter_action,
+			   build_dir=request.build_dir)
+	lines = kunit_parser.extract_tap_lines(output)
+	# Hack! Drop the dummy TAP version header that the executor prints out.
+	lines.pop()
+
+	# Filter out any extraneous non-test output that might have gotten mixed in.
+	return [l for l in output if re.match(r'^[^\s.]+\.[^\s.]+$', l)]
+
+def _list_tests_attr(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> Iterable[str]:
+	args = ['kunit.action=list_attr']
+
 	if request.kernel_args:
 		args.extend(request.kernel_args)
 
 	output = linux.run_kernel(args=args,
 			   timeout=request.timeout,
 			   filter_glob=request.filter_glob,
+			   filter=request.filter,
+			   filter_action=request.filter_action,
 			   build_dir=request.build_dir)
 	lines = kunit_parser.extract_tap_lines(output)
 	# Hack! Drop the dummy TAP version header that the executor prints out.
 	lines.pop()
 
 	# Filter out any extraneous non-test output that might have gotten mixed in.
-	return [l for l in lines if re.match(r'^[^\s.]+\.[^\s.]+$', l)]
+	return lines
 
 def _suites_from_test_list(tests: List[str]) -> List[str]:
 	"""Extracts all the suites from an ordered list of tests."""
@@ -128,10 +154,18 @@ def _suites_from_test_list(tests: List[str]) -> List[str]:
 			suites.append(suite)
 	return suites
 
-
-
 def exec_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> KunitResult:
 	filter_globs = [request.filter_glob]
+	if request.list_tests:
+		output = _list_tests(linux, request)
+		for line in output:
+			print(line.rstrip())
+		return KunitResult(status=KunitStatus.SUCCESS, elapsed_time=0.0)
+	if request.list_tests_attr:
+		attr_output = _list_tests_attr(linux, request)
+		for line in attr_output:
+			print(line.rstrip())
+		return KunitResult(status=KunitStatus.SUCCESS, elapsed_time=0.0)
 	if request.run_isolated:
 		tests = _list_tests(linux, request)
 		if request.run_isolated == 'test':
@@ -155,6 +189,8 @@ def exec_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -
 			args=request.kernel_args,
 			timeout=request.timeout,
 			filter_glob=filter_glob,
+			filter=request.filter,
+			filter_action=request.filter_action,
 			build_dir=request.build_dir)
 
 		_, test_result = parse_tests(request, metadata, run_result)
@@ -341,6 +377,16 @@ def add_exec_opts(parser: argparse.ArgumentParser) -> None:
 			    nargs='?',
 			    default='',
 			    metavar='filter_glob')
+	parser.add_argument('--filter',
+			    help='Filter KUnit tests with attributes, '
+			    'e.g. module=example or speed>slow',
+			    type=str,
+				default='')
+	parser.add_argument('--filter_action',
+			    help='If set to skip, filtered tests will be skipped, '
+				'e.g. --filter_action=skip. Otherwise they will not run.',
+			    type=str,
+				choices=['skip'])
 	parser.add_argument('--kernel_args',
 			    help='Kernel command-line parameters. Maybe be repeated',
 			     action='append', metavar='')
@@ -350,6 +396,12 @@ def add_exec_opts(parser: argparse.ArgumentParser) -> None:
 			    'what ran before it.',
 			    type=str,
 			    choices=['suite', 'test'])
+	parser.add_argument('--list_tests', help='If set, list all tests that will be '
+			    'run.',
+			    action='store_true')
+	parser.add_argument('--list_tests_attr', help='If set, list all tests and test '
+			    'attributes.',
+			    action='store_true')
 
 def add_parse_opts(parser: argparse.ArgumentParser) -> None:
 	parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. '
@@ -398,8 +450,12 @@ def run_handler(cli_args: argparse.Namespace) -> None:
 					json=cli_args.json,
 					timeout=cli_args.timeout,
 					filter_glob=cli_args.filter_glob,
+					filter=cli_args.filter,
+					filter_action=cli_args.filter_action,
 					kernel_args=cli_args.kernel_args,
-					run_isolated=cli_args.run_isolated)
+					run_isolated=cli_args.run_isolated,
+					list_tests=cli_args.list_tests,
+					list_tests_attr=cli_args.list_tests_attr)
 	result = run_tests(linux, request)
 	if result.status != KunitStatus.SUCCESS:
 		sys.exit(1)
@@ -441,8 +497,12 @@ def exec_handler(cli_args: argparse.Namespace) -> None:
 					json=cli_args.json,
 					timeout=cli_args.timeout,
 					filter_glob=cli_args.filter_glob,
+					filter=cli_args.filter,
+					filter_action=cli_args.filter_action,
 					kernel_args=cli_args.kernel_args,
-					run_isolated=cli_args.run_isolated)
+					run_isolated=cli_args.run_isolated,
+					list_tests=cli_args.list_tests,
+					list_tests_attr=cli_args.list_tests_attr)
 	result = exec_tests(linux, exec_request)
 	stdout.print_with_timestamp((
 		'Elapsed time: %.3fs\n') % (result.elapsed_time))
tools/testing/kunit/kunit_kernel.py +6 -2
index 7f648802caf6..0b6488efed47 100644
@@ -330,11 +330,15 @@ class LinuxSourceTree:
 			return False
 		return self.validate_config(build_dir)
 
-	def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', timeout: Optional[int]=None) -> Iterator[str]:
+	def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None) -> Iterator[str]:
 		if not args:
 			args = []
 		if filter_glob:
-			args.append('kunit.filter_glob='+filter_glob)
+			args.append('kunit.filter_glob=' + filter_glob)
+		if filter:
+			args.append('kunit.filter="' + filter + '"')
+		if filter_action:
+			args.append('kunit.filter_action=' + filter_action)
 		args.append('kunit.enable=1')
 
 		process = self._ops.start(args, build_dir)
tools/testing/kunit/kunit_parser.py +10 -1
index fbc094f0567e..79d8832c862a 100644
@@ -212,6 +212,7 @@ KTAP_START = re.compile(r'\s*KTAP version ([0-9]+)$')
 TAP_START = re.compile(r'\s*TAP version ([0-9]+)$')
 KTAP_END = re.compile(r'\s*(List of all partitions:|'
 	'Kernel panic - not syncing: VFS:|reboot: System halted)')
+EXECUTOR_ERROR = re.compile(r'\s*kunit executor: (.*)$')
 
 def extract_tap_lines(kernel_output: Iterable[str]) -> LineStream:
 	"""Extracts KTAP lines from the kernel output."""
@@ -242,6 +243,8 @@ def extract_tap_lines(kernel_output: Iterable[str]) -> LineStream:
 				# remove the prefix, if any.
 				line = line[prefix_len:]
 				yield line_num, line
+			elif EXECUTOR_ERROR.search(line):
+				yield line_num, line
 	return LineStream(lines=isolate_ktap_output(kernel_output))
 
 KTAP_VERSIONS = [1]
@@ -447,7 +450,7 @@ def parse_diagnostic(lines: LineStream) -> List[str]:
 	Log of diagnostic lines
 	"""
 	log = []  # type: List[str]
-	non_diagnostic_lines = [TEST_RESULT, TEST_HEADER, KTAP_START]
+	non_diagnostic_lines = [TEST_RESULT, TEST_HEADER, KTAP_START, TAP_START]
 	while lines and not any(re.match(lines.peek())
 			for re in non_diagnostic_lines):
 		log.append(lines.pop())
@@ -713,6 +716,11 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest:
 	"""
 	test = Test()
 	test.log.extend(log)
+
+	# Parse any errors prior to parsing tests
+	err_log = parse_diagnostic(lines)
+	test.log.extend(err_log)
+
 	if not is_subtest:
 		# If parsing the main/top-level test, parse KTAP version line and
 		# test plan
@@ -774,6 +782,7 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest:
 		# Don't override a bad status if this test had one reported.
 		# Assumption: no subtests means CRASHED is from Test.__init__()
 		if test.status in (TestStatus.TEST_CRASHED, TestStatus.SUCCESS):
+			print_log(test.log)
 			test.status = TestStatus.NO_TESTS
 			test.add_error('0 tests run!')
 
tools/testing/kunit/kunit_tool_test.py +18 -21
index be35999bb84f..b28c1510be2e 100755
@@ -597,7 +597,7 @@ class KUnitMainTest(unittest.TestCase):
 		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 0)
 		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir='.kunit', filter_glob='', timeout=300)
+			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
 		self.print_mock.assert_any_call(StrContains('Testing complete.'))
 
 	def test_run_passes_args_pass(self):
@@ -605,7 +605,7 @@ class KUnitMainTest(unittest.TestCase):
 		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
 		self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir='.kunit', filter_glob='', timeout=300)
+			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
 		self.print_mock.assert_any_call(StrContains('Testing complete.'))
 
 	def test_exec_passes_args_fail(self):
@@ -629,7 +629,7 @@ class KUnitMainTest(unittest.TestCase):
 			kunit.main(['run'])
 		self.assertEqual(e.exception.code, 1)
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir='.kunit', filter_glob='', timeout=300)
+			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
 		self.print_mock.assert_any_call(StrContains(' 0 tests run!'))
 
 	def test_exec_raw_output(self):
@@ -670,13 +670,13 @@ class KUnitMainTest(unittest.TestCase):
 		self.linux_source_mock.run_kernel = mock.Mock(return_value=[])
 		kunit.main(['run', '--raw_output', 'filter_glob'])
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir='.kunit', filter_glob='filter_glob', timeout=300)
+			args=None, build_dir='.kunit', filter_glob='filter_glob', filter='', filter_action=None, timeout=300)
 
 	def test_exec_timeout(self):
 		timeout = 3453
 		kunit.main(['exec', '--timeout', str(timeout)])
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir='.kunit', filter_glob='', timeout=timeout)
+			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout)
 		self.print_mock.assert_any_call(StrContains('Testing complete.'))
 
 	def test_run_timeout(self):
@@ -684,7 +684,7 @@ class KUnitMainTest(unittest.TestCase):
 		kunit.main(['run', '--timeout', str(timeout)])
 		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir='.kunit', filter_glob='', timeout=timeout)
+			args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout)
 		self.print_mock.assert_any_call(StrContains('Testing complete.'))
 
 	def test_run_builddir(self):
@@ -692,7 +692,7 @@ class KUnitMainTest(unittest.TestCase):
 		kunit.main(['run', '--build_dir=.kunit'])
 		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir=build_dir, filter_glob='', timeout=300)
+			args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300)
 		self.print_mock.assert_any_call(StrContains('Testing complete.'))
 
 	def test_config_builddir(self):
@@ -710,7 +710,7 @@ class KUnitMainTest(unittest.TestCase):
 		build_dir = '.kunit'
 		kunit.main(['exec', '--build_dir', build_dir])
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=None, build_dir=build_dir, filter_glob='', timeout=300)
+			args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300)
 		self.print_mock.assert_any_call(StrContains('Testing complete.'))
 
 	def test_run_kunitconfig(self):
@@ -786,7 +786,7 @@ class KUnitMainTest(unittest.TestCase):
 		kunit.main(['run', '--kernel_args=a=1', '--kernel_args=b=2'])
 		self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-		      args=['a=1','b=2'], build_dir='.kunit', filter_glob='', timeout=300)
+		      args=['a=1','b=2'], build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300)
 		self.print_mock.assert_any_call(StrContains('Testing complete.'))
 
 	def test_list_tests(self):
@@ -794,13 +794,11 @@ class KUnitMainTest(unittest.TestCase):
 		self.linux_source_mock.run_kernel.return_value = ['TAP version 14', 'init: random output'] + want
 
 		got = kunit._list_tests(self.linux_source_mock,
-				     kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', None, 'suite'))
-
+				     kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False))
 		self.assertEqual(got, want)
 		# Should respect the user's filter glob when listing tests.
 		self.linux_source_mock.run_kernel.assert_called_once_with(
-			args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', timeout=300)
-
+			args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', filter='', filter_action=None, timeout=300)
 
 	@mock.patch.object(kunit, '_list_tests')
 	def test_run_isolated_by_suite(self, mock_tests):
@@ -809,10 +807,10 @@ class KUnitMainTest(unittest.TestCase):
 
 		# Should respect the user's filter glob when listing tests.
 		mock_tests.assert_called_once_with(mock.ANY,
-				     kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*.test*', None, 'suite'))
+				     kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*.test*', '', None, None, 'suite', False, False))
 		self.linux_source_mock.run_kernel.assert_has_calls([
-			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', timeout=300),
-			mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', timeout=300),
+			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', filter='', filter_action=None, timeout=300),
+			mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', filter='', filter_action=None, timeout=300),
 		])
 
 	@mock.patch.object(kunit, '_list_tests')
@@ -822,13 +820,12 @@ class KUnitMainTest(unittest.TestCase):
 
 		# Should respect the user's filter glob when listing tests.
 		mock_tests.assert_called_once_with(mock.ANY,
-				     kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', None, 'test'))
+				     kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', '', None, None, 'test', False, False))
 		self.linux_source_mock.run_kernel.assert_has_calls([
-			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', timeout=300),
-			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', timeout=300),
-			mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', timeout=300),
+			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', filter='', filter_action=None, timeout=300),
+			mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', filter='', filter_action=None, timeout=300),
+			mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', filter='', filter_action=None, timeout=300),
 		])
 
-
 if __name__ == '__main__':
 	unittest.main()
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#11 lore raw

PATCH v3 6/9 kunit: memcpy: Mark tests as slow using test attributes

In reply to Rae Moar

Mark slow memcpy KUnit tests using test attributes.

Tests marked as slow are as follows: memcpy_large_test, memmove_test, memmove_large_test, and memmove_overlap_test. These tests were the slowest of the memcpy tests and relatively slower to most other KUnit tests. Most of these tests are already skipped when CONFIG_MEMCPY_SLOW_KUNIT_TEST is not enabled.

These tests can now be filtered using the KUnit test attribute filtering feature. Example: --filter "speed>slow". This will run only the tests that have speeds faster than slow. The slow attribute will also be outputted in KTAP.

Note: This patch is intended to replace the use of CONFIG_MEMCPY_SLOW_KUNIT_TEST and to potentially deprecate this feature. This patch does not remove the config option but does add a note to the config definition commenting on this future shift.

---

Changes since v2:
- No changes.
Changes since v1:
- No major changes
Changes since RFC v2:
- No changes.
Changes since RFC v1:
- Added note under CONFIG_MEMCPY_SLOW_KUNIT_TEST.

 lib/Kconfig.debug  | 3 +++
 lib/memcpy_kunit.c | 8 ++++----
 2 files changed, 7 insertions(+), 4 deletions(-)
lib/Kconfig.debug +3 -0
index 550cb967b668..1b3894e861f2 100644
@@ -2701,6 +2701,9 @@ config MEMCPY_SLOW_KUNIT_TEST
 	  and bit ranges. These can be very slow, so they are split out
 	  as a separate config, in case they need to be disabled.
 
+	  Note this config option will be replaced by the use of KUnit test
+	  attributes.
+
 config IS_SIGNED_TYPE_KUNIT_TEST
 	tristate "Test is_signed_type() macro" if !KUNIT_ALL_TESTS
 	depends on KUNIT
lib/memcpy_kunit.c +4 -4
index 887926f04731..440aee705ccc 100644
@@ -551,10 +551,10 @@ static void strtomem_test(struct kunit *test)
 static struct kunit_case memcpy_test_cases[] = {
 	KUNIT_CASE(memset_test),
 	KUNIT_CASE(memcpy_test),
-	KUNIT_CASE(memcpy_large_test),
-	KUNIT_CASE(memmove_test),
-	KUNIT_CASE(memmove_large_test),
-	KUNIT_CASE(memmove_overlap_test),
+	KUNIT_CASE_SLOW(memcpy_large_test),
+	KUNIT_CASE_SLOW(memmove_test),
+	KUNIT_CASE_SLOW(memmove_large_test),
+	KUNIT_CASE_SLOW(memmove_overlap_test),
 	KUNIT_CASE(strtomem_test),
 	{}
 };
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#12 lore raw

PATCH v3 7/9 kunit: time: Mark test as slow using test attributes

In reply to Rae Moar

Mark the time KUnit test, time64_to_tm_test_date_range, as slow using test attributes.

This test ran relatively much slower than most other KUnit tests.

By marking this test as slow, the test can now be filtered using the KUnit test attribute filtering feature. Example: --filter "speed>slow". This will run only the tests that have speeds faster than slow. The slow attribute will also be outputted in KTAP.

---

Changes since v2:
- No changes.
Changes since v1:
- No changes.
Changes since RFC v2:
- No changes.
Changes since RFC v1:
- No changes.

 kernel/time/time_test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/time/time_test.c +1 -1
index 831e8e779ace..ca058c8af6ba 100644
@@ -86,7 +86,7 @@ static void time64_to_tm_test_date_range(struct kunit *test)
 }
 
 static struct kunit_case time_test_cases[] = {
-	KUNIT_CASE(time64_to_tm_test_date_range),
+	KUNIT_CASE_SLOW(time64_to_tm_test_date_range),
 	{}
 };
 
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#13 lore raw

PATCH v3 8/9 kunit: add tests for filtering attributes

In reply to Rae Moar

Add four tests to executor_test.c to test behavior of filtering attributes.

- parse_filter_attr_test - to test the parsing of inputted filters

- filter_attr_test - to test the filtering procedure on attributes

- filter_attr_empty_test - to test the behavior when all tests are filtered out

- filter_attr_skip_test - to test the configurable filter_action=skip option

---

Changes since v2:
- No changes.
Changes since v1:
- No changes.
Changes since RFC v2:
- Change fake suite and test case names.
- Add a few ASSERT statements.
Changes since RFC v1:
- This is a new patch

 lib/kunit/executor_test.c | 116 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)
lib/kunit/executor_test.c +116 -0
index d7ab069324b5..01280cb8d451 100644
@@ -7,6 +7,7 @@
  */
 
 #include <kunit/test.h>
+#include <kunit/attributes.h>
 
 static void kfree_at_end(struct kunit *test, const void *to_free);
 static struct kunit_suite *alloc_fake_suite(struct kunit *test,
@@ -108,11 +109,126 @@ static void filter_suites_to_empty_test(struct kunit *test)
 				"should be empty to indicate no match");
 }
 
+static void parse_filter_attr_test(struct kunit *test)
+{
+	int j, filter_count;
+	struct kunit_attr_filter *parsed_filters;
+	char *filters = "speed>slow, module!=example";
+	int err = 0;
+
+	filter_count = kunit_get_filter_count(filters);
+	KUNIT_EXPECT_EQ(test, filter_count, 2);
+
+	parsed_filters = kunit_kcalloc(test, filter_count + 1, sizeof(*parsed_filters),
+			GFP_KERNEL);
+	for (j = 0; j < filter_count; j++) {
+		parsed_filters[j] = kunit_next_attr_filter(&filters, &err);
+		KUNIT_ASSERT_EQ_MSG(test, err, 0, "failed to parse filter '%s'", filters[j]);
+	}
+
+	KUNIT_EXPECT_STREQ(test, kunit_attr_filter_name(parsed_filters[0]), "speed");
+	KUNIT_EXPECT_STREQ(test, parsed_filters[0].input, ">slow");
+
+	KUNIT_EXPECT_STREQ(test, kunit_attr_filter_name(parsed_filters[1]), "module");
+	KUNIT_EXPECT_STREQ(test, parsed_filters[1].input, "!=example");
+}
+
+static struct kunit_case dummy_attr_test_cases[] = {
+	/* .run_case is not important, just needs to be non-NULL */
+	{ .name = "slow", .run_case = dummy_test, .module_name = "dummy",
+	  .attr.speed = KUNIT_SPEED_SLOW },
+	{ .name = "normal", .run_case = dummy_test, .module_name = "dummy" },
+	{},
+};
+
+static void filter_attr_test(struct kunit *test)
+{
+	struct kunit_suite *subsuite[3] = {NULL, NULL};
+	struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
+	struct suite_set got;
+	int err = 0;
+
+	subsuite[0] = alloc_fake_suite(test, "normal_suite", dummy_attr_test_cases);
+	subsuite[1] = alloc_fake_suite(test, "slow_suite", dummy_attr_test_cases);
+	subsuite[1]->attr.speed = KUNIT_SPEED_SLOW; // Set suite attribute
+
+	/*
+	 * Want: normal_suite(slow, normal), slow_suite(slow, normal),
+	 *		NULL -> normal_suite(normal), NULL
+	 *
+	 * The normal test in slow_suite is filtered out because the speed
+	 * attribute is unset and thus, the filtering is based on the parent attribute
+	 * of slow.
+	 */
+	got = kunit_filter_suites(&suite_set, NULL, "speed>slow", NULL, &err);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
+	KUNIT_ASSERT_EQ(test, err, 0);
+	kfree_at_end(test, got.start);
+
+	/* Validate we just have normal_suite */
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]);
+	KUNIT_EXPECT_STREQ(test, got.start[0]->name, "normal_suite");
+	KUNIT_ASSERT_EQ(test, got.end - got.start, 1);
+
+	/* Now validate we just have normal test case */
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]->test_cases);
+	KUNIT_EXPECT_STREQ(test, got.start[0]->test_cases[0].name, "normal");
+	KUNIT_EXPECT_FALSE(test, got.start[0]->test_cases[1].name);
+}
+
+static void filter_attr_empty_test(struct kunit *test)
+{
+	struct kunit_suite *subsuite[3] = {NULL, NULL};
+	struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
+	struct suite_set got;
+	int err = 0;
+
+	subsuite[0] = alloc_fake_suite(test, "suite1", dummy_attr_test_cases);
+	subsuite[1] = alloc_fake_suite(test, "suite2", dummy_attr_test_cases);
+
+	got = kunit_filter_suites(&suite_set, NULL, "module!=dummy", NULL, &err);
+	KUNIT_ASSERT_EQ(test, err, 0);
+	kfree_at_end(test, got.start); /* just in case */
+
+	KUNIT_EXPECT_PTR_EQ_MSG(test, got.start, got.end,
+				"should be empty to indicate no match");
+}
+
+static void filter_attr_skip_test(struct kunit *test)
+{
+	struct kunit_suite *subsuite[2] = {NULL};
+	struct suite_set suite_set = {.start = subsuite, .end = &subsuite[1]};
+	struct suite_set got;
+	int err = 0;
+
+	subsuite[0] = alloc_fake_suite(test, "suite", dummy_attr_test_cases);
+
+	/* Want: suite(slow, normal), NULL -> suite(slow with SKIP, normal), NULL */
+	got = kunit_filter_suites(&suite_set, NULL, "speed>slow", "skip", &err);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
+	KUNIT_ASSERT_EQ(test, err, 0);
+	kfree_at_end(test, got.start);
+
+	/* Validate we have both the slow and normal test */
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]->test_cases);
+	KUNIT_ASSERT_EQ(test, kunit_suite_num_test_cases(got.start[0]), 2);
+	KUNIT_EXPECT_STREQ(test, got.start[0]->test_cases[0].name, "slow");
+	KUNIT_EXPECT_STREQ(test, got.start[0]->test_cases[1].name, "normal");
+
+	/* Now ensure slow is skipped and normal is not */
+	KUNIT_EXPECT_EQ(test, got.start[0]->test_cases[0].status, KUNIT_SKIPPED);
+	KUNIT_EXPECT_FALSE(test, got.start[0]->test_cases[1].status);
+}
+
 static struct kunit_case executor_test_cases[] = {
 	KUNIT_CASE(parse_filter_test),
 	KUNIT_CASE(filter_suites_test),
 	KUNIT_CASE(filter_suites_test_glob_test),
 	KUNIT_CASE(filter_suites_to_empty_test),
+	KUNIT_CASE(parse_filter_attr_test),
+	KUNIT_CASE(filter_attr_test),
+	KUNIT_CASE(filter_attr_empty_test),
+	KUNIT_CASE(filter_attr_skip_test),
 	{}
 };
 
Signature · 1 line
2.41.0.487.g6d72f3e995-goog
author
#14 lore raw

PATCH v3 9/9 kunit: Add documentation of KUnit test attributes

In reply to Rae Moar

Add documentation on the use of test attributes under the section "Tips for Running KUnit Tests" in the KUnit docs.

Documentation includes three sections on how to mark tests with attributes, how attributes are reported, and how the user can filter tests using test attributes.

Add descriptions of new flags to list of command-line arguments.

---

Changes since v2:
- Add to list of command-line arguments in run_wrapper.rst. Changes since v1:
- No changes.
Changes since RFC v2:
- Add comment on KUNIT_CASE_SLOW() to documentation.
- Add comment on how to expose raw kernel output.
- Remove an extra line at the end of file.
Changes since RFC v1:
- This is a new patch

 Documentation/dev-tools/kunit/run_wrapper.rst |  12 ++
 .../dev-tools/kunit/running_tips.rst          | 166 ++++++++++++++++++
 2 files changed, 178 insertions(+)
Documentation/dev-tools/kunit/run_wrapper.rst +12 -0
index dafe8eb28d30..19ddf5e07013 100644
@@ -321,3 +321,15 @@ command line arguments:
 
 - ``--json``: If set, stores the test results in a JSON format and prints to `stdout` or
   saves to a file if a filename is specified.
+
+- ``--filter``: Specifies filters on test attributes, for example, ``speed!=slow``.
+  Multiple filters can be used by wrapping input in quotes and separating filters
+  by commas. Example: ``--filter "speed>slow, module=example"``.
+
+- ``--filter_action``: If set to ``skip``, filtered tests will be shown as skipped
+  in the output rather than showing no output.
+
+- ``--list_tests``: If set, lists all tests that will be run.
+
+- ``--list_tests_attr``: If set, lists all tests that will be run and all of their
+  attributes.
Documentation/dev-tools/kunit/running_tips.rst +166 -0

169 lines not on this page · raw message

Signature · 1 line
2.41.0.487.g6d72f3e995-goog
Reviewed-by on the v3 series
#15 lore raw
In reply to Rae Moar
Quoted 20 lines · Rae Moar' via KUnit Development

Hello everyone,

This patch series adds a test attributes framework to KUnit.

There has been interest in filtering out "slow" KUnit tests. Most notably, a new config, CONFIG_MEMCPY_SLOW_KUNIT_TEST, has been added to exclude a particularly slow memcpy test
(https://lore.kernel.org/all/[email protected]/).

This attributes framework can be used to save and access test associated data, including whether a test is slow. These attributes are reportable (via KTAP and command line output) and are also filterable.

This framework is designed to allow for the addition of other attributes in the future. These attributes could include whether the test can be run concurrently, test file path, etc.

To try out the framework I suggest running:
"./tools/testing/kunit/kunit.py run --filter speed!=slow"

Quoted 7 lines

This patch series was originally sent out as an RFC. Here is a link to the RFC v2:
https://lore.kernel.org/all/[email protected]/

Thanks!
Rae

Great addition to the KUnit framework!

On all the patch set

Best Regards,
~Arthur Grillo

Quoted 35 lines

Rae Moar (9):
kunit: Add test attributes API structure
kunit: Add speed attribute
kunit: Add module attribute
kunit: Add ability to filter attributes
kunit: tool: Add command line interface to filter and report
attributes
kunit: memcpy: Mark tests as slow using test attributes
kunit: time: Mark test as slow using test attributes
kunit: add tests for filtering attributes
kunit: Add documentation of KUnit test attributes

 Documentation/dev-tools/kunit/run_wrapper.rst |  12 +
 .../dev-tools/kunit/running_tips.rst          | 166 +++++++
 include/kunit/attributes.h                    |  50 +++
 include/kunit/test.h                          |  70 ++-
 kernel/time/time_test.c                       |   2 +-
 lib/Kconfig.debug                             |   3 +
 lib/kunit/Makefile                            |   3 +-
 lib/kunit/attributes.c                        | 418 ++++++++++++++++++
 lib/kunit/executor.c                          | 114 ++++-
 lib/kunit/executor_test.c                     | 128 +++++-
 lib/kunit/kunit-example-test.c                |   9 +
 lib/kunit/test.c                              |  27 +-
 lib/memcpy_kunit.c                            |   8 +-
 tools/testing/kunit/kunit.py                  |  70 ++-
 tools/testing/kunit/kunit_kernel.py           |   8 +-
 tools/testing/kunit/kunit_parser.py           |  11 +-
 tools/testing/kunit/kunit_tool_test.py        |  39 +-
 17 files changed, 1062 insertions(+), 76 deletions(-)
 create mode 100644 include/kunit/attributes.h
 create mode 100644 lib/kunit/attributes.c

base-commit: 64bd4641310c41a1ecf07c13c67bc0ed61045dfd

Reviewed-by on the v3 series
#16 lore raw
In reply to Rae Moar
Quoted 20 lines · Rae Moar' via KUnit Development

Hello everyone,

This patch series adds a test attributes framework to KUnit.

There has been interest in filtering out "slow" KUnit tests. Most notably, a new config, CONFIG_MEMCPY_SLOW_KUNIT_TEST, has been added to exclude a particularly slow memcpy test
(https://lore.kernel.org/all/[email protected]/).

This attributes framework can be used to save and access test associated data, including whether a test is slow. These attributes are reportable (via KTAP and command line output) and are also filterable.

This framework is designed to allow for the addition of other attributes in the future. These attributes could include whether the test can be run concurrently, test file path, etc.

To try out the framework I suggest running:
"./tools/testing/kunit/kunit.py run --filter speed!=slow"

Quoted 7 lines

This patch series was originally sent out as an RFC. Here is a link to the RFC v2:
https://lore.kernel.org/all/[email protected]/

Thanks!
Rae

Great addition to the KUnit framework!

On all the patch set

Best Regards,
~Arthur Grillo

Quoted 35 lines

Rae Moar (9):
kunit: Add test attributes API structure
kunit: Add speed attribute
kunit: Add module attribute
kunit: Add ability to filter attributes
kunit: tool: Add command line interface to filter and report
attributes
kunit: memcpy: Mark tests as slow using test attributes
kunit: time: Mark test as slow using test attributes
kunit: add tests for filtering attributes
kunit: Add documentation of KUnit test attributes

 Documentation/dev-tools/kunit/run_wrapper.rst |  12 +
 .../dev-tools/kunit/running_tips.rst          | 166 +++++++
 include/kunit/attributes.h                    |  50 +++
 include/kunit/test.h                          |  70 ++-
 kernel/time/time_test.c                       |   2 +-
 lib/Kconfig.debug                             |   3 +
 lib/kunit/Makefile                            |   3 +-
 lib/kunit/attributes.c                        | 418 ++++++++++++++++++
 lib/kunit/executor.c                          | 114 ++++-
 lib/kunit/executor_test.c                     | 128 +++++-
 lib/kunit/kunit-example-test.c                |   9 +
 lib/kunit/test.c                              |  27 +-
 lib/memcpy_kunit.c                            |   8 +-
 tools/testing/kunit/kunit.py                  |  70 ++-
 tools/testing/kunit/kunit_kernel.py           |   8 +-
 tools/testing/kunit/kunit_parser.py           |  11 +-
 tools/testing/kunit/kunit_tool_test.py        |  39 +-
 17 files changed, 1062 insertions(+), 76 deletions(-)
 create mode 100644 include/kunit/attributes.h
 create mode 100644 lib/kunit/attributes.c

base-commit: 64bd4641310c41a1ecf07c13c67bc0ed61045dfd

#17 lore raw
In reply to Rae Moar
Quoted 4 lines · Rae Moar

Hello everyone,

This patch series adds a test attributes framework to KUnit.

With this series in linux-next, almost 50% of my boot tests crash, and many others show warnings tracebacks.

Example crash from x86_64 boot attempt attached.

Guenter

---

[    6.165419] ok 6 lib_sort
[    6.166044]     KTAP version 1
[    6.166139]     # Subtest: kunit_executor_test
[    6.166247]     # module: kunit
[    6.166261]     1..8
[    6.168252]     ok 1 parse_filter_test
[    6.169579] BUG: unable to handle page fault for address: ffffd757e80004c8
[    6.169872] #PF: supervisor read access in kernel mode
[    6.169990] #PF: error_code(0x0000) - not-present page
[    6.170142] PGD ffd4067 P4D ffd4067 PUD 0
[    6.170346] Oops: 0000 [#1] PREEMPT SMP PTI
[    6.170521] CPU: 0 PID: 182 Comm: kunit_try_catch Tainted: G                 N 6.5.0-rc3-next-20230728 #1
[    6.170735] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014
[    6.171024] RIP: 0010:kfree+0x61/0x140
[    6.171313] Code: 80 48 01 e8 0f 82 ec 00 00 00 48 c7 c2 00 00 00 80 48 2b 15 29 bf aa 01 48 01 d0 48 c1 e8 0c 48 c1 e0 06 48 03 05 07 bf aa 01 <48> 8b 50 08 48 89 c7 f6 c2 01 0f 85 b3 00 00 00 66 90 48 8b 07 f6
[    6.171715] RSP: 0000:ffffb48f80267dc8 EFLAGS: 00000286
[    6.171847] RAX: ffffd757e80004c0 RBX: ffffa34583aef5d0 RCX: 0000000000000000
[    6.171998] RDX: 00005cba00000000 RSI: ffffffff95cb1ad5 RDI: ffffb48f80013cd0
[    6.172146] RBP: ffffb48f80013cd0 R08: 0000000000000001 R09: 0000000000000001
[    6.172293] R10: 0000000000000001 R11: 0000000000000001 R12: ffffb48f80267e74
[    6.172445] R13: 0000000000000000 R14: ffffa34583bf4000 R15: 0000000000000002
[    6.172620] FS:  0000000000000000(0000) GS:ffffa3458f800000(0000) knlGS:0000000000000000
[    6.172793] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    6.172917] CR2: ffffd757e80004c8 CR3: 0000000009048000 CR4: 00000000007506f0
[    6.173089] PKRU: 55555554
[    6.173177] Call Trace:
[    6.173313]  <TASK>
[    6.173494]  ? __die+0x1f/0x70
[    6.173600]  ? page_fault_oops+0x14a/0x460
[    6.173693]  ? search_exception_tables+0x37/0x50
[    6.173793]  ? fixup_exception+0x21/0x310
[    6.173887]  ? exc_page_fault+0xee/0x1c0
[    6.173977]  ? asm_exc_page_fault+0x26/0x30
[    6.174076]  ? kunit_filter_suites+0x3a5/0x460
[    6.174192]  ? kfree+0x61/0x140
[    6.174271]  ? kfree+0x106/0x140
[    6.174353]  kunit_filter_suites+0x3a5/0x460
[    6.174473]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10
[    6.174604]  filter_suites_test+0xea/0x2c0
[    6.174702]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10
[    6.174828]  kunit_generic_run_threadfn_adapter+0x15/0x20
[    6.174941]  kthread+0xef/0x120
[    6.175012]  ? __pfx_kthread+0x10/0x10
[    6.175098]  ret_from_fork+0x2f/0x50
[    6.175177]  ? __pfx_kthread+0x10/0x10
[    6.175260]  ret_from_fork_asm+0x1b/0x30
[    6.175387]  </TASK>
[    6.175466] Modules linked in:
[    6.175640] CR2: ffffd757e80004c8
[    6.175887] ---[ end trace 0000000000000000 ]---
[    6.176029] RIP: 0010:kfree+0x61/0x140
[    6.176119] Code: 80 48 01 e8 0f 82 ec 00 00 00 48 c7 c2 00 00 00 80 48 2b 15 29 bf aa 01 48 01 d0 48 c1 e8 0c 48 c1 e0 06 48 03 05 07 bf aa 01 <48> 8b 50 08 48 89 c7 f6 c2 01 0f 85 b3 00 00 00 66 90 48 8b 07 f6
[    6.176478] RSP: 0000:ffffb48f80267dc8 EFLAGS: 00000286
[    6.176589] RAX: ffffd757e80004c0 RBX: ffffa34583aef5d0 RCX: 0000000000000000
[    6.176729] RDX: 00005cba00000000 RSI: ffffffff95cb1ad5 RDI: ffffb48f80013cd0
[    6.176869] RBP: ffffb48f80013cd0 R08: 0000000000000001 R09: 0000000000000001
[    6.177008] R10: 0000000000000001 R11: 0000000000000001 R12: ffffb48f80267e74
[    6.177150] R13: 0000000000000000 R14: ffffa34583bf4000 R15: 0000000000000002
[    6.177290] FS:  0000000000000000(0000) GS:ffffa3458f800000(0000) knlGS:0000000000000000
[    6.177448] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    6.177564] CR2: ffffd757e80004c8 CR3: 0000000009048000 CR4: 00000000007506f0
[    6.177704] PKRU: 55555554
[    6.177807] note: kunit_try_catch[182] exited with irqs disabled
#18 lore raw
Quoted 7 lines · Guenter Roeck
Quoted 4 lines · Rae Moar

Hello everyone,

This patch series adds a test attributes framework to KUnit.

Quoted 8 lines

With this series in linux-next, almost 50% of my boot tests crash, and many others show warnings tracebacks.

Example crash from x86_64 boot attempt attached.

Guenter

---

Hmm... It's not reproducing here (even with KASAN), which is strange.

Does the patch here help?
https://lore.kernel.org/linux-kselftest/[email protected]/

-- David

Quoted 68 lines
[    6.165419] ok 6 lib_sort
[    6.166044]     KTAP version 1
[    6.166139]     # Subtest: kunit_executor_test
[    6.166247]     # module: kunit
[    6.166261]     1..8
[    6.168252]     ok 1 parse_filter_test
[    6.169579] BUG: unable to handle page fault for address: ffffd757e80004c8
[    6.169872] #PF: supervisor read access in kernel mode
[    6.169990] #PF: error_code(0x0000) - not-present page
[    6.170142] PGD ffd4067 P4D ffd4067 PUD 0
[    6.170346] Oops: 0000 [#1] PREEMPT SMP PTI
[    6.170521] CPU: 0 PID: 182 Comm: kunit_try_catch Tainted: G                 N 6.5.0-rc3-next-20230728 #1
[    6.170735] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014
[    6.171024] RIP: 0010:kfree+0x61/0x140
[    6.171313] Code: 80 48 01 e8 0f 82 ec 00 00 00 48 c7 c2 00 00 00 80 48 2b 15 29 bf aa 01 48 01 d0 48 c1 e8 0c 48 c1 e0 06 48 03 05 07 bf aa 01 <48> 8b 50 08 48 89 c7 f6 c2 01 0f 85 b3 00 00 00 66 90 48 8b 07 f6
[    6.171715] RSP: 0000:ffffb48f80267dc8 EFLAGS: 00000286
[    6.171847] RAX: ffffd757e80004c0 RBX: ffffa34583aef5d0 RCX: 0000000000000000
[    6.171998] RDX: 00005cba00000000 RSI: ffffffff95cb1ad5 RDI: ffffb48f80013cd0
[    6.172146] RBP: ffffb48f80013cd0 R08: 0000000000000001 R09: 0000000000000001
[    6.172293] R10: 0000000000000001 R11: 0000000000000001 R12: ffffb48f80267e74
[    6.172445] R13: 0000000000000000 R14: ffffa34583bf4000 R15: 0000000000000002
[    6.172620] FS:  0000000000000000(0000) GS:ffffa3458f800000(0000) knlGS:0000000000000000
[    6.172793] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    6.172917] CR2: ffffd757e80004c8 CR3: 0000000009048000 CR4: 00000000007506f0
[    6.173089] PKRU: 55555554
[    6.173177] Call Trace:
[    6.173313]  <TASK>
[    6.173494]  ? __die+0x1f/0x70
[    6.173600]  ? page_fault_oops+0x14a/0x460
[    6.173693]  ? search_exception_tables+0x37/0x50
[    6.173793]  ? fixup_exception+0x21/0x310
[    6.173887]  ? exc_page_fault+0xee/0x1c0
[    6.173977]  ? asm_exc_page_fault+0x26/0x30
[    6.174076]  ? kunit_filter_suites+0x3a5/0x460
[    6.174192]  ? kfree+0x61/0x140
[    6.174271]  ? kfree+0x106/0x140
[    6.174353]  kunit_filter_suites+0x3a5/0x460
[    6.174473]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10
[    6.174604]  filter_suites_test+0xea/0x2c0
[    6.174702]  ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10
[    6.174828]  kunit_generic_run_threadfn_adapter+0x15/0x20
[    6.174941]  kthread+0xef/0x120
[    6.175012]  ? __pfx_kthread+0x10/0x10
[    6.175098]  ret_from_fork+0x2f/0x50
[    6.175177]  ? __pfx_kthread+0x10/0x10
[    6.175260]  ret_from_fork_asm+0x1b/0x30
[    6.175387]  </TASK>
[    6.175466] Modules linked in:
[    6.175640] CR2: ffffd757e80004c8
[    6.175887] ---[ end trace 0000000000000000 ]---
[    6.176029] RIP: 0010:kfree+0x61/0x140
[    6.176119] Code: 80 48 01 e8 0f 82 ec 00 00 00 48 c7 c2 00 00 00 80 48 2b 15 29 bf aa 01 48 01 d0 48 c1 e8 0c 48 c1 e0 06 48 03 05 07 bf aa 01 <48> 8b 50 08 48 89 c7 f6 c2 01 0f 85 b3 00 00 00 66 90 48 8b 07 f6
[    6.176478] RSP: 0000:ffffb48f80267dc8 EFLAGS: 00000286
[    6.176589] RAX: ffffd757e80004c0 RBX: ffffa34583aef5d0 RCX: 0000000000000000
[    6.176729] RDX: 00005cba00000000 RSI: ffffffff95cb1ad5 RDI: ffffb48f80013cd0
[    6.176869] RBP: ffffb48f80013cd0 R08: 0000000000000001 R09: 0000000000000001
[    6.177008] R10: 0000000000000001 R11: 0000000000000001 R12: ffffb48f80267e74
[    6.177150] R13: 0000000000000000 R14: ffffa34583bf4000 R15: 0000000000000002
[    6.177290] FS:  0000000000000000(0000) GS:ffffa3458f800000(0000) knlGS:0000000000000000
[    6.177448] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    6.177564] CR2: ffffd757e80004c8 CR3: 0000000009048000 CR4: 00000000007506f0
[    6.177704] PKRU: 55555554
[    6.177807] note: kunit_try_catch[182] exited with irqs disabled

--
You received this message because you are subscribed to the Google Groups "KUnit Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/kunit-dev/59dc28b1-298c-4e7b-b35f-0b94071f4cb5%40roeck-us.net.

#19 lore raw
Quoted 13 lines · David Gow
Quoted 12 lines · Guenter Roeck
Quoted 4 lines · Rae Moar

Hello everyone,

This patch series adds a test attributes framework to KUnit.

With this series in linux-next, almost 50% of my boot tests crash, and many others show warnings tracebacks.

Example crash from x86_64 boot attempt attached.

Quoted 9 lines
Quoted 3 lines

Guenter

---

Hmm... It's not reproducing here (even with KASAN), which is strange.

Does the patch here help?
https://lore.kernel.org/linux-kselftest/[email protected]/

Yes, it does.

Guenter