printf: convert self-test to KUnit

Convert the printf() self-test to a KUnit test.

In the interest of keeping the patch reasonably-sized this doesn't
refactor the tests into proper parameterized tests - it's all one big
test case.

Signed-off-by: Tamir Duberstein <tamird@gmail.com>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Tested-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20250307-printf-kunit-convert-v6-1-4d85c361c241@gmail.com
Signed-off-by: Kees Cook <kees@kernel.org>
This commit is contained in:
Tamir Duberstein 2025-03-07 17:08:56 -05:00 committed by Kees Cook
parent 6ee149f61b
commit 7a79e7daa8
11 changed files with 132 additions and 106 deletions

View File

@ -661,7 +661,7 @@ Do *not* use it from C.
Thanks Thanks
====== ======
If you add other %p extensions, please extend <lib/test_printf.c> with If you add other %p extensions, please extend <lib/tests/printf_kunit.c>
one or more test cases, if at all feasible. with one or more test cases, if at all feasible.
Thank you for your cooperation and attention. Thank you for your cooperation and attention.

View File

@ -347,7 +347,7 @@ kselftest. We use kselftests for lib/ as an example.
1. Create the test module 1. Create the test module
2. Create the test script that will run (load/unload) the module 2. Create the test script that will run (load/unload) the module
e.g. ``tools/testing/selftests/lib/printf.sh`` e.g. ``tools/testing/selftests/lib/bitmap.sh``
3. Add line to config file e.g. ``tools/testing/selftests/lib/config`` 3. Add line to config file e.g. ``tools/testing/selftests/lib/config``

View File

@ -25411,8 +25411,8 @@ R: Sergey Senozhatsky <senozhatsky@chromium.org>
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git
F: Documentation/core-api/printk-formats.rst F: Documentation/core-api/printk-formats.rst
F: lib/test_printf.c
F: lib/test_scanf.c F: lib/test_scanf.c
F: lib/tests/printf_kunit.c
F: lib/vsprintf.c F: lib/vsprintf.c
VT1211 HARDWARE MONITOR DRIVER VT1211 HARDWARE MONITOR DRIVER

View File

@ -2427,6 +2427,15 @@ config ASYNC_RAID6_TEST
config TEST_HEXDUMP config TEST_HEXDUMP
tristate "Test functions located in the hexdump module at runtime" tristate "Test functions located in the hexdump module at runtime"
config PRINTF_KUNIT_TEST
tristate "KUnit test printf() family of functions at runtime" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
help
Enable this option to test the printf functions at runtime.
If unsure, say N.
config STRING_KUNIT_TEST config STRING_KUNIT_TEST
tristate "KUnit test string functions at runtime" if !KUNIT_ALL_TESTS tristate "KUnit test string functions at runtime" if !KUNIT_ALL_TESTS
depends on KUNIT depends on KUNIT
@ -2440,9 +2449,6 @@ config STRING_HELPERS_KUNIT_TEST
config TEST_KSTRTOX config TEST_KSTRTOX
tristate "Test kstrto*() family of functions at runtime" tristate "Test kstrto*() family of functions at runtime"
config TEST_PRINTF
tristate "Test printf() family of functions at runtime"
config TEST_SCANF config TEST_SCANF
tristate "Test scanf() family of functions at runtime" tristate "Test scanf() family of functions at runtime"

View File

@ -77,7 +77,6 @@ obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
obj-$(CONFIG_TEST_PRINTF) += test_printf.o
obj-$(CONFIG_TEST_SCANF) += test_scanf.o obj-$(CONFIG_TEST_SCANF) += test_scanf.o
obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o

View File

@ -29,6 +29,7 @@ obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o
CFLAGS_overflow_kunit.o = $(call cc-disable-warning, tautological-constant-out-of-range-compare) CFLAGS_overflow_kunit.o = $(call cc-disable-warning, tautological-constant-out-of-range-compare)
obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o
obj-$(CONFIG_PRINTF_KUNIT_TEST) += printf_kunit.o
obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o
obj-$(CONFIG_TEST_SORT) += test_sort.o obj-$(CONFIG_TEST_SORT) += test_sort.o

View File

@ -3,9 +3,7 @@
* Test cases for printf facility. * Test cases for printf facility.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <kunit/test.h>
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/printk.h> #include <linux/printk.h>
@ -25,8 +23,6 @@
#include <linux/property.h> #include <linux/property.h>
#include "../tools/testing/selftests/kselftest_module.h"
#define BUF_SIZE 256 #define BUF_SIZE 256
#define PAD_SIZE 16 #define PAD_SIZE 16
#define FILL_CHAR '$' #define FILL_CHAR '$'
@ -37,12 +33,14 @@
block \ block \
__diag_pop(); __diag_pop();
KSTM_MODULE_GLOBALS(); static unsigned int total_tests;
static char *test_buffer __initdata; static char *test_buffer;
static char *alloced_buffer __initdata; static char *alloced_buffer;
static int __printf(4, 0) __init static struct kunit *kunittest;
static void __printf(4, 0)
do_test(int bufsize, const char *expect, int elen, do_test(int bufsize, const char *expect, int elen,
const char *fmt, va_list ap) const char *fmt, va_list ap)
{ {
@ -57,52 +55,54 @@ do_test(int bufsize, const char *expect, int elen,
va_end(aq); va_end(aq);
if (ret != elen) { if (ret != elen) {
pr_warn("vsnprintf(buf, %d, \"%s\", ...) returned %d, expected %d\n", KUNIT_FAIL(kunittest, "vsnprintf(buf, %d, \"%s\", ...) returned %d, expected %d\n",
bufsize, fmt, ret, elen); bufsize, fmt, ret, elen);
return 1; return;
} }
if (memchr_inv(alloced_buffer, FILL_CHAR, PAD_SIZE)) { if (memchr_inv(alloced_buffer, FILL_CHAR, PAD_SIZE)) {
pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote before buffer\n", bufsize, fmt); KUNIT_FAIL(kunittest, "vsnprintf(buf, %d, \"%s\", ...) wrote before buffer\n",
return 1; bufsize, fmt);
return;
} }
if (!bufsize) { if (!bufsize) {
if (memchr_inv(test_buffer, FILL_CHAR, BUF_SIZE + PAD_SIZE)) { if (memchr_inv(test_buffer, FILL_CHAR, BUF_SIZE + PAD_SIZE)) {
pr_warn("vsnprintf(buf, 0, \"%s\", ...) wrote to buffer\n", KUNIT_FAIL(kunittest, "vsnprintf(buf, 0, \"%s\", ...) wrote to buffer\n", fmt);
fmt);
return 1;
} }
return 0; return;
} }
written = min(bufsize-1, elen); written = min(bufsize-1, elen);
if (test_buffer[written]) { if (test_buffer[written]) {
pr_warn("vsnprintf(buf, %d, \"%s\", ...) did not nul-terminate buffer\n", KUNIT_FAIL(kunittest,
"vsnprintf(buf, %d, \"%s\", ...) did not nul-terminate buffer\n",
bufsize, fmt); bufsize, fmt);
return 1; return;
} }
if (memchr_inv(test_buffer + written + 1, FILL_CHAR, bufsize - (written + 1))) { if (memchr_inv(test_buffer + written + 1, FILL_CHAR, bufsize - (written + 1))) {
pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote beyond the nul-terminator\n", KUNIT_FAIL(kunittest,
"vsnprintf(buf, %d, \"%s\", ...) wrote beyond the nul-terminator\n",
bufsize, fmt); bufsize, fmt);
return 1; return;
} }
if (memchr_inv(test_buffer + bufsize, FILL_CHAR, BUF_SIZE + PAD_SIZE - bufsize)) { if (memchr_inv(test_buffer + bufsize, FILL_CHAR, BUF_SIZE + PAD_SIZE - bufsize)) {
pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote beyond buffer\n", bufsize, fmt); KUNIT_FAIL(kunittest, "vsnprintf(buf, %d, \"%s\", ...) wrote beyond buffer\n",
return 1; bufsize, fmt);
return;
} }
if (memcmp(test_buffer, expect, written)) { if (memcmp(test_buffer, expect, written)) {
pr_warn("vsnprintf(buf, %d, \"%s\", ...) wrote '%s', expected '%.*s'\n", KUNIT_FAIL(kunittest,
"vsnprintf(buf, %d, \"%s\", ...) wrote '%s', expected '%.*s'\n",
bufsize, fmt, test_buffer, written, expect); bufsize, fmt, test_buffer, written, expect);
return 1; return;
} }
return 0;
} }
static void __printf(3, 4) __init static void __printf(3, 4)
__test(const char *expect, int elen, const char *fmt, ...) __test(const char *expect, int elen, const char *fmt, ...)
{ {
va_list ap; va_list ap;
@ -110,9 +110,9 @@ __test(const char *expect, int elen, const char *fmt, ...)
char *p; char *p;
if (elen >= BUF_SIZE) { if (elen >= BUF_SIZE) {
pr_err("error in test suite: expected output length %d too long. Format was '%s'.\n", KUNIT_FAIL(kunittest,
elen, fmt); "error in test suite: expected length (%d) >= BUF_SIZE (%d). fmt=\"%s\"\n",
failed_tests++; elen, BUF_SIZE, fmt);
return; return;
} }
@ -124,19 +124,19 @@ __test(const char *expect, int elen, const char *fmt, ...)
* enough and 0), and then we also test that kvasprintf would * enough and 0), and then we also test that kvasprintf would
* be able to print it as expected. * be able to print it as expected.
*/ */
failed_tests += do_test(BUF_SIZE, expect, elen, fmt, ap); do_test(BUF_SIZE, expect, elen, fmt, ap);
rand = get_random_u32_inclusive(1, elen + 1); rand = get_random_u32_inclusive(1, elen + 1);
/* Since elen < BUF_SIZE, we have 1 <= rand <= BUF_SIZE. */ /* Since elen < BUF_SIZE, we have 1 <= rand <= BUF_SIZE. */
failed_tests += do_test(rand, expect, elen, fmt, ap); do_test(rand, expect, elen, fmt, ap);
failed_tests += do_test(0, expect, elen, fmt, ap); do_test(0, expect, elen, fmt, ap);
p = kvasprintf(GFP_KERNEL, fmt, ap); p = kvasprintf(GFP_KERNEL, fmt, ap);
if (p) { if (p) {
total_tests++; total_tests++;
if (memcmp(p, expect, elen+1)) { if (memcmp(p, expect, elen+1)) {
pr_warn("kvasprintf(..., \"%s\", ...) returned '%s', expected '%s'\n", KUNIT_FAIL(kunittest,
"kvasprintf(..., \"%s\", ...) returned '%s', expected '%s'\n",
fmt, p, expect); fmt, p, expect);
failed_tests++;
} }
kfree(p); kfree(p);
} }
@ -146,7 +146,7 @@ __test(const char *expect, int elen, const char *fmt, ...)
#define test(expect, fmt, ...) \ #define test(expect, fmt, ...) \
__test(expect, strlen(expect), fmt, ##__VA_ARGS__) __test(expect, strlen(expect), fmt, ##__VA_ARGS__)
static void __init static void
test_basic(void) test_basic(void)
{ {
/* Work around annoying "warning: zero-length gnu_printf format string". */ /* Work around annoying "warning: zero-length gnu_printf format string". */
@ -158,7 +158,7 @@ test_basic(void)
__test("xxx\0yyy", 7, "xxx%cyyy", '\0'); __test("xxx\0yyy", 7, "xxx%cyyy", '\0');
} }
static void __init static void
test_number(void) test_number(void)
{ {
test("0x1234abcd ", "%#-12x", 0x1234abcd); test("0x1234abcd ", "%#-12x", 0x1234abcd);
@ -180,7 +180,7 @@ test_number(void)
test("00|0|0|0|0", "%.2d|%.1d|%.0d|%.*d|%1.0d", 0, 0, 0, 0, 0, 0); test("00|0|0|0|0", "%.2d|%.1d|%.0d|%.*d|%1.0d", 0, 0, 0, 0, 0, 0);
} }
static void __init static void
test_string(void) test_string(void)
{ {
test("", "%s%.0s", "", "123"); test("", "%s%.0s", "", "123");
@ -218,7 +218,7 @@ test_string(void)
#define ZEROS "00000000" /* hex 32 zero bits */ #define ZEROS "00000000" /* hex 32 zero bits */
#define ONES "ffffffff" /* hex 32 one bits */ #define ONES "ffffffff" /* hex 32 one bits */
static int __init static int
plain_format(void) plain_format(void)
{ {
char buf[PLAIN_BUF_SIZE]; char buf[PLAIN_BUF_SIZE];
@ -230,7 +230,8 @@ plain_format(void)
return -1; return -1;
if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) { if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
pr_warn("crng possibly not yet initialized. plain 'p' buffer contains \"%s\"", kunit_warn(kunittest,
"crng possibly not yet initialized. plain 'p' buffer contains \"%s\"\n",
PTR_VAL_NO_CRNG); PTR_VAL_NO_CRNG);
return 0; return 0;
} }
@ -250,7 +251,7 @@ plain_format(void)
#define ZEROS "" #define ZEROS ""
#define ONES "" #define ONES ""
static int __init static int
plain_format(void) plain_format(void)
{ {
/* Format is implicitly tested for 32 bit machines by plain_hash() */ /* Format is implicitly tested for 32 bit machines by plain_hash() */
@ -259,7 +260,7 @@ plain_format(void)
#endif /* BITS_PER_LONG == 64 */ #endif /* BITS_PER_LONG == 64 */
static int __init static int
plain_hash_to_buffer(const void *p, char *buf, size_t len) plain_hash_to_buffer(const void *p, char *buf, size_t len)
{ {
int nchars; int nchars;
@ -270,7 +271,8 @@ plain_hash_to_buffer(const void *p, char *buf, size_t len)
return -1; return -1;
if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) { if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
pr_warn("crng possibly not yet initialized. plain 'p' buffer contains \"%s\"", kunit_warn(kunittest,
"crng possibly not yet initialized. plain 'p' buffer contains \"%s\"\n",
PTR_VAL_NO_CRNG); PTR_VAL_NO_CRNG);
return 0; return 0;
} }
@ -278,7 +280,7 @@ plain_hash_to_buffer(const void *p, char *buf, size_t len)
return 0; return 0;
} }
static int __init static int
plain_hash(void) plain_hash(void)
{ {
char buf[PLAIN_BUF_SIZE]; char buf[PLAIN_BUF_SIZE];
@ -298,32 +300,29 @@ plain_hash(void)
* We can't use test() to test %p because we don't know what output to expect * We can't use test() to test %p because we don't know what output to expect
* after an address is hashed. * after an address is hashed.
*/ */
static void __init static void
plain(void) plain(void)
{ {
int err; int err;
if (no_hash_pointers) { if (no_hash_pointers) {
pr_warn("skipping plain 'p' tests"); kunit_warn(kunittest, "skipping plain 'p' tests");
skipped_tests += 2;
return; return;
} }
err = plain_hash(); err = plain_hash();
if (err) { if (err) {
pr_warn("plain 'p' does not appear to be hashed\n"); KUNIT_FAIL(kunittest, "plain 'p' does not appear to be hashed\n");
failed_tests++;
return; return;
} }
err = plain_format(); err = plain_format();
if (err) { if (err) {
pr_warn("hashing plain 'p' has unexpected format\n"); KUNIT_FAIL(kunittest, "hashing plain 'p' has unexpected format\n");
failed_tests++;
} }
} }
static void __init static void
test_hashed(const char *fmt, const void *p) test_hashed(const char *fmt, const void *p)
{ {
char buf[PLAIN_BUF_SIZE]; char buf[PLAIN_BUF_SIZE];
@ -343,7 +342,7 @@ test_hashed(const char *fmt, const void *p)
/* /*
* NULL pointers aren't hashed. * NULL pointers aren't hashed.
*/ */
static void __init static void
null_pointer(void) null_pointer(void)
{ {
test(ZEROS "00000000", "%p", NULL); test(ZEROS "00000000", "%p", NULL);
@ -354,7 +353,7 @@ null_pointer(void)
/* /*
* Error pointers aren't hashed. * Error pointers aren't hashed.
*/ */
static void __init static void
error_pointer(void) error_pointer(void)
{ {
test(ONES "fffffff5", "%p", ERR_PTR(-11)); test(ONES "fffffff5", "%p", ERR_PTR(-11));
@ -364,7 +363,7 @@ error_pointer(void)
#define PTR_INVALID ((void *)0x000000ab) #define PTR_INVALID ((void *)0x000000ab)
static void __init static void
invalid_pointer(void) invalid_pointer(void)
{ {
test_hashed("%p", PTR_INVALID); test_hashed("%p", PTR_INVALID);
@ -372,18 +371,18 @@ invalid_pointer(void)
test("(efault)", "%pE", PTR_INVALID); test("(efault)", "%pE", PTR_INVALID);
} }
static void __init static void
symbol_ptr(void) symbol_ptr(void)
{ {
} }
static void __init static void
kernel_ptr(void) kernel_ptr(void)
{ {
/* We can't test this without access to kptr_restrict. */ /* We can't test this without access to kptr_restrict. */
} }
static void __init static void
struct_resource(void) struct_resource(void)
{ {
struct resource test_resource = { struct resource test_resource = {
@ -432,7 +431,7 @@ struct_resource(void)
"%pR", &test_resource); "%pR", &test_resource);
} }
static void __init static void
struct_range(void) struct_range(void)
{ {
struct range test_range = DEFINE_RANGE(0xc0ffee00ba5eba11, struct range test_range = DEFINE_RANGE(0xc0ffee00ba5eba11,
@ -448,17 +447,17 @@ struct_range(void)
"%pra", &test_range); "%pra", &test_range);
} }
static void __init static void
addr(void) addr(void)
{ {
} }
static void __init static void
escaped_str(void) escaped_str(void)
{ {
} }
static void __init static void
hex_string(void) hex_string(void)
{ {
const char buf[3] = {0xc0, 0xff, 0xee}; const char buf[3] = {0xc0, 0xff, 0xee};
@ -469,7 +468,7 @@ hex_string(void)
"%*ph|%*phC|%*phD|%*phN", 3, buf, 3, buf, 3, buf, 3, buf); "%*ph|%*phC|%*phD|%*phN", 3, buf, 3, buf, 3, buf, 3, buf);
} }
static void __init static void
mac(void) mac(void)
{ {
const u8 addr[6] = {0x2d, 0x48, 0xd6, 0xfc, 0x7a, 0x05}; const u8 addr[6] = {0x2d, 0x48, 0xd6, 0xfc, 0x7a, 0x05};
@ -481,7 +480,7 @@ mac(void)
test("057afcd6482d", "%pmR", addr); test("057afcd6482d", "%pmR", addr);
} }
static void __init static void
ip4(void) ip4(void)
{ {
struct sockaddr_in sa; struct sockaddr_in sa;
@ -496,19 +495,19 @@ ip4(void)
test("001.002.003.004:12345|1.2.3.4:12345", "%piSp|%pISp", &sa, &sa); test("001.002.003.004:12345|1.2.3.4:12345", "%piSp|%pISp", &sa, &sa);
} }
static void __init static void
ip6(void) ip6(void)
{ {
} }
static void __init static void
ip(void) ip(void)
{ {
ip4(); ip4();
ip6(); ip6();
} }
static void __init static void
uuid(void) uuid(void)
{ {
const char uuid[16] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, const char uuid[16] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
@ -520,7 +519,7 @@ uuid(void)
test("03020100-0504-0706-0809-0A0B0C0D0E0F", "%pUL", uuid); test("03020100-0504-0706-0809-0A0B0C0D0E0F", "%pUL", uuid);
} }
static struct dentry test_dentry[4] __initdata = { static struct dentry test_dentry[4] = {
{ .d_parent = &test_dentry[0], { .d_parent = &test_dentry[0],
.d_name = QSTR_INIT(test_dentry[0].d_iname, 3), .d_name = QSTR_INIT(test_dentry[0].d_iname, 3),
.d_iname = "foo" }, .d_iname = "foo" },
@ -535,7 +534,7 @@ static struct dentry test_dentry[4] __initdata = {
.d_iname = "romeo" }, .d_iname = "romeo" },
}; };
static void __init static void
dentry(void) dentry(void)
{ {
test("foo", "%pd", &test_dentry[0]); test("foo", "%pd", &test_dentry[0]);
@ -556,12 +555,12 @@ dentry(void)
test(" bravo/alfa| bravo/alfa", "%12pd2|%*pd2", &test_dentry[2], 12, &test_dentry[2]); test(" bravo/alfa| bravo/alfa", "%12pd2|%*pd2", &test_dentry[2], 12, &test_dentry[2]);
} }
static void __init static void
struct_va_format(void) struct_va_format(void)
{ {
} }
static void __init static void
time_and_date(void) time_and_date(void)
{ {
/* 1543210543 */ /* 1543210543 */
@ -595,12 +594,12 @@ time_and_date(void)
test("15:32:23|0119-00-04", "%ptTtrs|%ptTdrs", &t, &t); test("15:32:23|0119-00-04", "%ptTtrs|%ptTdrs", &t, &t);
} }
static void __init static void
struct_clk(void) struct_clk(void)
{ {
} }
static void __init static void
large_bitmap(void) large_bitmap(void)
{ {
const int nbits = 1 << 16; const int nbits = 1 << 16;
@ -614,7 +613,7 @@ large_bitmap(void)
bitmap_free(bits); bitmap_free(bits);
} }
static void __init static void
bitmap(void) bitmap(void)
{ {
DECLARE_BITMAP(bits, 20); DECLARE_BITMAP(bits, 20);
@ -637,7 +636,7 @@ bitmap(void)
large_bitmap(); large_bitmap();
} }
static void __init static void
netdev_features(void) netdev_features(void)
{ {
} }
@ -663,7 +662,7 @@ static const struct page_flags_test pft[] = {
"%#x", "kasantag"}, "%#x", "kasantag"},
}; };
static void __init static void
page_flags_test(int section, int node, int zone, int last_cpupid, page_flags_test(int section, int node, int zone, int last_cpupid,
int kasan_tag, unsigned long flags, const char *name, int kasan_tag, unsigned long flags, const char *name,
char *cmp_buf) char *cmp_buf)
@ -701,7 +700,7 @@ page_flags_test(int section, int node, int zone, int last_cpupid,
test(cmp_buf, "%pGp", &flags); test(cmp_buf, "%pGp", &flags);
} }
static void __init static void
flags(void) flags(void)
{ {
unsigned long flags; unsigned long flags;
@ -749,7 +748,7 @@ flags(void)
kfree(cmp_buffer); kfree(cmp_buffer);
} }
static void __init fwnode_pointer(void) static void fwnode_pointer(void)
{ {
const struct software_node first = { .name = "first" }; const struct software_node first = { .name = "first" };
const struct software_node second = { .name = "second", .parent = &first }; const struct software_node second = { .name = "second", .parent = &first };
@ -763,7 +762,7 @@ static void __init fwnode_pointer(void)
rval = software_node_register_node_group(group); rval = software_node_register_node_group(group);
if (rval) { if (rval) {
pr_warn("cannot register softnodes; rval %d\n", rval); kunit_warn(kunittest, "cannot register softnodes; rval %d\n", rval);
return; return;
} }
@ -776,7 +775,7 @@ static void __init fwnode_pointer(void)
software_node_unregister_node_group(group); software_node_unregister_node_group(group);
} }
static void __init fourcc_pointer(void) static void fourcc_pointer(void)
{ {
struct { struct {
u32 code; u32 code;
@ -793,7 +792,7 @@ static void __init fourcc_pointer(void)
test(try[i].str, "%p4cc", &try[i].code); test(try[i].str, "%p4cc", &try[i].code);
} }
static void __init static void
errptr(void) errptr(void)
{ {
test("-1234", "%pe", ERR_PTR(-1234)); test("-1234", "%pe", ERR_PTR(-1234));
@ -813,7 +812,7 @@ errptr(void)
#endif #endif
} }
static void __init static void
test_pointer(void) test_pointer(void)
{ {
plain(); plain();
@ -842,13 +841,15 @@ test_pointer(void)
fourcc_pointer(); fourcc_pointer();
} }
static void __init selftest(void) static void printf_test(struct kunit *test)
{ {
alloced_buffer = kmalloc(BUF_SIZE + 2*PAD_SIZE, GFP_KERNEL); alloced_buffer = kmalloc(BUF_SIZE + 2*PAD_SIZE, GFP_KERNEL);
if (!alloced_buffer) if (!alloced_buffer)
return; return;
test_buffer = alloced_buffer + PAD_SIZE; test_buffer = alloced_buffer + PAD_SIZE;
kunittest = test;
test_basic(); test_basic();
test_number(); test_number();
test_string(); test_string();
@ -857,7 +858,31 @@ static void __init selftest(void)
kfree(alloced_buffer); kfree(alloced_buffer);
} }
KSTM_MODULE_LOADERS(test_printf); static int printf_suite_init(struct kunit_suite *suite)
{
total_tests = 0;
return 0;
}
static void printf_suite_exit(struct kunit_suite *suite)
{
kunit_info(suite, "ran %u tests\n", total_tests);
}
static struct kunit_case printf_test_cases[] = {
KUNIT_CASE(printf_test),
{}
};
static struct kunit_suite printf_test_suite = {
.name = "printf",
.suite_init = printf_suite_init,
.suite_exit = printf_suite_exit,
.test_cases = printf_test_cases,
};
kunit_test_suite(printf_test_suite);
MODULE_AUTHOR("Rasmus Villemoes <linux@rasmusvillemoes.dk>"); MODULE_AUTHOR("Rasmus Villemoes <linux@rasmusvillemoes.dk>");
MODULE_DESCRIPTION("Test cases for printf facility"); MODULE_DESCRIPTION("Test cases for printf facility");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -11,7 +11,7 @@
# SPDX-License-Identifier: GPL-2.0+ # SPDX-License-Identifier: GPL-2.0+
# $(dirname $0)/../kselftest/module.sh "description" module_name # $(dirname $0)/../kselftest/module.sh "description" module_name
# #
# Example: tools/testing/selftests/lib/printf.sh # Example: tools/testing/selftests/lib/bitmap.sh
desc="" # Output prefix. desc="" # Output prefix.
module="" # Filename (without the .ko). module="" # Filename (without the .ko).

View File

@ -4,5 +4,5 @@
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" # No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
all: all:
TEST_PROGS := printf.sh bitmap.sh scanf.sh TEST_PROGS := bitmap.sh scanf.sh
include ../lib.mk include ../lib.mk

View File

@ -1,4 +1,3 @@
CONFIG_TEST_PRINTF=m
CONFIG_TEST_SCANF=m CONFIG_TEST_SCANF=m
CONFIG_TEST_BITMAP=m CONFIG_TEST_BITMAP=m
CONFIG_TEST_BITOPS=m CONFIG_TEST_BITOPS=m

View File

@ -1,4 +0,0 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Tests the printf infrastructure using test_printf kernel module.
$(dirname $0)/../kselftest/module.sh "printf" test_printf