On Fri, Sep 12, 2025 at 9:10 AM Viacheslav Dubeyko <slava@xxxxxxxxxxx> wrote: > > This patch implements the initial Kunit based set of > unit tests for HFS string operations. It checks > functionality of hfs_strcmp(), hfs_hash_dentry(), > and hfs_compare_dentry() methods. > > ./tools/testing/kunit/kunit.py run --kunitconfig ./fs/hfs/.kunitconfig > > [16:04:50] Configuring KUnit Kernel ... > Regenerating .config ... > Populating config with: > $ make ARCH=um O=.kunit olddefconfig > [16:04:51] 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 > [16:04:59] Starting KUnit Kernel (1/1)... > [16:04:59] ============================================================ > Running tests with: > $ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt > [16:04:59] ================= hfs_string (3 subtests) ================== > [16:04:59] [PASSED] hfs_strcmp_test > [16:04:59] [PASSED] hfs_hash_dentry_test > [16:04:59] [PASSED] hfs_compare_dentry_test > [16:04:59] =================== [PASSED] hfs_string ==================== > [16:04:59] ============================================================ > [16:04:59] Testing complete. Ran 3 tests: passed: 3 > [16:04:59] Elapsed time: 9.087s total, 1.310s configuring, 7.611s building, 0.125s running > > v2 > Fix linker error. > > Signed-off-by: Viacheslav Dubeyko <slava@xxxxxxxxxxx> > cc: John Paul Adrian Glaubitz <glaubitz@xxxxxxxxxxxxxxxxxxx> > cc: Yangtao Li <frank.li@xxxxxxxx> > cc: linux-fsdevel@xxxxxxxxxxxxxxx > --- > fs/hfs/.kunitconfig | 7 +++ > fs/hfs/Kconfig | 15 +++++ > fs/hfs/Makefile | 2 + > fs/hfs/string.c | 3 + > fs/hfs/string_test.c | 132 +++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 159 insertions(+) > create mode 100644 fs/hfs/.kunitconfig > create mode 100644 fs/hfs/string_test.c > > diff --git a/fs/hfs/.kunitconfig b/fs/hfs/.kunitconfig > new file mode 100644 > index 000000000000..5caa9af1e3bb > --- /dev/null > +++ b/fs/hfs/.kunitconfig > @@ -0,0 +1,7 @@ > +CONFIG_KUNIT=y > +CONFIG_HFS_FS=y > +CONFIG_HFS_KUNIT_TEST=y > +CONFIG_BLOCK=y > +CONFIG_BUFFER_HEAD=y > +CONFIG_NLS=y > +CONFIG_LEGACY_DIRECT_IO=y > diff --git a/fs/hfs/Kconfig b/fs/hfs/Kconfig > index 5ea5cd8ecea9..7f3cbe43b4b7 100644 > --- a/fs/hfs/Kconfig > +++ b/fs/hfs/Kconfig > @@ -13,3 +13,18 @@ config HFS_FS > > To compile this file system support as a module, choose M here: the > module will be called hfs. > + > +config HFS_KUNIT_TEST > + tristate "KUnit tests for HFS filesystem" if !KUNIT_ALL_TESTS > + depends on HFS_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/. > diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile > index b65459bf3dc4..a7c9ce6b4609 100644 > --- a/fs/hfs/Makefile > +++ b/fs/hfs/Makefile > @@ -9,3 +9,5 @@ hfs-objs := bitmap.o bfind.o bnode.o brec.o btree.o \ > catalog.o dir.o extent.o inode.o attr.o mdb.o \ > part_tbl.o string.o super.o sysdep.o trans.o > > +# KUnit tests > +obj-$(CONFIG_HFS_KUNIT_TEST) += string_test.o > diff --git a/fs/hfs/string.c b/fs/hfs/string.c > index 3912209153a8..b011c1cbdf94 100644 > --- a/fs/hfs/string.c > +++ b/fs/hfs/string.c > @@ -65,6 +65,7 @@ int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this) > this->hash = end_name_hash(hash); > return 0; > } > +EXPORT_SYMBOL_GPL(hfs_hash_dentry); It seems we should use EXPORT_SYMBOL_IF_KUNIT here? See https://docs.kernel.org/dev-tools/kunit/usage.html#testing-static-functions Thanks, Chen Linxuan > > /* > * Compare two strings in the HFS filename character ordering > @@ -87,6 +88,7 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1, > } > return len1 - len2; > } > +EXPORT_SYMBOL_GPL(hfs_strcmp); > > /* > * Test for equality of two strings in the HFS filename character ordering. > @@ -112,3 +114,4 @@ int hfs_compare_dentry(const struct dentry *dentry, > } > return 0; > } > +EXPORT_SYMBOL_GPL(hfs_compare_dentry); > diff --git a/fs/hfs/string_test.c b/fs/hfs/string_test.c > new file mode 100644 > index 000000000000..de1928dc4ef4 > --- /dev/null > +++ b/fs/hfs/string_test.c > @@ -0,0 +1,132 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * KUnit tests for HFS string operations > + * > + * Copyright (C) 2025 Viacheslav Dubeyko <slava@xxxxxxxxxxx> > + */ > + > +#include <kunit/test.h> > +#include <linux/dcache.h> > +#include "hfs_fs.h" > + > +/* Test hfs_strcmp function */ > +static void hfs_strcmp_test(struct kunit *test) > +{ > + /* Test equal strings */ > + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "hello", 5)); > + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("test", 4, "test", 4)); > + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("", 0, "", 0)); > + > + /* Test unequal strings */ > + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("hello", 5, "world", 5)); > + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("test", 4, "testing", 7)); > + > + /* Test different lengths */ > + KUNIT_EXPECT_LT(test, hfs_strcmp("test", 4, "testing", 7), 0); > + KUNIT_EXPECT_GT(test, hfs_strcmp("testing", 7, "test", 4), 0); > + > + /* Test case insensitive comparison (HFS should handle case) */ > + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("Test", 4, "TEST", 4)); > + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "HELLO", 5)); > + > + /* Test with special characters */ > + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("file.txt", 8, "file.txt", 8)); > + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("file.txt", 8, "file.dat", 8)); > + > + /* Test boundary cases */ > + KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("a", 1, "a", 1)); > + KUNIT_EXPECT_NE(test, 0, hfs_strcmp("a", 1, "b", 1)); > +} > + > +/* Test hfs_hash_dentry function */ > +static void hfs_hash_dentry_test(struct kunit *test) > +{ > + struct qstr test_name1, test_name2, test_name3; > + struct dentry dentry = {}; > + char name1[] = "testfile"; > + char name2[] = "TestFile"; > + char name3[] = "different"; > + > + /* Initialize test strings */ > + test_name1.name = name1; > + test_name1.len = strlen(name1); > + test_name1.hash = 0; > + > + test_name2.name = name2; > + test_name2.len = strlen(name2); > + test_name2.hash = 0; > + > + test_name3.name = name3; > + test_name3.len = strlen(name3); > + test_name3.hash = 0; > + > + /* Test hashing */ > + KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name1)); > + KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name2)); > + KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name3)); > + > + /* Case insensitive names should hash the same */ > + KUNIT_EXPECT_EQ(test, test_name1.hash, test_name2.hash); > + > + /* Different names should have different hashes */ > + KUNIT_EXPECT_NE(test, test_name1.hash, test_name3.hash); > +} > + > +/* Test hfs_compare_dentry function */ > +static void hfs_compare_dentry_test(struct kunit *test) > +{ > + struct qstr test_name; > + struct dentry dentry = {}; > + char name[] = "TestFile"; > + > + test_name.name = name; > + test_name.len = strlen(name); > + > + /* Test exact match */ > + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8, > + "TestFile", &test_name)); > + > + /* Test case insensitive match */ > + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8, > + "testfile", &test_name)); > + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8, > + "TESTFILE", &test_name)); > + > + /* Test different names */ > + KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 8, > + "DiffFile", &test_name)); > + > + /* Test different lengths */ > + KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 7, > + "TestFil", &test_name)); > + KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 9, > + "TestFiles", &test_name)); > + > + /* Test empty string */ > + test_name.name = ""; > + test_name.len = 0; > + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 0, "", &test_name)); > + > + /* Test HFS_NAMELEN boundary */ > + test_name.name = "This_is_a_very_long_filename_that_exceeds_normal_limits"; > + test_name.len = strlen(test_name.name); > + KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, HFS_NAMELEN, > + "This_is_a_very_long_filename_th", &test_name)); > +} > + > +static struct kunit_case hfs_string_test_cases[] = { > + KUNIT_CASE(hfs_strcmp_test), > + KUNIT_CASE(hfs_hash_dentry_test), > + KUNIT_CASE(hfs_compare_dentry_test), > + {} > +}; > + > +static struct kunit_suite hfs_string_test_suite = { > + .name = "hfs_string", > + .test_cases = hfs_string_test_cases, > +}; > + > +kunit_test_suite(hfs_string_test_suite); > + > +MODULE_DESCRIPTION("KUnit tests for HFS string operations"); > +MODULE_LICENSE("GPL"); > -- > 2.43.0 > > >