Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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");