Skip to content

Commit acd8e84

Browse files
sulixshuahkh
authored andcommitted
kunit: Print test statistics on failure
When a number of tests fail, it can be useful to get higher-level statistics of how many tests are failing (or how many parameters are failing in parameterised tests), and in what cases or suites. This is already done by some non-KUnit tests, so add support for automatically generating these for KUnit tests. This change adds a 'kunit.stats_enabled' switch which has three values: - 0: No stats are printed (current behaviour) - 1: Stats are printed only for tests/suites with more than one subtest (new default) - 2: Always print test statistics For parameterised tests, the summary line looks as follows: " # inode_test_xtimestamp_decoding: pass:16 fail:0 skip:0 total:16" For test suites, there are two lines looking like this: "# ext4_inode_test: pass:1 fail:0 skip:0 total:1" "# Totals: pass:16 fail:0 skip:0 total:16" The first line gives the number of direct subtests, the second "Totals" line is the accumulated sum of all tests and test parameters. This format is based on the one used by kselftest[1]. [1]: https://elixir.bootlin.com/linux/latest/source/tools/testing/selftests/kselftest.h#L109 Signed-off-by: David Gow <davidgow@google.com> Reviewed-by: Brendan Higgins <brendanhiggins@google.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent 6a499c9 commit acd8e84

2 files changed

Lines changed: 110 additions & 1 deletion

File tree

lib/kunit/test.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <kunit/test-bug.h>
1111
#include <linux/kernel.h>
1212
#include <linux/kref.h>
13+
#include <linux/moduleparam.h>
1314
#include <linux/sched/debug.h>
1415
#include <linux/sched.h>
1516

@@ -51,6 +52,51 @@ void __kunit_fail_current_test(const char *file, int line, const char *fmt, ...)
5152
EXPORT_SYMBOL_GPL(__kunit_fail_current_test);
5253
#endif
5354

55+
/*
56+
* KUnit statistic mode:
57+
* 0 - disabled
58+
* 1 - only when there is more than one subtest
59+
* 2 - enabled
60+
*/
61+
static int kunit_stats_enabled = 1;
62+
module_param_named(stats_enabled, kunit_stats_enabled, int, 0644);
63+
MODULE_PARM_DESC(stats_enabled,
64+
"Print test stats: never (0), only for multiple subtests (1), or always (2)");
65+
66+
struct kunit_result_stats {
67+
unsigned long passed;
68+
unsigned long skipped;
69+
unsigned long failed;
70+
unsigned long total;
71+
};
72+
73+
static bool kunit_should_print_stats(struct kunit_result_stats stats)
74+
{
75+
if (kunit_stats_enabled == 0)
76+
return false;
77+
78+
if (kunit_stats_enabled == 2)
79+
return true;
80+
81+
return (stats.total > 1);
82+
}
83+
84+
static void kunit_print_test_stats(struct kunit *test,
85+
struct kunit_result_stats stats)
86+
{
87+
if (!kunit_should_print_stats(stats))
88+
return;
89+
90+
kunit_log(KERN_INFO, test,
91+
KUNIT_SUBTEST_INDENT
92+
"# %s: pass:%lu fail:%lu skip:%lu total:%lu",
93+
test->name,
94+
stats.passed,
95+
stats.failed,
96+
stats.skipped,
97+
stats.total);
98+
}
99+
54100
/*
55101
* Append formatted message to log, size of which is limited to
56102
* KUNIT_LOG_SIZE bytes (including null terminating byte).
@@ -393,15 +439,69 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
393439
test_case->status = KUNIT_SUCCESS;
394440
}
395441

442+
static void kunit_print_suite_stats(struct kunit_suite *suite,
443+
struct kunit_result_stats suite_stats,
444+
struct kunit_result_stats param_stats)
445+
{
446+
if (kunit_should_print_stats(suite_stats)) {
447+
kunit_log(KERN_INFO, suite,
448+
"# %s: pass:%lu fail:%lu skip:%lu total:%lu",
449+
suite->name,
450+
suite_stats.passed,
451+
suite_stats.failed,
452+
suite_stats.skipped,
453+
suite_stats.total);
454+
}
455+
456+
if (kunit_should_print_stats(param_stats)) {
457+
kunit_log(KERN_INFO, suite,
458+
"# Totals: pass:%lu fail:%lu skip:%lu total:%lu",
459+
param_stats.passed,
460+
param_stats.failed,
461+
param_stats.skipped,
462+
param_stats.total);
463+
}
464+
}
465+
466+
static void kunit_update_stats(struct kunit_result_stats *stats,
467+
enum kunit_status status)
468+
{
469+
switch (status) {
470+
case KUNIT_SUCCESS:
471+
stats->passed++;
472+
break;
473+
case KUNIT_SKIPPED:
474+
stats->skipped++;
475+
break;
476+
case KUNIT_FAILURE:
477+
stats->failed++;
478+
break;
479+
}
480+
481+
stats->total++;
482+
}
483+
484+
static void kunit_accumulate_stats(struct kunit_result_stats *total,
485+
struct kunit_result_stats add)
486+
{
487+
total->passed += add.passed;
488+
total->skipped += add.skipped;
489+
total->failed += add.failed;
490+
total->total += add.total;
491+
}
492+
396493
int kunit_run_tests(struct kunit_suite *suite)
397494
{
398495
char param_desc[KUNIT_PARAM_DESC_SIZE];
399496
struct kunit_case *test_case;
497+
struct kunit_result_stats suite_stats = { 0 };
498+
struct kunit_result_stats total_stats = { 0 };
400499

401500
kunit_print_subtest_start(suite);
402501

403502
kunit_suite_for_each_test_case(suite, test_case) {
404503
struct kunit test = { .param_value = NULL, .param_index = 0 };
504+
struct kunit_result_stats param_stats = { 0 };
405505
test_case->status = KUNIT_SKIPPED;
406506

407507
if (test_case->generate_params) {
@@ -431,14 +531,23 @@ int kunit_run_tests(struct kunit_suite *suite)
431531
test.param_value = test_case->generate_params(test.param_value, param_desc);
432532
test.param_index++;
433533
}
534+
535+
kunit_update_stats(&param_stats, test.status);
536+
434537
} while (test.param_value);
435538

539+
kunit_print_test_stats(&test, param_stats);
540+
436541
kunit_print_ok_not_ok(&test, true, test_case->status,
437542
kunit_test_case_num(suite, test_case),
438543
test_case->name,
439544
test.status_comment);
545+
546+
kunit_update_stats(&suite_stats, test_case->status);
547+
kunit_accumulate_stats(&total_stats, param_stats);
440548
}
441549

550+
kunit_print_suite_stats(suite, suite_stats, total_stats);
442551
kunit_print_subtest_end(suite);
443552

444553
return 0;

tools/testing/kunit/kunit_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def print_log(log) -> None:
133133
for m in log:
134134
print_with_timestamp(m)
135135

136-
TAP_ENTRIES = re.compile(r'^(TAP|[\s]*ok|[\s]*not ok|[\s]*[0-9]+\.\.[0-9]+|[\s]*#).*$')
136+
TAP_ENTRIES = re.compile(r'^(TAP|[\s]*ok|[\s]*not ok|[\s]*[0-9]+\.\.[0-9]+|[\s]*# (Subtest:|.*: kunit test case crashed!)).*$')
137137

138138
def consume_non_diagnostic(lines: LineStream) -> None:
139139
while lines and not TAP_ENTRIES.match(lines.peek()):

0 commit comments

Comments
 (0)