Merge tag 'linux-kselftest-kunit-5.15-rc1' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / lib / kunit / test.c
index 45f0688..f246b84 100644 (file)
@@ -10,6 +10,7 @@
 #include <kunit/test-bug.h>
 #include <linux/kernel.h>
 #include <linux/kref.h>
+#include <linux/moduleparam.h>
 #include <linux/sched/debug.h>
 #include <linux/sched.h>
 
@@ -51,6 +52,51 @@ void __kunit_fail_current_test(const char *file, int line, const char *fmt, ...)
 EXPORT_SYMBOL_GPL(__kunit_fail_current_test);
 #endif
 
+/*
+ * KUnit statistic mode:
+ * 0 - disabled
+ * 1 - only when there is more than one subtest
+ * 2 - enabled
+ */
+static int kunit_stats_enabled = 1;
+module_param_named(stats_enabled, kunit_stats_enabled, int, 0644);
+MODULE_PARM_DESC(stats_enabled,
+                 "Print test stats: never (0), only for multiple subtests (1), or always (2)");
+
+struct kunit_result_stats {
+       unsigned long passed;
+       unsigned long skipped;
+       unsigned long failed;
+       unsigned long total;
+};
+
+static bool kunit_should_print_stats(struct kunit_result_stats stats)
+{
+       if (kunit_stats_enabled == 0)
+               return false;
+
+       if (kunit_stats_enabled == 2)
+               return true;
+
+       return (stats.total > 1);
+}
+
+static void kunit_print_test_stats(struct kunit *test,
+                                  struct kunit_result_stats stats)
+{
+       if (!kunit_should_print_stats(stats))
+               return;
+
+       kunit_log(KERN_INFO, test,
+                 KUNIT_SUBTEST_INDENT
+                 "# %s: pass:%lu fail:%lu skip:%lu total:%lu",
+                 test->name,
+                 stats.passed,
+                 stats.failed,
+                 stats.skipped,
+                 stats.total);
+}
+
 /*
  * Append formatted message to log, size of which is limited to
  * KUNIT_LOG_SIZE bytes (including null terminating byte).
@@ -98,12 +144,14 @@ static void kunit_print_subtest_start(struct kunit_suite *suite)
 
 static void kunit_print_ok_not_ok(void *test_or_suite,
                                  bool is_test,
-                                 bool is_ok,
+                                 enum kunit_status status,
                                  size_t test_number,
-                                 const char *description)
+                                 const char *description,
+                                 const char *directive)
 {
        struct kunit_suite *suite = is_test ? NULL : test_or_suite;
        struct kunit *test = is_test ? test_or_suite : NULL;
+       const char *directive_header = (status == KUNIT_SKIPPED) ? " # SKIP " : "";
 
        /*
         * We do not log the test suite results as doing so would
@@ -114,25 +162,31 @@ static void kunit_print_ok_not_ok(void *test_or_suite,
         * representation.
         */
        if (suite)
-               pr_info("%s %zd - %s\n",
-                       kunit_status_to_string(is_ok),
-                       test_number, description);
+               pr_info("%s %zd - %s%s%s\n",
+                       kunit_status_to_ok_not_ok(status),
+                       test_number, description, directive_header,
+                       (status == KUNIT_SKIPPED) ? directive : "");
        else
-               kunit_log(KERN_INFO, test, KUNIT_SUBTEST_INDENT "%s %zd - %s",
-                         kunit_status_to_string(is_ok),
-                         test_number, description);
+               kunit_log(KERN_INFO, test,
+                         KUNIT_SUBTEST_INDENT "%s %zd - %s%s%s",
+                         kunit_status_to_ok_not_ok(status),
+                         test_number, description, directive_header,
+                         (status == KUNIT_SKIPPED) ? directive : "");
 }
 
-bool kunit_suite_has_succeeded(struct kunit_suite *suite)
+enum kunit_status kunit_suite_has_succeeded(struct kunit_suite *suite)
 {
        const struct kunit_case *test_case;
+       enum kunit_status status = KUNIT_SKIPPED;
 
        kunit_suite_for_each_test_case(suite, test_case) {
-               if (!test_case->success)
-                       return false;
+               if (test_case->status == KUNIT_FAILURE)
+                       return KUNIT_FAILURE;
+               else if (test_case->status == KUNIT_SUCCESS)
+                       status = KUNIT_SUCCESS;
        }
 
-       return true;
+       return status;
 }
 EXPORT_SYMBOL_GPL(kunit_suite_has_succeeded);
 
@@ -143,7 +197,8 @@ static void kunit_print_subtest_end(struct kunit_suite *suite)
        kunit_print_ok_not_ok((void *)suite, false,
                              kunit_suite_has_succeeded(suite),
                              kunit_suite_counter++,
-                             suite->name);
+                             suite->name,
+                             suite->status_comment);
 }
 
 unsigned int kunit_test_case_num(struct kunit_suite *suite,
@@ -252,7 +307,8 @@ void kunit_init_test(struct kunit *test, const char *name, char *log)
        test->log = log;
        if (test->log)
                test->log[0] = '\0';
-       test->success = true;
+       test->status = KUNIT_SUCCESS;
+       test->status_comment[0] = '\0';
 }
 EXPORT_SYMBOL_GPL(kunit_init_test);
 
@@ -376,19 +432,77 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
        context.test_case = test_case;
        kunit_try_catch_run(try_catch, &context);
 
-       test_case->success = test->success;
+       /* Propagate the parameter result to the test case. */
+       if (test->status == KUNIT_FAILURE)
+               test_case->status = KUNIT_FAILURE;
+       else if (test_case->status != KUNIT_FAILURE && test->status == KUNIT_SUCCESS)
+               test_case->status = KUNIT_SUCCESS;
+}
+
+static void kunit_print_suite_stats(struct kunit_suite *suite,
+                                   struct kunit_result_stats suite_stats,
+                                   struct kunit_result_stats param_stats)
+{
+       if (kunit_should_print_stats(suite_stats)) {
+               kunit_log(KERN_INFO, suite,
+                         "# %s: pass:%lu fail:%lu skip:%lu total:%lu",
+                         suite->name,
+                         suite_stats.passed,
+                         suite_stats.failed,
+                         suite_stats.skipped,
+                         suite_stats.total);
+       }
+
+       if (kunit_should_print_stats(param_stats)) {
+               kunit_log(KERN_INFO, suite,
+                         "# Totals: pass:%lu fail:%lu skip:%lu total:%lu",
+                         param_stats.passed,
+                         param_stats.failed,
+                         param_stats.skipped,
+                         param_stats.total);
+       }
+}
+
+static void kunit_update_stats(struct kunit_result_stats *stats,
+                              enum kunit_status status)
+{
+       switch (status) {
+       case KUNIT_SUCCESS:
+               stats->passed++;
+               break;
+       case KUNIT_SKIPPED:
+               stats->skipped++;
+               break;
+       case KUNIT_FAILURE:
+               stats->failed++;
+               break;
+       }
+
+       stats->total++;
+}
+
+static void kunit_accumulate_stats(struct kunit_result_stats *total,
+                                  struct kunit_result_stats add)
+{
+       total->passed += add.passed;
+       total->skipped += add.skipped;
+       total->failed += add.failed;
+       total->total += add.total;
 }
 
 int kunit_run_tests(struct kunit_suite *suite)
 {
        char param_desc[KUNIT_PARAM_DESC_SIZE];
        struct kunit_case *test_case;
+       struct kunit_result_stats suite_stats = { 0 };
+       struct kunit_result_stats total_stats = { 0 };
 
        kunit_print_subtest_start(suite);
 
        kunit_suite_for_each_test_case(suite, test_case) {
                struct kunit test = { .param_value = NULL, .param_index = 0 };
-               bool test_success = true;
+               struct kunit_result_stats param_stats = { 0 };
+               test_case->status = KUNIT_SKIPPED;
 
                if (test_case->generate_params) {
                        /* Get initial param. */
@@ -398,7 +512,6 @@ int kunit_run_tests(struct kunit_suite *suite)
 
                do {
                        kunit_run_case_catch_errors(suite, test_case, &test);
-                       test_success &= test_case->success;
 
                        if (test_case->generate_params) {
                                if (param_desc[0] == '\0') {
@@ -410,7 +523,7 @@ int kunit_run_tests(struct kunit_suite *suite)
                                          KUNIT_SUBTEST_INDENT
                                          "# %s: %s %d - %s",
                                          test_case->name,
-                                         kunit_status_to_string(test.success),
+                                         kunit_status_to_ok_not_ok(test.status),
                                          test.param_index + 1, param_desc);
 
                                /* Get next param. */
@@ -418,13 +531,23 @@ int kunit_run_tests(struct kunit_suite *suite)
                                test.param_value = test_case->generate_params(test.param_value, param_desc);
                                test.param_index++;
                        }
+
+                       kunit_update_stats(&param_stats, test.status);
+
                } while (test.param_value);
 
-               kunit_print_ok_not_ok(&test, true, test_success,
+               kunit_print_test_stats(&test, param_stats);
+
+               kunit_print_ok_not_ok(&test, true, test_case->status,
                                      kunit_test_case_num(suite, test_case),
-                                     test_case->name);
+                                     test_case->name,
+                                     test.status_comment);
+
+               kunit_update_stats(&suite_stats, test_case->status);
+               kunit_accumulate_stats(&total_stats, param_stats);
        }
 
+       kunit_print_suite_stats(suite, suite_stats, total_stats);
        kunit_print_subtest_end(suite);
 
        return 0;
@@ -434,6 +557,7 @@ EXPORT_SYMBOL_GPL(kunit_run_tests);
 static void kunit_init_suite(struct kunit_suite *suite)
 {
        kunit_debugfs_create_suite(suite);
+       suite->status_comment[0] = '\0';
 }
 
 int __kunit_test_suites_init(struct kunit_suite * const * const suites)
@@ -576,41 +700,43 @@ int kunit_destroy_resource(struct kunit *test, kunit_resource_match_t match,
 }
 EXPORT_SYMBOL_GPL(kunit_destroy_resource);
 
-struct kunit_kmalloc_params {
+struct kunit_kmalloc_array_params {
+       size_t n;
        size_t size;
        gfp_t gfp;
 };
 
-static int kunit_kmalloc_init(struct kunit_resource *res, void *context)
+static int kunit_kmalloc_array_init(struct kunit_resource *res, void *context)
 {
-       struct kunit_kmalloc_params *params = context;
+       struct kunit_kmalloc_array_params *params = context;
 
-       res->data = kmalloc(params->size, params->gfp);
+       res->data = kmalloc_array(params->n, params->size, params->gfp);
        if (!res->data)
                return -ENOMEM;
 
        return 0;
 }
 
-static void kunit_kmalloc_free(struct kunit_resource *res)
+static void kunit_kmalloc_array_free(struct kunit_resource *res)
 {
        kfree(res->data);
 }
 
-void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
+void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp)
 {
-       struct kunit_kmalloc_params params = {
+       struct kunit_kmalloc_array_params params = {
                .size = size,
+               .n = n,
                .gfp = gfp
        };
 
        return kunit_alloc_resource(test,
-                                   kunit_kmalloc_init,
-                                   kunit_kmalloc_free,
+                                   kunit_kmalloc_array_init,
+                                   kunit_kmalloc_array_free,
                                    gfp,
                                    &params);
 }
-EXPORT_SYMBOL_GPL(kunit_kmalloc);
+EXPORT_SYMBOL_GPL(kunit_kmalloc_array);
 
 void kunit_kfree(struct kunit *test, const void *ptr)
 {