]> git.apps.os.sepia.ceph.com Git - ceph-client.git/commitdiff
scanf: convert self-test to KUnit
authorTamir Duberstein <tamird@gmail.com>
Fri, 7 Mar 2025 11:27:36 +0000 (06:27 -0500)
committerKees Cook <kees@kernel.org>
Fri, 14 Mar 2025 20:55:37 +0000 (13:55 -0700)
Convert the scanf() 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.

Reviewed-by: David Gow <davidgow@google.com>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Tested-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Tamir Duberstein <tamird@gmail.com>
Link: https://lore.kernel.org/r/20250307-scanf-kunit-convert-v9-3-b98820fa39ff@gmail.com
Signed-off-by: Kees Cook <kees@kernel.org>
MAINTAINERS
lib/Kconfig.debug
lib/Makefile
lib/test_scanf.c [deleted file]
lib/tests/Makefile
lib/tests/scanf_kunit.c [new file with mode: 0644]
tools/testing/selftests/lib/Makefile
tools/testing/selftests/lib/config
tools/testing/selftests/lib/scanf.sh [deleted file]

index 2a476c2a6ab93f05894a1472ab50fc094fa9118a..2e7baadf3ef4aed9b6536c432310e2af27ec661b 100644 (file)
@@ -25411,8 +25411,8 @@ R:      Sergey Senozhatsky <senozhatsky@chromium.org>
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git
 F:     Documentation/core-api/printk-formats.rst
-F:     lib/test_scanf.c
 F:     lib/tests/printf_kunit.c
+F:     lib/tests/scanf_kunit.c
 F:     lib/vsprintf.c
 
 VT1211 HARDWARE MONITOR DRIVER
index 443036c36351dc7d6fcfea859e958e3d914991d5..2601e4fb403c13d745842bfdf179cb8d42a0b009 100644 (file)
@@ -2436,6 +2436,15 @@ config PRINTF_KUNIT_TEST
 
          If unsure, say N.
 
+config SCANF_KUNIT_TEST
+       tristate "KUnit test scanf() family of functions at runtime" if !KUNIT_ALL_TESTS
+       depends on KUNIT
+       default KUNIT_ALL_TESTS
+       help
+         Enable this option to test the scanf functions at runtime.
+
+         If unsure, say N.
+
 config STRING_KUNIT_TEST
        tristate "KUnit test string functions at runtime" if !KUNIT_ALL_TESTS
        depends on KUNIT
@@ -2449,9 +2458,6 @@ config STRING_HELPERS_KUNIT_TEST
 config TEST_KSTRTOX
        tristate "Test kstrto*() family of functions at runtime"
 
-config TEST_SCANF
-       tristate "Test scanf() family of functions at runtime"
-
 config TEST_BITMAP
        tristate "Test bitmap_*() family of functions at runtime"
        help
index b618d9d913bca083edbdb63e9908564492bc1e9a..89b8a4bce1086c9adb71fc40d6a89a678ff085f6 100644 (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_key_base.o
 obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
-obj-$(CONFIG_TEST_SCANF) += test_scanf.o
 
 obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
 ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_KASAN),yy)
diff --git a/lib/test_scanf.c b/lib/test_scanf.c
deleted file mode 100644 (file)
index cb0b57d..0000000
+++ /dev/null
@@ -1,811 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Test cases for sscanf facility.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/bitops.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/overflow.h>
-#include <linux/printk.h>
-#include <linux/prandom.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include "../tools/testing/selftests/kselftest_module.h"
-
-#define BUF_SIZE 1024
-
-KSTM_MODULE_GLOBALS();
-static char *test_buffer __initdata;
-static char *fmt_buffer __initdata;
-static struct rnd_state rnd_state __initdata;
-
-typedef int (*check_fn)(const char *file, const int line, const void *check_data,
-                       const char *string, const char *fmt, int n_args, va_list ap);
-
-static void __scanf(6, 8) __init
-_test(const char *file, const int line, check_fn fn, const void *check_data, const char *string,
-       const char *fmt, int n_args, ...)
-{
-       va_list ap, ap_copy;
-       int ret;
-
-       total_tests++;
-
-       va_start(ap, n_args);
-       va_copy(ap_copy, ap);
-       ret = vsscanf(string, fmt, ap_copy);
-       va_end(ap_copy);
-
-       if (ret != n_args) {
-               pr_warn("%s:%d: vsscanf(\"%s\", \"%s\", ...) returned %d expected %d\n",
-                       file, line, string, fmt, ret, n_args);
-               goto fail;
-       }
-
-       ret = (*fn)(file, line, check_data, string, fmt, n_args, ap);
-       if (ret)
-               goto fail;
-
-       va_end(ap);
-
-       return;
-
-fail:
-       failed_tests++;
-       va_end(ap);
-}
-
-#define _check_numbers_template(arg_fmt, expect, str, fmt, n_args, ap)         \
-do {                                                                           \
-       for (; n_args > 0; n_args--, expect++) {                                \
-               typeof(*expect) got = *va_arg(ap, typeof(expect));              \
-               if (got != *expect) {                                           \
-                       pr_warn("%s:%d, vsscanf(\"%s\", \"%s\", ...) expected " arg_fmt " got " arg_fmt "\n", \
-                               file, line, str, fmt, *expect, got);            \
-                       return 1;                                               \
-               }                                                               \
-       }                                                                       \
-       return 0;                                                               \
-} while (0)
-
-static int __init check_ull(const char *file, const int line, const void *check_data,
-                           const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const unsigned long long *pval = check_data;
-
-       _check_numbers_template("%llu", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_ll(const char *file, const int line, const void *check_data,
-                          const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const long long *pval = check_data;
-
-       _check_numbers_template("%lld", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_ulong(const char *file, const int line, const void *check_data,
-                             const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const unsigned long *pval = check_data;
-
-       _check_numbers_template("%lu", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_long(const char *file, const int line, const void *check_data,
-                            const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const long *pval = check_data;
-
-       _check_numbers_template("%ld", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_uint(const char *file, const int line, const void *check_data,
-                            const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const unsigned int *pval = check_data;
-
-       _check_numbers_template("%u", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_int(const char *file, const int line, const void *check_data,
-                           const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const int *pval = check_data;
-
-       _check_numbers_template("%d", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_ushort(const char *file, const int line, const void *check_data,
-                              const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const unsigned short *pval = check_data;
-
-       _check_numbers_template("%hu", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_short(const char *file, const int line, const void *check_data,
-                             const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const short *pval = check_data;
-
-       _check_numbers_template("%hd", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_uchar(const char *file, const int line, const void *check_data,
-                             const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const unsigned char *pval = check_data;
-
-       _check_numbers_template("%hhu", pval, string, fmt, n_args, ap);
-}
-
-static int __init check_char(const char *file, const int line, const void *check_data,
-                            const char *string, const char *fmt, int n_args, va_list ap)
-{
-       const signed char *pval = check_data;
-
-       _check_numbers_template("%hhd", pval, string, fmt, n_args, ap);
-}
-
-/* Selection of interesting numbers to test, copied from test-kstrtox.c */
-static const unsigned long long numbers[] __initconst = {
-       0x0ULL,
-       0x1ULL,
-       0x7fULL,
-       0x80ULL,
-       0x81ULL,
-       0xffULL,
-       0x100ULL,
-       0x101ULL,
-       0x7fffULL,
-       0x8000ULL,
-       0x8001ULL,
-       0xffffULL,
-       0x10000ULL,
-       0x10001ULL,
-       0x7fffffffULL,
-       0x80000000ULL,
-       0x80000001ULL,
-       0xffffffffULL,
-       0x100000000ULL,
-       0x100000001ULL,
-       0x7fffffffffffffffULL,
-       0x8000000000000000ULL,
-       0x8000000000000001ULL,
-       0xfffffffffffffffeULL,
-       0xffffffffffffffffULL,
-};
-
-#define value_representable_in_type(T, val)                                     \
-(is_signed_type(T)                                                              \
-       ? ((long long)(val) >= type_min(T)) && ((long long)(val) <= type_max(T)) \
-       : ((unsigned long long)(val) <= type_max(T)))
-
-
-#define test_one_number(T, gen_fmt, scan_fmt, val, fn)                 \
-do {                                                                   \
-       const T expect_val = (T)(val);                                  \
-       T result = ~expect_val; /* should be overwritten */             \
-                                                                       \
-       snprintf(test_buffer, BUF_SIZE, gen_fmt, expect_val);           \
-       _test(__FILE__, __LINE__, fn, &expect_val, test_buffer, "%" scan_fmt, 1, &result);      \
-} while (0)
-
-#define simple_numbers_loop(T, gen_fmt, scan_fmt, fn)                  \
-do {                                                                   \
-       int i;                                                          \
-                                                                       \
-       for (i = 0; i < ARRAY_SIZE(numbers); i++) {                     \
-               if (value_representable_in_type(T, numbers[i]))         \
-                       test_one_number(T, gen_fmt, scan_fmt,           \
-                                       numbers[i], fn);                \
-                                                                       \
-               if (value_representable_in_type(T, -numbers[i]))        \
-                       test_one_number(T, gen_fmt, scan_fmt,           \
-                                       -numbers[i], fn);               \
-       }                                                               \
-} while (0)
-
-static void __init numbers_simple(void)
-{
-       simple_numbers_loop(unsigned long long, "%llu",   "llu", check_ull);
-       simple_numbers_loop(long long,          "%lld",   "lld", check_ll);
-       simple_numbers_loop(long long,          "%lld",   "lli", check_ll);
-       simple_numbers_loop(unsigned long long, "%llx",   "llx", check_ull);
-       simple_numbers_loop(long long,          "%llx",   "llx", check_ll);
-       simple_numbers_loop(long long,          "0x%llx", "lli", check_ll);
-       simple_numbers_loop(unsigned long long, "0x%llx", "llx", check_ull);
-       simple_numbers_loop(long long,          "0x%llx", "llx", check_ll);
-
-       simple_numbers_loop(unsigned long,      "%lu",    "lu", check_ulong);
-       simple_numbers_loop(long,               "%ld",    "ld", check_long);
-       simple_numbers_loop(long,               "%ld",    "li", check_long);
-       simple_numbers_loop(unsigned long,      "%lx",    "lx", check_ulong);
-       simple_numbers_loop(long,               "%lx",    "lx", check_long);
-       simple_numbers_loop(long,               "0x%lx",  "li", check_long);
-       simple_numbers_loop(unsigned long,      "0x%lx",  "lx", check_ulong);
-       simple_numbers_loop(long,               "0x%lx",  "lx", check_long);
-
-       simple_numbers_loop(unsigned int,       "%u",     "u", check_uint);
-       simple_numbers_loop(int,                "%d",     "d", check_int);
-       simple_numbers_loop(int,                "%d",     "i", check_int);
-       simple_numbers_loop(unsigned int,       "%x",     "x", check_uint);
-       simple_numbers_loop(int,                "%x",     "x", check_int);
-       simple_numbers_loop(int,                "0x%x",   "i", check_int);
-       simple_numbers_loop(unsigned int,       "0x%x",   "x", check_uint);
-       simple_numbers_loop(int,                "0x%x",   "x", check_int);
-
-       simple_numbers_loop(unsigned short,     "%hu",    "hu", check_ushort);
-       simple_numbers_loop(short,              "%hd",    "hd", check_short);
-       simple_numbers_loop(short,              "%hd",    "hi", check_short);
-       simple_numbers_loop(unsigned short,     "%hx",    "hx", check_ushort);
-       simple_numbers_loop(short,              "%hx",    "hx", check_short);
-       simple_numbers_loop(short,              "0x%hx",  "hi", check_short);
-       simple_numbers_loop(unsigned short,     "0x%hx",  "hx", check_ushort);
-       simple_numbers_loop(short,              "0x%hx",  "hx", check_short);
-
-       simple_numbers_loop(unsigned char,      "%hhu",   "hhu", check_uchar);
-       simple_numbers_loop(signed char,        "%hhd",   "hhd", check_char);
-       simple_numbers_loop(signed char,        "%hhd",   "hhi", check_char);
-       simple_numbers_loop(unsigned char,      "%hhx",   "hhx", check_uchar);
-       simple_numbers_loop(signed char,        "%hhx",   "hhx", check_char);
-       simple_numbers_loop(signed char,        "0x%hhx", "hhi", check_char);
-       simple_numbers_loop(unsigned char,      "0x%hhx", "hhx", check_uchar);
-       simple_numbers_loop(signed char,        "0x%hhx", "hhx", check_char);
-}
-
-/*
- * This gives a better variety of number "lengths" in a small sample than
- * the raw prandom*() functions (Not mathematically rigorous!!).
- * Variabilty of length and value is more important than perfect randomness.
- */
-static u32 __init next_test_random(u32 max_bits)
-{
-       u32 n_bits = hweight32(prandom_u32_state(&rnd_state)) % (max_bits + 1);
-
-       return prandom_u32_state(&rnd_state) & GENMASK(n_bits, 0);
-}
-
-static unsigned long long __init next_test_random_ull(void)
-{
-       u32 rand1 = prandom_u32_state(&rnd_state);
-       u32 n_bits = (hweight32(rand1) * 3) % 64;
-       u64 val = (u64)prandom_u32_state(&rnd_state) * rand1;
-
-       return val & GENMASK_ULL(n_bits, 0);
-}
-
-#define random_for_type(T)                             \
-       ((T)(sizeof(T) <= sizeof(u32)                   \
-               ? next_test_random(BITS_PER_TYPE(T))    \
-               : next_test_random_ull()))
-
-/*
- * Define a pattern of negative and positive numbers to ensure we get
- * some of both within the small number of samples in a test string.
- */
-#define NEGATIVES_PATTERN 0x3246       /* 00110010 01000110 */
-
-#define fill_random_array(arr)                                                 \
-do {                                                                           \
-       unsigned int neg_pattern = NEGATIVES_PATTERN;                           \
-       int i;                                                                  \
-                                                                               \
-       for (i = 0; i < ARRAY_SIZE(arr); i++, neg_pattern >>= 1) {              \
-               (arr)[i] = random_for_type(typeof((arr)[0]));                   \
-               if (is_signed_type(typeof((arr)[0])) && (neg_pattern & 1))      \
-                       (arr)[i] = -(arr)[i];                                   \
-       }                                                                       \
-} while (0)
-
-/*
- * Convenience wrapper around snprintf() to append at buf_pos in buf,
- * updating buf_pos and returning the number of characters appended.
- * On error buf_pos is not changed and return value is 0.
- */
-static int __init __printf(4, 5)
-append_fmt(char *buf, int *buf_pos, int buf_len, const char *val_fmt, ...)
-{
-       va_list ap;
-       int field_len;
-
-       va_start(ap, val_fmt);
-       field_len = vsnprintf(buf + *buf_pos, buf_len - *buf_pos, val_fmt, ap);
-       va_end(ap);
-
-       if (field_len < 0)
-               field_len = 0;
-
-       *buf_pos += field_len;
-
-       return field_len;
-}
-
-/*
- * Convenience function to append the field delimiter string
- * to both the value string and format string buffers.
- */
-static void __init append_delim(char *str_buf, int *str_buf_pos, int str_buf_len,
-                               char *fmt_buf, int *fmt_buf_pos, int fmt_buf_len,
-                               const char *delim_str)
-{
-       append_fmt(str_buf, str_buf_pos, str_buf_len, delim_str);
-       append_fmt(fmt_buf, fmt_buf_pos, fmt_buf_len, delim_str);
-}
-
-#define test_array_8(fn, check_data, string, fmt, arr)                         \
-do {                                                                           \
-       BUILD_BUG_ON(ARRAY_SIZE(arr) != 8);                                     \
-       _test(__FILE__, __LINE__, fn, check_data, string, fmt, 8,               \
-               &(arr)[0], &(arr)[1], &(arr)[2], &(arr)[3],                     \
-               &(arr)[4], &(arr)[5], &(arr)[6], &(arr)[7]);                    \
-} while (0)
-
-#define numbers_list_8(T, gen_fmt, field_sep, scan_fmt, fn)                    \
-do {                                                                           \
-       int i, pos = 0, fmt_pos = 0;                                            \
-       T expect[8], result[8];                                                 \
-                                                                               \
-       fill_random_array(expect);                                              \
-                                                                               \
-       for (i = 0; i < ARRAY_SIZE(expect); i++) {                              \
-               if (i != 0)                                                     \
-                       append_delim(test_buffer, &pos, BUF_SIZE,               \
-                                    fmt_buffer, &fmt_pos, BUF_SIZE,            \
-                                    field_sep);                                \
-                                                                               \
-               append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt, expect[i]);    \
-               append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE, "%%%s", scan_fmt);   \
-       }                                                                       \
-                                                                               \
-       test_array_8(fn, expect, test_buffer, fmt_buffer, result);              \
-} while (0)
-
-#define numbers_list_fix_width(T, gen_fmt, field_sep, width, scan_fmt, fn)     \
-do {                                                                           \
-       char full_fmt[16];                                                      \
-                                                                               \
-       snprintf(full_fmt, sizeof(full_fmt), "%u%s", width, scan_fmt);          \
-       numbers_list_8(T, gen_fmt, field_sep, full_fmt, fn);                    \
-} while (0)
-
-#define numbers_list_val_width(T, gen_fmt, field_sep, scan_fmt, fn)            \
-do {                                                                           \
-       int i, val_len, pos = 0, fmt_pos = 0;                                   \
-       T expect[8], result[8];                                                 \
-                                                                               \
-       fill_random_array(expect);                                              \
-                                                                               \
-       for (i = 0; i < ARRAY_SIZE(expect); i++) {                              \
-               if (i != 0)                                                     \
-                       append_delim(test_buffer, &pos, BUF_SIZE,               \
-                                    fmt_buffer, &fmt_pos, BUF_SIZE, field_sep);\
-                                                                               \
-               val_len = append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt,      \
-                                    expect[i]);                                \
-               append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE,                      \
-                          "%%%u%s", val_len, scan_fmt);                        \
-       }                                                                       \
-                                                                               \
-       test_array_8(fn, expect, test_buffer, fmt_buffer, result);              \
-} while (0)
-
-static void __init numbers_list_ll(const char *delim)
-{
-       numbers_list_8(unsigned long long, "%llu",   delim, "llu", check_ull);
-       numbers_list_8(long long,          "%lld",   delim, "lld", check_ll);
-       numbers_list_8(long long,          "%lld",   delim, "lli", check_ll);
-       numbers_list_8(unsigned long long, "%llx",   delim, "llx", check_ull);
-       numbers_list_8(unsigned long long, "0x%llx", delim, "llx", check_ull);
-       numbers_list_8(long long,          "0x%llx", delim, "lli", check_ll);
-}
-
-static void __init numbers_list_l(const char *delim)
-{
-       numbers_list_8(unsigned long,      "%lu",    delim, "lu", check_ulong);
-       numbers_list_8(long,               "%ld",    delim, "ld", check_long);
-       numbers_list_8(long,               "%ld",    delim, "li", check_long);
-       numbers_list_8(unsigned long,      "%lx",    delim, "lx", check_ulong);
-       numbers_list_8(unsigned long,      "0x%lx",  delim, "lx", check_ulong);
-       numbers_list_8(long,               "0x%lx",  delim, "li", check_long);
-}
-
-static void __init numbers_list_d(const char *delim)
-{
-       numbers_list_8(unsigned int,       "%u",     delim, "u", check_uint);
-       numbers_list_8(int,                "%d",     delim, "d", check_int);
-       numbers_list_8(int,                "%d",     delim, "i", check_int);
-       numbers_list_8(unsigned int,       "%x",     delim, "x", check_uint);
-       numbers_list_8(unsigned int,       "0x%x",   delim, "x", check_uint);
-       numbers_list_8(int,                "0x%x",   delim, "i", check_int);
-}
-
-static void __init numbers_list_h(const char *delim)
-{
-       numbers_list_8(unsigned short,     "%hu",    delim, "hu", check_ushort);
-       numbers_list_8(short,              "%hd",    delim, "hd", check_short);
-       numbers_list_8(short,              "%hd",    delim, "hi", check_short);
-       numbers_list_8(unsigned short,     "%hx",    delim, "hx", check_ushort);
-       numbers_list_8(unsigned short,     "0x%hx",  delim, "hx", check_ushort);
-       numbers_list_8(short,              "0x%hx",  delim, "hi", check_short);
-}
-
-static void __init numbers_list_hh(const char *delim)
-{
-       numbers_list_8(unsigned char,      "%hhu",   delim, "hhu", check_uchar);
-       numbers_list_8(signed char,        "%hhd",   delim, "hhd", check_char);
-       numbers_list_8(signed char,        "%hhd",   delim, "hhi", check_char);
-       numbers_list_8(unsigned char,      "%hhx",   delim, "hhx", check_uchar);
-       numbers_list_8(unsigned char,      "0x%hhx", delim, "hhx", check_uchar);
-       numbers_list_8(signed char,        "0x%hhx", delim, "hhi", check_char);
-}
-
-static void __init numbers_list(const char *delim)
-{
-       numbers_list_ll(delim);
-       numbers_list_l(delim);
-       numbers_list_d(delim);
-       numbers_list_h(delim);
-       numbers_list_hh(delim);
-}
-
-static void __init numbers_list_field_width_ll(const char *delim)
-{
-       numbers_list_fix_width(unsigned long long, "%llu",   delim, 20, "llu", check_ull);
-       numbers_list_fix_width(long long,          "%lld",   delim, 20, "lld", check_ll);
-       numbers_list_fix_width(long long,          "%lld",   delim, 20, "lli", check_ll);
-       numbers_list_fix_width(unsigned long long, "%llx",   delim, 16, "llx", check_ull);
-       numbers_list_fix_width(unsigned long long, "0x%llx", delim, 18, "llx", check_ull);
-       numbers_list_fix_width(long long,          "0x%llx", delim, 18, "lli", check_ll);
-}
-
-static void __init numbers_list_field_width_l(const char *delim)
-{
-#if BITS_PER_LONG == 64
-       numbers_list_fix_width(unsigned long,   "%lu",       delim, 20, "lu", check_ulong);
-       numbers_list_fix_width(long,            "%ld",       delim, 20, "ld", check_long);
-       numbers_list_fix_width(long,            "%ld",       delim, 20, "li", check_long);
-       numbers_list_fix_width(unsigned long,   "%lx",       delim, 16, "lx", check_ulong);
-       numbers_list_fix_width(unsigned long,   "0x%lx",     delim, 18, "lx", check_ulong);
-       numbers_list_fix_width(long,            "0x%lx",     delim, 18, "li", check_long);
-#else
-       numbers_list_fix_width(unsigned long,   "%lu",       delim, 10, "lu", check_ulong);
-       numbers_list_fix_width(long,            "%ld",       delim, 11, "ld", check_long);
-       numbers_list_fix_width(long,            "%ld",       delim, 11, "li", check_long);
-       numbers_list_fix_width(unsigned long,   "%lx",       delim, 8,  "lx", check_ulong);
-       numbers_list_fix_width(unsigned long,   "0x%lx",     delim, 10, "lx", check_ulong);
-       numbers_list_fix_width(long,            "0x%lx",     delim, 10, "li", check_long);
-#endif
-}
-
-static void __init numbers_list_field_width_d(const char *delim)
-{
-       numbers_list_fix_width(unsigned int,    "%u",        delim, 10, "u", check_uint);
-       numbers_list_fix_width(int,             "%d",        delim, 11, "d", check_int);
-       numbers_list_fix_width(int,             "%d",        delim, 11, "i", check_int);
-       numbers_list_fix_width(unsigned int,    "%x",        delim, 8,  "x", check_uint);
-       numbers_list_fix_width(unsigned int,    "0x%x",      delim, 10, "x", check_uint);
-       numbers_list_fix_width(int,             "0x%x",      delim, 10, "i", check_int);
-}
-
-static void __init numbers_list_field_width_h(const char *delim)
-{
-       numbers_list_fix_width(unsigned short,  "%hu",       delim, 5, "hu", check_ushort);
-       numbers_list_fix_width(short,           "%hd",       delim, 6, "hd", check_short);
-       numbers_list_fix_width(short,           "%hd",       delim, 6, "hi", check_short);
-       numbers_list_fix_width(unsigned short,  "%hx",       delim, 4, "hx", check_ushort);
-       numbers_list_fix_width(unsigned short,  "0x%hx",     delim, 6, "hx", check_ushort);
-       numbers_list_fix_width(short,           "0x%hx",     delim, 6, "hi", check_short);
-}
-
-static void __init numbers_list_field_width_hh(const char *delim)
-{
-       numbers_list_fix_width(unsigned char,   "%hhu",      delim, 3, "hhu", check_uchar);
-       numbers_list_fix_width(signed char,     "%hhd",      delim, 4, "hhd", check_char);
-       numbers_list_fix_width(signed char,     "%hhd",      delim, 4, "hhi", check_char);
-       numbers_list_fix_width(unsigned char,   "%hhx",      delim, 2, "hhx", check_uchar);
-       numbers_list_fix_width(unsigned char,   "0x%hhx",    delim, 4, "hhx", check_uchar);
-       numbers_list_fix_width(signed char,     "0x%hhx",    delim, 4, "hhi", check_char);
-}
-
-/*
- * List of numbers separated by delim. Each field width specifier is the
- * maximum possible digits for the given type and base.
- */
-static void __init numbers_list_field_width_typemax(const char *delim)
-{
-       numbers_list_field_width_ll(delim);
-       numbers_list_field_width_l(delim);
-       numbers_list_field_width_d(delim);
-       numbers_list_field_width_h(delim);
-       numbers_list_field_width_hh(delim);
-}
-
-static void __init numbers_list_field_width_val_ll(const char *delim)
-{
-       numbers_list_val_width(unsigned long long, "%llu",   delim, "llu", check_ull);
-       numbers_list_val_width(long long,          "%lld",   delim, "lld", check_ll);
-       numbers_list_val_width(long long,          "%lld",   delim, "lli", check_ll);
-       numbers_list_val_width(unsigned long long, "%llx",   delim, "llx", check_ull);
-       numbers_list_val_width(unsigned long long, "0x%llx", delim, "llx", check_ull);
-       numbers_list_val_width(long long,          "0x%llx", delim, "lli", check_ll);
-}
-
-static void __init numbers_list_field_width_val_l(const char *delim)
-{
-       numbers_list_val_width(unsigned long,   "%lu",       delim, "lu", check_ulong);
-       numbers_list_val_width(long,            "%ld",       delim, "ld", check_long);
-       numbers_list_val_width(long,            "%ld",       delim, "li", check_long);
-       numbers_list_val_width(unsigned long,   "%lx",       delim, "lx", check_ulong);
-       numbers_list_val_width(unsigned long,   "0x%lx",     delim, "lx", check_ulong);
-       numbers_list_val_width(long,            "0x%lx",     delim, "li", check_long);
-}
-
-static void __init numbers_list_field_width_val_d(const char *delim)
-{
-       numbers_list_val_width(unsigned int,    "%u",        delim, "u", check_uint);
-       numbers_list_val_width(int,             "%d",        delim, "d", check_int);
-       numbers_list_val_width(int,             "%d",        delim, "i", check_int);
-       numbers_list_val_width(unsigned int,    "%x",        delim, "x", check_uint);
-       numbers_list_val_width(unsigned int,    "0x%x",      delim, "x", check_uint);
-       numbers_list_val_width(int,             "0x%x",      delim, "i", check_int);
-}
-
-static void __init numbers_list_field_width_val_h(const char *delim)
-{
-       numbers_list_val_width(unsigned short,  "%hu",       delim, "hu", check_ushort);
-       numbers_list_val_width(short,           "%hd",       delim, "hd", check_short);
-       numbers_list_val_width(short,           "%hd",       delim, "hi", check_short);
-       numbers_list_val_width(unsigned short,  "%hx",       delim, "hx", check_ushort);
-       numbers_list_val_width(unsigned short,  "0x%hx",     delim, "hx", check_ushort);
-       numbers_list_val_width(short,           "0x%hx",     delim, "hi", check_short);
-}
-
-static void __init numbers_list_field_width_val_hh(const char *delim)
-{
-       numbers_list_val_width(unsigned char,   "%hhu",      delim, "hhu", check_uchar);
-       numbers_list_val_width(signed char,     "%hhd",      delim, "hhd", check_char);
-       numbers_list_val_width(signed char,     "%hhd",      delim, "hhi", check_char);
-       numbers_list_val_width(unsigned char,   "%hhx",      delim, "hhx", check_uchar);
-       numbers_list_val_width(unsigned char,   "0x%hhx",    delim, "hhx", check_uchar);
-       numbers_list_val_width(signed char,     "0x%hhx",    delim, "hhi", check_char);
-}
-
-/*
- * List of numbers separated by delim. Each field width specifier is the
- * exact length of the corresponding value digits in the string being scanned.
- */
-static void __init numbers_list_field_width_val_width(const char *delim)
-{
-       numbers_list_field_width_val_ll(delim);
-       numbers_list_field_width_val_l(delim);
-       numbers_list_field_width_val_d(delim);
-       numbers_list_field_width_val_h(delim);
-       numbers_list_field_width_val_hh(delim);
-}
-
-/*
- * Slice a continuous string of digits without field delimiters, containing
- * numbers of varying length, using the field width to extract each group
- * of digits. For example the hex values c0,3,bf01,303 would have a
- * string representation of "c03bf01303" and extracted with "%2x%1x%4x%3x".
- */
-static void __init numbers_slice(void)
-{
-       numbers_list_field_width_val_width("");
-}
-
-#define test_number_prefix(T, str, scan_fmt, expect0, expect1, n_args, fn)     \
-do {                                                                           \
-       const T expect[2] = { expect0, expect1 };                               \
-       T result[2] = { (T)~expect[0], (T)~expect[1] };                         \
-                                                                               \
-       _test(__FILE__, __LINE__, fn, &expect, str, scan_fmt, n_args, &result[0], &result[1]);  \
-} while (0)
-
-/*
- * Number prefix is >= field width.
- * Expected behaviour is derived from testing userland sscanf.
- */
-static void __init numbers_prefix_overflow(void)
-{
-       /*
-        * Negative decimal with a field of width 1, should quit scanning
-        * and return 0.
-        */
-       test_number_prefix(long long,   "-1 1", "%1lld %lld",   0, 0, 0, check_ll);
-       test_number_prefix(long,        "-1 1", "%1ld %ld",     0, 0, 0, check_long);
-       test_number_prefix(int,         "-1 1", "%1d %d",       0, 0, 0, check_int);
-       test_number_prefix(short,       "-1 1", "%1hd %hd",     0, 0, 0, check_short);
-       test_number_prefix(signed char, "-1 1", "%1hhd %hhd",   0, 0, 0, check_char);
-
-       test_number_prefix(long long,   "-1 1", "%1lli %lli",   0, 0, 0, check_ll);
-       test_number_prefix(long,        "-1 1", "%1li %li",     0, 0, 0, check_long);
-       test_number_prefix(int,         "-1 1", "%1i %i",       0, 0, 0, check_int);
-       test_number_prefix(short,       "-1 1", "%1hi %hi",     0, 0, 0, check_short);
-       test_number_prefix(signed char, "-1 1", "%1hhi %hhi",   0, 0, 0, check_char);
-
-       /*
-        * 0x prefix in a field of width 1: 0 is a valid digit so should
-        * convert. Next field scan starts at the 'x' which isn't a digit so
-        * scan quits with one field converted.
-        */
-       test_number_prefix(unsigned long long,  "0xA7", "%1llx%llx", 0, 0, 1, check_ull);
-       test_number_prefix(unsigned long,       "0xA7", "%1lx%lx",   0, 0, 1, check_ulong);
-       test_number_prefix(unsigned int,        "0xA7", "%1x%x",     0, 0, 1, check_uint);
-       test_number_prefix(unsigned short,      "0xA7", "%1hx%hx",   0, 0, 1, check_ushort);
-       test_number_prefix(unsigned char,       "0xA7", "%1hhx%hhx", 0, 0, 1, check_uchar);
-       test_number_prefix(long long,           "0xA7", "%1lli%llx", 0, 0, 1, check_ll);
-       test_number_prefix(long,                "0xA7", "%1li%lx",   0, 0, 1, check_long);
-       test_number_prefix(int,                 "0xA7", "%1i%x",     0, 0, 1, check_int);
-       test_number_prefix(short,               "0xA7", "%1hi%hx",   0, 0, 1, check_short);
-       test_number_prefix(char,                "0xA7", "%1hhi%hhx", 0, 0, 1, check_char);
-
-       /*
-        * 0x prefix in a field of width 2 using %x conversion: first field
-        * converts to 0. Next field scan starts at the character after "0x".
-        * Both fields will convert.
-        */
-       test_number_prefix(unsigned long long,  "0xA7", "%2llx%llx", 0, 0xa7, 2, check_ull);
-       test_number_prefix(unsigned long,       "0xA7", "%2lx%lx",   0, 0xa7, 2, check_ulong);
-       test_number_prefix(unsigned int,        "0xA7", "%2x%x",     0, 0xa7, 2, check_uint);
-       test_number_prefix(unsigned short,      "0xA7", "%2hx%hx",   0, 0xa7, 2, check_ushort);
-       test_number_prefix(unsigned char,       "0xA7", "%2hhx%hhx", 0, 0xa7, 2, check_uchar);
-
-       /*
-        * 0x prefix in a field of width 2 using %i conversion: first field
-        * converts to 0. Next field scan starts at the character after "0x",
-        * which will convert if can be interpreted as decimal but will fail
-        * if it contains any hex digits (since no 0x prefix).
-        */
-       test_number_prefix(long long,   "0x67", "%2lli%lli", 0, 67, 2, check_ll);
-       test_number_prefix(long,        "0x67", "%2li%li",   0, 67, 2, check_long);
-       test_number_prefix(int,         "0x67", "%2i%i",     0, 67, 2, check_int);
-       test_number_prefix(short,       "0x67", "%2hi%hi",   0, 67, 2, check_short);
-       test_number_prefix(char,        "0x67", "%2hhi%hhi", 0, 67, 2, check_char);
-
-       test_number_prefix(long long,   "0xA7", "%2lli%lli", 0, 0,  1, check_ll);
-       test_number_prefix(long,        "0xA7", "%2li%li",   0, 0,  1, check_long);
-       test_number_prefix(int,         "0xA7", "%2i%i",     0, 0,  1, check_int);
-       test_number_prefix(short,       "0xA7", "%2hi%hi",   0, 0,  1, check_short);
-       test_number_prefix(char,        "0xA7", "%2hhi%hhi", 0, 0,  1, check_char);
-}
-
-#define _test_simple_strtoxx(T, fn, gen_fmt, expect, base)                     \
-do {                                                                           \
-       T got;                                                                  \
-       char *endp;                                                             \
-       int len;                                                                \
-       bool fail = false;                                                      \
-                                                                               \
-       total_tests++;                                                          \
-       len = snprintf(test_buffer, BUF_SIZE, gen_fmt, expect);                 \
-       got = (fn)(test_buffer, &endp, base);                                   \
-       if (got != (expect)) {                                                  \
-               fail = true;                                                    \
-               pr_warn(#fn "(\"%s\", %d): got " gen_fmt " expected " gen_fmt "\n", \
-                       test_buffer, base, got, expect);                        \
-       } else if (endp != test_buffer + len) {                                 \
-               fail = true;                                                    \
-               pr_warn(#fn "(\"%s\", %d) startp=0x%px got endp=0x%px expected 0x%px\n", \
-                       test_buffer, base, test_buffer,                         \
-                       test_buffer + len, endp);                               \
-       }                                                                       \
-                                                                               \
-       if (fail)                                                               \
-               failed_tests++;                                                 \
-} while (0)
-
-#define test_simple_strtoxx(T, fn, gen_fmt, base)                              \
-do {                                                                           \
-       int i;                                                                  \
-                                                                               \
-       for (i = 0; i < ARRAY_SIZE(numbers); i++) {                             \
-               _test_simple_strtoxx(T, fn, gen_fmt, (T)numbers[i], base);      \
-                                                                               \
-               if (is_signed_type(T))                                          \
-                       _test_simple_strtoxx(T, fn, gen_fmt,                    \
-                                             -(T)numbers[i], base);            \
-       }                                                                       \
-} while (0)
-
-static void __init test_simple_strtoull(void)
-{
-       test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu",   10);
-       test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu",   0);
-       test_simple_strtoxx(unsigned long long, simple_strtoull, "%llx",   16);
-       test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 16);
-       test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 0);
-}
-
-static void __init test_simple_strtoll(void)
-{
-       test_simple_strtoxx(long long, simple_strtoll, "%lld",   10);
-       test_simple_strtoxx(long long, simple_strtoll, "%lld",   0);
-       test_simple_strtoxx(long long, simple_strtoll, "%llx",   16);
-       test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 16);
-       test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 0);
-}
-
-static void __init test_simple_strtoul(void)
-{
-       test_simple_strtoxx(unsigned long, simple_strtoul, "%lu",   10);
-       test_simple_strtoxx(unsigned long, simple_strtoul, "%lu",   0);
-       test_simple_strtoxx(unsigned long, simple_strtoul, "%lx",   16);
-       test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 16);
-       test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 0);
-}
-
-static void __init test_simple_strtol(void)
-{
-       test_simple_strtoxx(long, simple_strtol, "%ld",   10);
-       test_simple_strtoxx(long, simple_strtol, "%ld",   0);
-       test_simple_strtoxx(long, simple_strtol, "%lx",   16);
-       test_simple_strtoxx(long, simple_strtol, "0x%lx", 16);
-       test_simple_strtoxx(long, simple_strtol, "0x%lx", 0);
-}
-
-/* Selection of common delimiters/separators between numbers in a string. */
-static const char * const number_delimiters[] __initconst = {
-       " ", ":", ",", "-", "/",
-};
-
-static void __init test_numbers(void)
-{
-       int i;
-
-       /* String containing only one number. */
-       numbers_simple();
-
-       /* String with multiple numbers separated by delimiter. */
-       for (i = 0; i < ARRAY_SIZE(number_delimiters); i++) {
-               numbers_list(number_delimiters[i]);
-
-               /* Field width may be longer than actual field digits. */
-               numbers_list_field_width_typemax(number_delimiters[i]);
-
-               /* Each field width exactly length of actual field digits. */
-               numbers_list_field_width_val_width(number_delimiters[i]);
-       }
-
-       /* Slice continuous sequence of digits using field widths. */
-       numbers_slice();
-
-       numbers_prefix_overflow();
-}
-
-static void __init selftest(void)
-{
-       test_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
-       if (!test_buffer)
-               return;
-
-       fmt_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
-       if (!fmt_buffer) {
-               kfree(test_buffer);
-               return;
-       }
-
-       prandom_seed_state(&rnd_state, 3141592653589793238ULL);
-
-       test_numbers();
-
-       test_simple_strtoull();
-       test_simple_strtoll();
-       test_simple_strtoul();
-       test_simple_strtol();
-
-       kfree(fmt_buffer);
-       kfree(test_buffer);
-}
-
-KSTM_MODULE_LOADERS(test_scanf);
-MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
-MODULE_DESCRIPTION("Test cases for sscanf facility");
-MODULE_LICENSE("GPL v2");
index 33e02744ec623caab589f19c1f7003556ac916a2..498915255860dd654657b787ee7f35e3b35dcde5 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o
 CFLAGS_overflow_kunit.o = $(call cc-disable-warning, tautological-constant-out-of-range-compare)
 obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o
 obj-$(CONFIG_PRINTF_KUNIT_TEST) += printf_kunit.o
+obj-$(CONFIG_SCANF_KUNIT_TEST) += scanf_kunit.o
 obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
 obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o
 obj-$(CONFIG_TEST_SORT) += test_sort.o
diff --git a/lib/tests/scanf_kunit.c b/lib/tests/scanf_kunit.c
new file mode 100644 (file)
index 0000000..94eb5ec
--- /dev/null
@@ -0,0 +1,803 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test cases for sscanf facility.
+ */
+
+#include <kunit/test.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/prandom.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#define BUF_SIZE 1024
+
+static char *test_buffer;
+static char *fmt_buffer;
+static struct rnd_state rnd_state;
+
+typedef void (*check_fn)(struct kunit *test, const char *file, const int line,
+                        const void *check_data, const char *string, const char *fmt, int n_args,
+                        va_list ap);
+
+static void __scanf(7, 9)
+_test(struct kunit *test, const char *file, const int line, check_fn fn, const void *check_data,
+       const char *string, const char *fmt, int n_args, ...)
+{
+       va_list ap, ap_copy;
+       int ret;
+
+       va_start(ap, n_args);
+       va_copy(ap_copy, ap);
+       ret = vsscanf(string, fmt, ap_copy);
+       va_end(ap_copy);
+
+       if (ret != n_args) {
+               KUNIT_FAIL(test, "%s:%d: vsscanf(\"%s\", \"%s\", ...) returned %d expected %d",
+                          file, line, string, fmt, ret, n_args);
+       } else {
+               (*fn)(test, file, line, check_data, string, fmt, n_args, ap);
+       }
+
+       va_end(ap);
+}
+
+#define _check_numbers_template(arg_fmt, expect, str, fmt, n_args, ap)         \
+do {                                                                           \
+       for (; n_args > 0; n_args--, expect++) {                                \
+               typeof(*expect) got = *va_arg(ap, typeof(expect));              \
+               if (got != *expect) {                                           \
+                       KUNIT_FAIL(test,                                        \
+                                  "%s:%d: vsscanf(\"%s\", \"%s\", ...) expected " arg_fmt " got " arg_fmt, \
+                                  file, line, str, fmt, *expect, got);         \
+                       return;                                                 \
+               }                                                               \
+       }                                                                       \
+} while (0)
+
+static void check_ull(struct kunit *test, const char *file, const int line, const void *check_data,
+                     const char *string, const char *fmt, int n_args, va_list ap)
+{
+       const unsigned long long *pval = check_data;
+
+       _check_numbers_template("%llu", pval, string, fmt, n_args, ap);
+}
+
+static void check_ll(struct kunit *test, const char *file, const int line, const void *check_data,
+                    const char *string, const char *fmt, int n_args, va_list ap)
+{
+       const long long *pval = check_data;
+
+       _check_numbers_template("%lld", pval, string, fmt, n_args, ap);
+}
+
+static void check_ulong(struct kunit *test, const char *file, const int line,
+                       const void *check_data, const char *string, const char *fmt, int n_args,
+                       va_list ap)
+{
+       const unsigned long *pval = check_data;
+
+       _check_numbers_template("%lu", pval, string, fmt, n_args, ap);
+}
+
+static void check_long(struct kunit *test, const char *file, const int line, const void *check_data,
+                      const char *string, const char *fmt, int n_args, va_list ap)
+{
+       const long *pval = check_data;
+
+       _check_numbers_template("%ld", pval, string, fmt, n_args, ap);
+}
+
+static void check_uint(struct kunit *test, const char *file, const int line, const void *check_data,
+                      const char *string, const char *fmt, int n_args, va_list ap)
+{
+       const unsigned int *pval = check_data;
+
+       _check_numbers_template("%u", pval, string, fmt, n_args, ap);
+}
+
+static void check_int(struct kunit *test, const char *file, const int line, const void *check_data,
+                     const char *string, const char *fmt, int n_args, va_list ap)
+{
+       const int *pval = check_data;
+
+       _check_numbers_template("%d", pval, string, fmt, n_args, ap);
+}
+
+static void check_ushort(struct kunit *test, const char *file, const int line,
+                        const void *check_data, const char *string, const char *fmt, int n_args,
+                        va_list ap)
+{
+       const unsigned short *pval = check_data;
+
+       _check_numbers_template("%hu", pval, string, fmt, n_args, ap);
+}
+
+static void check_short(struct kunit *test, const char *file, const int line,
+                       const void *check_data, const char *string, const char *fmt, int n_args,
+                       va_list ap)
+{
+       const short *pval = check_data;
+
+       _check_numbers_template("%hd", pval, string, fmt, n_args, ap);
+}
+
+static void check_uchar(struct kunit *test, const char *file, const int line,
+                       const void *check_data, const char *string, const char *fmt, int n_args,
+                       va_list ap)
+{
+       const unsigned char *pval = check_data;
+
+       _check_numbers_template("%hhu", pval, string, fmt, n_args, ap);
+}
+
+static void check_char(struct kunit *test, const char *file, const int line, const void *check_data,
+                      const char *string, const char *fmt, int n_args, va_list ap)
+{
+       const signed char *pval = check_data;
+
+       _check_numbers_template("%hhd", pval, string, fmt, n_args, ap);
+}
+
+/* Selection of interesting numbers to test, copied from test-kstrtox.c */
+static const unsigned long long numbers[] = {
+       0x0ULL,
+       0x1ULL,
+       0x7fULL,
+       0x80ULL,
+       0x81ULL,
+       0xffULL,
+       0x100ULL,
+       0x101ULL,
+       0x7fffULL,
+       0x8000ULL,
+       0x8001ULL,
+       0xffffULL,
+       0x10000ULL,
+       0x10001ULL,
+       0x7fffffffULL,
+       0x80000000ULL,
+       0x80000001ULL,
+       0xffffffffULL,
+       0x100000000ULL,
+       0x100000001ULL,
+       0x7fffffffffffffffULL,
+       0x8000000000000000ULL,
+       0x8000000000000001ULL,
+       0xfffffffffffffffeULL,
+       0xffffffffffffffffULL,
+};
+
+#define value_representable_in_type(T, val)                                     \
+(is_signed_type(T)                                                              \
+       ? ((long long)(val) >= type_min(T)) && ((long long)(val) <= type_max(T)) \
+       : ((unsigned long long)(val) <= type_max(T)))
+
+
+#define test_one_number(T, gen_fmt, scan_fmt, val, fn)                 \
+do {                                                                   \
+       const T expect_val = (T)(val);                                  \
+       T result = ~expect_val; /* should be overwritten */             \
+                                                                       \
+       snprintf(test_buffer, BUF_SIZE, gen_fmt, expect_val);           \
+       _test(test, __FILE__, __LINE__, fn, &expect_val, test_buffer, "%" scan_fmt, 1, &result);\
+} while (0)
+
+#define simple_numbers_loop(T, gen_fmt, scan_fmt, fn)                  \
+do {                                                                   \
+       int i;                                                          \
+                                                                       \
+       for (i = 0; i < ARRAY_SIZE(numbers); i++) {                     \
+               if (value_representable_in_type(T, numbers[i]))         \
+                       test_one_number(T, gen_fmt, scan_fmt,           \
+                                       numbers[i], fn);                \
+                                                                       \
+               if (value_representable_in_type(T, -numbers[i]))        \
+                       test_one_number(T, gen_fmt, scan_fmt,           \
+                                       -numbers[i], fn);               \
+       }                                                               \
+} while (0)
+
+static void numbers_simple(struct kunit *test)
+{
+       simple_numbers_loop(unsigned long long, "%llu",   "llu", check_ull);
+       simple_numbers_loop(long long,          "%lld",   "lld", check_ll);
+       simple_numbers_loop(long long,          "%lld",   "lli", check_ll);
+       simple_numbers_loop(unsigned long long, "%llx",   "llx", check_ull);
+       simple_numbers_loop(long long,          "%llx",   "llx", check_ll);
+       simple_numbers_loop(long long,          "0x%llx", "lli", check_ll);
+       simple_numbers_loop(unsigned long long, "0x%llx", "llx", check_ull);
+       simple_numbers_loop(long long,          "0x%llx", "llx", check_ll);
+
+       simple_numbers_loop(unsigned long,      "%lu",    "lu", check_ulong);
+       simple_numbers_loop(long,               "%ld",    "ld", check_long);
+       simple_numbers_loop(long,               "%ld",    "li", check_long);
+       simple_numbers_loop(unsigned long,      "%lx",    "lx", check_ulong);
+       simple_numbers_loop(long,               "%lx",    "lx", check_long);
+       simple_numbers_loop(long,               "0x%lx",  "li", check_long);
+       simple_numbers_loop(unsigned long,      "0x%lx",  "lx", check_ulong);
+       simple_numbers_loop(long,               "0x%lx",  "lx", check_long);
+
+       simple_numbers_loop(unsigned int,       "%u",     "u", check_uint);
+       simple_numbers_loop(int,                "%d",     "d", check_int);
+       simple_numbers_loop(int,                "%d",     "i", check_int);
+       simple_numbers_loop(unsigned int,       "%x",     "x", check_uint);
+       simple_numbers_loop(int,                "%x",     "x", check_int);
+       simple_numbers_loop(int,                "0x%x",   "i", check_int);
+       simple_numbers_loop(unsigned int,       "0x%x",   "x", check_uint);
+       simple_numbers_loop(int,                "0x%x",   "x", check_int);
+
+       simple_numbers_loop(unsigned short,     "%hu",    "hu", check_ushort);
+       simple_numbers_loop(short,              "%hd",    "hd", check_short);
+       simple_numbers_loop(short,              "%hd",    "hi", check_short);
+       simple_numbers_loop(unsigned short,     "%hx",    "hx", check_ushort);
+       simple_numbers_loop(short,              "%hx",    "hx", check_short);
+       simple_numbers_loop(short,              "0x%hx",  "hi", check_short);
+       simple_numbers_loop(unsigned short,     "0x%hx",  "hx", check_ushort);
+       simple_numbers_loop(short,              "0x%hx",  "hx", check_short);
+
+       simple_numbers_loop(unsigned char,      "%hhu",   "hhu", check_uchar);
+       simple_numbers_loop(signed char,        "%hhd",   "hhd", check_char);
+       simple_numbers_loop(signed char,        "%hhd",   "hhi", check_char);
+       simple_numbers_loop(unsigned char,      "%hhx",   "hhx", check_uchar);
+       simple_numbers_loop(signed char,        "%hhx",   "hhx", check_char);
+       simple_numbers_loop(signed char,        "0x%hhx", "hhi", check_char);
+       simple_numbers_loop(unsigned char,      "0x%hhx", "hhx", check_uchar);
+       simple_numbers_loop(signed char,        "0x%hhx", "hhx", check_char);
+}
+
+/*
+ * This gives a better variety of number "lengths" in a small sample than
+ * the raw prandom*() functions (Not mathematically rigorous!!).
+ * Variabilty of length and value is more important than perfect randomness.
+ */
+static u32 next_test_random(u32 max_bits)
+{
+       u32 n_bits = hweight32(prandom_u32_state(&rnd_state)) % (max_bits + 1);
+
+       return prandom_u32_state(&rnd_state) & GENMASK(n_bits, 0);
+}
+
+static unsigned long long next_test_random_ull(void)
+{
+       u32 rand1 = prandom_u32_state(&rnd_state);
+       u32 n_bits = (hweight32(rand1) * 3) % 64;
+       u64 val = (u64)prandom_u32_state(&rnd_state) * rand1;
+
+       return val & GENMASK_ULL(n_bits, 0);
+}
+
+#define random_for_type(T)                             \
+       ((T)(sizeof(T) <= sizeof(u32)                   \
+               ? next_test_random(BITS_PER_TYPE(T))    \
+               : next_test_random_ull()))
+
+/*
+ * Define a pattern of negative and positive numbers to ensure we get
+ * some of both within the small number of samples in a test string.
+ */
+#define NEGATIVES_PATTERN 0x3246       /* 00110010 01000110 */
+
+#define fill_random_array(arr)                                                 \
+do {                                                                           \
+       unsigned int neg_pattern = NEGATIVES_PATTERN;                           \
+       int i;                                                                  \
+                                                                               \
+       for (i = 0; i < ARRAY_SIZE(arr); i++, neg_pattern >>= 1) {              \
+               (arr)[i] = random_for_type(typeof((arr)[0]));                   \
+               if (is_signed_type(typeof((arr)[0])) && (neg_pattern & 1))      \
+                       (arr)[i] = -(arr)[i];                                   \
+       }                                                                       \
+} while (0)
+
+/*
+ * Convenience wrapper around snprintf() to append at buf_pos in buf,
+ * updating buf_pos and returning the number of characters appended.
+ * On error buf_pos is not changed and return value is 0.
+ */
+static int __printf(4, 5)
+append_fmt(char *buf, int *buf_pos, int buf_len, const char *val_fmt, ...)
+{
+       va_list ap;
+       int field_len;
+
+       va_start(ap, val_fmt);
+       field_len = vsnprintf(buf + *buf_pos, buf_len - *buf_pos, val_fmt, ap);
+       va_end(ap);
+
+       if (field_len < 0)
+               field_len = 0;
+
+       *buf_pos += field_len;
+
+       return field_len;
+}
+
+/*
+ * Convenience function to append the field delimiter string
+ * to both the value string and format string buffers.
+ */
+static void append_delim(char *str_buf, int *str_buf_pos, int str_buf_len,
+                               char *fmt_buf, int *fmt_buf_pos, int fmt_buf_len,
+                               const char *delim_str)
+{
+       append_fmt(str_buf, str_buf_pos, str_buf_len, delim_str);
+       append_fmt(fmt_buf, fmt_buf_pos, fmt_buf_len, delim_str);
+}
+
+#define test_array_8(fn, check_data, string, fmt, arr)                         \
+do {                                                                           \
+       BUILD_BUG_ON(ARRAY_SIZE(arr) != 8);                                     \
+       _test(test, __FILE__, __LINE__, fn, check_data, string, fmt, 8,         \
+               &(arr)[0], &(arr)[1], &(arr)[2], &(arr)[3],                     \
+               &(arr)[4], &(arr)[5], &(arr)[6], &(arr)[7]);                    \
+} while (0)
+
+#define numbers_list_8(T, gen_fmt, field_sep, scan_fmt, fn)                    \
+do {                                                                           \
+       int i, pos = 0, fmt_pos = 0;                                            \
+       T expect[8], result[8];                                                 \
+                                                                               \
+       fill_random_array(expect);                                              \
+                                                                               \
+       for (i = 0; i < ARRAY_SIZE(expect); i++) {                              \
+               if (i != 0)                                                     \
+                       append_delim(test_buffer, &pos, BUF_SIZE,               \
+                                    fmt_buffer, &fmt_pos, BUF_SIZE,            \
+                                    field_sep);                                \
+                                                                               \
+               append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt, expect[i]);    \
+               append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE, "%%%s", scan_fmt);   \
+       }                                                                       \
+                                                                               \
+       test_array_8(fn, expect, test_buffer, fmt_buffer, result);              \
+} while (0)
+
+#define numbers_list_fix_width(T, gen_fmt, field_sep, width, scan_fmt, fn)     \
+do {                                                                           \
+       char full_fmt[16];                                                      \
+                                                                               \
+       snprintf(full_fmt, sizeof(full_fmt), "%u%s", width, scan_fmt);          \
+       numbers_list_8(T, gen_fmt, field_sep, full_fmt, fn);                    \
+} while (0)
+
+#define numbers_list_val_width(T, gen_fmt, field_sep, scan_fmt, fn)            \
+do {                                                                           \
+       int i, val_len, pos = 0, fmt_pos = 0;                                   \
+       T expect[8], result[8];                                                 \
+                                                                               \
+       fill_random_array(expect);                                              \
+                                                                               \
+       for (i = 0; i < ARRAY_SIZE(expect); i++) {                              \
+               if (i != 0)                                                     \
+                       append_delim(test_buffer, &pos, BUF_SIZE,               \
+                                    fmt_buffer, &fmt_pos, BUF_SIZE, field_sep);\
+                                                                               \
+               val_len = append_fmt(test_buffer, &pos, BUF_SIZE, gen_fmt,      \
+                                    expect[i]);                                \
+               append_fmt(fmt_buffer, &fmt_pos, BUF_SIZE,                      \
+                          "%%%u%s", val_len, scan_fmt);                        \
+       }                                                                       \
+                                                                               \
+       test_array_8(fn, expect, test_buffer, fmt_buffer, result);              \
+} while (0)
+
+static void numbers_list_ll(struct kunit *test, const char *delim)
+{
+       numbers_list_8(unsigned long long, "%llu",   delim, "llu", check_ull);
+       numbers_list_8(long long,          "%lld",   delim, "lld", check_ll);
+       numbers_list_8(long long,          "%lld",   delim, "lli", check_ll);
+       numbers_list_8(unsigned long long, "%llx",   delim, "llx", check_ull);
+       numbers_list_8(unsigned long long, "0x%llx", delim, "llx", check_ull);
+       numbers_list_8(long long,          "0x%llx", delim, "lli", check_ll);
+}
+
+static void numbers_list_l(struct kunit *test, const char *delim)
+{
+       numbers_list_8(unsigned long,      "%lu",    delim, "lu", check_ulong);
+       numbers_list_8(long,               "%ld",    delim, "ld", check_long);
+       numbers_list_8(long,               "%ld",    delim, "li", check_long);
+       numbers_list_8(unsigned long,      "%lx",    delim, "lx", check_ulong);
+       numbers_list_8(unsigned long,      "0x%lx",  delim, "lx", check_ulong);
+       numbers_list_8(long,               "0x%lx",  delim, "li", check_long);
+}
+
+static void numbers_list_d(struct kunit *test, const char *delim)
+{
+       numbers_list_8(unsigned int,       "%u",     delim, "u", check_uint);
+       numbers_list_8(int,                "%d",     delim, "d", check_int);
+       numbers_list_8(int,                "%d",     delim, "i", check_int);
+       numbers_list_8(unsigned int,       "%x",     delim, "x", check_uint);
+       numbers_list_8(unsigned int,       "0x%x",   delim, "x", check_uint);
+       numbers_list_8(int,                "0x%x",   delim, "i", check_int);
+}
+
+static void numbers_list_h(struct kunit *test, const char *delim)
+{
+       numbers_list_8(unsigned short,     "%hu",    delim, "hu", check_ushort);
+       numbers_list_8(short,              "%hd",    delim, "hd", check_short);
+       numbers_list_8(short,              "%hd",    delim, "hi", check_short);
+       numbers_list_8(unsigned short,     "%hx",    delim, "hx", check_ushort);
+       numbers_list_8(unsigned short,     "0x%hx",  delim, "hx", check_ushort);
+       numbers_list_8(short,              "0x%hx",  delim, "hi", check_short);
+}
+
+static void numbers_list_hh(struct kunit *test, const char *delim)
+{
+       numbers_list_8(unsigned char,      "%hhu",   delim, "hhu", check_uchar);
+       numbers_list_8(signed char,        "%hhd",   delim, "hhd", check_char);
+       numbers_list_8(signed char,        "%hhd",   delim, "hhi", check_char);
+       numbers_list_8(unsigned char,      "%hhx",   delim, "hhx", check_uchar);
+       numbers_list_8(unsigned char,      "0x%hhx", delim, "hhx", check_uchar);
+       numbers_list_8(signed char,        "0x%hhx", delim, "hhi", check_char);
+}
+
+static void numbers_list(struct kunit *test, const char *delim)
+{
+       numbers_list_ll(test, delim);
+       numbers_list_l(test, delim);
+       numbers_list_d(test, delim);
+       numbers_list_h(test, delim);
+       numbers_list_hh(test, delim);
+}
+
+static void numbers_list_field_width_ll(struct kunit *test, const char *delim)
+{
+       numbers_list_fix_width(unsigned long long, "%llu",   delim, 20, "llu", check_ull);
+       numbers_list_fix_width(long long,          "%lld",   delim, 20, "lld", check_ll);
+       numbers_list_fix_width(long long,          "%lld",   delim, 20, "lli", check_ll);
+       numbers_list_fix_width(unsigned long long, "%llx",   delim, 16, "llx", check_ull);
+       numbers_list_fix_width(unsigned long long, "0x%llx", delim, 18, "llx", check_ull);
+       numbers_list_fix_width(long long,          "0x%llx", delim, 18, "lli", check_ll);
+}
+
+static void numbers_list_field_width_l(struct kunit *test, const char *delim)
+{
+#if BITS_PER_LONG == 64
+       numbers_list_fix_width(unsigned long,   "%lu",       delim, 20, "lu", check_ulong);
+       numbers_list_fix_width(long,            "%ld",       delim, 20, "ld", check_long);
+       numbers_list_fix_width(long,            "%ld",       delim, 20, "li", check_long);
+       numbers_list_fix_width(unsigned long,   "%lx",       delim, 16, "lx", check_ulong);
+       numbers_list_fix_width(unsigned long,   "0x%lx",     delim, 18, "lx", check_ulong);
+       numbers_list_fix_width(long,            "0x%lx",     delim, 18, "li", check_long);
+#else
+       numbers_list_fix_width(unsigned long,   "%lu",       delim, 10, "lu", check_ulong);
+       numbers_list_fix_width(long,            "%ld",       delim, 11, "ld", check_long);
+       numbers_list_fix_width(long,            "%ld",       delim, 11, "li", check_long);
+       numbers_list_fix_width(unsigned long,   "%lx",       delim, 8,  "lx", check_ulong);
+       numbers_list_fix_width(unsigned long,   "0x%lx",     delim, 10, "lx", check_ulong);
+       numbers_list_fix_width(long,            "0x%lx",     delim, 10, "li", check_long);
+#endif
+}
+
+static void numbers_list_field_width_d(struct kunit *test, const char *delim)
+{
+       numbers_list_fix_width(unsigned int,    "%u",        delim, 10, "u", check_uint);
+       numbers_list_fix_width(int,             "%d",        delim, 11, "d", check_int);
+       numbers_list_fix_width(int,             "%d",        delim, 11, "i", check_int);
+       numbers_list_fix_width(unsigned int,    "%x",        delim, 8,  "x", check_uint);
+       numbers_list_fix_width(unsigned int,    "0x%x",      delim, 10, "x", check_uint);
+       numbers_list_fix_width(int,             "0x%x",      delim, 10, "i", check_int);
+}
+
+static void numbers_list_field_width_h(struct kunit *test, const char *delim)
+{
+       numbers_list_fix_width(unsigned short,  "%hu",       delim, 5, "hu", check_ushort);
+       numbers_list_fix_width(short,           "%hd",       delim, 6, "hd", check_short);
+       numbers_list_fix_width(short,           "%hd",       delim, 6, "hi", check_short);
+       numbers_list_fix_width(unsigned short,  "%hx",       delim, 4, "hx", check_ushort);
+       numbers_list_fix_width(unsigned short,  "0x%hx",     delim, 6, "hx", check_ushort);
+       numbers_list_fix_width(short,           "0x%hx",     delim, 6, "hi", check_short);
+}
+
+static void numbers_list_field_width_hh(struct kunit *test, const char *delim)
+{
+       numbers_list_fix_width(unsigned char,   "%hhu",      delim, 3, "hhu", check_uchar);
+       numbers_list_fix_width(signed char,     "%hhd",      delim, 4, "hhd", check_char);
+       numbers_list_fix_width(signed char,     "%hhd",      delim, 4, "hhi", check_char);
+       numbers_list_fix_width(unsigned char,   "%hhx",      delim, 2, "hhx", check_uchar);
+       numbers_list_fix_width(unsigned char,   "0x%hhx",    delim, 4, "hhx", check_uchar);
+       numbers_list_fix_width(signed char,     "0x%hhx",    delim, 4, "hhi", check_char);
+}
+
+/*
+ * List of numbers separated by delim. Each field width specifier is the
+ * maximum possible digits for the given type and base.
+ */
+static void numbers_list_field_width_typemax(struct kunit *test, const char *delim)
+{
+       numbers_list_field_width_ll(test, delim);
+       numbers_list_field_width_l(test, delim);
+       numbers_list_field_width_d(test, delim);
+       numbers_list_field_width_h(test, delim);
+       numbers_list_field_width_hh(test, delim);
+}
+
+static void numbers_list_field_width_val_ll(struct kunit *test, const char *delim)
+{
+       numbers_list_val_width(unsigned long long, "%llu",   delim, "llu", check_ull);
+       numbers_list_val_width(long long,          "%lld",   delim, "lld", check_ll);
+       numbers_list_val_width(long long,          "%lld",   delim, "lli", check_ll);
+       numbers_list_val_width(unsigned long long, "%llx",   delim, "llx", check_ull);
+       numbers_list_val_width(unsigned long long, "0x%llx", delim, "llx", check_ull);
+       numbers_list_val_width(long long,          "0x%llx", delim, "lli", check_ll);
+}
+
+static void numbers_list_field_width_val_l(struct kunit *test, const char *delim)
+{
+       numbers_list_val_width(unsigned long,   "%lu",       delim, "lu", check_ulong);
+       numbers_list_val_width(long,            "%ld",       delim, "ld", check_long);
+       numbers_list_val_width(long,            "%ld",       delim, "li", check_long);
+       numbers_list_val_width(unsigned long,   "%lx",       delim, "lx", check_ulong);
+       numbers_list_val_width(unsigned long,   "0x%lx",     delim, "lx", check_ulong);
+       numbers_list_val_width(long,            "0x%lx",     delim, "li", check_long);
+}
+
+static void numbers_list_field_width_val_d(struct kunit *test, const char *delim)
+{
+       numbers_list_val_width(unsigned int,    "%u",        delim, "u", check_uint);
+       numbers_list_val_width(int,             "%d",        delim, "d", check_int);
+       numbers_list_val_width(int,             "%d",        delim, "i", check_int);
+       numbers_list_val_width(unsigned int,    "%x",        delim, "x", check_uint);
+       numbers_list_val_width(unsigned int,    "0x%x",      delim, "x", check_uint);
+       numbers_list_val_width(int,             "0x%x",      delim, "i", check_int);
+}
+
+static void numbers_list_field_width_val_h(struct kunit *test, const char *delim)
+{
+       numbers_list_val_width(unsigned short,  "%hu",       delim, "hu", check_ushort);
+       numbers_list_val_width(short,           "%hd",       delim, "hd", check_short);
+       numbers_list_val_width(short,           "%hd",       delim, "hi", check_short);
+       numbers_list_val_width(unsigned short,  "%hx",       delim, "hx", check_ushort);
+       numbers_list_val_width(unsigned short,  "0x%hx",     delim, "hx", check_ushort);
+       numbers_list_val_width(short,           "0x%hx",     delim, "hi", check_short);
+}
+
+static void numbers_list_field_width_val_hh(struct kunit *test, const char *delim)
+{
+       numbers_list_val_width(unsigned char,   "%hhu",      delim, "hhu", check_uchar);
+       numbers_list_val_width(signed char,     "%hhd",      delim, "hhd", check_char);
+       numbers_list_val_width(signed char,     "%hhd",      delim, "hhi", check_char);
+       numbers_list_val_width(unsigned char,   "%hhx",      delim, "hhx", check_uchar);
+       numbers_list_val_width(unsigned char,   "0x%hhx",    delim, "hhx", check_uchar);
+       numbers_list_val_width(signed char,     "0x%hhx",    delim, "hhi", check_char);
+}
+
+/*
+ * List of numbers separated by delim. Each field width specifier is the
+ * exact length of the corresponding value digits in the string being scanned.
+ */
+static void numbers_list_field_width_val_width(struct kunit *test, const char *delim)
+{
+       numbers_list_field_width_val_ll(test, delim);
+       numbers_list_field_width_val_l(test, delim);
+       numbers_list_field_width_val_d(test, delim);
+       numbers_list_field_width_val_h(test, delim);
+       numbers_list_field_width_val_hh(test, delim);
+}
+
+/*
+ * Slice a continuous string of digits without field delimiters, containing
+ * numbers of varying length, using the field width to extract each group
+ * of digits. For example the hex values c0,3,bf01,303 would have a
+ * string representation of "c03bf01303" and extracted with "%2x%1x%4x%3x".
+ */
+static void numbers_slice(struct kunit *test)
+{
+       numbers_list_field_width_val_width(test, "");
+}
+
+#define test_number_prefix(T, str, scan_fmt, expect0, expect1, n_args, fn)     \
+do {                                                                           \
+       const T expect[2] = { expect0, expect1 };                               \
+       T result[2] = { (T)~expect[0], (T)~expect[1] };                         \
+                                                                               \
+       _test(test, __FILE__, __LINE__, fn, &expect, str, scan_fmt, n_args, &result[0], &result[1]);\
+} while (0)
+
+/*
+ * Number prefix is >= field width.
+ * Expected behaviour is derived from testing userland sscanf.
+ */
+static void numbers_prefix_overflow(struct kunit *test)
+{
+       /*
+        * Negative decimal with a field of width 1, should quit scanning
+        * and return 0.
+        */
+       test_number_prefix(long long,   "-1 1", "%1lld %lld",   0, 0, 0, check_ll);
+       test_number_prefix(long,        "-1 1", "%1ld %ld",     0, 0, 0, check_long);
+       test_number_prefix(int,         "-1 1", "%1d %d",       0, 0, 0, check_int);
+       test_number_prefix(short,       "-1 1", "%1hd %hd",     0, 0, 0, check_short);
+       test_number_prefix(signed char, "-1 1", "%1hhd %hhd",   0, 0, 0, check_char);
+
+       test_number_prefix(long long,   "-1 1", "%1lli %lli",   0, 0, 0, check_ll);
+       test_number_prefix(long,        "-1 1", "%1li %li",     0, 0, 0, check_long);
+       test_number_prefix(int,         "-1 1", "%1i %i",       0, 0, 0, check_int);
+       test_number_prefix(short,       "-1 1", "%1hi %hi",     0, 0, 0, check_short);
+       test_number_prefix(signed char, "-1 1", "%1hhi %hhi",   0, 0, 0, check_char);
+
+       /*
+        * 0x prefix in a field of width 1: 0 is a valid digit so should
+        * convert. Next field scan starts at the 'x' which isn't a digit so
+        * scan quits with one field converted.
+        */
+       test_number_prefix(unsigned long long,  "0xA7", "%1llx%llx", 0, 0, 1, check_ull);
+       test_number_prefix(unsigned long,       "0xA7", "%1lx%lx",   0, 0, 1, check_ulong);
+       test_number_prefix(unsigned int,        "0xA7", "%1x%x",     0, 0, 1, check_uint);
+       test_number_prefix(unsigned short,      "0xA7", "%1hx%hx",   0, 0, 1, check_ushort);
+       test_number_prefix(unsigned char,       "0xA7", "%1hhx%hhx", 0, 0, 1, check_uchar);
+       test_number_prefix(long long,           "0xA7", "%1lli%llx", 0, 0, 1, check_ll);
+       test_number_prefix(long,                "0xA7", "%1li%lx",   0, 0, 1, check_long);
+       test_number_prefix(int,                 "0xA7", "%1i%x",     0, 0, 1, check_int);
+       test_number_prefix(short,               "0xA7", "%1hi%hx",   0, 0, 1, check_short);
+       test_number_prefix(char,                "0xA7", "%1hhi%hhx", 0, 0, 1, check_char);
+
+       /*
+        * 0x prefix in a field of width 2 using %x conversion: first field
+        * converts to 0. Next field scan starts at the character after "0x".
+        * Both fields will convert.
+        */
+       test_number_prefix(unsigned long long,  "0xA7", "%2llx%llx", 0, 0xa7, 2, check_ull);
+       test_number_prefix(unsigned long,       "0xA7", "%2lx%lx",   0, 0xa7, 2, check_ulong);
+       test_number_prefix(unsigned int,        "0xA7", "%2x%x",     0, 0xa7, 2, check_uint);
+       test_number_prefix(unsigned short,      "0xA7", "%2hx%hx",   0, 0xa7, 2, check_ushort);
+       test_number_prefix(unsigned char,       "0xA7", "%2hhx%hhx", 0, 0xa7, 2, check_uchar);
+
+       /*
+        * 0x prefix in a field of width 2 using %i conversion: first field
+        * converts to 0. Next field scan starts at the character after "0x",
+        * which will convert if can be interpreted as decimal but will fail
+        * if it contains any hex digits (since no 0x prefix).
+        */
+       test_number_prefix(long long,   "0x67", "%2lli%lli", 0, 67, 2, check_ll);
+       test_number_prefix(long,        "0x67", "%2li%li",   0, 67, 2, check_long);
+       test_number_prefix(int,         "0x67", "%2i%i",     0, 67, 2, check_int);
+       test_number_prefix(short,       "0x67", "%2hi%hi",   0, 67, 2, check_short);
+       test_number_prefix(char,        "0x67", "%2hhi%hhi", 0, 67, 2, check_char);
+
+       test_number_prefix(long long,   "0xA7", "%2lli%lli", 0, 0,  1, check_ll);
+       test_number_prefix(long,        "0xA7", "%2li%li",   0, 0,  1, check_long);
+       test_number_prefix(int,         "0xA7", "%2i%i",     0, 0,  1, check_int);
+       test_number_prefix(short,       "0xA7", "%2hi%hi",   0, 0,  1, check_short);
+       test_number_prefix(char,        "0xA7", "%2hhi%hhi", 0, 0,  1, check_char);
+}
+
+#define _test_simple_strtoxx(T, fn, gen_fmt, expect, base)                     \
+do {                                                                           \
+       T got;                                                                  \
+       char *endp;                                                             \
+       int len;                                                                \
+                                                                               \
+       len = snprintf(test_buffer, BUF_SIZE, gen_fmt, expect);                 \
+       got = (fn)(test_buffer, &endp, base);                                   \
+       if (got != (expect)) {                                                  \
+               KUNIT_FAIL(test, #fn "(\"%s\", %d): got " gen_fmt " expected " gen_fmt, \
+                          test_buffer, base, got, expect);                     \
+       } else if (endp != test_buffer + len) {                                 \
+               KUNIT_FAIL(test, #fn "(\"%s\", %d) startp=0x%px got endp=0x%px expected 0x%px", \
+                          test_buffer, base, test_buffer,                      \
+                          test_buffer + len, endp);                            \
+       }                                                                       \
+} while (0)
+
+#define test_simple_strtoxx(T, fn, gen_fmt, base)                              \
+do {                                                                           \
+       int i;                                                                  \
+                                                                               \
+       for (i = 0; i < ARRAY_SIZE(numbers); i++) {                             \
+               _test_simple_strtoxx(T, fn, gen_fmt, (T)numbers[i], base);      \
+                                                                               \
+               if (is_signed_type(T))                                          \
+                       _test_simple_strtoxx(T, fn, gen_fmt,                    \
+                                             -(T)numbers[i], base);            \
+       }                                                                       \
+} while (0)
+
+static void test_simple_strtoull(struct kunit *test)
+{
+       test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu",   10);
+       test_simple_strtoxx(unsigned long long, simple_strtoull, "%llu",   0);
+       test_simple_strtoxx(unsigned long long, simple_strtoull, "%llx",   16);
+       test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 16);
+       test_simple_strtoxx(unsigned long long, simple_strtoull, "0x%llx", 0);
+}
+
+static void test_simple_strtoll(struct kunit *test)
+{
+       test_simple_strtoxx(long long, simple_strtoll, "%lld",   10);
+       test_simple_strtoxx(long long, simple_strtoll, "%lld",   0);
+       test_simple_strtoxx(long long, simple_strtoll, "%llx",   16);
+       test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 16);
+       test_simple_strtoxx(long long, simple_strtoll, "0x%llx", 0);
+}
+
+static void test_simple_strtoul(struct kunit *test)
+{
+       test_simple_strtoxx(unsigned long, simple_strtoul, "%lu",   10);
+       test_simple_strtoxx(unsigned long, simple_strtoul, "%lu",   0);
+       test_simple_strtoxx(unsigned long, simple_strtoul, "%lx",   16);
+       test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 16);
+       test_simple_strtoxx(unsigned long, simple_strtoul, "0x%lx", 0);
+}
+
+static void test_simple_strtol(struct kunit *test)
+{
+       test_simple_strtoxx(long, simple_strtol, "%ld",   10);
+       test_simple_strtoxx(long, simple_strtol, "%ld",   0);
+       test_simple_strtoxx(long, simple_strtol, "%lx",   16);
+       test_simple_strtoxx(long, simple_strtol, "0x%lx", 16);
+       test_simple_strtoxx(long, simple_strtol, "0x%lx", 0);
+}
+
+/* Selection of common delimiters/separators between numbers in a string. */
+static const char * const number_delimiters[] = {
+       " ", ":", ",", "-", "/",
+};
+
+static void test_numbers(struct kunit *test)
+{
+       int i;
+
+       /* String containing only one number. */
+       numbers_simple(test);
+
+       /* String with multiple numbers separated by delimiter. */
+       for (i = 0; i < ARRAY_SIZE(number_delimiters); i++) {
+               numbers_list(test, number_delimiters[i]);
+
+               /* Field width may be longer than actual field digits. */
+               numbers_list_field_width_typemax(test, number_delimiters[i]);
+
+               /* Each field width exactly length of actual field digits. */
+               numbers_list_field_width_val_width(test, number_delimiters[i]);
+       }
+
+       /* Slice continuous sequence of digits using field widths. */
+       numbers_slice(test);
+
+       numbers_prefix_overflow(test);
+}
+
+static void scanf_test(struct kunit *test)
+{
+       test_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
+       if (!test_buffer)
+               return;
+
+       fmt_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
+       if (!fmt_buffer) {
+               kfree(test_buffer);
+               return;
+       }
+
+       prandom_seed_state(&rnd_state, 3141592653589793238ULL);
+
+       test_numbers(test);
+
+       test_simple_strtoull(test);
+       test_simple_strtoll(test);
+       test_simple_strtoul(test);
+       test_simple_strtol(test);
+
+       kfree(fmt_buffer);
+       kfree(test_buffer);
+}
+
+static struct kunit_case scanf_test_cases[] = {
+       KUNIT_CASE(scanf_test),
+       {}
+};
+
+static struct kunit_suite scanf_test_suite = {
+       .name = "scanf",
+       .test_cases = scanf_test_cases,
+};
+
+kunit_test_suite(scanf_test_suite);
+
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_DESCRIPTION("Test cases for sscanf facility");
+MODULE_LICENSE("GPL v2");
index befc4ab2c6712b4d1c925aff7b96575ca59fae44..f876bf4744e1a89e89c2d525047f1e486213c9a9 100644 (file)
@@ -4,5 +4,5 @@
 # No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
 all:
 
-TEST_PROGS := bitmap.sh scanf.sh
+TEST_PROGS := bitmap.sh
 include ../lib.mk
index f4b4b88222418de9c201b2332121831ad0670b26..81a1f64a22e860a69ec8e27b9efa2a3767b77940 100644 (file)
@@ -1,3 +1,2 @@
-CONFIG_TEST_SCANF=m
 CONFIG_TEST_BITMAP=m
 CONFIG_TEST_BITOPS=m
diff --git a/tools/testing/selftests/lib/scanf.sh b/tools/testing/selftests/lib/scanf.sh
deleted file mode 100755 (executable)
index b59b8ba..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
-# Tests the scanf infrastructure using test_scanf kernel module.
-$(dirname $0)/../kselftest/module.sh "scanf" test_scanf