Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kunit: flatten kunit_suite*** to kunit_suite** in .kunit_test_suites

We currently store kunit suites in the .kunit_test_suites ELF section as
a `struct kunit_suite***` (modulo some `const`s).
For every test file, we store a struct kunit_suite** NULL-terminated array.

This adds quite a bit of complexity to the test filtering code in the
executor.

Instead, let's just make the .kunit_test_suites section contain a single
giant array of struct kunit_suite pointers, which can then be directly
manipulated. This array is not NULL-terminated, and so none of the test
filtering code needs to NULL-terminate anything.

Tested-by: Maíra Canal <maira.canal@usp.br>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Co-developed-by: David Gow <davidgow@google.com>
Signed-off-by: David Gow <davidgow@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Daniel Latypov and committed by
Shuah Khan
e5857d39 3d6e4462

+82 -210
+6 -7
include/kunit/test.h
··· 237 237 unsigned int kunit_test_case_num(struct kunit_suite *suite, 238 238 struct kunit_case *test_case); 239 239 240 - int __kunit_test_suites_init(struct kunit_suite * const * const suites); 240 + int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites); 241 241 242 - void __kunit_test_suites_exit(struct kunit_suite **suites); 242 + void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites); 243 243 244 244 #if IS_BUILTIN(CONFIG_KUNIT) 245 245 int kunit_run_all_tests(void); ··· 250 250 } 251 251 #endif /* IS_BUILTIN(CONFIG_KUNIT) */ 252 252 253 - #define __kunit_test_suites(unique_array, unique_suites, ...) \ 253 + #define __kunit_test_suites(unique_array, ...) \ 254 254 MODULE_INFO(test, "Y"); \ 255 - static struct kunit_suite *unique_array[] = { __VA_ARGS__, NULL }; \ 256 - static struct kunit_suite **unique_suites \ 257 - __used __section(".kunit_test_suites") = unique_array 255 + static struct kunit_suite *unique_array[] \ 256 + __aligned(sizeof(struct kunit_suite *)) \ 257 + __used __section(".kunit_test_suites") = { __VA_ARGS__ } 258 258 259 259 /** 260 260 * kunit_test_suites() - used to register one or more &struct kunit_suite ··· 272 272 */ 273 273 #define kunit_test_suites(__suites...) \ 274 274 __kunit_test_suites(__UNIQUE_ID(array), \ 275 - __UNIQUE_ID(suites), \ 276 275 ##__suites) 277 276 278 277 #define kunit_test_suite(suite) kunit_test_suites(&suite)
+1 -1
include/linux/module.h
··· 507 507 #endif 508 508 #if IS_ENABLED(CONFIG_KUNIT) 509 509 int num_kunit_suites; 510 - struct kunit_suite ***kunit_suites; 510 + struct kunit_suite **kunit_suites; 511 511 #endif 512 512 513 513
+28 -87
lib/kunit/executor.c
··· 9 9 * These symbols point to the .kunit_test_suites section and are defined in 10 10 * include/asm-generic/vmlinux.lds.h, and consequently must be extern. 11 11 */ 12 - extern struct kunit_suite * const * const __kunit_suites_start[]; 13 - extern struct kunit_suite * const * const __kunit_suites_end[]; 12 + extern struct kunit_suite * const __kunit_suites_start[]; 13 + extern struct kunit_suite * const __kunit_suites_end[]; 14 14 15 15 #if IS_BUILTIN(CONFIG_KUNIT) 16 16 ··· 90 90 static char *kunit_shutdown; 91 91 core_param(kunit_shutdown, kunit_shutdown, charp, 0644); 92 92 93 - static struct kunit_suite * const * 94 - kunit_filter_subsuite(struct kunit_suite * const * const subsuite, 95 - struct kunit_test_filter *filter) 96 - { 97 - int i, n = 0; 98 - struct kunit_suite **filtered, *filtered_suite; 99 - 100 - n = 0; 101 - for (i = 0; subsuite[i]; ++i) { 102 - if (glob_match(filter->suite_glob, subsuite[i]->name)) 103 - ++n; 104 - } 105 - 106 - if (n == 0) 107 - return NULL; 108 - 109 - filtered = kmalloc_array(n + 1, sizeof(*filtered), GFP_KERNEL); 110 - if (!filtered) 111 - return ERR_PTR(-ENOMEM); 112 - 113 - n = 0; 114 - for (i = 0; subsuite[i] != NULL; ++i) { 115 - if (!glob_match(filter->suite_glob, subsuite[i]->name)) 116 - continue; 117 - filtered_suite = kunit_filter_tests(subsuite[i], filter->test_glob); 118 - if (IS_ERR(filtered_suite)) 119 - return ERR_CAST(filtered_suite); 120 - else if (filtered_suite) 121 - filtered[n++] = filtered_suite; 122 - } 123 - filtered[n] = NULL; 124 - 125 - return filtered; 126 - } 127 - 93 + /* Stores an array of suites, end points one past the end */ 128 94 struct suite_set { 129 - struct kunit_suite * const * const *start; 130 - struct kunit_suite * const * const *end; 95 + struct kunit_suite * const *start; 96 + struct kunit_suite * const *end; 131 97 }; 132 - 133 - static void kunit_free_subsuite(struct kunit_suite * const *subsuite) 134 - { 135 - unsigned int i; 136 - 137 - for (i = 0; subsuite[i]; i++) 138 - kfree(subsuite[i]); 139 - 140 - kfree(subsuite); 141 - } 142 98 143 99 static void kunit_free_suite_set(struct suite_set suite_set) 144 100 { 145 - struct kunit_suite * const * const *suites; 101 + struct kunit_suite * const *suites; 146 102 147 103 for (suites = suite_set.start; suites < suite_set.end; suites++) 148 - kunit_free_subsuite(*suites); 104 + kfree(*suites); 149 105 kfree(suite_set.start); 150 106 } 151 107 ··· 110 154 int *err) 111 155 { 112 156 int i; 113 - struct kunit_suite * const **copy, * const *filtered_subsuite; 157 + struct kunit_suite **copy, *filtered_suite; 114 158 struct suite_set filtered; 115 159 struct kunit_test_filter filter; 116 160 ··· 125 169 126 170 kunit_parse_filter_glob(&filter, filter_glob); 127 171 128 - for (i = 0; i < max; ++i) { 129 - filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], &filter); 130 - if (IS_ERR(filtered_subsuite)) { 131 - *err = PTR_ERR(filtered_subsuite); 172 + for (i = 0; &suite_set->start[i] != suite_set->end; i++) { 173 + if (!glob_match(filter.suite_glob, suite_set->start[i]->name)) 174 + continue; 175 + 176 + filtered_suite = kunit_filter_tests(suite_set->start[i], filter.test_glob); 177 + if (IS_ERR(filtered_suite)) { 178 + *err = PTR_ERR(filtered_suite); 132 179 return filtered; 133 180 } 134 - if (filtered_subsuite) 135 - *copy++ = filtered_subsuite; 181 + if (!filtered_suite) 182 + continue; 183 + 184 + *copy++ = filtered_suite; 136 185 } 137 186 filtered.end = copy; 138 187 ··· 160 199 161 200 } 162 201 163 - static void kunit_print_tap_header(struct suite_set *suite_set) 164 - { 165 - struct kunit_suite * const * const *suites, * const *subsuite; 166 - int num_of_suites = 0; 167 - 168 - for (suites = suite_set->start; suites < suite_set->end; suites++) 169 - for (subsuite = *suites; *subsuite != NULL; subsuite++) 170 - num_of_suites++; 171 - 172 - pr_info("TAP version 14\n"); 173 - pr_info("1..%d\n", num_of_suites); 174 - } 175 - 176 202 static void kunit_exec_run_tests(struct suite_set *suite_set) 177 203 { 178 - struct kunit_suite * const * const *suites; 204 + size_t num_suites = suite_set->end - suite_set->start; 179 205 180 - kunit_print_tap_header(suite_set); 206 + pr_info("TAP version 14\n"); 207 + pr_info("1..%zu\n", num_suites); 181 208 182 - for (suites = suite_set->start; suites < suite_set->end; suites++) 183 - __kunit_test_suites_init(*suites); 209 + __kunit_test_suites_init(suite_set->start, num_suites); 184 210 } 185 211 186 212 static void kunit_exec_list_tests(struct suite_set *suite_set) 187 213 { 188 - unsigned int i; 189 - struct kunit_suite * const * const *suites; 214 + struct kunit_suite * const *suites; 190 215 struct kunit_case *test_case; 191 216 192 217 /* Hack: print a tap header so kunit.py can find the start of KUnit output. */ 193 218 pr_info("TAP version 14\n"); 194 219 195 220 for (suites = suite_set->start; suites < suite_set->end; suites++) 196 - for (i = 0; (*suites)[i] != NULL; i++) { 197 - kunit_suite_for_each_test_case((*suites)[i], test_case) { 198 - pr_info("%s.%s\n", (*suites)[i]->name, test_case->name); 199 - } 221 + kunit_suite_for_each_test_case((*suites), test_case) { 222 + pr_info("%s.%s\n", (*suites)->name, test_case->name); 200 223 } 201 224 } 202 225 203 226 int kunit_run_all_tests(void) 204 227 { 205 - struct suite_set suite_set = { 206 - .start = __kunit_suites_start, 207 - .end = __kunit_suites_end, 208 - }; 228 + struct suite_set suite_set = {__kunit_suites_start, __kunit_suites_end}; 209 229 int err = 0; 210 230 211 231 if (filter_glob_param) { ··· 204 262 else 205 263 pr_err("kunit executor: unknown action '%s'\n", action_param); 206 264 207 - if (filter_glob_param) { /* a copy was made of each array */ 265 + if (filter_glob_param) { /* a copy was made of each suite */ 208 266 kunit_free_suite_set(suite_set); 209 267 } 210 - 211 268 212 269 out: 213 270 kunit_handle_shutdown();
+41 -103
lib/kunit/executor_test.c
··· 9 9 #include <kunit/test.h> 10 10 11 11 static void kfree_at_end(struct kunit *test, const void *to_free); 12 - static void free_subsuite_at_end(struct kunit *test, 13 - struct kunit_suite *const *to_free); 14 12 static struct kunit_suite *alloc_fake_suite(struct kunit *test, 15 13 const char *suite_name, 16 14 struct kunit_case *test_cases); ··· 39 41 kfree(filter.test_glob); 40 42 } 41 43 42 - static void filter_subsuite_test(struct kunit *test) 44 + static void filter_suites_test(struct kunit *test) 43 45 { 44 - struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; 45 - struct kunit_suite * const *filtered; 46 - struct kunit_test_filter filter = { 47 - .suite_glob = "suite2", 48 - .test_glob = NULL, 49 - }; 46 + struct kunit_suite *subsuite[3] = {NULL, NULL}; 47 + struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]}; 48 + struct suite_set got; 49 + int err = 0; 50 50 51 51 subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); 52 52 subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); 53 53 54 54 /* Want: suite1, suite2, NULL -> suite2, NULL */ 55 - filtered = kunit_filter_subsuite(subsuite, &filter); 56 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); 57 - free_subsuite_at_end(test, filtered); 55 + got = kunit_filter_suites(&suite_set, "suite2", &err); 56 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start); 57 + KUNIT_ASSERT_EQ(test, err, 0); 58 + kfree_at_end(test, got.start); 58 59 59 60 /* Validate we just have suite2 */ 60 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); 61 - KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); 62 - KUNIT_EXPECT_FALSE(test, filtered[1]); 61 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]); 62 + KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->name, "suite2"); 63 + 64 + /* Contains one element (end is 1 past end) */ 65 + KUNIT_ASSERT_EQ(test, got.end - got.start, 1); 63 66 } 64 67 65 - static void filter_subsuite_test_glob_test(struct kunit *test) 68 + static void filter_suites_test_glob_test(struct kunit *test) 66 69 { 67 - struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; 68 - struct kunit_suite * const *filtered; 69 - struct kunit_test_filter filter = { 70 - .suite_glob = "suite2", 71 - .test_glob = "test2", 72 - }; 70 + struct kunit_suite *subsuite[3] = {NULL, NULL}; 71 + struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]}; 72 + struct suite_set got; 73 + int err = 0; 73 74 74 75 subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); 75 76 subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); 76 77 77 78 /* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */ 78 - filtered = kunit_filter_subsuite(subsuite, &filter); 79 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); 80 - free_subsuite_at_end(test, filtered); 79 + got = kunit_filter_suites(&suite_set, "suite2.test2", &err); 80 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start); 81 + KUNIT_ASSERT_EQ(test, err, 0); 82 + kfree_at_end(test, got.start); 81 83 82 84 /* Validate we just have suite2 */ 83 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); 84 - KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); 85 - KUNIT_EXPECT_FALSE(test, filtered[1]); 85 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]); 86 + KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->name, "suite2"); 87 + KUNIT_ASSERT_EQ(test, got.end - got.start, 1); 86 88 87 89 /* Now validate we just have test2 */ 88 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]->test_cases); 89 - KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->test_cases[0].name, "test2"); 90 - KUNIT_EXPECT_FALSE(test, filtered[0]->test_cases[1].name); 90 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]->test_cases); 91 + KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->test_cases[0].name, "test2"); 92 + KUNIT_EXPECT_FALSE(test, got.start[0]->test_cases[1].name); 91 93 } 92 94 93 - static void filter_subsuite_to_empty_test(struct kunit *test) 95 + static void filter_suites_to_empty_test(struct kunit *test) 94 96 { 95 - struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; 96 - struct kunit_suite * const *filtered; 97 - struct kunit_test_filter filter = { 98 - .suite_glob = "not_found", 99 - .test_glob = NULL, 100 - }; 97 + struct kunit_suite *subsuite[3] = {NULL, NULL}; 98 + struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]}; 99 + struct suite_set got; 100 + int err = 0; 101 101 102 102 subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); 103 103 subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); 104 104 105 - filtered = kunit_filter_subsuite(subsuite, &filter); 106 - free_subsuite_at_end(test, filtered); /* just in case */ 105 + got = kunit_filter_suites(&suite_set, "not_found", &err); 106 + KUNIT_ASSERT_EQ(test, err, 0); 107 + kfree_at_end(test, got.start); /* just in case */ 107 108 108 - KUNIT_EXPECT_FALSE_MSG(test, filtered, 109 - "should be NULL to indicate no match"); 110 - } 111 - 112 - static void kfree_subsuites_at_end(struct kunit *test, struct suite_set *suite_set) 113 - { 114 - struct kunit_suite * const * const *suites; 115 - 116 - kfree_at_end(test, suite_set->start); 117 - for (suites = suite_set->start; suites < suite_set->end; suites++) 118 - free_subsuite_at_end(test, *suites); 119 - } 120 - 121 - static void filter_suites_test(struct kunit *test) 122 - { 123 - /* Suites per-file are stored as a NULL terminated array */ 124 - struct kunit_suite *subsuites[2][2] = { 125 - {NULL, NULL}, 126 - {NULL, NULL}, 127 - }; 128 - /* Match the memory layout of suite_set */ 129 - struct kunit_suite * const * const suites[2] = { 130 - subsuites[0], subsuites[1], 131 - }; 132 - 133 - const struct suite_set suite_set = { 134 - .start = suites, 135 - .end = suites + 2, 136 - }; 137 - struct suite_set filtered = {.start = NULL, .end = NULL}; 138 - int err = 0; 139 - 140 - /* Emulate two files, each having one suite */ 141 - subsuites[0][0] = alloc_fake_suite(test, "suite0", dummy_test_cases); 142 - subsuites[1][0] = alloc_fake_suite(test, "suite1", dummy_test_cases); 143 - 144 - /* Filter out suite1 */ 145 - filtered = kunit_filter_suites(&suite_set, "suite0", &err); 146 - kfree_subsuites_at_end(test, &filtered); /* let us use ASSERTs without leaking */ 147 - KUNIT_EXPECT_EQ(test, err, 0); 148 - KUNIT_ASSERT_EQ(test, filtered.end - filtered.start, (ptrdiff_t)1); 149 - 150 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start); 151 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0]); 152 - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0][0]); 153 - KUNIT_EXPECT_STREQ(test, (const char *)filtered.start[0][0]->name, "suite0"); 109 + KUNIT_EXPECT_PTR_EQ_MSG(test, got.start, got.end, 110 + "should be empty to indicate no match"); 154 111 } 155 112 156 113 static struct kunit_case executor_test_cases[] = { 157 114 KUNIT_CASE(parse_filter_test), 158 - KUNIT_CASE(filter_subsuite_test), 159 - KUNIT_CASE(filter_subsuite_test_glob_test), 160 - KUNIT_CASE(filter_subsuite_to_empty_test), 161 115 KUNIT_CASE(filter_suites_test), 116 + KUNIT_CASE(filter_suites_test_glob_test), 117 + KUNIT_CASE(filter_suites_to_empty_test), 162 118 {} 163 119 }; 164 120 ··· 140 188 return; 141 189 kunit_alloc_resource(test, NULL, kfree_res_free, GFP_KERNEL, 142 190 (void *)to_free); 143 - } 144 - 145 - static void free_subsuite_res_free(struct kunit_resource *res) 146 - { 147 - kunit_free_subsuite(res->data); 148 - } 149 - 150 - static void free_subsuite_at_end(struct kunit *test, 151 - struct kunit_suite *const *to_free) 152 - { 153 - if (IS_ERR_OR_NULL(to_free)) 154 - return; 155 - kunit_alloc_resource(test, NULL, free_subsuite_res_free, 156 - GFP_KERNEL, (void *)to_free); 157 191 } 158 192 159 193 static struct kunit_suite *alloc_fake_suite(struct kunit *test,
+6 -12
lib/kunit/test.c
··· 586 586 suite->suite_init_err = 0; 587 587 } 588 588 589 - int __kunit_test_suites_init(struct kunit_suite * const * const suites) 589 + int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites) 590 590 { 591 591 unsigned int i; 592 592 593 - for (i = 0; suites[i] != NULL; i++) { 593 + for (i = 0; i < num_suites; i++) { 594 594 kunit_init_suite(suites[i]); 595 595 kunit_run_tests(suites[i]); 596 596 } ··· 603 603 kunit_debugfs_destroy_suite(suite); 604 604 } 605 605 606 - void __kunit_test_suites_exit(struct kunit_suite **suites) 606 + void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites) 607 607 { 608 608 unsigned int i; 609 609 610 - for (i = 0; suites[i] != NULL; i++) 610 + for (i = 0; i < num_suites; i++) 611 611 kunit_exit_suite(suites[i]); 612 612 613 613 kunit_suite_counter = 1; ··· 617 617 #ifdef CONFIG_MODULES 618 618 static void kunit_module_init(struct module *mod) 619 619 { 620 - unsigned int i; 621 - 622 - for (i = 0; i < mod->num_kunit_suites; i++) 623 - __kunit_test_suites_init(mod->kunit_suites[i]); 620 + __kunit_test_suites_init(mod->kunit_suites, mod->num_kunit_suites); 624 621 } 625 622 626 623 static void kunit_module_exit(struct module *mod) 627 624 { 628 - unsigned int i; 629 - 630 - for (i = 0; i < mod->num_kunit_suites; i++) 631 - __kunit_test_suites_exit(mod->kunit_suites[i]); 625 + __kunit_test_suites_exit(mod->kunit_suites, mod->num_kunit_suites); 632 626 } 633 627 634 628 static int kunit_module_notify(struct notifier_block *nb, unsigned long val,