Skip to content

Commit 39e92cb

Browse files
rmr167shuahkh
authored andcommitted
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. Reviewed-by: David Gow <[email protected]> Signed-off-by: Rae Moar <[email protected]> Signed-off-by: Shuah Khan <[email protected]>
1 parent 64bd464 commit 39e92cb

File tree

6 files changed

+161
-12
lines changed

6 files changed

+161
-12
lines changed

include/kunit/attributes.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* KUnit API to save and access test attributes
4+
*
5+
* Copyright (C) 2023, Google LLC.
6+
* Author: Rae Moar <[email protected]>
7+
*/
8+
9+
#ifndef _KUNIT_ATTRIBUTES_H
10+
#define _KUNIT_ATTRIBUTES_H
11+
12+
/*
13+
* Print all test attributes for a test case or suite.
14+
* Output format for test cases: "# <test_name>.<attribute>: <value>"
15+
* Output format for test suites: "# <attribute>: <value>"
16+
*/
17+
void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level);
18+
19+
#endif /* _KUNIT_ATTRIBUTES_H */

include/kunit/test.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,16 @@ enum kunit_status {
6363
KUNIT_SKIPPED,
6464
};
6565

66+
/* Holds attributes for each test case and suite */
67+
struct kunit_attributes {};
68+
6669
/**
6770
* struct kunit_case - represents an individual test case.
6871
*
6972
* @run_case: the function representing the actual test case.
7073
* @name: the name of the test case.
7174
* @generate_params: the generator function for parameterized tests.
75+
* @attr: the attributes associated with the test
7276
*
7377
* A test case is a function with the signature,
7478
* ``void (*)(struct kunit *)``
@@ -104,6 +108,7 @@ struct kunit_case {
104108
void (*run_case)(struct kunit *test);
105109
const char *name;
106110
const void* (*generate_params)(const void *prev, char *desc);
111+
struct kunit_attributes attr;
107112

108113
/* private: internal use only. */
109114
enum kunit_status status;
@@ -133,6 +138,18 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
133138
*/
134139
#define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name }
135140

141+
/**
142+
* KUNIT_CASE_ATTR - A helper for creating a &struct kunit_case
143+
* with attributes
144+
*
145+
* @test_name: a reference to a test case function.
146+
* @attributes: a reference to a struct kunit_attributes object containing
147+
* test attributes
148+
*/
149+
#define KUNIT_CASE_ATTR(test_name, attributes) \
150+
{ .run_case = test_name, .name = #test_name, \
151+
.attr = attributes }
152+
136153
/**
137154
* KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case
138155
*
@@ -154,6 +171,20 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
154171
{ .run_case = test_name, .name = #test_name, \
155172
.generate_params = gen_params }
156173

174+
/**
175+
* KUNIT_CASE_PARAM_ATTR - A helper for creating a parameterized &struct
176+
* kunit_case with attributes
177+
*
178+
* @test_name: a reference to a test case function.
179+
* @gen_params: a reference to a parameter generator function.
180+
* @attributes: a reference to a struct kunit_attributes object containing
181+
* test attributes
182+
*/
183+
#define KUNIT_CASE_PARAM_ATTR(test_name, gen_params, attributes) \
184+
{ .run_case = test_name, .name = #test_name, \
185+
.generate_params = gen_params, \
186+
.attr = attributes }
187+
157188
/**
158189
* struct kunit_suite - describes a related collection of &struct kunit_case
159190
*
@@ -163,6 +194,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
163194
* @init: called before every test case.
164195
* @exit: called after every test case.
165196
* @test_cases: a null terminated array of test cases.
197+
* @attr: the attributes associated with the test suite
166198
*
167199
* A kunit_suite is a collection of related &struct kunit_case s, such that
168200
* @init is called before every test case and @exit is called after every
@@ -182,6 +214,7 @@ struct kunit_suite {
182214
int (*init)(struct kunit *test);
183215
void (*exit)(struct kunit *test);
184216
struct kunit_case *test_cases;
217+
struct kunit_attributes attr;
185218

186219
/* private: internal use only */
187220
char status_comment[KUNIT_STATUS_COMMENT_SIZE];

lib/kunit/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ kunit-objs += test.o \
66
string-stream.o \
77
assert.o \
88
try-catch.o \
9-
executor.o
9+
executor.o \
10+
attributes.o
1011

1112
ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
1213
kunit-objs += debugfs.o

lib/kunit/attributes.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* KUnit API to save and access test attributes
4+
*
5+
* Copyright (C) 2023, Google LLC.
6+
* Author: Rae Moar <[email protected]>
7+
*/
8+
9+
#include <kunit/test.h>
10+
#include <kunit/attributes.h>
11+
12+
/* Options for printing attributes:
13+
* PRINT_ALWAYS - attribute is printed for every test case and suite if set
14+
* PRINT_SUITE - attribute is printed for every suite if set but not for test cases
15+
* PRINT_NEVER - attribute is never printed
16+
*/
17+
enum print_ops {
18+
PRINT_ALWAYS,
19+
PRINT_SUITE,
20+
PRINT_NEVER,
21+
};
22+
23+
/**
24+
* struct kunit_attr - represents a test attribute and holds flexible
25+
* helper functions to interact with attribute.
26+
*
27+
* @name: name of test attribute, eg. speed
28+
* @get_attr: function to return attribute value given a test
29+
* @to_string: function to return string representation of given
30+
* attribute value
31+
* @filter: function to indicate whether a given attribute value passes a
32+
* filter
33+
*/
34+
struct kunit_attr {
35+
const char *name;
36+
void *(*get_attr)(void *test_or_suite, bool is_test);
37+
const char *(*to_string)(void *attr, bool *to_free);
38+
int (*filter)(void *attr, const char *input, int *err);
39+
void *attr_default;
40+
enum print_ops print;
41+
};
42+
43+
/* List of all Test Attributes */
44+
45+
static struct kunit_attr kunit_attr_list[] = {};
46+
47+
/* Helper Functions to Access Attributes */
48+
49+
void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level)
50+
{
51+
int i;
52+
bool to_free;
53+
void *attr;
54+
const char *attr_name, *attr_str;
55+
struct kunit_suite *suite = is_test ? NULL : test_or_suite;
56+
struct kunit_case *test = is_test ? test_or_suite : NULL;
57+
58+
for (i = 0; i < ARRAY_SIZE(kunit_attr_list); i++) {
59+
if (kunit_attr_list[i].print == PRINT_NEVER ||
60+
(test && kunit_attr_list[i].print == PRINT_SUITE))
61+
continue;
62+
attr = kunit_attr_list[i].get_attr(test_or_suite, is_test);
63+
if (attr) {
64+
attr_name = kunit_attr_list[i].name;
65+
attr_str = kunit_attr_list[i].to_string(attr, &to_free);
66+
if (test) {
67+
kunit_log(KERN_INFO, test, "%*s# %s.%s: %s",
68+
KUNIT_INDENT_LEN * test_level, "", test->name,
69+
attr_name, attr_str);
70+
} else {
71+
kunit_log(KERN_INFO, suite, "%*s# %s: %s",
72+
KUNIT_INDENT_LEN * test_level, "", attr_name, attr_str);
73+
}
74+
75+
/* Free to_string of attribute if needed */
76+
if (to_free)
77+
kfree(attr_str);
78+
}
79+
}
80+
}

lib/kunit/executor.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <linux/reboot.h>
44
#include <kunit/test.h>
5+
#include <kunit/attributes.h>
56
#include <linux/glob.h>
67
#include <linux/moduleparam.h>
78

@@ -24,7 +25,8 @@ module_param_named(action, action_param, charp, 0);
2425
MODULE_PARM_DESC(action,
2526
"Changes KUnit executor behavior, valid values are:\n"
2627
"<none>: run the tests like normal\n"
27-
"'list' to list test names instead of running them.\n");
28+
"'list' to list test names instead of running them.\n"
29+
"'list_attr' to list test names and attributes instead of running them.\n");
2830

2931
/* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */
3032
struct kunit_test_filter {
@@ -172,18 +174,27 @@ static void kunit_exec_run_tests(struct suite_set *suite_set)
172174
__kunit_test_suites_init(suite_set->start, num_suites);
173175
}
174176

175-
static void kunit_exec_list_tests(struct suite_set *suite_set)
177+
static void kunit_exec_list_tests(struct suite_set *suite_set, bool include_attr)
176178
{
177179
struct kunit_suite * const *suites;
178180
struct kunit_case *test_case;
179181

180182
/* Hack: print a ktap header so kunit.py can find the start of KUnit output. */
181183
pr_info("KTAP version 1\n");
182184

183-
for (suites = suite_set->start; suites < suite_set->end; suites++)
185+
for (suites = suite_set->start; suites < suite_set->end; suites++) {
186+
/* Print suite name and suite attributes */
187+
pr_info("%s\n", (*suites)->name);
188+
if (include_attr)
189+
kunit_print_attr((void *)(*suites), false, 0);
190+
191+
/* Print test case name and attributes in suite */
184192
kunit_suite_for_each_test_case((*suites), test_case) {
185193
pr_info("%s.%s\n", (*suites)->name, test_case->name);
194+
if (include_attr)
195+
kunit_print_attr((void *)test_case, true, 0);
186196
}
197+
}
187198
}
188199

189200
int kunit_run_all_tests(void)
@@ -206,7 +217,9 @@ int kunit_run_all_tests(void)
206217
if (!action_param)
207218
kunit_exec_run_tests(&suite_set);
208219
else if (strcmp(action_param, "list") == 0)
209-
kunit_exec_list_tests(&suite_set);
220+
kunit_exec_list_tests(&suite_set, false);
221+
else if (strcmp(action_param, "list_attr") == 0)
222+
kunit_exec_list_tests(&suite_set, true);
210223
else
211224
pr_err("kunit executor: unknown action '%s'\n", action_param);
212225

lib/kunit/test.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <kunit/resource.h>
1010
#include <kunit/test.h>
1111
#include <kunit/test-bug.h>
12+
#include <kunit/attributes.h>
1213
#include <linux/kernel.h>
1314
#include <linux/module.h>
1415
#include <linux/moduleparam.h>
@@ -168,6 +169,13 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite)
168169
}
169170
EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases);
170171

172+
/* Currently supported test levels */
173+
enum {
174+
KUNIT_LEVEL_SUITE = 0,
175+
KUNIT_LEVEL_CASE,
176+
KUNIT_LEVEL_CASE_PARAM,
177+
};
178+
171179
static void kunit_print_suite_start(struct kunit_suite *suite)
172180
{
173181
/*
@@ -181,17 +189,11 @@ static void kunit_print_suite_start(struct kunit_suite *suite)
181189
pr_info(KUNIT_SUBTEST_INDENT "KTAP version 1\n");
182190
pr_info(KUNIT_SUBTEST_INDENT "# Subtest: %s\n",
183191
suite->name);
192+
kunit_print_attr((void *)suite, false, KUNIT_LEVEL_CASE);
184193
pr_info(KUNIT_SUBTEST_INDENT "1..%zd\n",
185194
kunit_suite_num_test_cases(suite));
186195
}
187196

188-
/* Currently supported test levels */
189-
enum {
190-
KUNIT_LEVEL_SUITE = 0,
191-
KUNIT_LEVEL_CASE,
192-
KUNIT_LEVEL_CASE_PARAM,
193-
};
194-
195197
static void kunit_print_ok_not_ok(struct kunit *test,
196198
unsigned int test_level,
197199
enum kunit_status status,
@@ -651,6 +653,7 @@ int kunit_run_tests(struct kunit_suite *suite)
651653
}
652654
}
653655

656+
kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
654657

655658
kunit_print_test_stats(&test, param_stats);
656659

0 commit comments

Comments
 (0)