// 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_obj(struct test_mock_string_env); 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){ 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_obj(struct test_mock_sb); 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);Lines 1–500 of 1595. Load more
// 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_obj(struct test_mock_string_env); 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) { 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_obj(struct test_mock_sb); 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);