diff mbox series

unicode: Implement UTF-8 tests as a KUnit test

Message ID 20210531184742.1142042-1-krisman@collabora.com (mailing list archive)
State New, archived
Headers show
Series unicode: Implement UTF-8 tests as a KUnit test | expand

Commit Message

Gabriel Krisman Bertazi May 31, 2021, 6:47 p.m. UTC
From: Ricardo Cañuelo <ricardo.canuelo@collabora.com>

Hi,

This patch saw some review on the list around one year ago [1], and I
proposed it on a PR that ended up vanishing on the list noise [2], while
I moved on to other things.

Given the long time since last review, I'm proposing it for review again
instead of making it part of a new PR.  From the version I've been
carrying, the only update of this iteration is the KUNIT_ALL_TESTS
symbol dependency on Kconfig, and the CONFIG_ option rename to better
fit other KUNIT tests.  The actual code continues to work fine on top of
mainline.

Please, let me know what you think.

[1] https://www.spinics.net/lists/linux-fsdevel/msg166356.html
[2] https://patchwork.kernel.org/project/linux-fsdevel/patch/87blkap6az.fsf@collabora.com/

-- >8 --

This translates the existing UTF-8 unit test module into a
KUnit-compliant test suite. No functionality has been added or removed.

Some names changed to make the file name, the Kconfig option and test
suite name less specific, since this source file might hold more UTF-8
tests in the future.

Signed-off-by: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
[Fix checkpatch's complaint. Fix module build.
 Add KUNIT_ALL_TESTS to kconfig]
Co-developed-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
---
 fs/unicode/Kconfig                          |  21 ++-
 fs/unicode/Makefile                         |   2 +-
 fs/unicode/{utf8-selftest.c => utf8-test.c} | 199 +++++++++-----------
 3 files changed, 109 insertions(+), 113 deletions(-)
 rename fs/unicode/{utf8-selftest.c => utf8-test.c} (60%)

Comments

Daniel Latypov June 1, 2021, 4:52 p.m. UTC | #1
On Mon, May 31, 2021 at 11:47 AM Gabriel Krisman Bertazi
<krisman@collabora.com> wrote:
>
> From: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
>
> Hi,
>
> This patch saw some review on the list around one year ago [1], and I
> proposed it on a PR that ended up vanishing on the list noise [2], while
> I moved on to other things.
>
> Given the long time since last review, I'm proposing it for review again
> instead of making it part of a new PR.  From the version I've been
> carrying, the only update of this iteration is the KUNIT_ALL_TESTS
> symbol dependency on Kconfig, and the CONFIG_ option rename to better
> fit other KUNIT tests.  The actual code continues to work fine on top of
> mainline.
>
> Please, let me know what you think.
>
> [1] https://www.spinics.net/lists/linux-fsdevel/msg166356.html
> [2] https://patchwork.kernel.org/project/linux-fsdevel/patch/87blkap6az.fsf@collabora.com/
>
> -- >8 --
>
> This translates the existing UTF-8 unit test module into a
> KUnit-compliant test suite. No functionality has been added or removed.

Just a note: this is actually a safer conversion to KUnit than some
others we've seen.
Other tests sometimes return non-zero from modue_init() if any test
case failed, but KUnit does not, nor did this test.
So that's nice.

>
> Some names changed to make the file name, the Kconfig option and test
> suite name less specific, since this source file might hold more UTF-8
> tests in the future.
>
> Signed-off-by: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
> [Fix checkpatch's complaint. Fix module build.
>  Add KUNIT_ALL_TESTS to kconfig]
> Co-developed-by: Gabriel Krisman Bertazi <krisman@collabora.com>
> Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>

Acked-by: Daniel Latypov <dlatypov@google.com>

Looks good on the KUnit-side of things, thanks.
Just minor nits about the naming below.

> ---
>  fs/unicode/Kconfig                          |  21 ++-
>  fs/unicode/Makefile                         |   2 +-
>  fs/unicode/{utf8-selftest.c => utf8-test.c} | 199 +++++++++-----------

Very minor nit: utf8_test.c would be preferred.

Since the original version, there's now a "style guide",
https://www.kernel.org/doc/html/latest/dev-tools/kunit/style.html#test-file-and-module-names

>  3 files changed, 109 insertions(+), 113 deletions(-)
>  rename fs/unicode/{utf8-selftest.c => utf8-test.c} (60%)
>
> diff --git a/fs/unicode/Kconfig b/fs/unicode/Kconfig
> index 2c27b9a5cd6c..e29aca813374 100644
> --- a/fs/unicode/Kconfig
> +++ b/fs/unicode/Kconfig
> @@ -8,7 +8,20 @@ config UNICODE
>           Say Y here to enable UTF-8 NFD normalization and NFD+CF casefolding
>           support.
>
> -config UNICODE_NORMALIZATION_SELFTEST
> -       tristate "Test UTF-8 normalization support"
> -       depends on UNICODE
> -       default n
> +config UNICODE_KUNIT_TESTS
> +       tristate "KUnit test UTF-8 normalization support" if !KUNIT_ALL_TESTS
> +       depends on UNICODE && KUNIT
> +       default KUNIT_ALL_TESTS
> +       help
> +         This builds the KUnit test suite for Unicode normalization and
> +         casefolding support.
> +
> +         KUnit tests run during boot and output the results to the debug log
> +         in TAP format (http://testanything.org/). Only useful for kernel devs
> +         running KUnit test harness and are not for inclusion into a production
> +         build.
> +
> +         For more information on KUnit and unit tests in general please refer
> +         to the KUnit documentation in Documentation/dev-tools/kunit/.
> +
> +         If unsure, say N.
> diff --git a/fs/unicode/Makefile b/fs/unicode/Makefile
> index b88aecc86550..0e8e2192a715 100644
> --- a/fs/unicode/Makefile
> +++ b/fs/unicode/Makefile
> @@ -1,7 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0
>
>  obj-$(CONFIG_UNICODE) += unicode.o
> -obj-$(CONFIG_UNICODE_NORMALIZATION_SELFTEST) += utf8-selftest.o
> +obj-$(CONFIG_UNICODE_KUNIT_TESTS) += utf8-test.o
>
>  unicode-y := utf8-norm.o utf8-core.o
>
> diff --git a/fs/unicode/utf8-selftest.c b/fs/unicode/utf8-test.c
> similarity index 60%
> rename from fs/unicode/utf8-selftest.c
> rename to fs/unicode/utf8-test.c
> index 6fe8af7edccb..3ef3a80f9407 100644
> --- a/fs/unicode/utf8-selftest.c
> +++ b/fs/unicode/utf8-test.c
> @@ -1,39 +1,23 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  /*
> - * Kernel module for testing utf-8 support.
> + * KUnit tests for utf-8 support.
>   *
> - * Copyright 2017 Collabora Ltd.
> + * Copyright 2020 Collabora Ltd.
>   */
>
> -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> -
> -#include <linux/module.h>
> -#include <linux/printk.h>
> +#include <kunit/test.h>
>  #include <linux/unicode.h>
> -#include <linux/dcache.h>
> -
>  #include "utf8n.h"
>
> -unsigned int failed_tests;
> -unsigned int total_tests;
> -
>  /* Tests will be based on this version. */
>  #define latest_maj 12
>  #define latest_min 1
>  #define latest_rev 0
>
> -#define _test(cond, func, line, fmt, ...) do {                         \
> -               total_tests++;                                          \
> -               if (!cond) {                                            \
> -                       failed_tests++;                                 \
> -                       pr_err("test %s:%d Failed: %s%s",               \
> -                              func, line, #cond, (fmt?":":"."));       \
> -                       if (fmt)                                        \
> -                               pr_err(fmt, ##__VA_ARGS__);             \
> -               }                                                       \
> -       } while (0)
> -#define test_f(cond, fmt, ...) _test(cond, __func__, __LINE__, fmt, ##__VA_ARGS__)
> -#define test(cond) _test(cond, __func__, __LINE__, "")
> +#define str(s) #s
> +#define VERSION_STR(maj, min, rev) str(maj) "." str(min) "." str(rev)
> +
> +/* Test data */

(Not actionable, just a remark)

Just an FYI, there's now support for iterating over test data arrays:
https://www.kernel.org/doc/html/latest/dev-tools/kunit/usage.html#parameterized-testing

I think this is fine as-is as both test cases that use it have some setup.
That setup could be moved into an init func and store its result in
test->priv, but it's more readable as-is, IMO.

However one benefit is it would allow adding a "description" to each
test case to show on failure.
E.g. here's what a failure would look like right now:

[09:28:09] ============================================================
[09:28:09] ======== [FAILED] utf8-unit-test ========
[09:28:09] [PASSED] utf8_test_supported_versions
[09:28:09] [FAILED] utf8_test_nfdi
[09:28:09]     # utf8_test_nfdi: EXPECTATION FAILED at
fs/unicode/utf8-test.c:202
[09:28:09]     Expected c == nfdi_test_data[i].dec[j], but
[09:28:09]         c == 137
[09:28:09]         nfdi_test_data[i].dec[j] == 138
[09:28:09] Unexpected byte 0x89 should be 0x8a
[09:28:09]     not ok 2 - utf8_test_nfdi
[09:28:09]
[09:28:09] [PASSED] utf8_test_nfdicf
[09:28:09] [FAILED] utf8_test_comparisons
[09:28:09]     # utf8_test_comparisons: EXPECTATION FAILED at
fs/unicode/utf8-test.c:267
[09:28:09]     Expected utf8_strncmp(table, &s1, &s2) == 0, but
[09:28:09]         utf8_strncmp(table, &s1, &s2) == 1
[09:28:09]
[09:28:09] lj NJ comparison mismatch
[09:28:09]     not ok 4 - utf8_test_comparisons
[09:28:09]

It's a bit hard to grok where the failure lies and perhaps converting
the comments into a string that can be shown on failure would help.
But this is a pre-existing condition (and making the change seems is
something I'd be personally too lazy to do).

>
>  static const struct {
>         /* UTF-8 strings in this vector _must_ be NULL-terminated. */
> @@ -160,88 +144,117 @@ static const struct {
>         }
>  };
>
> -static void check_utf8_nfdi(void)
> +
> +/* Test cases */
> +
> +static void utf8_test_supported_versions(struct kunit *test)
> +{
> +       /* Unicode 7.0.0 should be supported. */
> +       KUNIT_EXPECT_TRUE(test, utf8version_is_supported(7, 0, 0));
> +
> +       /* Unicode 9.0.0 should be supported. */
> +       KUNIT_EXPECT_TRUE(test, utf8version_is_supported(9, 0, 0));
> +
> +       /* Unicode 1x.0.0 (the latest version) should be supported. */
> +       KUNIT_EXPECT_TRUE(test,
> +               utf8version_is_supported(latest_maj, latest_min, latest_rev));
> +
> +       /* Next versions don't exist. */
> +       KUNIT_EXPECT_FALSE(test,
> +               utf8version_is_supported(latest_maj + 1, 0, 0));
> +
> +       /* Test for invalid version values */
> +       KUNIT_EXPECT_FALSE(test, utf8version_is_supported(0, 0, 0));
> +       KUNIT_EXPECT_FALSE(test, utf8version_is_supported(-1, -1, -1));
> +}
> +
> +static void utf8_test_nfdi(struct kunit *test)
>  {
>         int i;
>         struct utf8cursor u8c;
>         const struct utf8data *data;
>
>         data = utf8nfdi(UNICODE_AGE(latest_maj, latest_min, latest_rev));
> -       if (!data) {
> -               pr_err("%s: Unable to load utf8-%d.%d.%d. Skipping.\n",
> -                      __func__, latest_maj, latest_min, latest_rev);
> -               return;
> -       }
> +       KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, data,
> +               "Unable to load utf8-%d.%d.%d. Skipping.",
> +               latest_maj, latest_min, latest_rev);
>
>         for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) {
> -               int len = strlen(nfdi_test_data[i].str);
> -               int nlen = strlen(nfdi_test_data[i].dec);
> +               size_t len = strlen(nfdi_test_data[i].str);
> +               size_t nlen = strlen(nfdi_test_data[i].dec);
>                 int j = 0;
>                 unsigned char c;
>
> -               test((utf8len(data, nfdi_test_data[i].str) == nlen));
> -               test((utf8nlen(data, nfdi_test_data[i].str, len) == nlen));
> +               KUNIT_EXPECT_EQ(test,
> +                       utf8len(data, nfdi_test_data[i].str),
> +                       (ssize_t)nlen);
> +               KUNIT_EXPECT_EQ(test,
> +                       utf8nlen(data, nfdi_test_data[i].str, len),
> +                       (ssize_t)nlen);
>
> -               if (utf8cursor(&u8c, data, nfdi_test_data[i].str) < 0)
> -                       pr_err("can't create cursor\n");
> +               KUNIT_ASSERT_EQ_MSG(test,
> +                       utf8cursor(&u8c, data, nfdi_test_data[i].str), 0,
> +                       "Can't create cursor");
>
>                 while ((c = utf8byte(&u8c)) > 0) {
> -                       test_f((c == nfdi_test_data[i].dec[j]),
> -                              "Unexpected byte 0x%x should be 0x%x\n",
> -                              c, nfdi_test_data[i].dec[j]);
> +                       KUNIT_EXPECT_EQ_MSG(test, c, nfdi_test_data[i].dec[j],
> +                               "Unexpected byte 0x%x should be 0x%x",
> +                               c, nfdi_test_data[i].dec[j]);
>                         j++;
>                 }
>
> -               test((j == nlen));
> +               KUNIT_EXPECT_EQ(test, j, (int)nlen);
>         }
>  }
>
> -static void check_utf8_nfdicf(void)
> +static void utf8_test_nfdicf(struct kunit *test)
>  {
>         int i;
>         struct utf8cursor u8c;
>         const struct utf8data *data;
>
>         data = utf8nfdicf(UNICODE_AGE(latest_maj, latest_min, latest_rev));
> -       if (!data) {
> -               pr_err("%s: Unable to load utf8-%d.%d.%d. Skipping.\n",
> -                      __func__, latest_maj, latest_min, latest_rev);
> -               return;
> -       }
> +       KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, data,
> +               "Unable to load utf8-%d.%d.%d. Skipping.",
> +               latest_maj, latest_min, latest_rev);
>
>         for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) {
> -               int len = strlen(nfdicf_test_data[i].str);
> -               int nlen = strlen(nfdicf_test_data[i].ncf);
> +               size_t len = strlen(nfdicf_test_data[i].str);
> +               size_t nlen = strlen(nfdicf_test_data[i].ncf);
>                 int j = 0;
>                 unsigned char c;
>
> -               test((utf8len(data, nfdicf_test_data[i].str) == nlen));
> -               test((utf8nlen(data, nfdicf_test_data[i].str, len) == nlen));
> +               KUNIT_EXPECT_EQ(test,
> +                       utf8len(data, nfdicf_test_data[i].str),
> +                       (ssize_t)nlen);
> +               KUNIT_EXPECT_EQ(test,
> +                       utf8nlen(data, nfdicf_test_data[i].str, len),
> +                       (ssize_t)nlen);
>
> -               if (utf8cursor(&u8c, data, nfdicf_test_data[i].str) < 0)
> -                       pr_err("can't create cursor\n");
> +               KUNIT_ASSERT_EQ_MSG(test,
> +                       utf8cursor(&u8c, data, nfdicf_test_data[i].str), 0,
> +                       "Can't create cursor");
>
>                 while ((c = utf8byte(&u8c)) > 0) {
> -                       test_f((c == nfdicf_test_data[i].ncf[j]),
> -                              "Unexpected byte 0x%x should be 0x%x\n",
> -                              c, nfdicf_test_data[i].ncf[j]);
> +                       KUNIT_EXPECT_EQ_MSG(test, c, nfdicf_test_data[i].ncf[j],
> +                               "Unexpected byte 0x%x should be 0x%x\n",
> +                               c, nfdicf_test_data[i].ncf[j]);
>                         j++;
>                 }
>
> -               test((j == nlen));
> +               KUNIT_EXPECT_EQ(test, j, (int)nlen);
>         }
>  }
>
> -static void check_utf8_comparisons(void)
> +static void utf8_test_comparisons(struct kunit *test)
>  {
>         int i;
> -       struct unicode_map *table = utf8_load("12.1.0");
> +       struct unicode_map *table;
>
> -       if (IS_ERR(table)) {
> -               pr_err("%s: Unable to load utf8 %d.%d.%d. Skipping.\n",
> -                      __func__, latest_maj, latest_min, latest_rev);
> -               return;
> -       }
> +       table = utf8_load(VERSION_STR(latest_maj, latest_min, latest_rev));
> +       KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, table,
> +               "Unable to load utf8-%d.%d.%d. Skipping.\n",
> +               latest_maj, latest_min, latest_rev);
>
>         for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) {
>                 const struct qstr s1 = {.name = nfdi_test_data[i].str,
> @@ -249,8 +262,8 @@ static void check_utf8_comparisons(void)
>                 const struct qstr s2 = {.name = nfdi_test_data[i].dec,
>                                         .len = sizeof(nfdi_test_data[i].dec)};
>
> -               test_f(!utf8_strncmp(table, &s1, &s2),
> -                      "%s %s comparison mismatch\n", s1.name, s2.name);
> +               KUNIT_EXPECT_EQ_MSG(test, utf8_strncmp(table, &s1, &s2), 0,
> +                       "%s %s comparison mismatch", s1.name, s2.name);
>         }
>
>         for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) {
> @@ -259,54 +272,24 @@ static void check_utf8_comparisons(void)
>                 const struct qstr s2 = {.name = nfdicf_test_data[i].ncf,
>                                         .len = sizeof(nfdicf_test_data[i].ncf)};
>
> -               test_f(!utf8_strncasecmp(table, &s1, &s2),
> -                      "%s %s comparison mismatch\n", s1.name, s2.name);
> +               KUNIT_EXPECT_EQ_MSG(test, utf8_strncasecmp(table, &s1, &s2), 0,
> +                       "%s %s comparison mismatch", s1.name, s2.name);
>         }
>
>         utf8_unload(table);
>  }
>
> -static void check_supported_versions(void)
> -{
> -       /* Unicode 7.0.0 should be supported. */
> -       test(utf8version_is_supported(7, 0, 0));
> -
> -       /* Unicode 9.0.0 should be supported. */
> -       test(utf8version_is_supported(9, 0, 0));
> -
> -       /* Unicode 1x.0.0 (the latest version) should be supported. */
> -       test(utf8version_is_supported(latest_maj, latest_min, latest_rev));
> -
> -       /* Next versions don't exist. */
> -       test(!utf8version_is_supported(13, 0, 0));
> -       test(!utf8version_is_supported(0, 0, 0));
> -       test(!utf8version_is_supported(-1, -1, -1));
> -}
> -
> -static int __init init_test_ucd(void)
> -{
> -       failed_tests = 0;
> -       total_tests = 0;
> -
> -       check_supported_versions();
> -       check_utf8_nfdi();
> -       check_utf8_nfdicf();
> -       check_utf8_comparisons();
> -
> -       if (!failed_tests)
> -               pr_info("All %u tests passed\n", total_tests);
> -       else
> -               pr_err("%u out of %u tests failed\n", failed_tests,
> -                      total_tests);
> -       return 0;
> -}
> -
> -static void __exit exit_test_ucd(void)
> -{
> -}
> +static struct kunit_case utf8_test_cases[] = {
> +       KUNIT_CASE(utf8_test_supported_versions),
> +       KUNIT_CASE(utf8_test_nfdi),
> +       KUNIT_CASE(utf8_test_nfdicf),
> +       KUNIT_CASE(utf8_test_comparisons),
> +       {}
> +};
>
> -module_init(init_test_ucd);
> -module_exit(exit_test_ucd);
> +static struct kunit_suite utf8_test_suite = {
> +       .name = "utf8-unit-test",

The guidance on suite naming lives at:
https://www.kernel.org/doc/html/latest/dev-tools/kunit/style.html#suites
Being pedantic, one might say we should call this "fs_utf8" or similar.

But the main points IMO are
* we should use _ instead of -
* we can drop "-unit-test" or "-tests" from suite names, as it's a bit
redundant.


> +       .test_cases = utf8_test_cases,
> +};
>
> -MODULE_AUTHOR("Gabriel Krisman Bertazi <krisman@collabora.co.uk>");
> -MODULE_LICENSE("GPL");
> +kunit_test_suite(utf8_test_suite);
> --
> 2.31.0
>
> --
> You received this message because you are subscribed to the Google Groups "KUnit Development" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to kunit-dev+unsubscribe@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/kunit-dev/20210531184742.1142042-1-krisman%40collabora.com.
diff mbox series

Patch

diff --git a/fs/unicode/Kconfig b/fs/unicode/Kconfig
index 2c27b9a5cd6c..e29aca813374 100644
--- a/fs/unicode/Kconfig
+++ b/fs/unicode/Kconfig
@@ -8,7 +8,20 @@  config UNICODE
 	  Say Y here to enable UTF-8 NFD normalization and NFD+CF casefolding
 	  support.
 
-config UNICODE_NORMALIZATION_SELFTEST
-	tristate "Test UTF-8 normalization support"
-	depends on UNICODE
-	default n
+config UNICODE_KUNIT_TESTS
+	tristate "KUnit test UTF-8 normalization support" if !KUNIT_ALL_TESTS
+	depends on UNICODE && KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  This builds the KUnit test suite for Unicode normalization and
+	  casefolding support.
+
+	  KUnit tests run during boot and output the results to the debug log
+	  in TAP format (http://testanything.org/). Only useful for kernel devs
+	  running KUnit test harness and are not for inclusion into a production
+	  build.
+
+	  For more information on KUnit and unit tests in general please refer
+	  to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+	  If unsure, say N.
diff --git a/fs/unicode/Makefile b/fs/unicode/Makefile
index b88aecc86550..0e8e2192a715 100644
--- a/fs/unicode/Makefile
+++ b/fs/unicode/Makefile
@@ -1,7 +1,7 @@ 
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_UNICODE) += unicode.o
-obj-$(CONFIG_UNICODE_NORMALIZATION_SELFTEST) += utf8-selftest.o
+obj-$(CONFIG_UNICODE_KUNIT_TESTS) += utf8-test.o
 
 unicode-y := utf8-norm.o utf8-core.o
 
diff --git a/fs/unicode/utf8-selftest.c b/fs/unicode/utf8-test.c
similarity index 60%
rename from fs/unicode/utf8-selftest.c
rename to fs/unicode/utf8-test.c
index 6fe8af7edccb..3ef3a80f9407 100644
--- a/fs/unicode/utf8-selftest.c
+++ b/fs/unicode/utf8-test.c
@@ -1,39 +1,23 @@ 
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Kernel module for testing utf-8 support.
+ * KUnit tests for utf-8 support.
  *
- * Copyright 2017 Collabora Ltd.
+ * Copyright 2020 Collabora Ltd.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/printk.h>
+#include <kunit/test.h>
 #include <linux/unicode.h>
-#include <linux/dcache.h>
-
 #include "utf8n.h"
 
-unsigned int failed_tests;
-unsigned int total_tests;
-
 /* Tests will be based on this version. */
 #define latest_maj 12
 #define latest_min 1
 #define latest_rev 0
 
-#define _test(cond, func, line, fmt, ...) do {				\
-		total_tests++;						\
-		if (!cond) {						\
-			failed_tests++;					\
-			pr_err("test %s:%d Failed: %s%s",		\
-			       func, line, #cond, (fmt?":":"."));	\
-			if (fmt)					\
-				pr_err(fmt, ##__VA_ARGS__);		\
-		}							\
-	} while (0)
-#define test_f(cond, fmt, ...) _test(cond, __func__, __LINE__, fmt, ##__VA_ARGS__)
-#define test(cond) _test(cond, __func__, __LINE__, "")
+#define str(s) #s
+#define VERSION_STR(maj, min, rev) str(maj) "." str(min) "." str(rev)
+
+/* Test data */
 
 static const struct {
 	/* UTF-8 strings in this vector _must_ be NULL-terminated. */
@@ -160,88 +144,117 @@  static const struct {
 	}
 };
 
-static void check_utf8_nfdi(void)
+
+/* Test cases */
+
+static void utf8_test_supported_versions(struct kunit *test)
+{
+	/* Unicode 7.0.0 should be supported. */
+	KUNIT_EXPECT_TRUE(test, utf8version_is_supported(7, 0, 0));
+
+	/* Unicode 9.0.0 should be supported. */
+	KUNIT_EXPECT_TRUE(test, utf8version_is_supported(9, 0, 0));
+
+	/* Unicode 1x.0.0 (the latest version) should be supported. */
+	KUNIT_EXPECT_TRUE(test,
+		utf8version_is_supported(latest_maj, latest_min, latest_rev));
+
+	/* Next versions don't exist. */
+	KUNIT_EXPECT_FALSE(test,
+		utf8version_is_supported(latest_maj + 1, 0, 0));
+
+	/* Test for invalid version values */
+	KUNIT_EXPECT_FALSE(test, utf8version_is_supported(0, 0, 0));
+	KUNIT_EXPECT_FALSE(test, utf8version_is_supported(-1, -1, -1));
+}
+
+static void utf8_test_nfdi(struct kunit *test)
 {
 	int i;
 	struct utf8cursor u8c;
 	const struct utf8data *data;
 
 	data = utf8nfdi(UNICODE_AGE(latest_maj, latest_min, latest_rev));
-	if (!data) {
-		pr_err("%s: Unable to load utf8-%d.%d.%d. Skipping.\n",
-		       __func__, latest_maj, latest_min, latest_rev);
-		return;
-	}
+	KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, data,
+		"Unable to load utf8-%d.%d.%d. Skipping.",
+		latest_maj, latest_min, latest_rev);
 
 	for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) {
-		int len = strlen(nfdi_test_data[i].str);
-		int nlen = strlen(nfdi_test_data[i].dec);
+		size_t len = strlen(nfdi_test_data[i].str);
+		size_t nlen = strlen(nfdi_test_data[i].dec);
 		int j = 0;
 		unsigned char c;
 
-		test((utf8len(data, nfdi_test_data[i].str) == nlen));
-		test((utf8nlen(data, nfdi_test_data[i].str, len) == nlen));
+		KUNIT_EXPECT_EQ(test,
+			utf8len(data, nfdi_test_data[i].str),
+			(ssize_t)nlen);
+		KUNIT_EXPECT_EQ(test,
+			utf8nlen(data, nfdi_test_data[i].str, len),
+			(ssize_t)nlen);
 
-		if (utf8cursor(&u8c, data, nfdi_test_data[i].str) < 0)
-			pr_err("can't create cursor\n");
+		KUNIT_ASSERT_EQ_MSG(test,
+			utf8cursor(&u8c, data, nfdi_test_data[i].str), 0,
+			"Can't create cursor");
 
 		while ((c = utf8byte(&u8c)) > 0) {
-			test_f((c == nfdi_test_data[i].dec[j]),
-			       "Unexpected byte 0x%x should be 0x%x\n",
-			       c, nfdi_test_data[i].dec[j]);
+			KUNIT_EXPECT_EQ_MSG(test, c, nfdi_test_data[i].dec[j],
+				"Unexpected byte 0x%x should be 0x%x",
+				c, nfdi_test_data[i].dec[j]);
 			j++;
 		}
 
-		test((j == nlen));
+		KUNIT_EXPECT_EQ(test, j, (int)nlen);
 	}
 }
 
-static void check_utf8_nfdicf(void)
+static void utf8_test_nfdicf(struct kunit *test)
 {
 	int i;
 	struct utf8cursor u8c;
 	const struct utf8data *data;
 
 	data = utf8nfdicf(UNICODE_AGE(latest_maj, latest_min, latest_rev));
-	if (!data) {
-		pr_err("%s: Unable to load utf8-%d.%d.%d. Skipping.\n",
-		       __func__, latest_maj, latest_min, latest_rev);
-		return;
-	}
+	KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, data,
+		"Unable to load utf8-%d.%d.%d. Skipping.",
+		latest_maj, latest_min, latest_rev);
 
 	for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) {
-		int len = strlen(nfdicf_test_data[i].str);
-		int nlen = strlen(nfdicf_test_data[i].ncf);
+		size_t len = strlen(nfdicf_test_data[i].str);
+		size_t nlen = strlen(nfdicf_test_data[i].ncf);
 		int j = 0;
 		unsigned char c;
 
-		test((utf8len(data, nfdicf_test_data[i].str) == nlen));
-		test((utf8nlen(data, nfdicf_test_data[i].str, len) == nlen));
+		KUNIT_EXPECT_EQ(test,
+			utf8len(data, nfdicf_test_data[i].str),
+			(ssize_t)nlen);
+		KUNIT_EXPECT_EQ(test,
+			utf8nlen(data, nfdicf_test_data[i].str, len),
+			(ssize_t)nlen);
 
-		if (utf8cursor(&u8c, data, nfdicf_test_data[i].str) < 0)
-			pr_err("can't create cursor\n");
+		KUNIT_ASSERT_EQ_MSG(test,
+			utf8cursor(&u8c, data, nfdicf_test_data[i].str), 0,
+			"Can't create cursor");
 
 		while ((c = utf8byte(&u8c)) > 0) {
-			test_f((c == nfdicf_test_data[i].ncf[j]),
-			       "Unexpected byte 0x%x should be 0x%x\n",
-			       c, nfdicf_test_data[i].ncf[j]);
+			KUNIT_EXPECT_EQ_MSG(test, c, nfdicf_test_data[i].ncf[j],
+				"Unexpected byte 0x%x should be 0x%x\n",
+				c, nfdicf_test_data[i].ncf[j]);
 			j++;
 		}
 
-		test((j == nlen));
+		KUNIT_EXPECT_EQ(test, j, (int)nlen);
 	}
 }
 
-static void check_utf8_comparisons(void)
+static void utf8_test_comparisons(struct kunit *test)
 {
 	int i;
-	struct unicode_map *table = utf8_load("12.1.0");
+	struct unicode_map *table;
 
-	if (IS_ERR(table)) {
-		pr_err("%s: Unable to load utf8 %d.%d.%d. Skipping.\n",
-		       __func__, latest_maj, latest_min, latest_rev);
-		return;
-	}
+	table = utf8_load(VERSION_STR(latest_maj, latest_min, latest_rev));
+	KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, table,
+		"Unable to load utf8-%d.%d.%d. Skipping.\n",
+		latest_maj, latest_min, latest_rev);
 
 	for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) {
 		const struct qstr s1 = {.name = nfdi_test_data[i].str,
@@ -249,8 +262,8 @@  static void check_utf8_comparisons(void)
 		const struct qstr s2 = {.name = nfdi_test_data[i].dec,
 					.len = sizeof(nfdi_test_data[i].dec)};
 
-		test_f(!utf8_strncmp(table, &s1, &s2),
-		       "%s %s comparison mismatch\n", s1.name, s2.name);
+		KUNIT_EXPECT_EQ_MSG(test, utf8_strncmp(table, &s1, &s2), 0,
+			"%s %s comparison mismatch", s1.name, s2.name);
 	}
 
 	for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) {
@@ -259,54 +272,24 @@  static void check_utf8_comparisons(void)
 		const struct qstr s2 = {.name = nfdicf_test_data[i].ncf,
 					.len = sizeof(nfdicf_test_data[i].ncf)};
 
-		test_f(!utf8_strncasecmp(table, &s1, &s2),
-		       "%s %s comparison mismatch\n", s1.name, s2.name);
+		KUNIT_EXPECT_EQ_MSG(test, utf8_strncasecmp(table, &s1, &s2), 0,
+			"%s %s comparison mismatch", s1.name, s2.name);
 	}
 
 	utf8_unload(table);
 }
 
-static void check_supported_versions(void)
-{
-	/* Unicode 7.0.0 should be supported. */
-	test(utf8version_is_supported(7, 0, 0));
-
-	/* Unicode 9.0.0 should be supported. */
-	test(utf8version_is_supported(9, 0, 0));
-
-	/* Unicode 1x.0.0 (the latest version) should be supported. */
-	test(utf8version_is_supported(latest_maj, latest_min, latest_rev));
-
-	/* Next versions don't exist. */
-	test(!utf8version_is_supported(13, 0, 0));
-	test(!utf8version_is_supported(0, 0, 0));
-	test(!utf8version_is_supported(-1, -1, -1));
-}
-
-static int __init init_test_ucd(void)
-{
-	failed_tests = 0;
-	total_tests = 0;
-
-	check_supported_versions();
-	check_utf8_nfdi();
-	check_utf8_nfdicf();
-	check_utf8_comparisons();
-
-	if (!failed_tests)
-		pr_info("All %u tests passed\n", total_tests);
-	else
-		pr_err("%u out of %u tests failed\n", failed_tests,
-		       total_tests);
-	return 0;
-}
-
-static void __exit exit_test_ucd(void)
-{
-}
+static struct kunit_case utf8_test_cases[] = {
+	KUNIT_CASE(utf8_test_supported_versions),
+	KUNIT_CASE(utf8_test_nfdi),
+	KUNIT_CASE(utf8_test_nfdicf),
+	KUNIT_CASE(utf8_test_comparisons),
+	{}
+};
 
-module_init(init_test_ucd);
-module_exit(exit_test_ucd);
+static struct kunit_suite utf8_test_suite = {
+	.name = "utf8-unit-test",
+	.test_cases = utf8_test_cases,
+};
 
-MODULE_AUTHOR("Gabriel Krisman Bertazi <krisman@collabora.co.uk>");
-MODULE_LICENSE("GPL");
+kunit_test_suite(utf8_test_suite);