Skip to content

Commit 529534e

Browse files
rmr167shuahkh
authored andcommitted
kunit: Add ability to filter attributes
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. Reviewed-by: David Gow <[email protected]> Signed-off-by: Rae Moar <[email protected]> Signed-off-by: Shuah Khan <[email protected]>
1 parent a00a727 commit 529534e

File tree

5 files changed

+389
-28
lines changed

5 files changed

+389
-28
lines changed

include/kunit/attributes.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,42 @@
99
#ifndef _KUNIT_ATTRIBUTES_H
1010
#define _KUNIT_ATTRIBUTES_H
1111

12+
/*
13+
* struct kunit_attr_filter - representation of attributes filter with the
14+
* attribute object and string input
15+
*/
16+
struct kunit_attr_filter {
17+
struct kunit_attr *attr;
18+
char *input;
19+
};
20+
21+
/*
22+
* Returns the name of the filter's attribute.
23+
*/
24+
const char *kunit_attr_filter_name(struct kunit_attr_filter filter);
25+
1226
/*
1327
* Print all test attributes for a test case or suite.
1428
* Output format for test cases: "# <test_name>.<attribute>: <value>"
1529
* Output format for test suites: "# <attribute>: <value>"
1630
*/
1731
void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level);
1832

33+
/*
34+
* Returns the number of fitlers in input.
35+
*/
36+
int kunit_get_filter_count(char *input);
37+
38+
/*
39+
* Parse attributes filter input and return an objects containing the
40+
* attribute object and the string input of the next filter.
41+
*/
42+
struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err);
43+
44+
/*
45+
* Returns a copy of the suite containing only tests that pass the filter.
46+
*/
47+
struct kunit_suite *kunit_filter_attr_tests(const struct kunit_suite *const suite,
48+
struct kunit_attr_filter filter, char *action, int *err);
49+
1950
#endif /* _KUNIT_ATTRIBUTES_H */

lib/kunit/attributes.c

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,104 @@ static const char *attr_string_to_string(void *attr, bool *to_free)
6767
return (char *) attr;
6868
}
6969

70+
/* Filter Methods */
71+
72+
static const char op_list[] = "<>!=";
73+
74+
/*
75+
* Returns whether the inputted integer value matches the filter given
76+
* by the operation string and inputted integer.
77+
*/
78+
static int int_filter(long val, const char *op, int input, int *err)
79+
{
80+
if (!strncmp(op, "<=", 2))
81+
return (val <= input);
82+
else if (!strncmp(op, ">=", 2))
83+
return (val >= input);
84+
else if (!strncmp(op, "!=", 2))
85+
return (val != input);
86+
else if (!strncmp(op, ">", 1))
87+
return (val > input);
88+
else if (!strncmp(op, "<", 1))
89+
return (val < input);
90+
else if (!strncmp(op, "=", 1))
91+
return (val == input);
92+
*err = -EINVAL;
93+
pr_err("kunit executor: invalid filter operation: %s\n", op);
94+
return false;
95+
}
96+
97+
/*
98+
* Returns whether the inputted enum value "attr" matches the filter given
99+
* by the input string. Note: the str_list includes the corresponding string
100+
* list to the enum values.
101+
*/
102+
static int attr_enum_filter(void *attr, const char *input, int *err,
103+
const char * const str_list[], int max)
104+
{
105+
int i, j, input_int;
106+
long test_val = (long)attr;
107+
const char *input_val = NULL;
108+
109+
for (i = 0; input[i]; i++) {
110+
if (!strchr(op_list, input[i])) {
111+
input_val = input + i;
112+
break;
113+
}
114+
}
115+
116+
if (!input_val) {
117+
*err = -EINVAL;
118+
pr_err("kunit executor: filter value not found: %s\n", input);
119+
return false;
120+
}
121+
122+
for (j = 0; j <= max; j++) {
123+
if (!strcmp(input_val, str_list[j]))
124+
input_int = j;
125+
}
126+
127+
if (!input_int) {
128+
*err = -EINVAL;
129+
pr_err("kunit executor: invalid filter input: %s\n", input);
130+
return false;
131+
}
132+
133+
return int_filter(test_val, input, input_int, err);
134+
}
135+
136+
static int attr_speed_filter(void *attr, const char *input, int *err)
137+
{
138+
return attr_enum_filter(attr, input, err, speed_str_list, KUNIT_SPEED_MAX);
139+
}
140+
141+
/*
142+
* Returns whether the inputted string value (attr) matches the filter given
143+
* by the input string.
144+
*/
145+
static int attr_string_filter(void *attr, const char *input, int *err)
146+
{
147+
char *str = attr;
148+
149+
if (!strncmp(input, "<", 1)) {
150+
*err = -EINVAL;
151+
pr_err("kunit executor: invalid filter input: %s\n", input);
152+
return false;
153+
} else if (!strncmp(input, ">", 1)) {
154+
*err = -EINVAL;
155+
pr_err("kunit executor: invalid filter input: %s\n", input);
156+
return false;
157+
} else if (!strncmp(input, "!=", 2)) {
158+
return (strcmp(input + 2, str) != 0);
159+
} else if (!strncmp(input, "=", 1)) {
160+
return (strcmp(input + 1, str) == 0);
161+
}
162+
*err = -EINVAL;
163+
pr_err("kunit executor: invalid filter operation: %s\n", input);
164+
return false;
165+
}
166+
167+
70168
/* Get Attribute Methods */
71169

72170
static void *attr_speed_get(void *test_or_suite, bool is_test)
@@ -99,20 +197,27 @@ static struct kunit_attr kunit_attr_list[] = {
99197
.name = "speed",
100198
.get_attr = attr_speed_get,
101199
.to_string = attr_speed_to_string,
200+
.filter = attr_speed_filter,
102201
.attr_default = (void *)KUNIT_SPEED_NORMAL,
103202
.print = PRINT_ALWAYS,
104203
},
105204
{
106205
.name = "module",
107206
.get_attr = attr_module_get,
108207
.to_string = attr_string_to_string,
208+
.filter = attr_string_filter,
109209
.attr_default = (void *)"",
110210
.print = PRINT_SUITE,
111211
}
112212
};
113213

114214
/* Helper Functions to Access Attributes */
115215

216+
const char *kunit_attr_filter_name(struct kunit_attr_filter filter)
217+
{
218+
return filter.attr->name;
219+
}
220+
116221
void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level)
117222
{
118223
int i;
@@ -145,3 +250,169 @@ void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level
145250
}
146251
}
147252
}
253+
254+
/* Helper Functions to Filter Attributes */
255+
256+
int kunit_get_filter_count(char *input)
257+
{
258+
int i, comma_index, count = 0;
259+
260+
for (i = 0; input[i]; i++) {
261+
if (input[i] == ',') {
262+
if ((i - comma_index) > 1)
263+
count++;
264+
comma_index = i;
265+
}
266+
}
267+
if ((i - comma_index) > 0)
268+
count++;
269+
return count;
270+
}
271+
272+
struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err)
273+
{
274+
struct kunit_attr_filter filter = {};
275+
int i, j, comma_index, new_start_index;
276+
int op_index = -1, attr_index = -1;
277+
char op;
278+
char *input = *filters;
279+
280+
/* Parse input until operation */
281+
for (i = 0; input[i]; i++) {
282+
if (op_index < 0 && strchr(op_list, input[i])) {
283+
op_index = i;
284+
} else if (!comma_index && input[i] == ',') {
285+
comma_index = i;
286+
} else if (comma_index && input[i] != ' ') {
287+
new_start_index = i;
288+
break;
289+
}
290+
}
291+
292+
if (op_index <= 0) {
293+
*err = -EINVAL;
294+
pr_err("kunit executor: filter operation not found: %s\n", input);
295+
return filter;
296+
}
297+
298+
/* Temporarily set operator to \0 character. */
299+
op = input[op_index];
300+
input[op_index] = '\0';
301+
302+
/* Find associated kunit_attr object */
303+
for (j = 0; j < ARRAY_SIZE(kunit_attr_list); j++) {
304+
if (!strcmp(input, kunit_attr_list[j].name)) {
305+
attr_index = j;
306+
break;
307+
}
308+
}
309+
310+
input[op_index] = op;
311+
312+
if (attr_index < 0) {
313+
*err = -EINVAL;
314+
pr_err("kunit executor: attribute not found: %s\n", input);
315+
} else {
316+
filter.attr = &kunit_attr_list[attr_index];
317+
}
318+
319+
if (comma_index) {
320+
input[comma_index] = '\0';
321+
filter.input = input + op_index;
322+
input = input + new_start_index;
323+
} else {
324+
filter.input = input + op_index;
325+
input = NULL;
326+
}
327+
328+
*filters = input;
329+
330+
return filter;
331+
}
332+
333+
struct kunit_suite *kunit_filter_attr_tests(const struct kunit_suite *const suite,
334+
struct kunit_attr_filter filter, char *action, int *err)
335+
{
336+
int n = 0;
337+
struct kunit_case *filtered, *test_case;
338+
struct kunit_suite *copy;
339+
void *suite_val, *test_val;
340+
bool suite_result, test_result, default_result, result;
341+
342+
/* Allocate memory for new copy of suite and list of test cases */
343+
copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL);
344+
if (!copy)
345+
return ERR_PTR(-ENOMEM);
346+
347+
kunit_suite_for_each_test_case(suite, test_case) { n++; }
348+
349+
filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
350+
if (!filtered) {
351+
kfree(copy);
352+
return ERR_PTR(-ENOMEM);
353+
}
354+
355+
n = 0;
356+
357+
/* Save filtering result on default value */
358+
default_result = filter.attr->filter(filter.attr->attr_default, filter.input, err);
359+
if (*err) {
360+
kfree(copy);
361+
kfree(filtered);
362+
return NULL;
363+
}
364+
365+
/* Save suite attribute value and filtering result on that value */
366+
suite_val = filter.attr->get_attr((void *)suite, false);
367+
suite_result = filter.attr->filter(suite_val, filter.input, err);
368+
if (*err) {
369+
kfree(copy);
370+
kfree(filtered);
371+
return NULL;
372+
}
373+
374+
/* For each test case, save test case if passes filtering. */
375+
kunit_suite_for_each_test_case(suite, test_case) {
376+
test_val = filter.attr->get_attr((void *) test_case, true);
377+
test_result = filter.attr->filter(filter.attr->get_attr(test_case, true),
378+
filter.input, err);
379+
if (*err) {
380+
kfree(copy);
381+
kfree(filtered);
382+
return NULL;
383+
}
384+
385+
/*
386+
* If attribute value of test case is set, filter on that value.
387+
* If not, filter on suite value if set. If not, filter on
388+
* default value.
389+
*/
390+
result = false;
391+
if (test_val) {
392+
if (test_result)
393+
result = true;
394+
} else if (suite_val) {
395+
if (suite_result)
396+
result = true;
397+
} else if (default_result) {
398+
result = true;
399+
}
400+
401+
if (result) {
402+
filtered[n++] = *test_case;
403+
} else if (action && strcmp(action, "skip") == 0) {
404+
test_case->status = KUNIT_SKIPPED;
405+
filtered[n++] = *test_case;
406+
}
407+
}
408+
409+
if (n == 0) {
410+
kfree(copy);
411+
kfree(filtered);
412+
return NULL;
413+
}
414+
415+
copy->test_cases = filtered;
416+
417+
return copy;
418+
}

0 commit comments

Comments
 (0)