]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
lib/crc16_kunit.c: add KUnit tests for crc16
authorVinicius Peixoto <vpeixoto@lkcamp.dev>
Sat, 12 Oct 2024 07:43:49 +0000 (04:43 -0300)
committerAndrew Morton <akpm@linux-foundation.org>
Wed, 6 Nov 2024 01:12:31 +0000 (17:12 -0800)
Add Kunit tests for the kernel's implementation of the standard CRC-16
algorithm (<linux/crc16.h>).  The test data consists of 100
randomly-generated test cases, validated against a naive CRC-16
implementation.

This test follows roughly the same logic as lib/crc32test.c, but without
the performance measurements.

Link: https://lkml.kernel.org/r/20241012-crc16-kunit-v3-1-0ca75cb58ca9@lkcamp.dev
Signed-off-by: Vinicius Peixoto <vpeixoto@lkcamp.dev>
Co-developed-by: Enzo Bertoloti <ebertoloti@lkcamp.dev>
Signed-off-by: Enzo Bertoloti <ebertoloti@lkcamp.dev>
Co-developed-by: Fabricio Gasperin <fgasperin@lkcamp.dev>
Signed-off-by: Fabricio Gasperin <fgasperin@lkcamp.dev>
Suggested-by: David Laight <David.Laight@ACULAB.COM>
Cc: Brendan Higgins <brendan.higgins@linux.dev>
Cc: David Gow <davidgow@google.com>
Cc: Rae Moar <rmoar@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
lib/Kconfig.debug
lib/Makefile
lib/crc16_kunit.c [new file with mode: 0644]

index 409dd193c09b4ddcdb84b1c16beb22daa7e25d55..eda319e9d5695300de78ed5c7186d3697eb75d47 100644 (file)
@@ -2850,6 +2850,15 @@ config USERCOPY_KUNIT_TEST
          on the copy_to/from_user infrastructure, making sure basic
          user/kernel boundary testing is working.
 
+config CRC16_KUNIT_TEST
+       tristate "KUnit tests for CRC16"
+       depends on KUNIT
+       default KUNIT_ALL_TESTS
+       select CRC16
+       help
+         Enable this option to run unit tests for the kernel's CRC16
+         implementation (<linux/crc16.h>).
+
 config TEST_UDELAY
        tristate "udelay test driver"
        help
index 773adf88af41665b2419202e5427e0513c6becae..1faed6414a85fd366b4966a00e8ba231d7546e14 100644 (file)
@@ -389,6 +389,7 @@ CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN)
 obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o
 obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
 obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
+obj-$(CONFIG_CRC16_KUNIT_TEST) += crc16_kunit.o
 
 obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
 
diff --git a/lib/crc16_kunit.c b/lib/crc16_kunit.c
new file mode 100644 (file)
index 0000000..0918c98
--- /dev/null
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnits tests for CRC16.
+ *
+ * Copyright (C) 2024, LKCAMP
+ * Author: Vinicius Peixoto <vpeixoto@lkcamp.dev>
+ * Author: Fabricio Gasperin <fgasperin@lkcamp.dev>
+ * Author: Enzo Bertoloti <ebertoloti@lkcamp.dev>
+ */
+#include <kunit/test.h>
+#include <linux/crc16.h>
+#include <linux/prandom.h>
+
+#define CRC16_KUNIT_DATA_SIZE 4096
+#define CRC16_KUNIT_TEST_SIZE 100
+#define CRC16_KUNIT_SEED 0x12345678
+
+/**
+ * struct crc16_test - CRC16 test data
+ * @crc: initial input value to CRC16
+ * @start: Start index within the data buffer
+ * @length: Length of the data
+ */
+static struct crc16_test {
+       u16 crc;
+       u16 start;
+       u16 length;
+} tests[CRC16_KUNIT_TEST_SIZE];
+
+u8 data[CRC16_KUNIT_DATA_SIZE];
+
+
+/* Naive implementation of CRC16 for validation purposes */
+static inline u16 _crc16_naive_byte(u16 crc, u8 data)
+{
+       u8 i = 0;
+
+       crc ^= (u16) data;
+       for (i = 0; i < 8; i++) {
+               if (crc & 0x01)
+                       crc = (crc >> 1) ^ 0xa001;
+               else
+                       crc = crc >> 1;
+       }
+
+       return crc;
+}
+
+
+static inline u16 _crc16_naive(u16 crc, u8 *buffer, size_t len)
+{
+       while (len--)
+               crc = _crc16_naive_byte(crc, *buffer++);
+       return crc;
+}
+
+
+/* Small helper for generating pseudorandom 16-bit data */
+static inline u16 _rand16(void)
+{
+       static u32 rand = CRC16_KUNIT_SEED;
+
+       rand = next_pseudo_random32(rand);
+       return rand & 0xFFFF;
+}
+
+
+static int crc16_init_test_data(struct kunit_suite *suite)
+{
+       size_t i;
+
+       /* Fill the data buffer with random bytes */
+       for (i = 0; i < CRC16_KUNIT_DATA_SIZE; i++)
+               data[i] = _rand16() & 0xFF;
+
+       /* Generate random test data while ensuring the random
+        * start + length values won't overflow the 4096-byte
+        * buffer (0x7FF * 2 = 0xFFE < 0x1000)
+        */
+       for (size_t i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) {
+               tests[i].crc = _rand16();
+               tests[i].start = _rand16() & 0x7FF;
+               tests[i].length = _rand16() & 0x7FF;
+       }
+
+       return 0;
+}
+
+static void crc16_test_empty(struct kunit *test)
+{
+       u16 crc;
+
+       /* The result for empty data should be the same as the
+        * initial crc
+        */
+       crc = crc16(0x00, data, 0);
+       KUNIT_EXPECT_EQ(test, crc, 0);
+       crc = crc16(0xFF, data, 0);
+       KUNIT_EXPECT_EQ(test, crc, 0xFF);
+}
+
+static void crc16_test_correctness(struct kunit *test)
+{
+       size_t i;
+       u16 crc, crc_naive;
+
+       for (i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) {
+               /* Compare results with the naive crc16 implementation */
+               crc = crc16(tests[i].crc, data + tests[i].start,
+                           tests[i].length);
+               crc_naive = _crc16_naive(tests[i].crc, data + tests[i].start,
+                                        tests[i].length);
+               KUNIT_EXPECT_EQ(test, crc, crc_naive);
+       }
+}
+
+
+static void crc16_test_combine(struct kunit *test)
+{
+       size_t i, j;
+       u16 crc, crc_naive;
+
+       /* Make sure that combining two consecutive crc16 calculations
+        * yields the same result as calculating the crc16 for the whole thing
+        */
+       for (i = 0; i < CRC16_KUNIT_TEST_SIZE; i++) {
+               crc_naive = crc16(tests[i].crc, data + tests[i].start, tests[i].length);
+               for (j = 0; j < tests[i].length; j++) {
+                       crc = crc16(tests[i].crc, data + tests[i].start, j);
+                       crc = crc16(crc, data + tests[i].start + j, tests[i].length - j);
+                       KUNIT_EXPECT_EQ(test, crc, crc_naive);
+               }
+       }
+}
+
+
+static struct kunit_case crc16_test_cases[] = {
+       KUNIT_CASE(crc16_test_empty),
+       KUNIT_CASE(crc16_test_combine),
+       KUNIT_CASE(crc16_test_correctness),
+       {},
+};
+
+static struct kunit_suite crc16_test_suite = {
+       .name = "crc16",
+       .test_cases = crc16_test_cases,
+       .suite_init = crc16_init_test_data,
+};
+kunit_test_suite(crc16_test_suite);
+
+MODULE_AUTHOR("Fabricio Gasperin <fgasperin@lkcamp.dev>");
+MODULE_AUTHOR("Vinicius Peixoto <vpeixoto@lkcamp.dev>");
+MODULE_AUTHOR("Enzo Bertoloti <ebertoloti@lkcamp.dev>");
+MODULE_DESCRIPTION("Unit tests for crc16");
+MODULE_LICENSE("GPL");