File Index
Files and directories
Sign in
FILE

unicode_test.c

File preview with syntax highlighting.

linux-6.19.14/fs/hfsplus/unicode_test.c C
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * KUnit tests for HFS+ Unicode string operations
   4 *
   5 * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com>
   6 */
   7
   8#include <kunit/test.h>
   9#include <linux/nls.h>
  10#include <linux/dcache.h>
  11#include <linux/stringhash.h>
  12#include "hfsplus_fs.h"
  13
  14struct test_mock_string_env {
  15	struct hfsplus_unistr str1;
  16	struct hfsplus_unistr str2;
  17	char *buf;
  18	u32 buf_size;
  19};
  20
  21static struct test_mock_string_env *setup_mock_str_env(u32 buf_size)
  22{
  23	struct test_mock_string_env *env;
  24
  25	env = kzalloc(sizeof(struct test_mock_string_env), GFP_KERNEL);
  26	if (!env)
  27		return NULL;
  28
  29	env->buf = kzalloc(buf_size, GFP_KERNEL);
  30	if (!env->buf) {
  31		kfree(env);
  32		return NULL;
  33	}
  34
  35	env->buf_size = buf_size;
  36
  37	return env;
  38}
  39
  40static void free_mock_str_env(struct test_mock_string_env *env)
  41{
  42	if (env->buf)
  43		kfree(env->buf);
  44	kfree(env);
  45}
  46
  47/* Helper function to create hfsplus_unistr */
  48static void create_unistr(struct hfsplus_unistr *ustr, const char *ascii_str)
  49{
  50	int len = strlen(ascii_str);
  51	int i;
  52
  53	memset(ustr->unicode, 0, sizeof(ustr->unicode));
  54
  55	ustr->length = cpu_to_be16(len);
  56	for (i = 0; i < len && i < HFSPLUS_MAX_STRLEN; i++)
  57		ustr->unicode[i] = cpu_to_be16((u16)ascii_str[i]);
  58}
  59
  60static void corrupt_unistr(struct hfsplus_unistr *ustr)
  61{
  62	ustr->length = cpu_to_be16(U16_MAX);
  63}
  64
  65/* Test hfsplus_strcasecmp function */
  66static void hfsplus_strcasecmp_test(struct kunit *test)
  67{
  68	struct test_mock_string_env *mock_env;
  69
  70	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
  71	KUNIT_ASSERT_NOT_NULL(test, mock_env);
  72
  73	/* Test identical strings */
  74	create_unistr(&mock_env->str1, "hello");
  75	create_unistr(&mock_env->str2, "hello");
  76	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
  77						    &mock_env->str2));
  78
  79	/* Test case insensitive comparison */
  80	create_unistr(&mock_env->str1, "Hello");
  81	create_unistr(&mock_env->str2, "hello");
  82	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
  83						    &mock_env->str2));
  84
  85	create_unistr(&mock_env->str1, "HELLO");
  86	create_unistr(&mock_env->str2, "hello");
  87	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
  88						    &mock_env->str2));
  89
  90	/* Test different strings */
  91	create_unistr(&mock_env->str1, "apple");
  92	create_unistr(&mock_env->str2, "banana");
  93	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
  94						 &mock_env->str2), 0);
  95
  96	create_unistr(&mock_env->str1, "zebra");
  97	create_unistr(&mock_env->str2, "apple");
  98	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
  99						 &mock_env->str2), 0);
 100
 101	/* Test different lengths */
 102	create_unistr(&mock_env->str1, "test");
 103	create_unistr(&mock_env->str2, "testing");
 104	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
 105						 &mock_env->str2), 0);
 106
 107	create_unistr(&mock_env->str1, "testing");
 108	create_unistr(&mock_env->str2, "test");
 109	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
 110						 &mock_env->str2), 0);
 111
 112	/* Test empty strings */
 113	create_unistr(&mock_env->str1, "");
 114	create_unistr(&mock_env->str2, "");
 115	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
 116						    &mock_env->str2));
 117
 118	create_unistr(&mock_env->str1, "");
 119	create_unistr(&mock_env->str2, "test");
 120	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
 121						 &mock_env->str2), 0);
 122
 123	/* Test single characters */
 124	create_unistr(&mock_env->str1, "A");
 125	create_unistr(&mock_env->str2, "a");
 126	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
 127						    &mock_env->str2));
 128
 129	create_unistr(&mock_env->str1, "A");
 130	create_unistr(&mock_env->str2, "B");
 131	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
 132						 &mock_env->str2), 0);
 133
 134	/* Test maximum length strings */
 135	memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
 136	mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';
 137	create_unistr(&mock_env->str1, mock_env->buf);
 138	create_unistr(&mock_env->str2, mock_env->buf);
 139	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
 140						    &mock_env->str2));
 141
 142	/* Change one character in the middle */
 143	mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';
 144	create_unistr(&mock_env->str2, mock_env->buf);
 145	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
 146						 &mock_env->str2), 0);
 147
 148	/* Test corrupted strings */
 149	create_unistr(&mock_env->str1, "");
 150	corrupt_unistr(&mock_env->str1);
 151	create_unistr(&mock_env->str2, "");
 152	KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,
 153						    &mock_env->str2));
 154
 155	create_unistr(&mock_env->str1, "");
 156	create_unistr(&mock_env->str2, "");
 157	corrupt_unistr(&mock_env->str2);
 158	KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,
 159						    &mock_env->str2));
 160
 161	create_unistr(&mock_env->str1, "test");
 162	corrupt_unistr(&mock_env->str1);
 163	create_unistr(&mock_env->str2, "testing");
 164	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
 165						 &mock_env->str2), 0);
 166
 167	create_unistr(&mock_env->str1, "test");
 168	create_unistr(&mock_env->str2, "testing");
 169	corrupt_unistr(&mock_env->str2);
 170	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
 171						 &mock_env->str2), 0);
 172
 173	create_unistr(&mock_env->str1, "testing");
 174	corrupt_unistr(&mock_env->str1);
 175	create_unistr(&mock_env->str2, "test");
 176	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
 177						 &mock_env->str2), 0);
 178
 179	create_unistr(&mock_env->str1, "testing");
 180	create_unistr(&mock_env->str2, "test");
 181	corrupt_unistr(&mock_env->str2);
 182	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
 183						 &mock_env->str2), 0);
 184
 185	free_mock_str_env(mock_env);
 186}
 187
 188/* Test hfsplus_strcmp function (case-sensitive) */
 189static void hfsplus_strcmp_test(struct kunit *test)
 190{
 191	struct test_mock_string_env *mock_env;
 192
 193	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 194	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 195
 196	/* Test identical strings */
 197	create_unistr(&mock_env->str1, "hello");
 198	create_unistr(&mock_env->str2, "hello");
 199	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
 200						&mock_env->str2));
 201
 202	/* Test case sensitive comparison - should NOT be equal */
 203	create_unistr(&mock_env->str1, "Hello");
 204	create_unistr(&mock_env->str2, "hello");
 205	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
 206						&mock_env->str2));
 207	 /* 'H' < 'h' in Unicode */
 208	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
 209					     &mock_env->str2), 0);
 210
 211	/* Test lexicographic ordering */
 212	create_unistr(&mock_env->str1, "apple");
 213	create_unistr(&mock_env->str2, "banana");
 214	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
 215					     &mock_env->str2), 0);
 216
 217	create_unistr(&mock_env->str1, "zebra");
 218	create_unistr(&mock_env->str2, "apple");
 219	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
 220					     &mock_env->str2), 0);
 221
 222	/* Test different lengths with common prefix */
 223	create_unistr(&mock_env->str1, "test");
 224	create_unistr(&mock_env->str2, "testing");
 225	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
 226					     &mock_env->str2), 0);
 227
 228	create_unistr(&mock_env->str1, "testing");
 229	create_unistr(&mock_env->str2, "test");
 230	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
 231					     &mock_env->str2), 0);
 232
 233	/* Test empty strings */
 234	create_unistr(&mock_env->str1, "");
 235	create_unistr(&mock_env->str2, "");
 236	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
 237						&mock_env->str2));
 238
 239	/* Test maximum length strings */
 240	memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
 241	mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';
 242	create_unistr(&mock_env->str1, mock_env->buf);
 243	create_unistr(&mock_env->str2, mock_env->buf);
 244	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
 245						&mock_env->str2));
 246
 247	/* Change one character in the middle */
 248	mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';
 249	create_unistr(&mock_env->str2, mock_env->buf);
 250	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
 251					     &mock_env->str2), 0);
 252
 253	/* Test corrupted strings */
 254	create_unistr(&mock_env->str1, "");
 255	corrupt_unistr(&mock_env->str1);
 256	create_unistr(&mock_env->str2, "");
 257	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
 258						&mock_env->str2));
 259
 260	create_unistr(&mock_env->str1, "");
 261	create_unistr(&mock_env->str2, "");
 262	corrupt_unistr(&mock_env->str2);
 263	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
 264						&mock_env->str2));
 265
 266	create_unistr(&mock_env->str1, "test");
 267	corrupt_unistr(&mock_env->str1);
 268	create_unistr(&mock_env->str2, "testing");
 269	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
 270					     &mock_env->str2), 0);
 271
 272	create_unistr(&mock_env->str1, "test");
 273	create_unistr(&mock_env->str2, "testing");
 274	corrupt_unistr(&mock_env->str2);
 275	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
 276					     &mock_env->str2), 0);
 277
 278	create_unistr(&mock_env->str1, "testing");
 279	corrupt_unistr(&mock_env->str1);
 280	create_unistr(&mock_env->str2, "test");
 281	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
 282					     &mock_env->str2), 0);
 283
 284	create_unistr(&mock_env->str1, "testing");
 285	create_unistr(&mock_env->str2, "test");
 286	corrupt_unistr(&mock_env->str2);
 287	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
 288					     &mock_env->str2), 0);
 289
 290	free_mock_str_env(mock_env);
 291}
 292
 293/* Test Unicode edge cases */
 294static void hfsplus_unicode_edge_cases_test(struct kunit *test)
 295{
 296	struct test_mock_string_env *mock_env;
 297
 298	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 299	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 300
 301	/* Test with special characters */
 302	mock_env->str1.length = cpu_to_be16(3);
 303	mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */
 304	mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */
 305	mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */
 306
 307	mock_env->str2.length = cpu_to_be16(3);
 308	mock_env->str2.unicode[0] = cpu_to_be16(0x00E9); /* é */
 309	mock_env->str2.unicode[1] = cpu_to_be16(0x00F1); /* ñ */
 310	mock_env->str2.unicode[2] = cpu_to_be16(0x00FC); /* ü */
 311
 312	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
 313						&mock_env->str2));
 314	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
 315						    &mock_env->str2));
 316
 317	/* Test with different special characters */
 318	mock_env->str2.unicode[1] = cpu_to_be16(0x00F2); /* ò */
 319	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
 320						&mock_env->str2));
 321
 322	/* Test null characters within string (should be handled correctly) */
 323	mock_env->str1.length = cpu_to_be16(3);
 324	mock_env->str1.unicode[0] = cpu_to_be16('a');
 325	mock_env->str1.unicode[1] = cpu_to_be16(0x0000); /* null */
 326	mock_env->str1.unicode[2] = cpu_to_be16('b');
 327
 328	mock_env->str2.length = cpu_to_be16(3);
 329	mock_env->str2.unicode[0] = cpu_to_be16('a');
 330	mock_env->str2.unicode[1] = cpu_to_be16(0x0000); /* null */
 331	mock_env->str2.unicode[2] = cpu_to_be16('b');
 332
 333	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
 334						&mock_env->str2));
 335
 336	free_mock_str_env(mock_env);
 337}
 338
 339/* Test boundary conditions */
 340static void hfsplus_unicode_boundary_test(struct kunit *test)
 341{
 342	struct test_mock_string_env *mock_env;
 343	int i;
 344
 345	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 346	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 347
 348	/* Test maximum length boundary */
 349	mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
 350	mock_env->str2.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
 351
 352	for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) {
 353		mock_env->str1.unicode[i] = cpu_to_be16('A');
 354		mock_env->str2.unicode[i] = cpu_to_be16('A');
 355	}
 356
 357	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
 358						&mock_env->str2));
 359
 360	/* Change last character */
 361	mock_env->str2.unicode[HFSPLUS_MAX_STRLEN - 1] = cpu_to_be16('B');
 362	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
 363					     &mock_env->str2), 0);
 364
 365	/* Test zero length strings */
 366	mock_env->str1.length = cpu_to_be16(0);
 367	mock_env->str2.length = cpu_to_be16(0);
 368	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
 369						&mock_env->str2));
 370	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
 371						    &mock_env->str2));
 372
 373	/* Test one character vs empty */
 374	mock_env->str1.length = cpu_to_be16(1);
 375	mock_env->str1.unicode[0] = cpu_to_be16('A');
 376	mock_env->str2.length = cpu_to_be16(0);
 377	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
 378					     &mock_env->str2), 0);
 379	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
 380						 &mock_env->str2), 0);
 381
 382	free_mock_str_env(mock_env);
 383}
 384
 385/* Mock superblock and NLS table for testing hfsplus_uni2asc */
 386struct test_mock_sb {
 387	struct nls_table nls;
 388	struct hfsplus_sb_info sb_info;
 389	struct super_block sb;
 390};
 391
 392static struct test_mock_sb *setup_mock_sb(void)
 393{
 394	struct test_mock_sb *ptr;
 395
 396	ptr = kzalloc(sizeof(struct test_mock_sb), GFP_KERNEL);
 397	if (!ptr)
 398		return NULL;
 399
 400	ptr->nls.charset = "utf8";
 401	ptr->nls.uni2char = NULL; /* Will use default behavior */
 402	ptr->sb_info.nls = &ptr->nls;
 403	ptr->sb.s_fs_info = &ptr->sb_info;
 404
 405	/* Set default flags - no decomposition, no case folding */
 406	clear_bit(HFSPLUS_SB_NODECOMPOSE, &ptr->sb_info.flags);
 407	clear_bit(HFSPLUS_SB_CASEFOLD, &ptr->sb_info.flags);
 408
 409	return ptr;
 410}
 411
 412static void free_mock_sb(struct test_mock_sb *ptr)
 413{
 414	kfree(ptr);
 415}
 416
 417/* Simple uni2char implementation for testing */
 418static int test_uni2char(wchar_t uni, unsigned char *out, int boundlen)
 419{
 420	if (boundlen <= 0)
 421		return -ENAMETOOLONG;
 422
 423	if (uni < 0x80) {
 424		*out = (unsigned char)uni;
 425		return 1;
 426	}
 427
 428	/* For non-ASCII, just use '?' as fallback */
 429	*out = '?';
 430	return 1;
 431}
 432
 433/* Test hfsplus_uni2asc basic functionality */
 434static void hfsplus_uni2asc_basic_test(struct kunit *test)
 435{
 436	struct test_mock_sb *mock_sb;
 437	struct test_mock_string_env *mock_env;
 438	int len, result;
 439
 440	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 441	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 442
 443	mock_sb = setup_mock_sb();
 444	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 445
 446	mock_sb->nls.uni2char = test_uni2char;
 447
 448	/* Test simple ASCII string conversion */
 449	create_unistr(&mock_env->str1, "hello");
 450	len = mock_env->buf_size;
 451	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 452				     mock_env->buf, &len);
 453
 454	KUNIT_EXPECT_EQ(test, 0, result);
 455	KUNIT_EXPECT_EQ(test, 5, len);
 456	KUNIT_EXPECT_STREQ(test, "hello", mock_env->buf);
 457
 458	/* Test empty string */
 459	create_unistr(&mock_env->str1, "");
 460	len = mock_env->buf_size;
 461	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 462				     mock_env->buf, &len);
 463
 464	KUNIT_EXPECT_EQ(test, 0, result);
 465	KUNIT_EXPECT_EQ(test, 0, len);
 466
 467	/* Test single character */
 468	create_unistr(&mock_env->str1, "A");
 469	len = mock_env->buf_size;
 470	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 471				     mock_env->buf, &len);
 472
 473	KUNIT_EXPECT_EQ(test, 0, result);
 474	KUNIT_EXPECT_EQ(test, 1, len);
 475	KUNIT_EXPECT_EQ(test, 'A', mock_env->buf[0]);
 476
 477	free_mock_str_env(mock_env);
 478	free_mock_sb(mock_sb);
 479}
 480
 481/* Test special character handling */
 482static void hfsplus_uni2asc_special_chars_test(struct kunit *test)
 483{
 484	struct test_mock_sb *mock_sb;
 485	struct test_mock_string_env *mock_env;
 486	int len, result;
 487
 488	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 489	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 490
 491	mock_sb = setup_mock_sb();
 492	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 493
 494	mock_sb->nls.uni2char = test_uni2char;
 495
 496	/* Test null character conversion (should become 0x2400) */
 497	mock_env->str1.length = cpu_to_be16(1);
 498	mock_env->str1.unicode[0] = cpu_to_be16(0x0000);
 499	len = mock_env->buf_size;
 500	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 501				     mock_env->buf, &len);
 502
 503	KUNIT_EXPECT_EQ(test, 0, result);
 504	KUNIT_EXPECT_EQ(test, 1, len);
 505	/* Our test implementation returns '?' for non-ASCII */
 506	KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]);
 507
 508	/* Test forward slash conversion (should become colon) */
 509	mock_env->str1.length = cpu_to_be16(1);
 510	mock_env->str1.unicode[0] = cpu_to_be16('/');
 511	len = mock_env->buf_size;
 512	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 513				     mock_env->buf, &len);
 514
 515	KUNIT_EXPECT_EQ(test, 0, result);
 516	KUNIT_EXPECT_EQ(test, 1, len);
 517	KUNIT_EXPECT_EQ(test, ':', mock_env->buf[0]);
 518
 519	/* Test string with mixed special characters */
 520	mock_env->str1.length = cpu_to_be16(3);
 521	mock_env->str1.unicode[0] = cpu_to_be16('a');
 522	mock_env->str1.unicode[1] = cpu_to_be16('/');
 523	mock_env->str1.unicode[2] = cpu_to_be16('b');
 524	len = mock_env->buf_size;
 525	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 526				     mock_env->buf, &len);
 527
 528	KUNIT_EXPECT_EQ(test, 0, result);
 529	KUNIT_EXPECT_EQ(test, 3, len);
 530	KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[0]);
 531	KUNIT_EXPECT_EQ(test, ':', mock_env->buf[1]);
 532	KUNIT_EXPECT_EQ(test, 'b', mock_env->buf[2]);
 533
 534	free_mock_str_env(mock_env);
 535	free_mock_sb(mock_sb);
 536}
 537
 538/* Test buffer length handling */
 539static void hfsplus_uni2asc_buffer_test(struct kunit *test)
 540{
 541	struct test_mock_sb *mock_sb;
 542	struct test_mock_string_env *mock_env;
 543	int len, result;
 544
 545	mock_env = setup_mock_str_env(10);
 546	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 547
 548	mock_sb = setup_mock_sb();
 549	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 550
 551	mock_sb->nls.uni2char = test_uni2char;
 552
 553	/* Test insufficient buffer space */
 554	create_unistr(&mock_env->str1, "toolongstring");
 555	len = 5; /* Buffer too small */
 556	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 557				     mock_env->buf, &len);
 558
 559	KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
 560	KUNIT_EXPECT_EQ(test, 5, len); /* Should be set to consumed length */
 561
 562	/* Test exact buffer size */
 563	create_unistr(&mock_env->str1, "exact");
 564	len = 5;
 565	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 566				     mock_env->buf, &len);
 567
 568	KUNIT_EXPECT_EQ(test, 0, result);
 569	KUNIT_EXPECT_EQ(test, 5, len);
 570
 571	/* Test zero length buffer */
 572	create_unistr(&mock_env->str1, "test");
 573	len = 0;
 574	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 575				     mock_env->buf, &len);
 576
 577	KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
 578	KUNIT_EXPECT_EQ(test, 0, len);
 579
 580	free_mock_str_env(mock_env);
 581	free_mock_sb(mock_sb);
 582}
 583
 584/* Test corrupted unicode string handling */
 585static void hfsplus_uni2asc_corrupted_test(struct kunit *test)
 586{
 587	struct test_mock_sb *mock_sb;
 588	struct test_mock_string_env *mock_env;
 589	int len, result;
 590
 591	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 592	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 593
 594	mock_sb = setup_mock_sb();
 595	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 596
 597	mock_sb->nls.uni2char = test_uni2char;
 598
 599	/* Test corrupted length (too large) */
 600	create_unistr(&mock_env->str1, "test");
 601	corrupt_unistr(&mock_env->str1); /* Sets length to U16_MAX */
 602	len = mock_env->buf_size;
 603
 604	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 605				     mock_env->buf, &len);
 606
 607	/* Should still work but with corrected length */
 608	KUNIT_EXPECT_EQ(test, 0, result);
 609	/*
 610	 * Length should be corrected to HFSPLUS_MAX_STRLEN
 611	 * and processed accordingly
 612	 */
 613	KUNIT_EXPECT_GT(test, len, 0);
 614
 615	free_mock_str_env(mock_env);
 616	free_mock_sb(mock_sb);
 617}
 618
 619/* Test edge cases and boundary conditions */
 620static void hfsplus_uni2asc_edge_cases_test(struct kunit *test)
 621{
 622	struct test_mock_sb *mock_sb;
 623	struct test_mock_string_env *mock_env;
 624	int len, result;
 625	int i;
 626
 627	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN * 2);
 628	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 629
 630	mock_sb = setup_mock_sb();
 631	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 632
 633	mock_sb->nls.uni2char = test_uni2char;
 634
 635	/* Test maximum length string */
 636	mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
 637	for (i = 0; i < HFSPLUS_MAX_STRLEN; i++)
 638		mock_env->str1.unicode[i] = cpu_to_be16('a');
 639
 640	len = mock_env->buf_size;
 641	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 642				     mock_env->buf, &len);
 643
 644	KUNIT_EXPECT_EQ(test, 0, result);
 645	KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, len);
 646
 647	/* Verify all characters are 'a' */
 648	for (i = 0; i < HFSPLUS_MAX_STRLEN; i++)
 649		KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[i]);
 650
 651	/* Test string with high Unicode values (non-ASCII) */
 652	mock_env->str1.length = cpu_to_be16(3);
 653	mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */
 654	mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */
 655	mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */
 656	len = mock_env->buf_size;
 657	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
 658				     mock_env->buf, &len);
 659
 660	KUNIT_EXPECT_EQ(test, 0, result);
 661	KUNIT_EXPECT_EQ(test, 3, len);
 662	/* Our test implementation converts non-ASCII to '?' */
 663	KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]);
 664	KUNIT_EXPECT_EQ(test, '?', mock_env->buf[1]);
 665	KUNIT_EXPECT_EQ(test, '?', mock_env->buf[2]);
 666
 667	free_mock_str_env(mock_env);
 668	free_mock_sb(mock_sb);
 669}
 670
 671/* Simple char2uni implementation for testing */
 672static int test_char2uni(const unsigned char *rawstring,
 673			 int boundlen, wchar_t *uni)
 674{
 675	if (boundlen <= 0)
 676		return -EINVAL;
 677
 678	*uni = (wchar_t)*rawstring;
 679	return 1;
 680}
 681
 682/* Helper function to check unicode string contents */
 683static void check_unistr_content(struct kunit *test,
 684				 struct hfsplus_unistr *ustr,
 685				 const char *expected_ascii)
 686{
 687	int expected_len = strlen(expected_ascii);
 688	int actual_len = be16_to_cpu(ustr->length);
 689	int i;
 690
 691	KUNIT_EXPECT_EQ(test, expected_len, actual_len);
 692
 693	for (i = 0; i < expected_len && i < actual_len; i++) {
 694		u16 expected_char = (u16)expected_ascii[i];
 695		u16 actual_char = be16_to_cpu(ustr->unicode[i]);
 696
 697		KUNIT_EXPECT_EQ(test, expected_char, actual_char);
 698	}
 699}
 700
 701/* Test hfsplus_asc2uni basic functionality */
 702static void hfsplus_asc2uni_basic_test(struct kunit *test)
 703{
 704	struct test_mock_sb *mock_sb;
 705	struct test_mock_string_env *mock_env;
 706	int result;
 707
 708	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 709	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 710
 711	mock_sb = setup_mock_sb();
 712	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 713
 714	mock_sb->nls.char2uni = test_char2uni;
 715
 716	/* Test simple ASCII string conversion */
 717	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 718				 HFSPLUS_MAX_STRLEN, "hello", 5);
 719
 720	KUNIT_EXPECT_EQ(test, 0, result);
 721	check_unistr_content(test, &mock_env->str1, "hello");
 722
 723	/* Test empty string */
 724	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 725				 HFSPLUS_MAX_STRLEN, "", 0);
 726
 727	KUNIT_EXPECT_EQ(test, 0, result);
 728	KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
 729
 730	/* Test single character */
 731	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 732				 HFSPLUS_MAX_STRLEN, "A", 1);
 733
 734	KUNIT_EXPECT_EQ(test, 0, result);
 735	check_unistr_content(test, &mock_env->str1, "A");
 736
 737	/* Test null-terminated string with explicit length */
 738	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 739				 HFSPLUS_MAX_STRLEN, "test\0extra", 4);
 740
 741	KUNIT_EXPECT_EQ(test, 0, result);
 742	check_unistr_content(test, &mock_env->str1, "test");
 743
 744	free_mock_str_env(mock_env);
 745	free_mock_sb(mock_sb);
 746}
 747
 748/* Test special character handling in asc2uni */
 749static void hfsplus_asc2uni_special_chars_test(struct kunit *test)
 750{
 751	struct test_mock_sb *mock_sb;
 752	struct test_mock_string_env *mock_env;
 753	int result;
 754
 755	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 756	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 757
 758	mock_sb = setup_mock_sb();
 759	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 760
 761	mock_sb->nls.char2uni = test_char2uni;
 762
 763	/* Test colon conversion (should become forward slash) */
 764	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 765				 HFSPLUS_MAX_STRLEN, ":", 1);
 766
 767	KUNIT_EXPECT_EQ(test, 0, result);
 768	KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(mock_env->str1.length));
 769	KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0]));
 770
 771	/* Test string with mixed special characters */
 772	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 773				 HFSPLUS_MAX_STRLEN, "a:b", 3);
 774
 775	KUNIT_EXPECT_EQ(test, 0, result);
 776	KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
 777	KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(mock_env->str1.unicode[0]));
 778	KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1]));
 779	KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(mock_env->str1.unicode[2]));
 780
 781	/* Test multiple special characters */
 782	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 783				 HFSPLUS_MAX_STRLEN, ":::", 3);
 784
 785	KUNIT_EXPECT_EQ(test, 0, result);
 786	KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
 787	KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0]));
 788	KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1]));
 789	KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[2]));
 790
 791	free_mock_str_env(mock_env);
 792	free_mock_sb(mock_sb);
 793}
 794
 795/* Test buffer length limits */
 796static void hfsplus_asc2uni_buffer_limits_test(struct kunit *test)
 797{
 798	struct test_mock_sb *mock_sb;
 799	struct test_mock_string_env *mock_env;
 800	int result;
 801
 802	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 10);
 803	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 804
 805	mock_sb = setup_mock_sb();
 806	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 807
 808	mock_sb->nls.char2uni = test_char2uni;
 809
 810	/* Test exact maximum length */
 811	memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
 812	result = hfsplus_asc2uni(&mock_sb->sb,
 813				 &mock_env->str1, HFSPLUS_MAX_STRLEN,
 814				 mock_env->buf, HFSPLUS_MAX_STRLEN);
 815
 816	KUNIT_EXPECT_EQ(test, 0, result);
 817	KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
 818			be16_to_cpu(mock_env->str1.length));
 819
 820	/* Test exceeding maximum length */
 821	memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN + 5);
 822	result = hfsplus_asc2uni(&mock_sb->sb,
 823				 &mock_env->str1, HFSPLUS_MAX_STRLEN,
 824				 mock_env->buf, HFSPLUS_MAX_STRLEN + 5);
 825
 826	KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
 827	KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
 828			be16_to_cpu(mock_env->str1.length));
 829
 830	/* Test with smaller max_unistr_len */
 831	result = hfsplus_asc2uni(&mock_sb->sb,
 832				 &mock_env->str1, 5, "toolongstring", 13);
 833
 834	KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
 835	KUNIT_EXPECT_EQ(test, 5, be16_to_cpu(mock_env->str1.length));
 836
 837	/* Test zero max length */
 838	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4);
 839
 840	KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
 841	KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
 842
 843	free_mock_str_env(mock_env);
 844	free_mock_sb(mock_sb);
 845}
 846
 847/* Test error handling and edge cases */
 848static void hfsplus_asc2uni_edge_cases_test(struct kunit *test)
 849{
 850	struct test_mock_sb *mock_sb;
 851	struct hfsplus_unistr ustr;
 852	char test_str[] = {'a', '\0', 'b'};
 853	int result;
 854
 855	mock_sb = setup_mock_sb();
 856	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 857
 858	mock_sb->nls.char2uni = test_char2uni;
 859
 860	/* Test zero length input */
 861	result = hfsplus_asc2uni(&mock_sb->sb,
 862				 &ustr, HFSPLUS_MAX_STRLEN, "test", 0);
 863
 864	KUNIT_EXPECT_EQ(test, 0, result);
 865	KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.length));
 866
 867	/* Test input with length mismatch */
 868	result = hfsplus_asc2uni(&mock_sb->sb,
 869				 &ustr, HFSPLUS_MAX_STRLEN, "hello", 3);
 870
 871	KUNIT_EXPECT_EQ(test, 0, result);
 872	check_unistr_content(test, &ustr, "hel");
 873
 874	/* Test with various printable ASCII characters */
 875	result = hfsplus_asc2uni(&mock_sb->sb,
 876				 &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9);
 877
 878	KUNIT_EXPECT_EQ(test, 0, result);
 879	check_unistr_content(test, &ustr, "ABC123!@#");
 880
 881	/* Test null character in the middle */
 882	result = hfsplus_asc2uni(&mock_sb->sb,
 883				 &ustr, HFSPLUS_MAX_STRLEN, test_str, 3);
 884
 885	KUNIT_EXPECT_EQ(test, 0, result);
 886	KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length));
 887	KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(ustr.unicode[0]));
 888	KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.unicode[1]));
 889	KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(ustr.unicode[2]));
 890
 891	free_mock_sb(mock_sb);
 892}
 893
 894/* Test decomposition flag behavior */
 895static void hfsplus_asc2uni_decompose_test(struct kunit *test)
 896{
 897	struct test_mock_sb *mock_sb;
 898	struct test_mock_string_env *mock_env;
 899	int result;
 900
 901	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
 902	KUNIT_ASSERT_NOT_NULL(test, mock_env);
 903
 904	mock_sb = setup_mock_sb();
 905	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 906
 907	mock_sb->nls.char2uni = test_char2uni;
 908
 909	/* Test with decomposition disabled (default) */
 910	clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
 911	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
 912				 HFSPLUS_MAX_STRLEN, "test", 4);
 913
 914	KUNIT_EXPECT_EQ(test, 0, result);
 915	check_unistr_content(test, &mock_env->str1, "test");
 916
 917	/* Test with decomposition enabled */
 918	set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
 919	result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str2,
 920				 HFSPLUS_MAX_STRLEN, "test", 4);
 921
 922	KUNIT_EXPECT_EQ(test, 0, result);
 923	check_unistr_content(test, &mock_env->str2, "test");
 924
 925	/* For simple ASCII, both should produce the same result */
 926	KUNIT_EXPECT_EQ(test,
 927			be16_to_cpu(mock_env->str1.length),
 928			be16_to_cpu(mock_env->str2.length));
 929
 930	free_mock_str_env(mock_env);
 931	free_mock_sb(mock_sb);
 932}
 933
 934/* Mock dentry for testing hfsplus_hash_dentry */
 935static struct dentry test_dentry;
 936
 937static void setup_mock_dentry(struct super_block *sb)
 938{
 939	memset(&test_dentry, 0, sizeof(test_dentry));
 940	test_dentry.d_sb = sb;
 941}
 942
 943/* Helper function to create qstr */
 944static void create_qstr(struct qstr *str, const char *name)
 945{
 946	str->name = name;
 947	str->len = strlen(name);
 948	str->hash = 0; /* Will be set by hash function */
 949}
 950
 951/* Test hfsplus_hash_dentry basic functionality */
 952static void hfsplus_hash_dentry_basic_test(struct kunit *test)
 953{
 954	struct test_mock_sb *mock_sb;
 955	struct qstr str1, str2;
 956	int result;
 957
 958	mock_sb = setup_mock_sb();
 959	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
 960
 961	setup_mock_dentry(&mock_sb->sb);
 962	mock_sb->nls.char2uni = test_char2uni;
 963
 964	/* Test basic string hashing */
 965	create_qstr(&str1, "hello");
 966	result = hfsplus_hash_dentry(&test_dentry, &str1);
 967
 968	KUNIT_EXPECT_EQ(test, 0, result);
 969	KUNIT_EXPECT_NE(test, 0, str1.hash);
 970
 971	/* Test that identical strings produce identical hashes */
 972	create_qstr(&str2, "hello");
 973	result = hfsplus_hash_dentry(&test_dentry, &str2);
 974
 975	KUNIT_EXPECT_EQ(test, 0, result);
 976	KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
 977
 978	/* Test empty string */
 979	create_qstr(&str1, "");
 980	result = hfsplus_hash_dentry(&test_dentry, &str1);
 981
 982	/* Empty string should still produce a hash */
 983	KUNIT_EXPECT_EQ(test, 0, result);
 984
 985	/* Test single character */
 986	create_qstr(&str1, "A");
 987	result = hfsplus_hash_dentry(&test_dentry, &str1);
 988
 989	KUNIT_EXPECT_EQ(test, 0, result);
 990	KUNIT_EXPECT_NE(test, 0, str1.hash);
 991
 992	free_mock_sb(mock_sb);
 993}
 994
 995/* Test case folding behavior in hash */
 996static void hfsplus_hash_dentry_casefold_test(struct kunit *test)
 997{
 998	struct test_mock_sb *mock_sb;
 999	struct qstr str1, str2;
1000	int result;
1001
1002	mock_sb = setup_mock_sb();
1003	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1004
1005	setup_mock_dentry(&mock_sb->sb);
1006	mock_sb->nls.char2uni = test_char2uni;
1007
1008	/* Test with case folding disabled (default) */
1009	clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
1010
1011	create_qstr(&str1, "Hello");
1012	result = hfsplus_hash_dentry(&test_dentry, &str1);
1013	KUNIT_EXPECT_EQ(test, 0, result);
1014
1015	create_qstr(&str2, "hello");
1016	result = hfsplus_hash_dentry(&test_dentry, &str2);
1017	KUNIT_EXPECT_EQ(test, 0, result);
1018
1019	/*
1020	 * Without case folding, different cases
1021	 * should produce different hashes
1022	 */
1023	KUNIT_EXPECT_NE(test, str1.hash, str2.hash);
1024
1025	/* Test with case folding enabled */
1026	set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
1027
1028	create_qstr(&str1, "Hello");
1029	result = hfsplus_hash_dentry(&test_dentry, &str1);
1030	KUNIT_EXPECT_EQ(test, 0, result);
1031
1032	create_qstr(&str2, "hello");
1033	result = hfsplus_hash_dentry(&test_dentry, &str2);
1034	KUNIT_EXPECT_EQ(test, 0, result);
1035
1036	/* With case folding, different cases should produce same hash */
1037	KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
1038
1039	/* Test mixed case */
1040	create_qstr(&str1, "HeLLo");
1041	result = hfsplus_hash_dentry(&test_dentry, &str1);
1042	KUNIT_EXPECT_EQ(test, 0, result);
1043	KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
1044
1045	free_mock_sb(mock_sb);
1046}
1047
1048/* Test special character handling in hash */
1049static void hfsplus_hash_dentry_special_chars_test(struct kunit *test)
1050{
1051	struct test_mock_sb *mock_sb;
1052	struct qstr str1, str2;
1053	int result;
1054
1055	mock_sb = setup_mock_sb();
1056	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1057
1058	setup_mock_dentry(&mock_sb->sb);
1059	mock_sb->nls.char2uni = test_char2uni;
1060
1061	/* Test colon conversion (: becomes /) */
1062	create_qstr(&str1, "file:name");
1063	result = hfsplus_hash_dentry(&test_dentry, &str1);
1064	KUNIT_EXPECT_EQ(test, 0, result);
1065
1066	create_qstr(&str2, "file/name");
1067	result = hfsplus_hash_dentry(&test_dentry, &str2);
1068	KUNIT_EXPECT_EQ(test, 0, result);
1069
1070	/* After conversion, these should produce the same hash */
1071	KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
1072
1073	/* Test multiple special characters */
1074	create_qstr(&str1, ":::");
1075	result = hfsplus_hash_dentry(&test_dentry, &str1);
1076	KUNIT_EXPECT_EQ(test, 0, result);
1077
1078	create_qstr(&str2, "///");
1079	result = hfsplus_hash_dentry(&test_dentry, &str2);
1080	KUNIT_EXPECT_EQ(test, 0, result);
1081
1082	KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
1083
1084	free_mock_sb(mock_sb);
1085}
1086
1087/* Test decomposition flag behavior in hash */
1088static void hfsplus_hash_dentry_decompose_test(struct kunit *test)
1089{
1090	struct test_mock_sb *mock_sb;
1091	struct qstr str1, str2;
1092	int result;
1093
1094	mock_sb = setup_mock_sb();
1095	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1096
1097	setup_mock_dentry(&mock_sb->sb);
1098	mock_sb->nls.char2uni = test_char2uni;
1099
1100	/* Test with decomposition disabled (default) */
1101	clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
1102
1103	create_qstr(&str1, "test");
1104	result = hfsplus_hash_dentry(&test_dentry, &str1);
1105	KUNIT_EXPECT_EQ(test, 0, result);
1106
1107	/* Test with decomposition enabled */
1108	set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
1109
1110	create_qstr(&str2, "test");
1111	result = hfsplus_hash_dentry(&test_dentry, &str2);
1112	KUNIT_EXPECT_EQ(test, 0, result);
1113
1114	/*
1115	 * For simple ASCII, decomposition shouldn't change
1116	 * the hash much but the function should still work correctly
1117	 */
1118	KUNIT_EXPECT_NE(test, 0, str2.hash);
1119
1120	free_mock_sb(mock_sb);
1121}
1122
1123/* Test hash consistency and distribution */
1124static void hfsplus_hash_dentry_consistency_test(struct kunit *test)
1125{
1126	struct test_mock_sb *mock_sb;
1127	struct qstr str1, str2, str3;
1128	unsigned long hash1;
1129	int result;
1130
1131	mock_sb = setup_mock_sb();
1132	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1133
1134	setup_mock_dentry(&mock_sb->sb);
1135	mock_sb->nls.char2uni = test_char2uni;
1136
1137	/* Test that same string always produces same hash */
1138	create_qstr(&str1, "consistent");
1139	result = hfsplus_hash_dentry(&test_dentry, &str1);
1140	KUNIT_EXPECT_EQ(test, 0, result);
1141	hash1 = str1.hash;
1142
1143	create_qstr(&str2, "consistent");
1144	result = hfsplus_hash_dentry(&test_dentry, &str2);
1145	KUNIT_EXPECT_EQ(test, 0, result);
1146
1147	KUNIT_EXPECT_EQ(test, hash1, str2.hash);
1148
1149	/* Test that different strings produce different hashes */
1150	create_qstr(&str3, "different");
1151	result = hfsplus_hash_dentry(&test_dentry, &str3);
1152	KUNIT_EXPECT_EQ(test, 0, result);
1153
1154	KUNIT_EXPECT_NE(test, str1.hash, str3.hash);
1155
1156	/* Test similar strings should have different hashes */
1157	create_qstr(&str1, "file1");
1158	result = hfsplus_hash_dentry(&test_dentry, &str1);
1159	KUNIT_EXPECT_EQ(test, 0, result);
1160
1161	create_qstr(&str2, "file2");
1162	result = hfsplus_hash_dentry(&test_dentry, &str2);
1163	KUNIT_EXPECT_EQ(test, 0, result);
1164
1165	KUNIT_EXPECT_NE(test, str1.hash, str2.hash);
1166
1167	free_mock_sb(mock_sb);
1168}
1169
1170/* Test edge cases and boundary conditions */
1171static void hfsplus_hash_dentry_edge_cases_test(struct kunit *test)
1172{
1173	struct test_mock_sb *mock_sb;
1174	struct test_mock_string_env *mock_env;
1175	struct qstr str;
1176	int result;
1177
1178	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
1179	KUNIT_ASSERT_NOT_NULL(test, mock_env);
1180
1181	mock_sb = setup_mock_sb();
1182	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1183
1184	setup_mock_dentry(&mock_sb->sb);
1185	mock_sb->nls.char2uni = test_char2uni;
1186
1187	/* Test very long filename */
1188	memset(mock_env->buf, 'a', mock_env->buf_size - 1);
1189	mock_env->buf[mock_env->buf_size - 1] = '\0';
1190
1191	create_qstr(&str, mock_env->buf);
1192	result = hfsplus_hash_dentry(&test_dentry, &str);
1193
1194	KUNIT_EXPECT_EQ(test, 0, result);
1195	KUNIT_EXPECT_NE(test, 0, str.hash);
1196
1197	/* Test filename with all printable ASCII characters */
1198	create_qstr(&str, "!@#$%^&*()_+-=[]{}|;':\",./<>?");
1199	result = hfsplus_hash_dentry(&test_dentry, &str);
1200
1201	KUNIT_EXPECT_EQ(test, 0, result);
1202	KUNIT_EXPECT_NE(test, 0, str.hash);
1203
1204	/* Test with embedded null (though not typical for filenames) */
1205	str.name = "file\0hidden";
1206	str.len = 11; /* Include the null and text after it */
1207	str.hash = 0;
1208	result = hfsplus_hash_dentry(&test_dentry, &str);
1209
1210	KUNIT_EXPECT_EQ(test, 0, result);
1211	KUNIT_EXPECT_NE(test, 0, str.hash);
1212
1213	free_mock_str_env(mock_env);
1214	free_mock_sb(mock_sb);
1215}
1216
1217/* Test hfsplus_compare_dentry basic functionality */
1218static void hfsplus_compare_dentry_basic_test(struct kunit *test)
1219{
1220	struct test_mock_sb *mock_sb;
1221	struct qstr name;
1222	int result;
1223
1224	mock_sb = setup_mock_sb();
1225	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1226
1227	setup_mock_dentry(&mock_sb->sb);
1228	mock_sb->nls.char2uni = test_char2uni;
1229
1230	/* Test identical strings */
1231	create_qstr(&name, "hello");
1232	result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
1233	KUNIT_EXPECT_EQ(test, 0, result);
1234
1235	/* Test different strings - lexicographic order */
1236	create_qstr(&name, "world");
1237	result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
1238	KUNIT_EXPECT_LT(test, result, 0); /* "hello" < "world" */
1239
1240	result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name);
1241	KUNIT_EXPECT_EQ(test, 0, result);
1242
1243	create_qstr(&name, "hello");
1244	result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name);
1245	KUNIT_EXPECT_GT(test, result, 0); /* "world" > "hello" */
1246
1247	/* Test empty strings */
1248	create_qstr(&name, "");
1249	result = hfsplus_compare_dentry(&test_dentry, 0, "", &name);
1250	KUNIT_EXPECT_EQ(test, 0, result);
1251
1252	/* Test one empty, one non-empty */
1253	create_qstr(&name, "test");
1254	result = hfsplus_compare_dentry(&test_dentry, 0, "", &name);
1255	KUNIT_EXPECT_LT(test, result, 0); /* "" < "test" */
1256
1257	create_qstr(&name, "");
1258	result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
1259	KUNIT_EXPECT_GT(test, result, 0); /* "test" > "" */
1260
1261	free_mock_sb(mock_sb);
1262}
1263
1264/* Test case folding behavior in comparison */
1265static void hfsplus_compare_dentry_casefold_test(struct kunit *test)
1266{
1267	struct test_mock_sb *mock_sb;
1268	struct qstr name;
1269	int result;
1270
1271	mock_sb = setup_mock_sb();
1272	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1273
1274	setup_mock_dentry(&mock_sb->sb);
1275	mock_sb->nls.char2uni = test_char2uni;
1276
1277	/* Test with case folding disabled (default) */
1278	clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
1279
1280	create_qstr(&name, "hello");
1281	result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name);
1282	/* Case sensitive: "Hello" != "hello" */
1283	KUNIT_EXPECT_NE(test, 0, result);
1284
1285	create_qstr(&name, "Hello");
1286	result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
1287	/* Case sensitive: "hello" != "Hello" */
1288	KUNIT_EXPECT_NE(test, 0, result);
1289
1290	/* Test with case folding enabled */
1291	set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
1292
1293	create_qstr(&name, "hello");
1294	result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name);
1295	/* Case insensitive: "Hello" == "hello" */
1296	KUNIT_EXPECT_EQ(test, 0, result);
1297
1298	create_qstr(&name, "Hello");
1299	result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
1300	/* Case insensitive: "hello" == "Hello" */
1301	KUNIT_EXPECT_EQ(test, 0, result);
1302
1303	/* Test mixed case */
1304	create_qstr(&name, "TeSt");
1305	result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
1306	KUNIT_EXPECT_EQ(test, 0, result);
1307
1308	create_qstr(&name, "test");
1309	result = hfsplus_compare_dentry(&test_dentry, 4, "TEST", &name);
1310	KUNIT_EXPECT_EQ(test, 0, result);
1311
1312	free_mock_sb(mock_sb);
1313}
1314
1315/* Test special character handling in comparison */
1316static void hfsplus_compare_dentry_special_chars_test(struct kunit *test)
1317{
1318	struct test_mock_sb *mock_sb;
1319	struct qstr name;
1320	int result;
1321
1322	mock_sb = setup_mock_sb();
1323	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1324
1325	setup_mock_dentry(&mock_sb->sb);
1326	mock_sb->nls.char2uni = test_char2uni;
1327
1328	/* Test colon conversion (: becomes /) */
1329	create_qstr(&name, "file/name");
1330	result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
1331	/* "file:name" == "file/name" after conversion */
1332	KUNIT_EXPECT_EQ(test, 0, result);
1333
1334	create_qstr(&name, "file:name");
1335	result = hfsplus_compare_dentry(&test_dentry, 9, "file/name", &name);
1336	/* "file/name" == "file:name" after conversion */
1337	KUNIT_EXPECT_EQ(test, 0, result);
1338
1339	/* Test multiple special characters */
1340	create_qstr(&name, "///");
1341	result = hfsplus_compare_dentry(&test_dentry, 3, ":::", &name);
1342	KUNIT_EXPECT_EQ(test, 0, result);
1343
1344	/* Test mixed special and regular characters */
1345	create_qstr(&name, "a/b:c");
1346	result = hfsplus_compare_dentry(&test_dentry, 5, "a:b/c", &name);
1347	/* Both become "a/b/c" after conversion */
1348	KUNIT_EXPECT_EQ(test, 0, result);
1349
1350	free_mock_sb(mock_sb);
1351}
1352
1353/* Test length differences */
1354static void hfsplus_compare_dentry_length_test(struct kunit *test)
1355{
1356	struct test_mock_sb *mock_sb;
1357	struct qstr name;
1358	int result;
1359
1360	mock_sb = setup_mock_sb();
1361	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1362
1363	setup_mock_dentry(&mock_sb->sb);
1364	mock_sb->nls.char2uni = test_char2uni;
1365
1366	/* Test different lengths with common prefix */
1367	create_qstr(&name, "testing");
1368	result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
1369	KUNIT_EXPECT_LT(test, result, 0); /* "test" < "testing" */
1370
1371	create_qstr(&name, "test");
1372	result = hfsplus_compare_dentry(&test_dentry, 7, "testing", &name);
1373	KUNIT_EXPECT_GT(test, result, 0); /* "testing" > "test" */
1374
1375	/* Test exact length match */
1376	create_qstr(&name, "exact");
1377	result = hfsplus_compare_dentry(&test_dentry, 5, "exact", &name);
1378	KUNIT_EXPECT_EQ(test, 0, result);
1379
1380	/* Test length parameter vs actual string content */
1381	create_qstr(&name, "hello");
1382	result = hfsplus_compare_dentry(&test_dentry, 3, "hel", &name);
1383	KUNIT_EXPECT_LT(test, result, 0); /* "hel" < "hello" */
1384
1385	/* Test longer first string but shorter length parameter */
1386	create_qstr(&name, "hi");
1387	result = hfsplus_compare_dentry(&test_dentry, 2, "hello", &name);
1388	/* "he" < "hi" (only first 2 chars compared) */
1389	KUNIT_EXPECT_LT(test, result, 0);
1390
1391	free_mock_sb(mock_sb);
1392}
1393
1394/* Test decomposition flag behavior */
1395static void hfsplus_compare_dentry_decompose_test(struct kunit *test)
1396{
1397	struct test_mock_sb *mock_sb;
1398	struct qstr name;
1399	int result;
1400
1401	mock_sb = setup_mock_sb();
1402	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1403
1404	setup_mock_dentry(&mock_sb->sb);
1405	mock_sb->nls.char2uni = test_char2uni;
1406
1407	/* Test with decomposition disabled (default) */
1408	clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
1409
1410	create_qstr(&name, "test");
1411	result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
1412	KUNIT_EXPECT_EQ(test, 0, result);
1413
1414	/* Test with decomposition enabled */
1415	set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
1416
1417	create_qstr(&name, "test");
1418	result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
1419	KUNIT_EXPECT_EQ(test, 0, result);
1420
1421	/* For simple ASCII, decomposition shouldn't affect the result */
1422	create_qstr(&name, "different");
1423	result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
1424	KUNIT_EXPECT_NE(test, 0, result);
1425
1426	free_mock_sb(mock_sb);
1427}
1428
1429/* Test edge cases and boundary conditions */
1430static void hfsplus_compare_dentry_edge_cases_test(struct kunit *test)
1431{
1432	struct test_mock_sb *mock_sb;
1433	struct qstr name;
1434	char *long_str;
1435	char *long_str2;
1436	u32 str_size = HFSPLUS_MAX_STRLEN + 1;
1437	struct qstr null_name = {
1438		.name = "a\0b",
1439		.len = 3,
1440		.hash = 0
1441	};
1442	int result;
1443
1444	mock_sb = setup_mock_sb();
1445	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1446
1447	setup_mock_dentry(&mock_sb->sb);
1448	mock_sb->nls.char2uni = test_char2uni;
1449
1450	long_str = kzalloc(str_size, GFP_KERNEL);
1451	KUNIT_ASSERT_NOT_NULL(test, long_str);
1452
1453	long_str2 = kzalloc(str_size, GFP_KERNEL);
1454	KUNIT_ASSERT_NOT_NULL(test, long_str2);
1455
1456	/* Test very long strings */
1457	memset(long_str, 'a', str_size - 1);
1458	long_str[str_size - 1] = '\0';
1459
1460	create_qstr(&name, long_str);
1461	result = hfsplus_compare_dentry(&test_dentry, str_size - 1,
1462					long_str, &name);
1463	KUNIT_EXPECT_EQ(test, 0, result);
1464
1465	/* Test with difference at the end of long strings */
1466	memset(long_str2, 'a', str_size - 1);
1467	long_str2[str_size - 1] = '\0';
1468	long_str2[str_size - 2] = 'b';
1469	create_qstr(&name, long_str2);
1470	result = hfsplus_compare_dentry(&test_dentry, str_size - 1,
1471					long_str, &name);
1472	KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */
1473
1474	/* Test single character differences */
1475	create_qstr(&name, "b");
1476	result = hfsplus_compare_dentry(&test_dentry, 1, "a", &name);
1477	KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */
1478
1479	create_qstr(&name, "a");
1480	result = hfsplus_compare_dentry(&test_dentry, 1, "b", &name);
1481	KUNIT_EXPECT_GT(test, result, 0); /* 'b' > 'a' */
1482
1483	/* Test with null characters in the middle */
1484	result = hfsplus_compare_dentry(&test_dentry, 3, "a\0b", &null_name);
1485	KUNIT_EXPECT_EQ(test, 0, result);
1486
1487	/* Test all printable ASCII characters */
1488	create_qstr(&name, "!@#$%^&*()");
1489	result = hfsplus_compare_dentry(&test_dentry, 10, "!@#$%^&*()", &name);
1490	KUNIT_EXPECT_EQ(test, 0, result);
1491
1492	kfree(long_str);
1493	kfree(long_str2);
1494	free_mock_sb(mock_sb);
1495}
1496
1497/* Test combined flag behaviors */
1498static void hfsplus_compare_dentry_combined_flags_test(struct kunit *test)
1499{
1500	struct test_mock_sb *mock_sb;
1501	struct qstr name;
1502	int result;
1503
1504	mock_sb = setup_mock_sb();
1505	KUNIT_ASSERT_NOT_NULL(test, mock_sb);
1506
1507	setup_mock_dentry(&mock_sb->sb);
1508	mock_sb->nls.char2uni = test_char2uni;
1509
1510	/* Test with both casefold and decompose enabled */
1511	set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
1512	set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
1513
1514	create_qstr(&name, "hello");
1515	result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name);
1516	KUNIT_EXPECT_EQ(test, 0, result);
1517
1518	/* Test special chars with case folding */
1519	create_qstr(&name, "File/Name");
1520	result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
1521	KUNIT_EXPECT_EQ(test, 0, result);
1522
1523	/* Test with both flags disabled */
1524	clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
1525	clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
1526
1527	create_qstr(&name, "hello");
1528	result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name);
1529	KUNIT_EXPECT_NE(test, 0, result); /* Case sensitive */
1530
1531	/* But special chars should still be converted */
1532	create_qstr(&name, "file/name");
1533	result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
1534	KUNIT_EXPECT_EQ(test, 0, result);
1535
1536	free_mock_sb(mock_sb);
1537}
1538
1539static struct kunit_case hfsplus_unicode_test_cases[] = {
1540	KUNIT_CASE(hfsplus_strcasecmp_test),
1541	KUNIT_CASE(hfsplus_strcmp_test),
1542	KUNIT_CASE(hfsplus_unicode_edge_cases_test),
1543	KUNIT_CASE(hfsplus_unicode_boundary_test),
1544	KUNIT_CASE(hfsplus_uni2asc_basic_test),
1545	KUNIT_CASE(hfsplus_uni2asc_special_chars_test),
1546	KUNIT_CASE(hfsplus_uni2asc_buffer_test),
1547	KUNIT_CASE(hfsplus_uni2asc_corrupted_test),
1548	KUNIT_CASE(hfsplus_uni2asc_edge_cases_test),
1549	KUNIT_CASE(hfsplus_asc2uni_basic_test),
1550	KUNIT_CASE(hfsplus_asc2uni_special_chars_test),
1551	KUNIT_CASE(hfsplus_asc2uni_buffer_limits_test),
1552	KUNIT_CASE(hfsplus_asc2uni_edge_cases_test),
1553	KUNIT_CASE(hfsplus_asc2uni_decompose_test),
1554	KUNIT_CASE(hfsplus_hash_dentry_basic_test),
1555	KUNIT_CASE(hfsplus_hash_dentry_casefold_test),
1556	KUNIT_CASE(hfsplus_hash_dentry_special_chars_test),
1557	KUNIT_CASE(hfsplus_hash_dentry_decompose_test),
1558	KUNIT_CASE(hfsplus_hash_dentry_consistency_test),
1559	KUNIT_CASE(hfsplus_hash_dentry_edge_cases_test),
1560	KUNIT_CASE(hfsplus_compare_dentry_basic_test),
1561	KUNIT_CASE(hfsplus_compare_dentry_casefold_test),
1562	KUNIT_CASE(hfsplus_compare_dentry_special_chars_test),
1563	KUNIT_CASE(hfsplus_compare_dentry_length_test),
1564	KUNIT_CASE(hfsplus_compare_dentry_decompose_test),
1565	KUNIT_CASE(hfsplus_compare_dentry_edge_cases_test),
1566	KUNIT_CASE(hfsplus_compare_dentry_combined_flags_test),
1567	{}
1568};
1569
1570static struct kunit_suite hfsplus_unicode_test_suite = {
1571	.name = "hfsplus_unicode",
1572	.test_cases = hfsplus_unicode_test_cases,
1573};
1574
1575kunit_test_suite(hfsplus_unicode_test_suite);
1576
1577MODULE_DESCRIPTION("KUnit tests for HFS+ Unicode string operations");
1578MODULE_LICENSE("GPL");
1579MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
~/ Read-only file preview.