attributes.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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 <rmoar@google.com>
  7. */
  8. #include <kunit/test.h>
  9. #include <kunit/attributes.h>
  10. /* Options for printing attributes:
  11. * PRINT_ALWAYS - attribute is printed for every test case and suite if set
  12. * PRINT_SUITE - attribute is printed for every suite if set but not for test cases
  13. * PRINT_NEVER - attribute is never printed
  14. */
  15. enum print_ops {
  16. PRINT_ALWAYS,
  17. PRINT_SUITE,
  18. PRINT_NEVER,
  19. };
  20. /**
  21. * struct kunit_attr - represents a test attribute and holds flexible
  22. * helper functions to interact with attribute.
  23. *
  24. * @name: name of test attribute, eg. speed
  25. * @get_attr: function to return attribute value given a test
  26. * @to_string: function to return string representation of given
  27. * attribute value
  28. * @filter: function to indicate whether a given attribute value passes a
  29. * filter
  30. * @attr_default: default attribute value used during filtering
  31. * @print: value of enum print_ops to indicate when to print attribute
  32. */
  33. struct kunit_attr {
  34. const char *name;
  35. void *(*get_attr)(void *test_or_suite, bool is_test);
  36. const char *(*to_string)(void *attr, bool *to_free);
  37. int (*filter)(void *attr, const char *input, int *err);
  38. void *attr_default;
  39. enum print_ops print;
  40. };
  41. /* String Lists for enum Attributes */
  42. static const char * const speed_str_list[] = {"unset", "very_slow", "slow", "normal"};
  43. /* To String Methods */
  44. static const char *attr_enum_to_string(void *attr, const char * const str_list[], bool *to_free)
  45. {
  46. long val = (long)attr;
  47. *to_free = false;
  48. if (!val)
  49. return NULL;
  50. return str_list[val];
  51. }
  52. static const char *attr_bool_to_string(void *attr, bool *to_free)
  53. {
  54. bool val = (bool)attr;
  55. *to_free = false;
  56. if (val)
  57. return "true";
  58. return "false";
  59. }
  60. static const char *attr_speed_to_string(void *attr, bool *to_free)
  61. {
  62. return attr_enum_to_string(attr, speed_str_list, to_free);
  63. }
  64. static const char *attr_string_to_string(void *attr, bool *to_free)
  65. {
  66. *to_free = false;
  67. return (char *) attr;
  68. }
  69. /* Filter Methods */
  70. static const char op_list[] = "<>!=";
  71. /*
  72. * Returns whether the inputted integer value matches the filter given
  73. * by the operation string and inputted integer.
  74. */
  75. static int int_filter(long val, const char *op, int input, int *err)
  76. {
  77. if (!strncmp(op, "<=", 2))
  78. return (val <= input);
  79. else if (!strncmp(op, ">=", 2))
  80. return (val >= input);
  81. else if (!strncmp(op, "!=", 2))
  82. return (val != input);
  83. else if (!strncmp(op, ">", 1))
  84. return (val > input);
  85. else if (!strncmp(op, "<", 1))
  86. return (val < input);
  87. else if (!strncmp(op, "=", 1))
  88. return (val == input);
  89. *err = -EINVAL;
  90. pr_err("kunit executor: invalid filter operation: %s\n", op);
  91. return false;
  92. }
  93. /*
  94. * Returns whether the inputted enum value "attr" matches the filter given
  95. * by the input string. Note: the str_list includes the corresponding string
  96. * list to the enum values.
  97. */
  98. static int attr_enum_filter(void *attr, const char *input, int *err,
  99. const char * const str_list[], int max)
  100. {
  101. int i, j, input_int = -1;
  102. long test_val = (long)attr;
  103. const char *input_val = NULL;
  104. for (i = 0; input[i]; i++) {
  105. if (!strchr(op_list, input[i])) {
  106. input_val = input + i;
  107. break;
  108. }
  109. }
  110. if (!input_val) {
  111. *err = -EINVAL;
  112. pr_err("kunit executor: filter value not found: %s\n", input);
  113. return false;
  114. }
  115. for (j = 0; j <= max; j++) {
  116. if (!strcmp(input_val, str_list[j]))
  117. input_int = j;
  118. }
  119. if (input_int < 0) {
  120. *err = -EINVAL;
  121. pr_err("kunit executor: invalid filter input: %s\n", input);
  122. return false;
  123. }
  124. return int_filter(test_val, input, input_int, err);
  125. }
  126. static int attr_speed_filter(void *attr, const char *input, int *err)
  127. {
  128. return attr_enum_filter(attr, input, err, speed_str_list, KUNIT_SPEED_MAX);
  129. }
  130. /*
  131. * Returns whether the inputted string value (attr) matches the filter given
  132. * by the input string.
  133. */
  134. static int attr_string_filter(void *attr, const char *input, int *err)
  135. {
  136. char *str = attr;
  137. if (!strncmp(input, "<", 1)) {
  138. *err = -EINVAL;
  139. pr_err("kunit executor: invalid filter input: %s\n", input);
  140. return false;
  141. } else if (!strncmp(input, ">", 1)) {
  142. *err = -EINVAL;
  143. pr_err("kunit executor: invalid filter input: %s\n", input);
  144. return false;
  145. } else if (!strncmp(input, "!=", 2)) {
  146. return (strcmp(input + 2, str) != 0);
  147. } else if (!strncmp(input, "=", 1)) {
  148. return (strcmp(input + 1, str) == 0);
  149. }
  150. *err = -EINVAL;
  151. pr_err("kunit executor: invalid filter operation: %s\n", input);
  152. return false;
  153. }
  154. static int attr_bool_filter(void *attr, const char *input, int *err)
  155. {
  156. int i, input_int = -1;
  157. long val = (long)attr;
  158. const char *input_str = NULL;
  159. for (i = 0; input[i]; i++) {
  160. if (!strchr(op_list, input[i])) {
  161. input_str = input + i;
  162. break;
  163. }
  164. }
  165. if (!input_str) {
  166. *err = -EINVAL;
  167. pr_err("kunit executor: filter value not found: %s\n", input);
  168. return false;
  169. }
  170. if (!strcmp(input_str, "true"))
  171. input_int = (int)true;
  172. else if (!strcmp(input_str, "false"))
  173. input_int = (int)false;
  174. else {
  175. *err = -EINVAL;
  176. pr_err("kunit executor: invalid filter input: %s\n", input);
  177. return false;
  178. }
  179. return int_filter(val, input, input_int, err);
  180. }
  181. /* Get Attribute Methods */
  182. static void *attr_speed_get(void *test_or_suite, bool is_test)
  183. {
  184. struct kunit_suite *suite = is_test ? NULL : test_or_suite;
  185. struct kunit_case *test = is_test ? test_or_suite : NULL;
  186. if (test)
  187. return ((void *) test->attr.speed);
  188. else
  189. return ((void *) suite->attr.speed);
  190. }
  191. static void *attr_module_get(void *test_or_suite, bool is_test)
  192. {
  193. struct kunit_suite *suite = is_test ? NULL : test_or_suite;
  194. struct kunit_case *test = is_test ? test_or_suite : NULL;
  195. // Suites get their module attribute from their first test_case
  196. if (test)
  197. return ((void *) test->module_name);
  198. else if (kunit_suite_num_test_cases(suite) > 0)
  199. return ((void *) suite->test_cases[0].module_name);
  200. else
  201. return (void *) "";
  202. }
  203. static void *attr_is_init_get(void *test_or_suite, bool is_test)
  204. {
  205. struct kunit_suite *suite = is_test ? NULL : test_or_suite;
  206. struct kunit_case *test = is_test ? test_or_suite : NULL;
  207. if (test)
  208. return ((void *) NULL);
  209. else
  210. return ((void *) suite->is_init);
  211. }
  212. /* List of all Test Attributes */
  213. static struct kunit_attr kunit_attr_list[] = {
  214. {
  215. .name = "speed",
  216. .get_attr = attr_speed_get,
  217. .to_string = attr_speed_to_string,
  218. .filter = attr_speed_filter,
  219. .attr_default = (void *)KUNIT_SPEED_NORMAL,
  220. .print = PRINT_ALWAYS,
  221. },
  222. {
  223. .name = "module",
  224. .get_attr = attr_module_get,
  225. .to_string = attr_string_to_string,
  226. .filter = attr_string_filter,
  227. .attr_default = (void *)"",
  228. .print = PRINT_SUITE,
  229. },
  230. {
  231. .name = "is_init",
  232. .get_attr = attr_is_init_get,
  233. .to_string = attr_bool_to_string,
  234. .filter = attr_bool_filter,
  235. .attr_default = (void *)false,
  236. .print = PRINT_SUITE,
  237. }
  238. };
  239. /* Helper Functions to Access Attributes */
  240. const char *kunit_attr_filter_name(struct kunit_attr_filter filter)
  241. {
  242. return filter.attr->name;
  243. }
  244. void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level)
  245. {
  246. int i;
  247. bool to_free = false;
  248. void *attr;
  249. const char *attr_name, *attr_str;
  250. struct kunit_suite *suite = is_test ? NULL : test_or_suite;
  251. struct kunit_case *test = is_test ? test_or_suite : NULL;
  252. for (i = 0; i < ARRAY_SIZE(kunit_attr_list); i++) {
  253. if (kunit_attr_list[i].print == PRINT_NEVER ||
  254. (test && kunit_attr_list[i].print == PRINT_SUITE))
  255. continue;
  256. attr = kunit_attr_list[i].get_attr(test_or_suite, is_test);
  257. if (attr) {
  258. attr_name = kunit_attr_list[i].name;
  259. attr_str = kunit_attr_list[i].to_string(attr, &to_free);
  260. if (test) {
  261. kunit_log(KERN_INFO, test, "%*s# %s.%s: %s",
  262. KUNIT_INDENT_LEN * test_level, "", test->name,
  263. attr_name, attr_str);
  264. } else {
  265. kunit_log(KERN_INFO, suite, "%*s# %s: %s",
  266. KUNIT_INDENT_LEN * test_level, "", attr_name, attr_str);
  267. }
  268. /* Free to_string of attribute if needed */
  269. if (to_free)
  270. kfree(attr_str);
  271. }
  272. }
  273. }
  274. /* Helper Functions to Filter Attributes */
  275. int kunit_get_filter_count(char *input)
  276. {
  277. int i, comma_index = 0, count = 0;
  278. for (i = 0; input[i]; i++) {
  279. if (input[i] == ',') {
  280. if ((i - comma_index) > 1)
  281. count++;
  282. comma_index = i;
  283. }
  284. }
  285. if ((i - comma_index) > 0)
  286. count++;
  287. return count;
  288. }
  289. struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err)
  290. {
  291. struct kunit_attr_filter filter = {};
  292. int i, j, comma_index = 0, new_start_index = 0;
  293. int op_index = -1, attr_index = -1;
  294. char op;
  295. char *input = *filters;
  296. /* Parse input until operation */
  297. for (i = 0; input[i]; i++) {
  298. if (op_index < 0 && strchr(op_list, input[i])) {
  299. op_index = i;
  300. } else if (!comma_index && input[i] == ',') {
  301. comma_index = i;
  302. } else if (comma_index && input[i] != ' ') {
  303. new_start_index = i;
  304. break;
  305. }
  306. }
  307. if (op_index <= 0) {
  308. *err = -EINVAL;
  309. pr_err("kunit executor: filter operation not found: %s\n", input);
  310. return filter;
  311. }
  312. /* Temporarily set operator to \0 character. */
  313. op = input[op_index];
  314. input[op_index] = '\0';
  315. /* Find associated kunit_attr object */
  316. for (j = 0; j < ARRAY_SIZE(kunit_attr_list); j++) {
  317. if (!strcmp(input, kunit_attr_list[j].name)) {
  318. attr_index = j;
  319. break;
  320. }
  321. }
  322. input[op_index] = op;
  323. if (attr_index < 0) {
  324. *err = -EINVAL;
  325. pr_err("kunit executor: attribute not found: %s\n", input);
  326. } else {
  327. filter.attr = &kunit_attr_list[attr_index];
  328. }
  329. if (comma_index > 0) {
  330. input[comma_index] = '\0';
  331. filter.input = input + op_index;
  332. input = input + new_start_index;
  333. } else {
  334. filter.input = input + op_index;
  335. input = NULL;
  336. }
  337. *filters = input;
  338. return filter;
  339. }
  340. struct kunit_suite *kunit_filter_attr_tests(const struct kunit_suite *const suite,
  341. struct kunit_attr_filter filter, char *action, int *err)
  342. {
  343. int n = 0;
  344. struct kunit_case *filtered, *test_case;
  345. struct kunit_suite *copy;
  346. void *suite_val, *test_val;
  347. bool suite_result, test_result, default_result, result;
  348. /* Allocate memory for new copy of suite and list of test cases */
  349. copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL);
  350. if (!copy)
  351. return ERR_PTR(-ENOMEM);
  352. kunit_suite_for_each_test_case(suite, test_case) { n++; }
  353. filtered = kzalloc_objs(*filtered, n + 1);
  354. if (!filtered) {
  355. kfree(copy);
  356. return ERR_PTR(-ENOMEM);
  357. }
  358. n = 0;
  359. /* Save filtering result on default value */
  360. default_result = filter.attr->filter(filter.attr->attr_default, filter.input, err);
  361. if (*err)
  362. goto err;
  363. /* Save suite attribute value and filtering result on that value */
  364. suite_val = filter.attr->get_attr((void *)suite, false);
  365. suite_result = filter.attr->filter(suite_val, filter.input, err);
  366. if (*err)
  367. goto err;
  368. /* For each test case, save test case if passes filtering. */
  369. kunit_suite_for_each_test_case(suite, test_case) {
  370. test_val = filter.attr->get_attr((void *) test_case, true);
  371. test_result = filter.attr->filter(filter.attr->get_attr(test_case, true),
  372. filter.input, err);
  373. if (*err)
  374. goto err;
  375. /*
  376. * If attribute value of test case is set, filter on that value.
  377. * If not, filter on suite value if set. If not, filter on
  378. * default value.
  379. */
  380. result = false;
  381. if (test_val) {
  382. if (test_result)
  383. result = true;
  384. } else if (suite_val) {
  385. if (suite_result)
  386. result = true;
  387. } else if (default_result) {
  388. result = true;
  389. }
  390. if (result) {
  391. filtered[n++] = *test_case;
  392. } else if (action && strcmp(action, "skip") == 0) {
  393. test_case->status = KUNIT_SKIPPED;
  394. filtered[n++] = *test_case;
  395. }
  396. }
  397. err:
  398. if (n == 0 || *err) {
  399. kfree(copy);
  400. kfree(filtered);
  401. return NULL;
  402. }
  403. copy->test_cases = filtered;
  404. return copy;
  405. }