]> Gentwo Git Trees - linux/.git/commitdiff
hfsplus: introduce KUnit tests for HFS+ string operations
authorViacheslav Dubeyko <slava@dubeyko.com>
Tue, 25 Nov 2025 00:04:29 +0000 (16:04 -0800)
committerViacheslav Dubeyko <slava@dubeyko.com>
Tue, 25 Nov 2025 00:12:51 +0000 (16:12 -0800)
This patch implements the Kunit based set of
unit tests for HFS+ string operations. It checks
functionality of hfsplus_strcasecmp(), hfsplus_strcmp(),
hfsplus_uni2asc(), hfsplus_asc2uni(), hfsplus_hash_dentry(),
and hfsplus_compare_dentry().

./tools/testing/kunit/kunit.py run --kunitconfig ./fs/hfsplus/.kunitconfig
[14:38:05] Configuring KUnit Kernel ...
[14:38:05] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=22
[14:38:09] Starting KUnit Kernel (1/1)...
[14:38:09] ============================================================
Running tests with:
$ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[14:38:09] ============== hfsplus_unicode (27 subtests) ===============
[14:38:09] [PASSED] hfsplus_strcasecmp_test
[14:38:09] [PASSED] hfsplus_strcmp_test
[14:38:09] [PASSED] hfsplus_unicode_edge_cases_test
[14:38:09] [PASSED] hfsplus_unicode_boundary_test
[14:38:09] [PASSED] hfsplus_uni2asc_basic_test
[14:38:09] [PASSED] hfsplus_uni2asc_special_chars_test
[14:38:09] [PASSED] hfsplus_uni2asc_buffer_test
[14:38:09] [PASSED] hfsplus_uni2asc_corrupted_test
[14:38:09] [PASSED] hfsplus_uni2asc_edge_cases_test
[14:38:09] [PASSED] hfsplus_asc2uni_basic_test
[14:38:09] [PASSED] hfsplus_asc2uni_special_chars_test
[14:38:09] [PASSED] hfsplus_asc2uni_buffer_limits_test
[14:38:09] [PASSED] hfsplus_asc2uni_edge_cases_test
[14:38:09] [PASSED] hfsplus_asc2uni_decompose_test
[14:38:09] [PASSED] hfsplus_hash_dentry_basic_test
[14:38:09] [PASSED] hfsplus_hash_dentry_casefold_test
[14:38:09] [PASSED] hfsplus_hash_dentry_special_chars_test
[14:38:09] [PASSED] hfsplus_hash_dentry_decompose_test
[14:38:09] [PASSED] hfsplus_hash_dentry_consistency_test
[14:38:09] [PASSED] hfsplus_hash_dentry_edge_cases_test
[14:38:09] [PASSED] hfsplus_compare_dentry_basic_test
[14:38:09] [PASSED] hfsplus_compare_dentry_casefold_test
[14:38:09] [PASSED] hfsplus_compare_dentry_special_chars_test
[14:38:09] [PASSED] hfsplus_compare_dentry_length_test
[14:38:09] [PASSED] hfsplus_compare_dentry_decompose_test
[14:38:09] [PASSED] hfsplus_compare_dentry_edge_cases_test
[14:38:09] [PASSED] hfsplus_compare_dentry_combined_flags_test
[14:38:09] ================= [PASSED] hfsplus_unicode =================
[14:38:09] ============================================================
[14:38:09] Testing complete. Ran 27 tests: passed: 27
[14:38:09] Elapsed time: 3.875s total, 0.001s configuring, 3.707s building, 0.115s running

v2
Rework memory management model.

Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
cc: Yangtao Li <frank.li@vivo.com>
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
fs/hfsplus/.kunitconfig [new file with mode: 0644]
fs/hfsplus/Kconfig
fs/hfsplus/Makefile
fs/hfsplus/unicode.c
fs/hfsplus/unicode_test.c [new file with mode: 0644]

diff --git a/fs/hfsplus/.kunitconfig b/fs/hfsplus/.kunitconfig
new file mode 100644 (file)
index 0000000..6c96dc7
--- /dev/null
@@ -0,0 +1,8 @@
+CONFIG_KUNIT=y
+CONFIG_HFSPLUS_FS=y
+CONFIG_HFSPLUS_KUNIT_TEST=y
+CONFIG_BLOCK=y
+CONFIG_BUFFER_HEAD=y
+CONFIG_NLS=y
+CONFIG_NLS_UTF8=y
+CONFIG_LEGACY_DIRECT_IO=y
index 8ce4a33a9ac7881fca1aadb3650b1ad4a45afa5f..ca8401cb6954d556076a2f72ad7f4927d2ce89da 100644 (file)
@@ -14,3 +14,18 @@ config HFSPLUS_FS
          MacOS 8. It includes all Mac specific filesystem data such as
          data forks and creator codes, but it also has several UNIX
          style features such as file ownership and permissions.
+
+config HFSPLUS_KUNIT_TEST
+       tristate "KUnit tests for HFS+ filesystem" if !KUNIT_ALL_TESTS
+       depends on HFSPLUS_FS && KUNIT
+       default KUNIT_ALL_TESTS
+       help
+         This builds KUnit tests for the HFS+ filesystem.
+
+         KUnit tests run during boot and output the results to the debug
+         log in TAP format (https://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/.
index 9ed20e64b983937085d0a471574c91d630b107e7..f2a9ae697e81141019615ecdd3d16b40bd255d69 100644 (file)
@@ -8,3 +8,6 @@ obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o
 hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \
                bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o \
                attributes.o xattr.o xattr_user.o xattr_security.o xattr_trusted.o
+
+# KUnit tests
+obj-$(CONFIG_HFSPLUS_KUNIT_TEST) += unicode_test.o
index 11e08a4a18b2957a53c25a29ecf78db5dbee6bac..d3a142f4518b4edc27773b622fc44276e552d2d5 100644 (file)
@@ -11,6 +11,9 @@
 
 #include <linux/types.h>
 #include <linux/nls.h>
+
+#include <kunit/visibility.h>
+
 #include "hfsplus_fs.h"
 #include "hfsplus_raw.h"
 
@@ -72,6 +75,7 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *s1,
                        return 0;
        }
 }
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcasecmp);
 
 /* Compare names as a sequence of 16-bit unsigned integers */
 int hfsplus_strcmp(const struct hfsplus_unistr *s1,
@@ -110,7 +114,7 @@ int hfsplus_strcmp(const struct hfsplus_unistr *s1,
        return len1 < len2 ? -1 :
               len1 > len2 ? 1 : 0;
 }
-
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcmp);
 
 #define Hangul_SBase   0xac00
 #define Hangul_LBase   0x1100
@@ -143,8 +147,9 @@ static u16 *hfsplus_compose_lookup(u16 *p, u16 cc)
        return NULL;
 }
 
-static int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr,
-                   int max_len, char *astr, int *len_p)
+static int hfsplus_uni2asc(struct super_block *sb,
+                          const struct hfsplus_unistr *ustr,
+                          int max_len, char *astr, int *len_p)
 {
        const hfsplus_unichr *ip;
        struct nls_table *nls = HFSPLUS_SB(sb)->nls;
@@ -285,6 +290,7 @@ inline int hfsplus_uni2asc_str(struct super_block *sb,
 {
        return hfsplus_uni2asc(sb, ustr, HFSPLUS_MAX_STRLEN, astr, len_p);
 }
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_str);
 
 inline int hfsplus_uni2asc_xattr_str(struct super_block *sb,
                                     const struct hfsplus_attr_unistr *ustr,
@@ -293,6 +299,7 @@ inline int hfsplus_uni2asc_xattr_str(struct super_block *sb,
        return hfsplus_uni2asc(sb, (const struct hfsplus_unistr *)ustr,
                               HFSPLUS_ATTR_MAX_STRLEN, astr, len_p);
 }
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_xattr_str);
 
 /*
  * Convert one or more ASCII characters into a single unicode character.
@@ -420,6 +427,7 @@ int hfsplus_asc2uni(struct super_block *sb,
                return -ENAMETOOLONG;
        return 0;
 }
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_asc2uni);
 
 /*
  * Hash a string to an integer as appropriate for the HFS+ filesystem.
@@ -472,6 +480,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
 
        return 0;
 }
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_hash_dentry);
 
 /*
  * Compare strings with HFS+ filename ordering.
@@ -563,3 +572,4 @@ int hfsplus_compare_dentry(const struct dentry *dentry,
                return 1;
        return 0;
 }
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_compare_dentry);
diff --git a/fs/hfsplus/unicode_test.c b/fs/hfsplus/unicode_test.c
new file mode 100644 (file)
index 0000000..5a7a685
--- /dev/null
@@ -0,0 +1,1579 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for HFS+ Unicode string operations
+ *
+ * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/nls.h>
+#include <linux/dcache.h>
+#include <linux/stringhash.h>
+#include "hfsplus_fs.h"
+
+struct test_mock_string_env {
+       struct hfsplus_unistr str1;
+       struct hfsplus_unistr str2;
+       char *buf;
+       u32 buf_size;
+};
+
+static struct test_mock_string_env *setup_mock_str_env(u32 buf_size)
+{
+       struct test_mock_string_env *env;
+
+       env = kzalloc(sizeof(struct test_mock_string_env), GFP_KERNEL);
+       if (!env)
+               return NULL;
+
+       env->buf = kzalloc(buf_size, GFP_KERNEL);
+       if (!env->buf) {
+               kfree(env);
+               return NULL;
+       }
+
+       env->buf_size = buf_size;
+
+       return env;
+}
+
+static void free_mock_str_env(struct test_mock_string_env *env)
+{
+       if (env->buf)
+               kfree(env->buf);
+       kfree(env);
+}
+
+/* Helper function to create hfsplus_unistr */
+static void create_unistr(struct hfsplus_unistr *ustr, const char *ascii_str)
+{
+       int len = strlen(ascii_str);
+       int i;
+
+       memset(ustr->unicode, 0, sizeof(ustr->unicode));
+
+       ustr->length = cpu_to_be16(len);
+       for (i = 0; i < len && i < HFSPLUS_MAX_STRLEN; i++)
+               ustr->unicode[i] = cpu_to_be16((u16)ascii_str[i]);
+}
+
+static void corrupt_unistr(struct hfsplus_unistr *ustr)
+{
+       ustr->length = cpu_to_be16(U16_MAX);
+}
+
+/* Test hfsplus_strcasecmp function */
+static void hfsplus_strcasecmp_test(struct kunit *test)
+{
+       struct test_mock_string_env *mock_env;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       /* Test identical strings */
+       create_unistr(&mock_env->str1, "hello");
+       create_unistr(&mock_env->str2, "hello");
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       /* Test case insensitive comparison */
+       create_unistr(&mock_env->str1, "Hello");
+       create_unistr(&mock_env->str2, "hello");
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       create_unistr(&mock_env->str1, "HELLO");
+       create_unistr(&mock_env->str2, "hello");
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       /* Test different strings */
+       create_unistr(&mock_env->str1, "apple");
+       create_unistr(&mock_env->str2, "banana");
+       KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "zebra");
+       create_unistr(&mock_env->str2, "apple");
+       KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       /* Test different lengths */
+       create_unistr(&mock_env->str1, "test");
+       create_unistr(&mock_env->str2, "testing");
+       KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "testing");
+       create_unistr(&mock_env->str2, "test");
+       KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       /* Test empty strings */
+       create_unistr(&mock_env->str1, "");
+       create_unistr(&mock_env->str2, "");
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       create_unistr(&mock_env->str1, "");
+       create_unistr(&mock_env->str2, "test");
+       KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       /* Test single characters */
+       create_unistr(&mock_env->str1, "A");
+       create_unistr(&mock_env->str2, "a");
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       create_unistr(&mock_env->str1, "A");
+       create_unistr(&mock_env->str2, "B");
+       KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       /* Test maximum length strings */
+       memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
+       mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';
+       create_unistr(&mock_env->str1, mock_env->buf);
+       create_unistr(&mock_env->str2, mock_env->buf);
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       /* Change one character in the middle */
+       mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';
+       create_unistr(&mock_env->str2, mock_env->buf);
+       KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       /* Test corrupted strings */
+       create_unistr(&mock_env->str1, "");
+       corrupt_unistr(&mock_env->str1);
+       create_unistr(&mock_env->str2, "");
+       KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       create_unistr(&mock_env->str1, "");
+       create_unistr(&mock_env->str2, "");
+       corrupt_unistr(&mock_env->str2);
+       KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       create_unistr(&mock_env->str1, "test");
+       corrupt_unistr(&mock_env->str1);
+       create_unistr(&mock_env->str2, "testing");
+       KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "test");
+       create_unistr(&mock_env->str2, "testing");
+       corrupt_unistr(&mock_env->str2);
+       KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "testing");
+       corrupt_unistr(&mock_env->str1);
+       create_unistr(&mock_env->str2, "test");
+       KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "testing");
+       create_unistr(&mock_env->str2, "test");
+       corrupt_unistr(&mock_env->str2);
+       KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       free_mock_str_env(mock_env);
+}
+
+/* Test hfsplus_strcmp function (case-sensitive) */
+static void hfsplus_strcmp_test(struct kunit *test)
+{
+       struct test_mock_string_env *mock_env;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       /* Test identical strings */
+       create_unistr(&mock_env->str1, "hello");
+       create_unistr(&mock_env->str2, "hello");
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       /* Test case sensitive comparison - should NOT be equal */
+       create_unistr(&mock_env->str1, "Hello");
+       create_unistr(&mock_env->str2, "hello");
+       KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+        /* 'H' < 'h' in Unicode */
+       KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       /* Test lexicographic ordering */
+       create_unistr(&mock_env->str1, "apple");
+       create_unistr(&mock_env->str2, "banana");
+       KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "zebra");
+       create_unistr(&mock_env->str2, "apple");
+       KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       /* Test different lengths with common prefix */
+       create_unistr(&mock_env->str1, "test");
+       create_unistr(&mock_env->str2, "testing");
+       KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "testing");
+       create_unistr(&mock_env->str2, "test");
+       KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       /* Test empty strings */
+       create_unistr(&mock_env->str1, "");
+       create_unistr(&mock_env->str2, "");
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       /* Test maximum length strings */
+       memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
+       mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';
+       create_unistr(&mock_env->str1, mock_env->buf);
+       create_unistr(&mock_env->str2, mock_env->buf);
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       /* Change one character in the middle */
+       mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';
+       create_unistr(&mock_env->str2, mock_env->buf);
+       KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       /* Test corrupted strings */
+       create_unistr(&mock_env->str1, "");
+       corrupt_unistr(&mock_env->str1);
+       create_unistr(&mock_env->str2, "");
+       KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       create_unistr(&mock_env->str1, "");
+       create_unistr(&mock_env->str2, "");
+       corrupt_unistr(&mock_env->str2);
+       KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       create_unistr(&mock_env->str1, "test");
+       corrupt_unistr(&mock_env->str1);
+       create_unistr(&mock_env->str2, "testing");
+       KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "test");
+       create_unistr(&mock_env->str2, "testing");
+       corrupt_unistr(&mock_env->str2);
+       KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "testing");
+       corrupt_unistr(&mock_env->str1);
+       create_unistr(&mock_env->str2, "test");
+       KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       create_unistr(&mock_env->str1, "testing");
+       create_unistr(&mock_env->str2, "test");
+       corrupt_unistr(&mock_env->str2);
+       KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       free_mock_str_env(mock_env);
+}
+
+/* Test Unicode edge cases */
+static void hfsplus_unicode_edge_cases_test(struct kunit *test)
+{
+       struct test_mock_string_env *mock_env;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       /* Test with special characters */
+       mock_env->str1.length = cpu_to_be16(3);
+       mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* Ã© */
+       mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* Ã± */
+       mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* Ã¼ */
+
+       mock_env->str2.length = cpu_to_be16(3);
+       mock_env->str2.unicode[0] = cpu_to_be16(0x00E9); /* Ã© */
+       mock_env->str2.unicode[1] = cpu_to_be16(0x00F1); /* Ã± */
+       mock_env->str2.unicode[2] = cpu_to_be16(0x00FC); /* Ã¼ */
+
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       /* Test with different special characters */
+       mock_env->str2.unicode[1] = cpu_to_be16(0x00F2); /* Ã² */
+       KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       /* Test null characters within string (should be handled correctly) */
+       mock_env->str1.length = cpu_to_be16(3);
+       mock_env->str1.unicode[0] = cpu_to_be16('a');
+       mock_env->str1.unicode[1] = cpu_to_be16(0x0000); /* null */
+       mock_env->str1.unicode[2] = cpu_to_be16('b');
+
+       mock_env->str2.length = cpu_to_be16(3);
+       mock_env->str2.unicode[0] = cpu_to_be16('a');
+       mock_env->str2.unicode[1] = cpu_to_be16(0x0000); /* null */
+       mock_env->str2.unicode[2] = cpu_to_be16('b');
+
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       free_mock_str_env(mock_env);
+}
+
+/* Test boundary conditions */
+static void hfsplus_unicode_boundary_test(struct kunit *test)
+{
+       struct test_mock_string_env *mock_env;
+       int i;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       /* Test maximum length boundary */
+       mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
+       mock_env->str2.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
+
+       for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) {
+               mock_env->str1.unicode[i] = cpu_to_be16('A');
+               mock_env->str2.unicode[i] = cpu_to_be16('A');
+       }
+
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+
+       /* Change last character */
+       mock_env->str2.unicode[HFSPLUS_MAX_STRLEN - 1] = cpu_to_be16('B');
+       KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+
+       /* Test zero length strings */
+       mock_env->str1.length = cpu_to_be16(0);
+       mock_env->str2.length = cpu_to_be16(0);
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+                                               &mock_env->str2));
+       KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+                                                   &mock_env->str2));
+
+       /* Test one character vs empty */
+       mock_env->str1.length = cpu_to_be16(1);
+       mock_env->str1.unicode[0] = cpu_to_be16('A');
+       mock_env->str2.length = cpu_to_be16(0);
+       KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+                                            &mock_env->str2), 0);
+       KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+                                                &mock_env->str2), 0);
+
+       free_mock_str_env(mock_env);
+}
+
+/* Mock superblock and NLS table for testing hfsplus_uni2asc */
+struct test_mock_sb {
+       struct nls_table nls;
+       struct hfsplus_sb_info sb_info;
+       struct super_block sb;
+};
+
+static struct test_mock_sb *setup_mock_sb(void)
+{
+       struct test_mock_sb *ptr;
+
+       ptr = kzalloc(sizeof(struct test_mock_sb), GFP_KERNEL);
+       if (!ptr)
+               return NULL;
+
+       ptr->nls.charset = "utf8";
+       ptr->nls.uni2char = NULL; /* Will use default behavior */
+       ptr->sb_info.nls = &ptr->nls;
+       ptr->sb.s_fs_info = &ptr->sb_info;
+
+       /* Set default flags - no decomposition, no case folding */
+       clear_bit(HFSPLUS_SB_NODECOMPOSE, &ptr->sb_info.flags);
+       clear_bit(HFSPLUS_SB_CASEFOLD, &ptr->sb_info.flags);
+
+       return ptr;
+}
+
+static void free_mock_sb(struct test_mock_sb *ptr)
+{
+       kfree(ptr);
+}
+
+/* Simple uni2char implementation for testing */
+static int test_uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       if (uni < 0x80) {
+               *out = (unsigned char)uni;
+               return 1;
+       }
+
+       /* For non-ASCII, just use '?' as fallback */
+       *out = '?';
+       return 1;
+}
+
+/* Test hfsplus_uni2asc basic functionality */
+static void hfsplus_uni2asc_basic_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int len, result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.uni2char = test_uni2char;
+
+       /* Test simple ASCII string conversion */
+       create_unistr(&mock_env->str1, "hello");
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 5, len);
+       KUNIT_EXPECT_STREQ(test, "hello", mock_env->buf);
+
+       /* Test empty string */
+       create_unistr(&mock_env->str1, "");
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 0, len);
+
+       /* Test single character */
+       create_unistr(&mock_env->str1, "A");
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 1, len);
+       KUNIT_EXPECT_EQ(test, 'A', mock_env->buf[0]);
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test special character handling */
+static void hfsplus_uni2asc_special_chars_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int len, result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.uni2char = test_uni2char;
+
+       /* Test null character conversion (should become 0x2400) */
+       mock_env->str1.length = cpu_to_be16(1);
+       mock_env->str1.unicode[0] = cpu_to_be16(0x0000);
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 1, len);
+       /* Our test implementation returns '?' for non-ASCII */
+       KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]);
+
+       /* Test forward slash conversion (should become colon) */
+       mock_env->str1.length = cpu_to_be16(1);
+       mock_env->str1.unicode[0] = cpu_to_be16('/');
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 1, len);
+       KUNIT_EXPECT_EQ(test, ':', mock_env->buf[0]);
+
+       /* Test string with mixed special characters */
+       mock_env->str1.length = cpu_to_be16(3);
+       mock_env->str1.unicode[0] = cpu_to_be16('a');
+       mock_env->str1.unicode[1] = cpu_to_be16('/');
+       mock_env->str1.unicode[2] = cpu_to_be16('b');
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 3, len);
+       KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[0]);
+       KUNIT_EXPECT_EQ(test, ':', mock_env->buf[1]);
+       KUNIT_EXPECT_EQ(test, 'b', mock_env->buf[2]);
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test buffer length handling */
+static void hfsplus_uni2asc_buffer_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int len, result;
+
+       mock_env = setup_mock_str_env(10);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.uni2char = test_uni2char;
+
+       /* Test insufficient buffer space */
+       create_unistr(&mock_env->str1, "toolongstring");
+       len = 5; /* Buffer too small */
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+       KUNIT_EXPECT_EQ(test, 5, len); /* Should be set to consumed length */
+
+       /* Test exact buffer size */
+       create_unistr(&mock_env->str1, "exact");
+       len = 5;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 5, len);
+
+       /* Test zero length buffer */
+       create_unistr(&mock_env->str1, "test");
+       len = 0;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+       KUNIT_EXPECT_EQ(test, 0, len);
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test corrupted unicode string handling */
+static void hfsplus_uni2asc_corrupted_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int len, result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.uni2char = test_uni2char;
+
+       /* Test corrupted length (too large) */
+       create_unistr(&mock_env->str1, "test");
+       corrupt_unistr(&mock_env->str1); /* Sets length to U16_MAX */
+       len = mock_env->buf_size;
+
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       /* Should still work but with corrected length */
+       KUNIT_EXPECT_EQ(test, 0, result);
+       /*
+        * Length should be corrected to HFSPLUS_MAX_STRLEN
+        * and processed accordingly
+        */
+       KUNIT_EXPECT_GT(test, len, 0);
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test edge cases and boundary conditions */
+static void hfsplus_uni2asc_edge_cases_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int len, result;
+       int i;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN * 2);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.uni2char = test_uni2char;
+
+       /* Test maximum length string */
+       mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
+       for (i = 0; i < HFSPLUS_MAX_STRLEN; i++)
+               mock_env->str1.unicode[i] = cpu_to_be16('a');
+
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, len);
+
+       /* Verify all characters are 'a' */
+       for (i = 0; i < HFSPLUS_MAX_STRLEN; i++)
+               KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[i]);
+
+       /* Test string with high Unicode values (non-ASCII) */
+       mock_env->str1.length = cpu_to_be16(3);
+       mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* Ã© */
+       mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* Ã± */
+       mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* Ã¼ */
+       len = mock_env->buf_size;
+       result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+                                    mock_env->buf, &len);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 3, len);
+       /* Our test implementation converts non-ASCII to '?' */
+       KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]);
+       KUNIT_EXPECT_EQ(test, '?', mock_env->buf[1]);
+       KUNIT_EXPECT_EQ(test, '?', mock_env->buf[2]);
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Simple char2uni implementation for testing */
+static int test_char2uni(const unsigned char *rawstring,
+                        int boundlen, wchar_t *uni)
+{
+       if (boundlen <= 0)
+               return -EINVAL;
+
+       *uni = (wchar_t)*rawstring;
+       return 1;
+}
+
+/* Helper function to check unicode string contents */
+static void check_unistr_content(struct kunit *test,
+                                struct hfsplus_unistr *ustr,
+                                const char *expected_ascii)
+{
+       int expected_len = strlen(expected_ascii);
+       int actual_len = be16_to_cpu(ustr->length);
+       int i;
+
+       KUNIT_EXPECT_EQ(test, expected_len, actual_len);
+
+       for (i = 0; i < expected_len && i < actual_len; i++) {
+               u16 expected_char = (u16)expected_ascii[i];
+               u16 actual_char = be16_to_cpu(ustr->unicode[i]);
+
+               KUNIT_EXPECT_EQ(test, expected_char, actual_char);
+       }
+}
+
+/* Test hfsplus_asc2uni basic functionality */
+static void hfsplus_asc2uni_basic_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test simple ASCII string conversion */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, "hello", 5);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       check_unistr_content(test, &mock_env->str1, "hello");
+
+       /* Test empty string */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, "", 0);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
+
+       /* Test single character */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, "A", 1);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       check_unistr_content(test, &mock_env->str1, "A");
+
+       /* Test null-terminated string with explicit length */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, "test\0extra", 4);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       check_unistr_content(test, &mock_env->str1, "test");
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test special character handling in asc2uni */
+static void hfsplus_asc2uni_special_chars_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test colon conversion (should become forward slash) */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, ":", 1);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(mock_env->str1.length));
+       KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0]));
+
+       /* Test string with mixed special characters */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, "a:b", 3);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
+       KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(mock_env->str1.unicode[0]));
+       KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1]));
+       KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(mock_env->str1.unicode[2]));
+
+       /* Test multiple special characters */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, ":::", 3);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
+       KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0]));
+       KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1]));
+       KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[2]));
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test buffer length limits */
+static void hfsplus_asc2uni_buffer_limits_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 10);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test exact maximum length */
+       memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
+       result = hfsplus_asc2uni(&mock_sb->sb,
+                                &mock_env->str1, HFSPLUS_MAX_STRLEN,
+                                mock_env->buf, HFSPLUS_MAX_STRLEN);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
+                       be16_to_cpu(mock_env->str1.length));
+
+       /* Test exceeding maximum length */
+       memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN + 5);
+       result = hfsplus_asc2uni(&mock_sb->sb,
+                                &mock_env->str1, HFSPLUS_MAX_STRLEN,
+                                mock_env->buf, HFSPLUS_MAX_STRLEN + 5);
+
+       KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+       KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
+                       be16_to_cpu(mock_env->str1.length));
+
+       /* Test with smaller max_unistr_len */
+       result = hfsplus_asc2uni(&mock_sb->sb,
+                                &mock_env->str1, 5, "toolongstring", 13);
+
+       KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+       KUNIT_EXPECT_EQ(test, 5, be16_to_cpu(mock_env->str1.length));
+
+       /* Test zero max length */
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4);
+
+       KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+       KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test error handling and edge cases */
+static void hfsplus_asc2uni_edge_cases_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct hfsplus_unistr ustr;
+       char test_str[] = {'a', '\0', 'b'};
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test zero length input */
+       result = hfsplus_asc2uni(&mock_sb->sb,
+                                &ustr, HFSPLUS_MAX_STRLEN, "test", 0);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.length));
+
+       /* Test input with length mismatch */
+       result = hfsplus_asc2uni(&mock_sb->sb,
+                                &ustr, HFSPLUS_MAX_STRLEN, "hello", 3);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       check_unistr_content(test, &ustr, "hel");
+
+       /* Test with various printable ASCII characters */
+       result = hfsplus_asc2uni(&mock_sb->sb,
+                                &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       check_unistr_content(test, &ustr, "ABC123!@#");
+
+       /* Test null character in the middle */
+       result = hfsplus_asc2uni(&mock_sb->sb,
+                                &ustr, HFSPLUS_MAX_STRLEN, test_str, 3);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length));
+       KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(ustr.unicode[0]));
+       KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.unicode[1]));
+       KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(ustr.unicode[2]));
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test decomposition flag behavior */
+static void hfsplus_asc2uni_decompose_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       int result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test with decomposition disabled (default) */
+       clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+                                HFSPLUS_MAX_STRLEN, "test", 4);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       check_unistr_content(test, &mock_env->str1, "test");
+
+       /* Test with decomposition enabled */
+       set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+       result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str2,
+                                HFSPLUS_MAX_STRLEN, "test", 4);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       check_unistr_content(test, &mock_env->str2, "test");
+
+       /* For simple ASCII, both should produce the same result */
+       KUNIT_EXPECT_EQ(test,
+                       be16_to_cpu(mock_env->str1.length),
+                       be16_to_cpu(mock_env->str2.length));
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Mock dentry for testing hfsplus_hash_dentry */
+static struct dentry test_dentry;
+
+static void setup_mock_dentry(struct super_block *sb)
+{
+       memset(&test_dentry, 0, sizeof(test_dentry));
+       test_dentry.d_sb = sb;
+}
+
+/* Helper function to create qstr */
+static void create_qstr(struct qstr *str, const char *name)
+{
+       str->name = name;
+       str->len = strlen(name);
+       str->hash = 0; /* Will be set by hash function */
+}
+
+/* Test hfsplus_hash_dentry basic functionality */
+static void hfsplus_hash_dentry_basic_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr str1, str2;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test basic string hashing */
+       create_qstr(&str1, "hello");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_NE(test, 0, str1.hash);
+
+       /* Test that identical strings produce identical hashes */
+       create_qstr(&str2, "hello");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+       /* Test empty string */
+       create_qstr(&str1, "");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+
+       /* Empty string should still produce a hash */
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test single character */
+       create_qstr(&str1, "A");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_NE(test, 0, str1.hash);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test case folding behavior in hash */
+static void hfsplus_hash_dentry_casefold_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr str1, str2;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test with case folding disabled (default) */
+       clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+       create_qstr(&str1, "Hello");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&str2, "hello");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /*
+        * Without case folding, different cases
+        * should produce different hashes
+        */
+       KUNIT_EXPECT_NE(test, str1.hash, str2.hash);
+
+       /* Test with case folding enabled */
+       set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+       create_qstr(&str1, "Hello");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&str2, "hello");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* With case folding, different cases should produce same hash */
+       KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+       /* Test mixed case */
+       create_qstr(&str1, "HeLLo");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test special character handling in hash */
+static void hfsplus_hash_dentry_special_chars_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr str1, str2;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test colon conversion (: becomes /) */
+       create_qstr(&str1, "file:name");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&str2, "file/name");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* After conversion, these should produce the same hash */
+       KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+       /* Test multiple special characters */
+       create_qstr(&str1, ":::");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&str2, "///");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test decomposition flag behavior in hash */
+static void hfsplus_hash_dentry_decompose_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr str1, str2;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test with decomposition disabled (default) */
+       clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+       create_qstr(&str1, "test");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test with decomposition enabled */
+       set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+       create_qstr(&str2, "test");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /*
+        * For simple ASCII, decomposition shouldn't change
+        * the hash much but the function should still work correctly
+        */
+       KUNIT_EXPECT_NE(test, 0, str2.hash);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test hash consistency and distribution */
+static void hfsplus_hash_dentry_consistency_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr str1, str2, str3;
+       unsigned long hash1;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test that same string always produces same hash */
+       create_qstr(&str1, "consistent");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+       hash1 = str1.hash;
+
+       create_qstr(&str2, "consistent");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       KUNIT_EXPECT_EQ(test, hash1, str2.hash);
+
+       /* Test that different strings produce different hashes */
+       create_qstr(&str3, "different");
+       result = hfsplus_hash_dentry(&test_dentry, &str3);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       KUNIT_EXPECT_NE(test, str1.hash, str3.hash);
+
+       /* Test similar strings should have different hashes */
+       create_qstr(&str1, "file1");
+       result = hfsplus_hash_dentry(&test_dentry, &str1);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&str2, "file2");
+       result = hfsplus_hash_dentry(&test_dentry, &str2);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       KUNIT_EXPECT_NE(test, str1.hash, str2.hash);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test edge cases and boundary conditions */
+static void hfsplus_hash_dentry_edge_cases_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct test_mock_string_env *mock_env;
+       struct qstr str;
+       int result;
+
+       mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+       KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test very long filename */
+       memset(mock_env->buf, 'a', mock_env->buf_size - 1);
+       mock_env->buf[mock_env->buf_size - 1] = '\0';
+
+       create_qstr(&str, mock_env->buf);
+       result = hfsplus_hash_dentry(&test_dentry, &str);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_NE(test, 0, str.hash);
+
+       /* Test filename with all printable ASCII characters */
+       create_qstr(&str, "!@#$%^&*()_+-=[]{}|;':\",./<>?");
+       result = hfsplus_hash_dentry(&test_dentry, &str);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_NE(test, 0, str.hash);
+
+       /* Test with embedded null (though not typical for filenames) */
+       str.name = "file\0hidden";
+       str.len = 11; /* Include the null and text after it */
+       str.hash = 0;
+       result = hfsplus_hash_dentry(&test_dentry, &str);
+
+       KUNIT_EXPECT_EQ(test, 0, result);
+       KUNIT_EXPECT_NE(test, 0, str.hash);
+
+       free_mock_str_env(mock_env);
+       free_mock_sb(mock_sb);
+}
+
+/* Test hfsplus_compare_dentry basic functionality */
+static void hfsplus_compare_dentry_basic_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr name;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test identical strings */
+       create_qstr(&name, "hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test different strings - lexicographic order */
+       create_qstr(&name, "world");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+       KUNIT_EXPECT_LT(test, result, 0); /* "hello" < "world" */
+
+       result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&name, "hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name);
+       KUNIT_EXPECT_GT(test, result, 0); /* "world" > "hello" */
+
+       /* Test empty strings */
+       create_qstr(&name, "");
+       result = hfsplus_compare_dentry(&test_dentry, 0, "", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test one empty, one non-empty */
+       create_qstr(&name, "test");
+       result = hfsplus_compare_dentry(&test_dentry, 0, "", &name);
+       KUNIT_EXPECT_LT(test, result, 0); /* "" < "test" */
+
+       create_qstr(&name, "");
+       result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+       KUNIT_EXPECT_GT(test, result, 0); /* "test" > "" */
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test case folding behavior in comparison */
+static void hfsplus_compare_dentry_casefold_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr name;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test with case folding disabled (default) */
+       clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+       create_qstr(&name, "hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name);
+       /* Case sensitive: "Hello" != "hello" */
+       KUNIT_EXPECT_NE(test, 0, result);
+
+       create_qstr(&name, "Hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+       /* Case sensitive: "hello" != "Hello" */
+       KUNIT_EXPECT_NE(test, 0, result);
+
+       /* Test with case folding enabled */
+       set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+       create_qstr(&name, "hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name);
+       /* Case insensitive: "Hello" == "hello" */
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&name, "Hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+       /* Case insensitive: "hello" == "Hello" */
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test mixed case */
+       create_qstr(&name, "TeSt");
+       result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&name, "test");
+       result = hfsplus_compare_dentry(&test_dentry, 4, "TEST", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test special character handling in comparison */
+static void hfsplus_compare_dentry_special_chars_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr name;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test colon conversion (: becomes /) */
+       create_qstr(&name, "file/name");
+       result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
+       /* "file:name" == "file/name" after conversion */
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       create_qstr(&name, "file:name");
+       result = hfsplus_compare_dentry(&test_dentry, 9, "file/name", &name);
+       /* "file/name" == "file:name" after conversion */
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test multiple special characters */
+       create_qstr(&name, "///");
+       result = hfsplus_compare_dentry(&test_dentry, 3, ":::", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test mixed special and regular characters */
+       create_qstr(&name, "a/b:c");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "a:b/c", &name);
+       /* Both become "a/b/c" after conversion */
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test length differences */
+static void hfsplus_compare_dentry_length_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr name;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test different lengths with common prefix */
+       create_qstr(&name, "testing");
+       result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+       KUNIT_EXPECT_LT(test, result, 0); /* "test" < "testing" */
+
+       create_qstr(&name, "test");
+       result = hfsplus_compare_dentry(&test_dentry, 7, "testing", &name);
+       KUNIT_EXPECT_GT(test, result, 0); /* "testing" > "test" */
+
+       /* Test exact length match */
+       create_qstr(&name, "exact");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "exact", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test length parameter vs actual string content */
+       create_qstr(&name, "hello");
+       result = hfsplus_compare_dentry(&test_dentry, 3, "hel", &name);
+       KUNIT_EXPECT_LT(test, result, 0); /* "hel" < "hello" */
+
+       /* Test longer first string but shorter length parameter */
+       create_qstr(&name, "hi");
+       result = hfsplus_compare_dentry(&test_dentry, 2, "hello", &name);
+       /* "he" < "hi" (only first 2 chars compared) */
+       KUNIT_EXPECT_LT(test, result, 0);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test decomposition flag behavior */
+static void hfsplus_compare_dentry_decompose_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr name;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test with decomposition disabled (default) */
+       clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+       create_qstr(&name, "test");
+       result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test with decomposition enabled */
+       set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+       create_qstr(&name, "test");
+       result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* For simple ASCII, decomposition shouldn't affect the result */
+       create_qstr(&name, "different");
+       result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+       KUNIT_EXPECT_NE(test, 0, result);
+
+       free_mock_sb(mock_sb);
+}
+
+/* Test edge cases and boundary conditions */
+static void hfsplus_compare_dentry_edge_cases_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr name;
+       char *long_str;
+       char *long_str2;
+       u32 str_size = HFSPLUS_MAX_STRLEN + 1;
+       struct qstr null_name = {
+               .name = "a\0b",
+               .len = 3,
+               .hash = 0
+       };
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       long_str = kzalloc(str_size, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, long_str);
+
+       long_str2 = kzalloc(str_size, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, long_str2);
+
+       /* Test very long strings */
+       memset(long_str, 'a', str_size - 1);
+       long_str[str_size - 1] = '\0';
+
+       create_qstr(&name, long_str);
+       result = hfsplus_compare_dentry(&test_dentry, str_size - 1,
+                                       long_str, &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test with difference at the end of long strings */
+       memset(long_str2, 'a', str_size - 1);
+       long_str2[str_size - 1] = '\0';
+       long_str2[str_size - 2] = 'b';
+       create_qstr(&name, long_str2);
+       result = hfsplus_compare_dentry(&test_dentry, str_size - 1,
+                                       long_str, &name);
+       KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */
+
+       /* Test single character differences */
+       create_qstr(&name, "b");
+       result = hfsplus_compare_dentry(&test_dentry, 1, "a", &name);
+       KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */
+
+       create_qstr(&name, "a");
+       result = hfsplus_compare_dentry(&test_dentry, 1, "b", &name);
+       KUNIT_EXPECT_GT(test, result, 0); /* 'b' > 'a' */
+
+       /* Test with null characters in the middle */
+       result = hfsplus_compare_dentry(&test_dentry, 3, "a\0b", &null_name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test all printable ASCII characters */
+       create_qstr(&name, "!@#$%^&*()");
+       result = hfsplus_compare_dentry(&test_dentry, 10, "!@#$%^&*()", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       kfree(long_str);
+       kfree(long_str2);
+       free_mock_sb(mock_sb);
+}
+
+/* Test combined flag behaviors */
+static void hfsplus_compare_dentry_combined_flags_test(struct kunit *test)
+{
+       struct test_mock_sb *mock_sb;
+       struct qstr name;
+       int result;
+
+       mock_sb = setup_mock_sb();
+       KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+       setup_mock_dentry(&mock_sb->sb);
+       mock_sb->nls.char2uni = test_char2uni;
+
+       /* Test with both casefold and decompose enabled */
+       set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+       set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+       create_qstr(&name, "hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test special chars with case folding */
+       create_qstr(&name, "File/Name");
+       result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       /* Test with both flags disabled */
+       clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+       clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+       create_qstr(&name, "hello");
+       result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name);
+       KUNIT_EXPECT_NE(test, 0, result); /* Case sensitive */
+
+       /* But special chars should still be converted */
+       create_qstr(&name, "file/name");
+       result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
+       KUNIT_EXPECT_EQ(test, 0, result);
+
+       free_mock_sb(mock_sb);
+}
+
+static struct kunit_case hfsplus_unicode_test_cases[] = {
+       KUNIT_CASE(hfsplus_strcasecmp_test),
+       KUNIT_CASE(hfsplus_strcmp_test),
+       KUNIT_CASE(hfsplus_unicode_edge_cases_test),
+       KUNIT_CASE(hfsplus_unicode_boundary_test),
+       KUNIT_CASE(hfsplus_uni2asc_basic_test),
+       KUNIT_CASE(hfsplus_uni2asc_special_chars_test),
+       KUNIT_CASE(hfsplus_uni2asc_buffer_test),
+       KUNIT_CASE(hfsplus_uni2asc_corrupted_test),
+       KUNIT_CASE(hfsplus_uni2asc_edge_cases_test),
+       KUNIT_CASE(hfsplus_asc2uni_basic_test),
+       KUNIT_CASE(hfsplus_asc2uni_special_chars_test),
+       KUNIT_CASE(hfsplus_asc2uni_buffer_limits_test),
+       KUNIT_CASE(hfsplus_asc2uni_edge_cases_test),
+       KUNIT_CASE(hfsplus_asc2uni_decompose_test),
+       KUNIT_CASE(hfsplus_hash_dentry_basic_test),
+       KUNIT_CASE(hfsplus_hash_dentry_casefold_test),
+       KUNIT_CASE(hfsplus_hash_dentry_special_chars_test),
+       KUNIT_CASE(hfsplus_hash_dentry_decompose_test),
+       KUNIT_CASE(hfsplus_hash_dentry_consistency_test),
+       KUNIT_CASE(hfsplus_hash_dentry_edge_cases_test),
+       KUNIT_CASE(hfsplus_compare_dentry_basic_test),
+       KUNIT_CASE(hfsplus_compare_dentry_casefold_test),
+       KUNIT_CASE(hfsplus_compare_dentry_special_chars_test),
+       KUNIT_CASE(hfsplus_compare_dentry_length_test),
+       KUNIT_CASE(hfsplus_compare_dentry_decompose_test),
+       KUNIT_CASE(hfsplus_compare_dentry_edge_cases_test),
+       KUNIT_CASE(hfsplus_compare_dentry_combined_flags_test),
+       {}
+};
+
+static struct kunit_suite hfsplus_unicode_test_suite = {
+       .name = "hfsplus_unicode",
+       .test_cases = hfsplus_unicode_test_cases,
+};
+
+kunit_test_suite(hfsplus_unicode_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for HFS+ Unicode string operations");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");