@@ -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
72170static 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+
116221void 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