summaryrefslogtreecommitdiff
path: root/lib/kunit/test.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kunit/test.c')
-rw-r--r--lib/kunit/test.c258
1 files changed, 117 insertions, 141 deletions
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index c7ed4aabec04..90640a43cf62 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -6,11 +6,13 @@
* Author: Brendan Higgins <brendanhiggins@google.com>
*/
+#include <kunit/resource.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
#include <linux/kernel.h>
-#include <linux/kref.h>
+#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/panic.h>
#include <linux/sched/debug.h>
#include <linux/sched.h>
@@ -53,6 +55,17 @@ EXPORT_SYMBOL_GPL(__kunit_fail_current_test);
#endif
/*
+ * Enable KUnit tests to run.
+ */
+#ifdef CONFIG_KUNIT_DEFAULT_ENABLED
+static bool enable_param = true;
+#else
+static bool enable_param;
+#endif
+module_param_named(enable, enable_param, bool, 0);
+MODULE_PARM_DESC(enable, "Enable KUnit tests");
+
+/*
* KUnit statistic mode:
* 0 - disabled
* 1 - only when there is more than one subtest
@@ -134,7 +147,7 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite)
}
EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases);
-static void kunit_print_subtest_start(struct kunit_suite *suite)
+static void kunit_print_suite_start(struct kunit_suite *suite)
{
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "# Subtest: %s",
suite->name);
@@ -179,6 +192,9 @@ enum kunit_status kunit_suite_has_succeeded(struct kunit_suite *suite)
const struct kunit_case *test_case;
enum kunit_status status = KUNIT_SKIPPED;
+ if (suite->suite_init_err)
+ return KUNIT_FAILURE;
+
kunit_suite_for_each_test_case(suite, test_case) {
if (test_case->status == KUNIT_FAILURE)
return KUNIT_FAILURE;
@@ -192,7 +208,7 @@ EXPORT_SYMBOL_GPL(kunit_suite_has_succeeded);
static size_t kunit_suite_counter = 1;
-static void kunit_print_subtest_end(struct kunit_suite *suite)
+static void kunit_print_suite_end(struct kunit_suite *suite)
{
kunit_print_ok_not_ok((void *)suite, false,
kunit_suite_has_succeeded(suite),
@@ -240,7 +256,9 @@ static void kunit_print_string_stream(struct kunit *test,
}
}
-static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
+static void kunit_fail(struct kunit *test, const struct kunit_loc *loc,
+ enum kunit_assert_type type, const struct kunit_assert *assert,
+ assert_format_t assert_format, const struct va_format *message)
{
struct string_stream *stream;
@@ -250,16 +268,17 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
if (!stream) {
WARN(true,
"Could not allocate stream to print failed assertion in %s:%d\n",
- assert->file,
- assert->line);
+ loc->file,
+ loc->line);
return;
}
- assert->format(assert, stream);
+ kunit_assert_prologue(loc, type, stream);
+ assert_format(assert, message, stream);
kunit_print_string_stream(test, stream);
- WARN_ON(string_stream_destroy(stream));
+ string_stream_destroy(stream);
}
static void __noreturn kunit_abort(struct kunit *test)
@@ -275,29 +294,28 @@ static void __noreturn kunit_abort(struct kunit *test)
WARN_ONCE(true, "Throw could not abort from test!\n");
}
-void kunit_do_assertion(struct kunit *test,
- struct kunit_assert *assert,
- bool pass,
- const char *fmt, ...)
+void kunit_do_failed_assertion(struct kunit *test,
+ const struct kunit_loc *loc,
+ enum kunit_assert_type type,
+ const struct kunit_assert *assert,
+ assert_format_t assert_format,
+ const char *fmt, ...)
{
va_list args;
-
- if (pass)
- return;
-
+ struct va_format message;
va_start(args, fmt);
- assert->message.fmt = fmt;
- assert->message.va = &args;
+ message.fmt = fmt;
+ message.va = &args;
- kunit_fail(test, assert);
+ kunit_fail(test, loc, type, assert, assert_format, &message);
va_end(args);
- if (assert->type == KUNIT_ASSERTION)
+ if (type == KUNIT_ASSERTION)
kunit_abort(test);
}
-EXPORT_SYMBOL_GPL(kunit_do_assertion);
+EXPORT_SYMBOL_GPL(kunit_do_failed_assertion);
void kunit_init_test(struct kunit *test, const char *name, char *log)
{
@@ -497,7 +515,19 @@ int kunit_run_tests(struct kunit_suite *suite)
struct kunit_result_stats suite_stats = { 0 };
struct kunit_result_stats total_stats = { 0 };
- kunit_print_subtest_start(suite);
+ /* Taint the kernel so we know we've run tests. */
+ add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
+
+ if (suite->suite_init) {
+ suite->suite_init_err = suite->suite_init(suite);
+ if (suite->suite_init_err) {
+ kunit_err(suite, KUNIT_SUBTEST_INDENT
+ "# failed to initialize (%d)", suite->suite_init_err);
+ goto suite_end;
+ }
+ }
+
+ kunit_print_suite_start(suite);
kunit_suite_for_each_test_case(suite, test_case) {
struct kunit test = { .param_value = NULL, .param_index = 0 };
@@ -550,8 +580,12 @@ int kunit_run_tests(struct kunit_suite *suite)
kunit_accumulate_stats(&total_stats, param_stats);
}
+ if (suite->suite_exit)
+ suite->suite_exit(suite);
+
kunit_print_suite_stats(suite, suite_stats, total_stats);
- kunit_print_subtest_end(suite);
+suite_end:
+ kunit_print_suite_end(suite);
return 0;
}
@@ -561,13 +595,24 @@ static void kunit_init_suite(struct kunit_suite *suite)
{
kunit_debugfs_create_suite(suite);
suite->status_comment[0] = '\0';
+ suite->suite_init_err = 0;
}
-int __kunit_test_suites_init(struct kunit_suite * const * const suites)
+bool kunit_enabled(void)
+{
+ return enable_param;
+}
+
+int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites)
{
unsigned int i;
- for (i = 0; suites[i] != NULL; i++) {
+ if (!kunit_enabled() && num_suites > 0) {
+ pr_info("kunit: disabled\n");
+ return 0;
+ }
+
+ for (i = 0; i < num_suites; i++) {
kunit_init_suite(suites[i]);
kunit_run_tests(suites[i]);
}
@@ -580,130 +625,56 @@ static void kunit_exit_suite(struct kunit_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;
- for (i = 0; suites[i] != NULL; i++)
+ if (!kunit_enabled())
+ return;
+
+ for (i = 0; i < num_suites; i++)
kunit_exit_suite(suites[i]);
kunit_suite_counter = 1;
}
EXPORT_SYMBOL_GPL(__kunit_test_suites_exit);
-/*
- * Used for static resources and when a kunit_resource * has been created by
- * kunit_alloc_resource(). When an init function is supplied, @data is passed
- * into the init function; otherwise, we simply set the resource data field to
- * the data value passed in.
- */
-int kunit_add_resource(struct kunit *test,
- kunit_resource_init_t init,
- kunit_resource_free_t free,
- struct kunit_resource *res,
- void *data)
+#ifdef CONFIG_MODULES
+static void kunit_module_init(struct module *mod)
{
- int ret = 0;
- unsigned long flags;
-
- res->free = free;
- kref_init(&res->refcount);
-
- if (init) {
- ret = init(res, data);
- if (ret)
- return ret;
- } else {
- res->data = data;
- }
-
- spin_lock_irqsave(&test->lock, flags);
- list_add_tail(&res->node, &test->resources);
- /* refcount for list is established by kref_init() */
- spin_unlock_irqrestore(&test->lock, flags);
-
- return ret;
+ __kunit_test_suites_init(mod->kunit_suites, mod->num_kunit_suites);
}
-EXPORT_SYMBOL_GPL(kunit_add_resource);
-int kunit_add_named_resource(struct kunit *test,
- kunit_resource_init_t init,
- kunit_resource_free_t free,
- struct kunit_resource *res,
- const char *name,
- void *data)
+static void kunit_module_exit(struct module *mod)
{
- struct kunit_resource *existing;
-
- if (!name)
- return -EINVAL;
-
- existing = kunit_find_named_resource(test, name);
- if (existing) {
- kunit_put_resource(existing);
- return -EEXIST;
- }
-
- res->name = name;
-
- return kunit_add_resource(test, init, free, res, data);
+ __kunit_test_suites_exit(mod->kunit_suites, mod->num_kunit_suites);
}
-EXPORT_SYMBOL_GPL(kunit_add_named_resource);
-struct kunit_resource *kunit_alloc_and_get_resource(struct kunit *test,
- kunit_resource_init_t init,
- kunit_resource_free_t free,
- gfp_t internal_gfp,
- void *data)
+static int kunit_module_notify(struct notifier_block *nb, unsigned long val,
+ void *data)
{
- struct kunit_resource *res;
- int ret;
-
- res = kzalloc(sizeof(*res), internal_gfp);
- if (!res)
- return NULL;
+ struct module *mod = data;
- ret = kunit_add_resource(test, init, free, res, data);
- if (!ret) {
- /*
- * bump refcount for get; kunit_resource_put() should be called
- * when done.
- */
- kunit_get_resource(res);
- return res;
+ switch (val) {
+ case MODULE_STATE_LIVE:
+ kunit_module_init(mod);
+ break;
+ case MODULE_STATE_GOING:
+ kunit_module_exit(mod);
+ break;
+ case MODULE_STATE_COMING:
+ case MODULE_STATE_UNFORMED:
+ break;
}
- return NULL;
-}
-EXPORT_SYMBOL_GPL(kunit_alloc_and_get_resource);
-
-void kunit_remove_resource(struct kunit *test, struct kunit_resource *res)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&test->lock, flags);
- list_del(&res->node);
- spin_unlock_irqrestore(&test->lock, flags);
- kunit_put_resource(res);
-}
-EXPORT_SYMBOL_GPL(kunit_remove_resource);
-
-int kunit_destroy_resource(struct kunit *test, kunit_resource_match_t match,
- void *match_data)
-{
- struct kunit_resource *res = kunit_find_resource(test, match,
- match_data);
-
- if (!res)
- return -ENOENT;
-
- kunit_remove_resource(test, res);
-
- /* We have a reference also via _find(); drop it. */
- kunit_put_resource(res);
return 0;
}
-EXPORT_SYMBOL_GPL(kunit_destroy_resource);
+
+static struct notifier_block kunit_mod_nb = {
+ .notifier_call = kunit_module_notify,
+ .priority = 0,
+};
+#endif
struct kunit_kmalloc_array_params {
size_t n;
@@ -743,21 +714,20 @@ void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(kunit_kmalloc_array);
-void kunit_kfree(struct kunit *test, const void *ptr)
+static inline bool kunit_kfree_match(struct kunit *test,
+ struct kunit_resource *res, void *match_data)
{
- struct kunit_resource *res;
-
- res = kunit_find_resource(test, kunit_resource_instance_match,
- (void *)ptr);
-
- /*
- * Removing the resource from the list of resources drops the
- * reference count to 1; the final put will trigger the free.
- */
- kunit_remove_resource(test, res);
+ /* Only match resources allocated with kunit_kmalloc() and friends. */
+ return res->free == kunit_kmalloc_array_free && res->data == match_data;
+}
- kunit_put_resource(res);
+void kunit_kfree(struct kunit *test, const void *ptr)
+{
+ if (!ptr)
+ return;
+ if (kunit_destroy_resource(test, kunit_kfree_match, (void *)ptr))
+ KUNIT_FAIL(test, "kunit_kfree: %px already freed or not allocated by kunit", ptr);
}
EXPORT_SYMBOL_GPL(kunit_kfree);
@@ -799,13 +769,19 @@ EXPORT_SYMBOL_GPL(kunit_cleanup);
static int __init kunit_init(void)
{
kunit_debugfs_init();
-
+#ifdef CONFIG_MODULES
+ return register_module_notifier(&kunit_mod_nb);
+#else
return 0;
+#endif
}
late_initcall(kunit_init);
static void __exit kunit_exit(void)
{
+#ifdef CONFIG_MODULES
+ unregister_module_notifier(&kunit_mod_nb);
+#endif
kunit_debugfs_cleanup();
}
module_exit(kunit_exit);