Module is under config option CONFIG_TEST_SCANF. This is just basic cases,
complete coverage isn't that easy for such complicated function.

Signed-off-by: Konstantin Khlebnikov <khlebni...@yandex-team.ru>
---
 lib/Kconfig.debug |    3 +
 lib/Makefile      |    1 
 lib/test_scanf.c  |  252 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 256 insertions(+)
 create mode 100644 lib/test_scanf.c

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 91ed81250fb3..b50ac3c81961 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1806,6 +1806,9 @@ config TEST_STRING_HELPERS
 config TEST_KSTRTOX
        tristate "Test kstrto*() family of functions at runtime"
 
+config TEST_SCANF
+       tristate "Test scanf() family of functions at runtime"
+
 config TEST_PRINTF
        tristate "Test printf() family of functions at runtime"
 
diff --git a/lib/Makefile b/lib/Makefile
index 647517940b29..b1d538d7180e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
 obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
 obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
 obj-$(CONFIG_TEST_PRINTF) += test_printf.o
+obj-$(CONFIG_TEST_SCANF) += test_scanf.o
 obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
 obj-$(CONFIG_TEST_BITFIELD) += test_bitfield.o
 obj-$(CONFIG_TEST_UUID) += test_uuid.o
diff --git a/lib/test_scanf.c b/lib/test_scanf.c
new file mode 100644
index 000000000000..1a4f8f145a46
--- /dev/null
+++ b/lib/test_scanf.c
@@ -0,0 +1,252 @@
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#ifndef KERN_EOT
+#define KERN_EOT ""
+#define SCANF_MORE 0
+#endif
+
+#define TEST_ONE(str, fmt, type, type_fmt, ret, val)   \
+       do {                                            \
+               type _neg = (type)~(type)(val);         \
+               type _val = _neg;                       \
+               int _ret = sscanf(str, fmt, &_val);     \
+               WARN(_ret != (ret), "scanf(\"%s\", \"%s\", ...) returned %d, 
expected %d", str, fmt, _ret, ret);        \
+               WARN(ret && _ret && _val != (type)(val), "scanf(\"%s\", \"%s\", 
...) matched value " type_fmt ", expected " type_fmt, str, fmt, _val, 
(type)(val));     \
+               WARN(!_ret && _val != _neg, "scanf(\"%s\", \"%s\", ...) 
unmached value overwritten with " type_fmt, str, fmt, _val);    \
+       } while (0);
+
+#define TEST_STR(str, fmt, len, ret, val)              \
+       do {                                            \
+               char _val[len + 1] = {0,};              \
+               int _ret = sscanf(str, fmt, _val);      \
+               WARN(_ret != (ret), "scanf(\"%s\", \"%s\", ...) returned %d, 
expected %d", str, fmt, _ret, ret);        \
+               WARN(ret && _ret && strncmp(_val, val, len), "scanf(\"%s\", 
\"%s\", ...) matched value \"%.*s\", expected \"%.*s\"", str, fmt, len + 1, 
_val, len + 1, val);    \
+               WARN(!_ret && _val[0], "scanf(\"%s\", \"%s\", ...) unmached 
value overwritten with \"%.*s\"", str, fmt, len + 1, _val); \
+       } while (0);
+
+#define TEST_VA(str, fmt, ret, ...)                    \
+       do {                                            \
+               int _ret = sscanf(str, fmt, ##__VA_ARGS__);     \
+               WARN(_ret != (ret), "scanf(\"%s\", \"%s\", ...) returned %d, 
expected %d", str, fmt, _ret, ret);        \
+       } while (0);
+
+#define TEST_SC(str, fmt, ret, val)    TEST_ONE(str, fmt, signed char, "%d", 
ret, val)
+#define TEST_UC(str, fmt, ret, val)    TEST_ONE(str, fmt, unsigned char, "%u", 
ret, val)
+#define TEST_SS(str, fmt, ret, val)    TEST_ONE(str, fmt, short, "%d", ret, 
val)
+#define TEST_US(str, fmt, ret, val)    TEST_ONE(str, fmt, unsigned short, 
"%u", ret, val)
+#define TEST_SI(str, fmt, ret, val)    TEST_ONE(str, fmt, int, "%d", ret, val)
+#define TEST_UI(str, fmt, ret, val)    TEST_ONE(str, fmt, unsigned int, "%u", 
ret, val)
+#define TEST_SL(str, fmt, ret, val)    TEST_ONE(str, fmt, long, "%ld", ret, 
val)
+#define TEST_UL(str, fmt, ret, val)    TEST_ONE(str, fmt, unsigned long, 
"%lu", ret, val)
+#define TEST_SQ(str, fmt, ret, val)    TEST_ONE(str, fmt, long long, "%llu", 
ret, val)
+#define TEST_UQ(str, fmt, ret, val)    TEST_ONE(str, fmt, unsigned long long, 
"%llu", ret, val)
+
+static void __init test_base(void)
+{
+       TEST_UI("10", "%u", 1, 10);
+       TEST_SI("10", "%d", 1, 10);
+       TEST_UI("10", "%o", 1, 8);
+       TEST_UI("10", "%x", 1, 16);
+       TEST_SI("10", "%i", 1, 10);
+       TEST_SI("010", "%i", 1, 8);
+       TEST_SI("0x10", "%i", 1, 16);
+
+       TEST_UI("9a", "%u", 1, 9);
+       TEST_SI("9a", "%d", 1, 9);
+       TEST_UI("78", "%o", 1, 7);
+       TEST_UI("fg", "%x", 1, 15);
+       TEST_SI("9a", "%i", 1, 9);
+       TEST_SI("078", "%i", 1, 7);
+       TEST_SI("0xfg", "%i", 1, 15);
+
+       TEST_UI("a", "%u", 0, 0);
+       TEST_SI("a", "%d", 0, 0);
+       TEST_UI("8", "%o", 0, 0);
+       TEST_UI("g", "%x", 0, 0);
+       TEST_SI("a", "%i", 0, 0);
+}
+
+static void __init test_sign(void)
+{
+       TEST_UI("+0", "%u", 0, 0);
+       TEST_UI("-0", "%u", 0, 0);
+       TEST_UI("+1", "%u", 0, 0);
+       TEST_UI("-1", "%u", 0, 0);
+       TEST_UI("++1", "%u", 0, 0);
+       TEST_UI("--1", "%u", 0, 0);
+       TEST_UI("+-1", "%u", 0, 0);
+       TEST_UI("-+1", "%u", 0, 0);
+       TEST_UI("+", "%u", 0, 0);
+       TEST_UI("-", "%u", 0, 0);
+
+       TEST_SI("+0", "%d", 1, 0);
+       TEST_SI("-0", "%d", 1, 0);
+       TEST_SI("+1", "%d", 1, 1);
+       TEST_SI("-1", "%d", 1, -1);
+       TEST_SI("++1", "%d", 0, 0);
+       TEST_SI("--1", "%d", 0, 0);
+       TEST_SI("+-1", "%d", 0, 0);
+       TEST_SI("-+1", "%d", 0, 0);
+       TEST_SI("+", "%d", 0, 0);
+       TEST_SI("-", "%d", 0, 0);
+}
+
+static void __init test_range(void)
+{
+       TEST_UC("255", "%hhu", 1, 255);
+       TEST_US("65535", "%hu", 1, 65535);
+       TEST_UI("4294967295", "%u", 1, 4294967295);
+       TEST_UL("4294967295", "%lu", 1, 4294967295);
+#if BITS_PER_LONG == 32
+       TEST_UL("18446744073709551615", "%lu", 0, 0);
+#else
+       TEST_UL("18446744073709551615", "%lu", 1, 18446744073709551615ull);
+#endif
+       TEST_UQ("18446744073709551615", "%llu", 1, 18446744073709551615ull);
+
+
+       TEST_SC("-128", "%hhd", 1, -128);
+       TEST_SC("127", "%hhd", 1, 127);
+       TEST_SS("-32768", "%hd", 1, -32768);
+       TEST_SS("32767", "%hd", 1, 32767);
+       TEST_SI("-2147483648", "%d", 1, -2147483648);
+       TEST_SI("2147483647", "%d", 1, 2147483647);
+       TEST_SL("-2147483648", "%ld", 1, -2147483648);
+       TEST_SL("2147483647", "%ld", 1, 2147483647);
+#if BITS_PER_LONG == 32
+       TEST_SL("-9223372036854775808", "%ld", 0, 0);
+       TEST_SL("9223372036854775807", "%ld", 0, 0);
+#else
+       TEST_SL("-9223372036854775808", "%ld", 1, -9223372036854775808ull);
+       TEST_SL("9223372036854775807", "%ld", 1, 9223372036854775807ll);
+#endif
+       TEST_SQ("-9223372036854775808", "%lld", 1, -9223372036854775808ull);
+       TEST_SQ("9223372036854775807", "%lld", 1, 9223372036854775807ll);
+
+
+       TEST_UC("256", "%hhu", 0, 0);
+       TEST_US("65536", "%hu", 0, 0);
+       TEST_UI("4294967296", "%u", 0, 0);
+#if BITS_PER_LONG == 32
+       TEST_UL("4294967296", "%lu", 0, 0);
+#else
+       TEST_UL("4294967296", "%lu", 1, 4294967296);
+#endif
+       TEST_UL("18446744073709551616", "%lu", 0, 0);
+       TEST_UQ("18446744073709551616", "%llu", 0, 0);
+
+       TEST_SC("-129", "%hhd", 0, 0);
+       TEST_SC("128", "%hhd", 0, 0);
+       TEST_SS("-32769", "%hd", 0, 0);
+       TEST_SS("32768", "%hd", 0, 0);
+       TEST_SI("-2147483649", "%d", 0, 0);
+       TEST_SI("2147483648", "%d", 0, 0);
+#if BITS_PER_LONG == 32
+       TEST_SL("-2147483649", "%ld", 0, 0);
+       TEST_SL("2147483648", "%ld", 0, 0);
+#else
+       TEST_SL("-2147483649", "%ld", 1, -2147483649);
+       TEST_SL("2147483648", "%ld", 1, 2147483648);
+#endif
+       TEST_SL("-9223372036854775809", "%ld", 0, 0);
+       TEST_SL("9223372036854775808", "%ld", 0, 0);
+       TEST_SQ("-9223372036854775809", "%lld", 0, 0);
+       TEST_SQ("9223372036854775808", "%lld", 0, 0);
+}
+
+static void __init test_eot(void)
+{
+       TEST_UI("123", "%u" KERN_EOT, 1, 123);
+       TEST_UI("123 ", "%u" KERN_EOT, 1 | SCANF_MORE, 123);
+       TEST_UI("123 ", "%u " KERN_EOT, 1, 123);
+       TEST_UI("123 \t\n", "%u " KERN_EOT, 1, 123);
+       TEST_UI("123z ", "%u " KERN_EOT, 1 | SCANF_MORE, 123);
+
+       TEST_SI("1M", "%d", 1, 1);
+       TEST_SI("1M", "%d" KERN_EOT, 1 | SCANF_MORE, 1);
+
+       TEST_SI("1\n", "%d" KERN_EOT, 1 | SCANF_MORE, 1);
+       TEST_SI("1\n", "%d " KERN_EOT, 1, 1);
+}
+
+static void __init test_set(void)
+{
+       // TODO it would be nice to make field width mandatory here
+       TEST_STR("abc", "%s", 3, 1, "abc");
+
+       TEST_STR("abc", "%3s", 3, 1, "abc");
+       TEST_STR("abc", "%2s", 3, 1, "ab");
+       TEST_STR("abc", "%1s", 3, 1, "a");
+
+       TEST_STR(" a\n", "%3s", 3, 1, "a");
+
+       TEST_STR("abc", "%3c", 3, 1, "abc");
+       TEST_STR(" a\n", "%3c", 3, 1, " a\n");
+
+       TEST_STR("abc", "%[a-z]", 3, 0, "");    // no width
+
+       TEST_STR("abc", "%3[abc]", 3, 1, "abc");
+       TEST_STR("abc", "%3[^abc]", 3, 0, "");
+
+       TEST_STR("abc", "%3[xyz]", 3, 0, "");
+       TEST_STR("abc", "%3[^xyz]", 3, 1, "abc");
+
+       TEST_STR(" a", "%3[a]", 3, 0, "");
+       TEST_STR(" a", "%3[ a]", 3, 1, " a");
+
+       TEST_STR("abcd", "%5[a-c]", 5, 1, "abc");
+       TEST_STR("abc", "%3[^a-b]", 3, 0, "");
+
+       TEST_STR("bd", "%3[a-c]", 3, 1, "b");
+       TEST_STR("db", "%3[^a-c]", 3, 1, "d");
+
+       TEST_STR("q1w2e3", "%8[a-z0-9]", 8, 1, "q1w2e3");
+       TEST_STR("q1w2e3", "%8[^a-z0-9]", 8, 0, "");
+
+       TEST_STR("a-", "%3[a-b]", 3, 1, "a");
+       TEST_STR("a-", "%3[a-]", 3, 1, "a-");
+       TEST_STR("a-", "%3[^a-]", 3, 0, "");
+
+       TEST_STR("-", "%3[-]", 3, 1, "-");
+       TEST_STR("-", "%3[^-]", 3, 0, "");
+
+       TEST_STR("]ab", "%3[]]", 3, 1, "]");
+       TEST_STR("a]b", "%3[^]]", 3, 1, "a");
+
+       TEST_STR("]-abc", "%3[]-]", 3, 1, "]-");
+       TEST_STR("abc]-", "%3[^]-]", 3, 1, "abc");
+}
+
+static void __init test_va(void)
+{
+       int x, y, z;
+       char a, b, c;
+
+       TEST_VA(" 1 ", "%d%d", 1, &x, &y);
+       TEST_VA(" 1 2 ", "%d%d", 2, &x, &y);
+       TEST_VA(" 1 2 3 ", "%d%d%d", 3, &x, &y, &z);
+       TEST_VA(" 1 2 3 ", "%d%d", 2, &x, &y);
+
+       TEST_VA(" 1x 2 ", "%d%d", 1, &x, &y);
+
+       TEST_VA("1", "%d%c", 1, &x, &a);
+       TEST_VA("1 ", "%d%c", 2, &x, &a);
+       TEST_VA("1 ", "%d %c", 1, &x, &a);
+
+       TEST_VA(" \t\n", "%c%c%c", 3, &a, &b, &c);
+}
+
+static int __init test_scanf_init(void)
+{
+       test_base();
+       test_sign();
+       test_range();
+       test_eot();
+       test_set();
+       test_va();
+       return -EINVAL;
+}
+module_init(test_scanf_init);
+MODULE_LICENSE("GPL");

Reply via email to