Commit e5857d39 authored by Daniel Latypov's avatar Daniel Latypov Committed by Shuah Khan

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: default avatarMaíra Canal <maira.canal@usp.br>
Reviewed-by: default avatarBrendan Higgins <brendanhiggins@google.com>
Signed-off-by: default avatarDaniel Latypov <dlatypov@google.com>
Co-developed-by: default avatarDavid Gow <davidgow@google.com>
Signed-off-by: default avatarDavid Gow <davidgow@google.com>
Signed-off-by: default avatarShuah Khan <skhan@linuxfoundation.org>
parent 3d6e4462
...@@ -237,9 +237,9 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite); ...@@ -237,9 +237,9 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite);
unsigned int kunit_test_case_num(struct kunit_suite *suite, unsigned int kunit_test_case_num(struct kunit_suite *suite,
struct kunit_case *test_case); struct kunit_case *test_case);
int __kunit_test_suites_init(struct kunit_suite * const * const suites); int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites);
void __kunit_test_suites_exit(struct kunit_suite **suites); void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites);
#if IS_BUILTIN(CONFIG_KUNIT) #if IS_BUILTIN(CONFIG_KUNIT)
int kunit_run_all_tests(void); int kunit_run_all_tests(void);
...@@ -250,11 +250,11 @@ static inline int kunit_run_all_tests(void) ...@@ -250,11 +250,11 @@ static inline int kunit_run_all_tests(void)
} }
#endif /* IS_BUILTIN(CONFIG_KUNIT) */ #endif /* IS_BUILTIN(CONFIG_KUNIT) */
#define __kunit_test_suites(unique_array, unique_suites, ...) \ #define __kunit_test_suites(unique_array, ...) \
MODULE_INFO(test, "Y"); \ MODULE_INFO(test, "Y"); \
static struct kunit_suite *unique_array[] = { __VA_ARGS__, NULL }; \ static struct kunit_suite *unique_array[] \
static struct kunit_suite **unique_suites \ __aligned(sizeof(struct kunit_suite *)) \
__used __section(".kunit_test_suites") = unique_array __used __section(".kunit_test_suites") = { __VA_ARGS__ }
/** /**
* kunit_test_suites() - used to register one or more &struct kunit_suite * kunit_test_suites() - used to register one or more &struct kunit_suite
...@@ -272,7 +272,6 @@ static inline int kunit_run_all_tests(void) ...@@ -272,7 +272,6 @@ static inline int kunit_run_all_tests(void)
*/ */
#define kunit_test_suites(__suites...) \ #define kunit_test_suites(__suites...) \
__kunit_test_suites(__UNIQUE_ID(array), \ __kunit_test_suites(__UNIQUE_ID(array), \
__UNIQUE_ID(suites), \
##__suites) ##__suites)
#define kunit_test_suite(suite) kunit_test_suites(&suite) #define kunit_test_suite(suite) kunit_test_suites(&suite)
......
...@@ -507,7 +507,7 @@ struct module { ...@@ -507,7 +507,7 @@ struct module {
#endif #endif
#if IS_ENABLED(CONFIG_KUNIT) #if IS_ENABLED(CONFIG_KUNIT)
int num_kunit_suites; int num_kunit_suites;
struct kunit_suite ***kunit_suites; struct kunit_suite **kunit_suites;
#endif #endif
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
* These symbols point to the .kunit_test_suites section and are defined in * These symbols point to the .kunit_test_suites section and are defined in
* include/asm-generic/vmlinux.lds.h, and consequently must be extern. * include/asm-generic/vmlinux.lds.h, and consequently must be extern.
*/ */
extern struct kunit_suite * const * const __kunit_suites_start[]; extern struct kunit_suite * const __kunit_suites_start[];
extern struct kunit_suite * const * const __kunit_suites_end[]; extern struct kunit_suite * const __kunit_suites_end[];
#if IS_BUILTIN(CONFIG_KUNIT) #if IS_BUILTIN(CONFIG_KUNIT)
...@@ -90,62 +90,18 @@ kunit_filter_tests(const struct kunit_suite *const suite, const char *test_glob) ...@@ -90,62 +90,18 @@ kunit_filter_tests(const struct kunit_suite *const suite, const char *test_glob)
static char *kunit_shutdown; static char *kunit_shutdown;
core_param(kunit_shutdown, kunit_shutdown, charp, 0644); core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
static struct kunit_suite * const * /* Stores an array of suites, end points one past the end */
kunit_filter_subsuite(struct kunit_suite * const * const subsuite,
struct kunit_test_filter *filter)
{
int i, n = 0;
struct kunit_suite **filtered, *filtered_suite;
n = 0;
for (i = 0; subsuite[i]; ++i) {
if (glob_match(filter->suite_glob, subsuite[i]->name))
++n;
}
if (n == 0)
return NULL;
filtered = kmalloc_array(n + 1, sizeof(*filtered), GFP_KERNEL);
if (!filtered)
return ERR_PTR(-ENOMEM);
n = 0;
for (i = 0; subsuite[i] != NULL; ++i) {
if (!glob_match(filter->suite_glob, subsuite[i]->name))
continue;
filtered_suite = kunit_filter_tests(subsuite[i], filter->test_glob);
if (IS_ERR(filtered_suite))
return ERR_CAST(filtered_suite);
else if (filtered_suite)
filtered[n++] = filtered_suite;
}
filtered[n] = NULL;
return filtered;
}
struct suite_set { struct suite_set {
struct kunit_suite * const * const *start; struct kunit_suite * const *start;
struct kunit_suite * const * const *end; struct kunit_suite * const *end;
}; };
static void kunit_free_subsuite(struct kunit_suite * const *subsuite)
{
unsigned int i;
for (i = 0; subsuite[i]; i++)
kfree(subsuite[i]);
kfree(subsuite);
}
static void kunit_free_suite_set(struct suite_set suite_set) static void kunit_free_suite_set(struct suite_set suite_set)
{ {
struct kunit_suite * const * const *suites; struct kunit_suite * const *suites;
for (suites = suite_set.start; suites < suite_set.end; suites++) for (suites = suite_set.start; suites < suite_set.end; suites++)
kunit_free_subsuite(*suites); kfree(*suites);
kfree(suite_set.start); kfree(suite_set.start);
} }
...@@ -154,7 +110,7 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, ...@@ -154,7 +110,7 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
int *err) int *err)
{ {
int i; int i;
struct kunit_suite * const **copy, * const *filtered_subsuite; struct kunit_suite **copy, *filtered_suite;
struct suite_set filtered; struct suite_set filtered;
struct kunit_test_filter filter; struct kunit_test_filter filter;
...@@ -169,14 +125,19 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, ...@@ -169,14 +125,19 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
kunit_parse_filter_glob(&filter, filter_glob); kunit_parse_filter_glob(&filter, filter_glob);
for (i = 0; i < max; ++i) { for (i = 0; &suite_set->start[i] != suite_set->end; i++) {
filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], &filter); if (!glob_match(filter.suite_glob, suite_set->start[i]->name))
if (IS_ERR(filtered_subsuite)) { continue;
*err = PTR_ERR(filtered_subsuite);
filtered_suite = kunit_filter_tests(suite_set->start[i], filter.test_glob);
if (IS_ERR(filtered_suite)) {
*err = PTR_ERR(filtered_suite);
return filtered; return filtered;
} }
if (filtered_subsuite) if (!filtered_suite)
*copy++ = filtered_subsuite; continue;
*copy++ = filtered_suite;
} }
filtered.end = copy; filtered.end = copy;
...@@ -199,52 +160,33 @@ static void kunit_handle_shutdown(void) ...@@ -199,52 +160,33 @@ static void kunit_handle_shutdown(void)
} }
static void kunit_print_tap_header(struct suite_set *suite_set)
{
struct kunit_suite * const * const *suites, * const *subsuite;
int num_of_suites = 0;
for (suites = suite_set->start; suites < suite_set->end; suites++)
for (subsuite = *suites; *subsuite != NULL; subsuite++)
num_of_suites++;
pr_info("TAP version 14\n");
pr_info("1..%d\n", num_of_suites);
}
static void kunit_exec_run_tests(struct suite_set *suite_set) static void kunit_exec_run_tests(struct suite_set *suite_set)
{ {
struct kunit_suite * const * const *suites; size_t num_suites = suite_set->end - suite_set->start;
kunit_print_tap_header(suite_set); pr_info("TAP version 14\n");
pr_info("1..%zu\n", num_suites);
for (suites = suite_set->start; suites < suite_set->end; suites++) __kunit_test_suites_init(suite_set->start, num_suites);
__kunit_test_suites_init(*suites);
} }
static void kunit_exec_list_tests(struct suite_set *suite_set) static void kunit_exec_list_tests(struct suite_set *suite_set)
{ {
unsigned int i; struct kunit_suite * const *suites;
struct kunit_suite * const * const *suites;
struct kunit_case *test_case; struct kunit_case *test_case;
/* Hack: print a tap header so kunit.py can find the start of KUnit output. */ /* Hack: print a tap header so kunit.py can find the start of KUnit output. */
pr_info("TAP version 14\n"); pr_info("TAP version 14\n");
for (suites = suite_set->start; suites < suite_set->end; suites++) for (suites = suite_set->start; suites < suite_set->end; suites++)
for (i = 0; (*suites)[i] != NULL; i++) { kunit_suite_for_each_test_case((*suites), test_case) {
kunit_suite_for_each_test_case((*suites)[i], test_case) { pr_info("%s.%s\n", (*suites)->name, test_case->name);
pr_info("%s.%s\n", (*suites)[i]->name, test_case->name);
}
} }
} }
int kunit_run_all_tests(void) int kunit_run_all_tests(void)
{ {
struct suite_set suite_set = { struct suite_set suite_set = {__kunit_suites_start, __kunit_suites_end};
.start = __kunit_suites_start,
.end = __kunit_suites_end,
};
int err = 0; int err = 0;
if (filter_glob_param) { if (filter_glob_param) {
...@@ -262,11 +204,10 @@ int kunit_run_all_tests(void) ...@@ -262,11 +204,10 @@ int kunit_run_all_tests(void)
else else
pr_err("kunit executor: unknown action '%s'\n", action_param); pr_err("kunit executor: unknown action '%s'\n", action_param);
if (filter_glob_param) { /* a copy was made of each array */ if (filter_glob_param) { /* a copy was made of each suite */
kunit_free_suite_set(suite_set); kunit_free_suite_set(suite_set);
} }
out: out:
kunit_handle_shutdown(); kunit_handle_shutdown();
return err; return err;
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
#include <kunit/test.h> #include <kunit/test.h>
static void kfree_at_end(struct kunit *test, const void *to_free); static void kfree_at_end(struct kunit *test, const void *to_free);
static void free_subsuite_at_end(struct kunit *test,
struct kunit_suite *const *to_free);
static struct kunit_suite *alloc_fake_suite(struct kunit *test, static struct kunit_suite *alloc_fake_suite(struct kunit *test,
const char *suite_name, const char *suite_name,
struct kunit_case *test_cases); struct kunit_case *test_cases);
...@@ -41,126 +39,80 @@ static void parse_filter_test(struct kunit *test) ...@@ -41,126 +39,80 @@ static void parse_filter_test(struct kunit *test)
kfree(filter.test_glob); kfree(filter.test_glob);
} }
static void filter_subsuite_test(struct kunit *test) static void filter_suites_test(struct kunit *test)
{ {
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite *subsuite[3] = {NULL, NULL};
struct kunit_suite * const *filtered; struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
struct kunit_test_filter filter = { struct suite_set got;
.suite_glob = "suite2", int err = 0;
.test_glob = NULL,
};
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
/* Want: suite1, suite2, NULL -> suite2, NULL */ /* Want: suite1, suite2, NULL -> suite2, NULL */
filtered = kunit_filter_subsuite(subsuite, &filter); got = kunit_filter_suites(&suite_set, "suite2", &err);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
free_subsuite_at_end(test, filtered); KUNIT_ASSERT_EQ(test, err, 0);
kfree_at_end(test, got.start);
/* Validate we just have suite2 */ /* Validate we just have suite2 */
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]);
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->name, "suite2");
KUNIT_EXPECT_FALSE(test, filtered[1]);
/* Contains one element (end is 1 past end) */
KUNIT_ASSERT_EQ(test, got.end - got.start, 1);
} }
static void filter_subsuite_test_glob_test(struct kunit *test) static void filter_suites_test_glob_test(struct kunit *test)
{ {
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite *subsuite[3] = {NULL, NULL};
struct kunit_suite * const *filtered; struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
struct kunit_test_filter filter = { struct suite_set got;
.suite_glob = "suite2", int err = 0;
.test_glob = "test2",
};
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
/* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */ /* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */
filtered = kunit_filter_subsuite(subsuite, &filter); got = kunit_filter_suites(&suite_set, "suite2.test2", &err);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
free_subsuite_at_end(test, filtered); KUNIT_ASSERT_EQ(test, err, 0);
kfree_at_end(test, got.start);
/* Validate we just have suite2 */ /* Validate we just have suite2 */
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]);
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->name, "suite2");
KUNIT_EXPECT_FALSE(test, filtered[1]); KUNIT_ASSERT_EQ(test, got.end - got.start, 1);
/* Now validate we just have test2 */ /* Now validate we just have test2 */
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]->test_cases); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]->test_cases);
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->test_cases[0].name, "test2"); KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->test_cases[0].name, "test2");
KUNIT_EXPECT_FALSE(test, filtered[0]->test_cases[1].name); KUNIT_EXPECT_FALSE(test, got.start[0]->test_cases[1].name);
} }
static void filter_subsuite_to_empty_test(struct kunit *test) static void filter_suites_to_empty_test(struct kunit *test)
{ {
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite *subsuite[3] = {NULL, NULL};
struct kunit_suite * const *filtered; struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
struct kunit_test_filter filter = { struct suite_set got;
.suite_glob = "not_found", int err = 0;
.test_glob = NULL,
};
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
filtered = kunit_filter_subsuite(subsuite, &filter); got = kunit_filter_suites(&suite_set, "not_found", &err);
free_subsuite_at_end(test, filtered); /* just in case */ KUNIT_ASSERT_EQ(test, err, 0);
kfree_at_end(test, got.start); /* just in case */
KUNIT_EXPECT_FALSE_MSG(test, filtered, KUNIT_EXPECT_PTR_EQ_MSG(test, got.start, got.end,
"should be NULL to indicate no match"); "should be empty to indicate no match");
}
static void kfree_subsuites_at_end(struct kunit *test, struct suite_set *suite_set)
{
struct kunit_suite * const * const *suites;
kfree_at_end(test, suite_set->start);
for (suites = suite_set->start; suites < suite_set->end; suites++)
free_subsuite_at_end(test, *suites);
}
static void filter_suites_test(struct kunit *test)
{
/* Suites per-file are stored as a NULL terminated array */
struct kunit_suite *subsuites[2][2] = {
{NULL, NULL},
{NULL, NULL},
};
/* Match the memory layout of suite_set */
struct kunit_suite * const * const suites[2] = {
subsuites[0], subsuites[1],
};
const struct suite_set suite_set = {
.start = suites,
.end = suites + 2,
};
struct suite_set filtered = {.start = NULL, .end = NULL};
int err = 0;
/* Emulate two files, each having one suite */
subsuites[0][0] = alloc_fake_suite(test, "suite0", dummy_test_cases);
subsuites[1][0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
/* Filter out suite1 */
filtered = kunit_filter_suites(&suite_set, "suite0", &err);
kfree_subsuites_at_end(test, &filtered); /* let us use ASSERTs without leaking */
KUNIT_EXPECT_EQ(test, err, 0);
KUNIT_ASSERT_EQ(test, filtered.end - filtered.start, (ptrdiff_t)1);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0]);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0][0]);
KUNIT_EXPECT_STREQ(test, (const char *)filtered.start[0][0]->name, "suite0");
} }
static struct kunit_case executor_test_cases[] = { static struct kunit_case executor_test_cases[] = {
KUNIT_CASE(parse_filter_test), KUNIT_CASE(parse_filter_test),
KUNIT_CASE(filter_subsuite_test),
KUNIT_CASE(filter_subsuite_test_glob_test),
KUNIT_CASE(filter_subsuite_to_empty_test),
KUNIT_CASE(filter_suites_test), KUNIT_CASE(filter_suites_test),
KUNIT_CASE(filter_suites_test_glob_test),
KUNIT_CASE(filter_suites_to_empty_test),
{} {}
}; };
...@@ -190,20 +142,6 @@ static void kfree_at_end(struct kunit *test, const void *to_free) ...@@ -190,20 +142,6 @@ static void kfree_at_end(struct kunit *test, const void *to_free)
(void *)to_free); (void *)to_free);
} }
static void free_subsuite_res_free(struct kunit_resource *res)
{
kunit_free_subsuite(res->data);
}
static void free_subsuite_at_end(struct kunit *test,
struct kunit_suite *const *to_free)
{
if (IS_ERR_OR_NULL(to_free))
return;
kunit_alloc_resource(test, NULL, free_subsuite_res_free,
GFP_KERNEL, (void *)to_free);
}
static struct kunit_suite *alloc_fake_suite(struct kunit *test, static struct kunit_suite *alloc_fake_suite(struct kunit *test,
const char *suite_name, const char *suite_name,
struct kunit_case *test_cases) struct kunit_case *test_cases)
......
...@@ -586,11 +586,11 @@ static void kunit_init_suite(struct kunit_suite *suite) ...@@ -586,11 +586,11 @@ static void kunit_init_suite(struct kunit_suite *suite)
suite->suite_init_err = 0; suite->suite_init_err = 0;
} }
int __kunit_test_suites_init(struct kunit_suite * const * const suites) int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites)
{ {
unsigned int i; unsigned int i;
for (i = 0; suites[i] != NULL; i++) { for (i = 0; i < num_suites; i++) {
kunit_init_suite(suites[i]); kunit_init_suite(suites[i]);
kunit_run_tests(suites[i]); kunit_run_tests(suites[i]);
} }
...@@ -603,11 +603,11 @@ static void kunit_exit_suite(struct kunit_suite *suite) ...@@ -603,11 +603,11 @@ static void kunit_exit_suite(struct kunit_suite *suite)
kunit_debugfs_destroy_suite(suite); kunit_debugfs_destroy_suite(suite);
} }
void __kunit_test_suites_exit(struct kunit_suite **suites) void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites)
{ {
unsigned int i; unsigned int i;
for (i = 0; suites[i] != NULL; i++) for (i = 0; i < num_suites; i++)
kunit_exit_suite(suites[i]); kunit_exit_suite(suites[i]);
kunit_suite_counter = 1; kunit_suite_counter = 1;
...@@ -617,18 +617,12 @@ EXPORT_SYMBOL_GPL(__kunit_test_suites_exit); ...@@ -617,18 +617,12 @@ EXPORT_SYMBOL_GPL(__kunit_test_suites_exit);
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
static void kunit_module_init(struct module *mod) static void kunit_module_init(struct module *mod)
{ {
unsigned int i; __kunit_test_suites_init(mod->kunit_suites, mod->num_kunit_suites);
for (i = 0; i < mod->num_kunit_suites; i++)
__kunit_test_suites_init(mod->kunit_suites[i]);
} }
static void kunit_module_exit(struct module *mod) static void kunit_module_exit(struct module *mod)
{ {
unsigned int i; __kunit_test_suites_exit(mod->kunit_suites, mod->num_kunit_suites);
for (i = 0; i < mod->num_kunit_suites; i++)
__kunit_test_suites_exit(mod->kunit_suites[i]);
} }
static int kunit_module_notify(struct notifier_block *nb, unsigned long val, static int kunit_module_notify(struct notifier_block *nb, unsigned long val,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment