FreeRDP
Loading...
Searching...
No Matches
TestUnicodeConversion.c
1
2#include <stdio.h>
3#include <winpr/wtypes.h>
4#include <winpr/crt.h>
5#include <winpr/assert.h>
6#include <winpr/error.h>
7#include <winpr/print.h>
8#include <winpr/windows.h>
9
10#if defined(BUILD_TESTING_INTERNAL)
11#include "../unicode.h"
12#endif
13
14#define TESTCASE_BUFFER_SIZE 8192
15
16#ifndef MIN
17#define MIN(x, y) (((x) < (y)) ? (x) : (y))
18#endif
19
20typedef struct
21{
22 const char* utf8;
23 size_t utf8len;
24 const WCHAR* utf16;
25 size_t utf16len;
26} testcase_t;
27
28// TODO: The unit tests do not check for valid code points, so always end the test
29// strings with a simple ASCII symbol for now.
30static const testcase_t unit_testcases[] = {
31 { "foo", 3, (const WCHAR*)"f\x00o\x00o\x00\x00\x00", 3 },
32 { "foo", 4, (const WCHAR*)"f\x00o\x00o\x00\x00\x00", 4 },
33 { "βœŠπŸŽ…Δ™Κ₯κ£Έπ‘—Ša", 19,
34 (const WCHAR*)"\x0a\x27\x3c\xd8\x85\xdf\x19\x01\xa5\x02\xf8\xa8\x05\xd8\xca\xdd\x61\x00\x00"
35 "\x00",
36 9 }
37};
38
39static void create_prefix(char* prefix, size_t prefixlen, size_t buffersize, SSIZE_T rc,
40 SSIZE_T inputlen, const testcase_t* test, const char* fkt, size_t line)
41{
42 (void)_snprintf(prefix, prefixlen,
43 "[%s:%" PRIuz "] '%s' [utf8: %" PRIuz ", utf16: %" PRIuz "] buffersize: %" PRIuz
44 ", rc: %" PRIdz ", inputlen: %" PRIdz ":: ",
45 fkt, line, test->utf8, test->utf8len, test->utf16len, buffersize, rc, inputlen);
46}
47
48static BOOL check_short_buffer(const char* prefix, int rc, size_t buffersize,
49 const testcase_t* test, BOOL utf8)
50{
51 if ((rc > 0) && ((size_t)rc <= buffersize))
52 return TRUE;
53
54 size_t len = test->utf8len;
55 if (!utf8)
56 len = test->utf16len;
57
58 if (buffersize > len)
59 {
60 (void)fprintf(stderr,
61 "%s length does not match buffersize: %" PRId32 " != %" PRIuz
62 ",but is large enough to hold result\n",
63 prefix, rc, buffersize);
64 return FALSE;
65 }
66 const DWORD err = GetLastError();
67 if (err != ERROR_INSUFFICIENT_BUFFER)
68 {
69
70 (void)fprintf(stderr,
71 "%s length does not match buffersize: %" PRId32 " != %" PRIuz
72 ", unexpected GetLastError() 0x%08" PRIx32 "\n",
73 prefix, rc, buffersize, err);
74 return FALSE;
75 }
76 else
77 return TRUE;
78}
79
80#define compare_utf16(what, buffersize, rc, inputlen, test) \
81 compare_utf16_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
82static BOOL compare_utf16_int(const WCHAR* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen,
83 const testcase_t* test, const char* fkt, size_t line)
84{
85 char prefix[8192] = WINPR_C_ARRAY_INIT;
86 create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
87
88 WINPR_ASSERT(what || (buffersize == 0));
89 WINPR_ASSERT(test);
90
91 const size_t welen = _wcsnlen(test->utf16, test->utf16len);
92 if (buffersize > welen)
93 {
94 if ((rc < 0) || ((size_t)rc != welen))
95 {
96 (void)fprintf(stderr,
97 "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n",
98 prefix, rc, welen);
99 return FALSE;
100 }
101 }
102 else
103 {
104 if (!check_short_buffer(prefix, WINPR_ASSERTING_INT_CAST(SSIZE_T, rc), buffersize, test,
105 FALSE))
106 return FALSE;
107 }
108
109 if ((rc > 0) && (buffersize > (size_t)rc))
110 {
111 const size_t wlen = _wcsnlen(what, buffersize);
112 if ((rc < 0) || (wlen > (size_t)rc))
113 {
114 (void)fprintf(stderr, "%s length does not match wcslen: %" PRIdz " < %" PRIuz "\n",
115 prefix, rc, wlen);
116 return FALSE;
117 }
118 }
119
120 if (rc >= 0)
121 {
122 if (memcmp(test->utf16, what, rc * sizeof(WCHAR)) != 0)
123 {
124 (void)fprintf(stderr, "%s contents does not match expectations: TODO '%s' != '%s'\n",
125 prefix, test->utf8, test->utf8);
126 return FALSE;
127 }
128 }
129
130 printf("%s success\n", prefix);
131
132 return TRUE;
133}
134
135#define compare_utf8(what, buffersize, rc, inputlen, test) \
136 compare_utf8_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
137static BOOL compare_utf8_int(const char* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen,
138 const testcase_t* test, const char* fkt, size_t line)
139{
140 char prefix[8192] = WINPR_C_ARRAY_INIT;
141 create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
142
143 WINPR_ASSERT(what || (buffersize == 0));
144 WINPR_ASSERT(test);
145
146 const size_t slen = strnlen(test->utf8, test->utf8len);
147 if (buffersize > slen)
148 {
149 if ((rc < 0) || ((size_t)rc != slen))
150 {
151 (void)fprintf(stderr,
152 "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n",
153 prefix, rc, slen);
154 return FALSE;
155 }
156 }
157 else
158 {
159 if (!check_short_buffer(prefix, WINPR_ASSERTING_INT_CAST(SSIZE_T, rc), buffersize, test,
160 TRUE))
161 return FALSE;
162 }
163
164 if ((rc > 0) && (buffersize > (size_t)rc))
165 {
166 const size_t wlen = strnlen(what, buffersize);
167 if (wlen != (size_t)rc)
168 {
169 (void)fprintf(stderr, "%s length does not match strnlen: %" PRIdz " != %" PRIuz "\n",
170 prefix, rc, wlen);
171 return FALSE;
172 }
173 }
174
175 if (rc >= 0)
176 {
177 if (memcmp(test->utf8, what, rc) != 0)
178 {
179 (void)fprintf(stderr, "%s contents does not match expectations: '%s' != '%s'\n", prefix,
180 what, test->utf8);
181 return FALSE;
182 }
183 }
184
185 printf("%s success\n", prefix);
186
187 return TRUE;
188}
189
190static BOOL test_convert_to_utf16(const testcase_t* test)
191{
192 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
193 test->utf16len - 1 };
194 const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
195
196 const SSIZE_T rc2 = ConvertUtf8ToWChar(test->utf8, nullptr, 0);
197 const size_t wlen = _wcsnlen(test->utf16, test->utf16len);
198 if ((rc2 < 0) || ((size_t)rc2 != wlen))
199 {
200 char prefix[8192] = WINPR_C_ARRAY_INIT;
201 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
202 (void)fprintf(stderr,
203 "%s ConvertUtf8ToWChar(%s, nullptr, 0) expected %" PRIuz ", got %" PRIdz "\n",
204 prefix, test->utf8, wlen, rc2);
205 return FALSE;
206 }
207 for (size_t x = 0; x < max; x++)
208 {
209 WCHAR buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
210 const SSIZE_T rc = ConvertUtf8ToWChar(test->utf8, buffer, len[x]);
211 if (!compare_utf16(buffer, len[x], rc, -1, test))
212 return FALSE;
213 }
214
215 return TRUE;
216}
217
218static BOOL test_convert_to_utf16_n(const testcase_t* test)
219{
220 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
221 test->utf16len - 1 };
222 const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
223
224 const SSIZE_T rc2 = ConvertUtf8NToWChar(test->utf8, test->utf8len, nullptr, 0);
225 const size_t wlen = _wcsnlen(test->utf16, test->utf16len);
226 if ((rc2 < 0) || ((size_t)rc2 != wlen))
227 {
228 char prefix[8192] = WINPR_C_ARRAY_INIT;
229 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2,
230 WINPR_ASSERTING_INT_CAST(SSIZE_T, test->utf8len), test, __func__, __LINE__);
231 (void)fprintf(stderr,
232 "%s ConvertUtf8NToWChar(%s, %" PRIuz ", nullptr, 0) expected %" PRIuz
233 ", got %" PRIdz "\n",
234 prefix, test->utf8, test->utf8len, wlen, rc2);
235 return FALSE;
236 }
237
238 for (size_t x = 0; x < max; x++)
239 {
240 const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
241 test->utf8len - 1 };
242 const size_t imax = test->utf8len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
243
244 for (size_t y = 0; y < imax; y++)
245 {
246 WCHAR buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
247 SSIZE_T rc = ConvertUtf8NToWChar(test->utf8, ilen[x], buffer, len[x]);
248 if (!compare_utf16(buffer, len[x], rc, ilen[x], test))
249 return FALSE;
250 }
251 }
252 return TRUE;
253}
254
255static BOOL test_convert_to_utf8(const testcase_t* test)
256{
257 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
258 test->utf8len - 1 };
259 const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
260
261 const SSIZE_T rc2 = ConvertWCharToUtf8(test->utf16, nullptr, 0);
262 const size_t wlen = strnlen(test->utf8, test->utf8len);
263 if ((rc2 < 0) || ((size_t)rc2 != wlen))
264 {
265 char prefix[8192] = WINPR_C_ARRAY_INIT;
266 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
267 (void)fprintf(stderr,
268 "%s ConvertWCharToUtf8(%s, nullptr, 0) expected %" PRIuz ", got %" PRIdz "\n",
269 prefix, test->utf8, wlen, rc2);
270 return FALSE;
271 }
272
273 for (size_t x = 0; x < max; x++)
274 {
275 char buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
276 SSIZE_T rc = ConvertWCharToUtf8(test->utf16, buffer, len[x]);
277 if (!compare_utf8(buffer, len[x], rc, -1, test))
278 return FALSE;
279 }
280
281 return TRUE;
282}
283
284static BOOL test_convert_to_utf8_n(const testcase_t* test)
285{
286 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
287 test->utf8len - 1 };
288 const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
289
290 const SSIZE_T rc2 = ConvertWCharNToUtf8(test->utf16, test->utf16len, nullptr, 0);
291 const size_t wlen = strnlen(test->utf8, test->utf8len);
292 if ((rc2 < 0) || ((size_t)rc2 != wlen))
293 {
294 char prefix[8192] = WINPR_C_ARRAY_INIT;
295 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2,
296 WINPR_ASSERTING_INT_CAST(SSIZE_T, test->utf16len), test, __func__, __LINE__);
297 (void)fprintf(stderr,
298 "%s ConvertWCharNToUtf8(%s, %" PRIuz ", nullptr, 0) expected %" PRIuz
299 ", got %" PRIdz "\n",
300 prefix, test->utf8, test->utf16len, wlen, rc2);
301 return FALSE;
302 }
303
304 for (size_t x = 0; x < max; x++)
305 {
306 const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
307 test->utf16len - 1 };
308 const size_t imax = test->utf16len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
309
310 for (size_t y = 0; y < imax; y++)
311 {
312 char buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
313 SSIZE_T rc = ConvertWCharNToUtf8(test->utf16, ilen[x], buffer, len[x]);
314 if (!compare_utf8(buffer, len[x], rc, ilen[x], test))
315 return FALSE;
316 }
317 }
318
319 return TRUE;
320}
321
322static BOOL test_conversion(const testcase_t* testcases, size_t count)
323{
324 WINPR_ASSERT(testcases || (count == 0));
325 for (size_t x = 0; x < count; x++)
326 {
327 const testcase_t* test = &testcases[x];
328
329 printf("Running test case %" PRIuz " [%s]\n", x, test->utf8);
330 if (!test_convert_to_utf16(test))
331 {
332 (void)fprintf(stderr, "Test case %" PRIuz " [%s] convert to UTF16 failed\n",
333 test->utf8);
334 return FALSE;
335 }
336 if (!test_convert_to_utf16_n(test))
337 {
338 (void)fprintf(stderr, "Test case %" PRIuz " [%s] convert to UTF16N failed\n",
339 test->utf8);
340 return FALSE;
341 }
342 if (!test_convert_to_utf8(test))
343 {
344 (void)fprintf(stderr, "Test case %" PRIuz " [%s] convert to UTF8 failed\n", test->utf8);
345 return FALSE;
346 }
347 if (!test_convert_to_utf8_n(test))
348 {
349 (void)fprintf(stderr, "Test case %" PRIuz " [%s] convert to UTF8N failed\n",
350 test->utf8);
351 return FALSE;
352 }
353 }
354 return TRUE;
355}
356
357#if defined(WITH_WINPR_DEPRECATED)
358
359#define compare_win_utf16(what, buffersize, rc, inputlen, test) \
360 compare_win_utf16_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
361static BOOL compare_win_utf16_int(const WCHAR* what, size_t buffersize, int rc, int inputlen,
362 const testcase_t* test, const char* fkt, size_t line)
363{
364 char prefix[8192] = WINPR_C_ARRAY_INIT;
365 create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
366
367 WINPR_ASSERT(what || (buffersize == 0));
368 WINPR_ASSERT(test);
369
370 BOOL isNullTerminated = TRUE;
371 if (inputlen > 0)
372 isNullTerminated = strnlen(test->utf8, inputlen) < inputlen;
373 size_t welen = _wcsnlen(test->utf16, buffersize);
374 if (isNullTerminated)
375 welen++;
376
377 if (buffersize >= welen)
378 {
379 if ((inputlen >= 0) && (rc > buffersize))
380 {
381 (void)fprintf(stderr, "%s length does not match expectation: %d > %" PRIuz "\n", prefix,
382 rc, buffersize);
383 return FALSE;
384 }
385 else if ((inputlen < 0) && (rc != welen))
386 {
387 (void)fprintf(stderr, "%s length does not match expectation: %d != %" PRIuz "\n",
388 prefix, rc, welen);
389 return FALSE;
390 }
391 }
392 else
393 {
394 if (!check_short_buffer(prefix, rc, buffersize, test, FALSE))
395 return FALSE;
396 }
397
398 if ((rc > 0) && (buffersize > rc))
399 {
400 size_t wlen = _wcsnlen(what, buffersize);
401 if (isNullTerminated)
402 wlen++;
403 if ((inputlen >= 0) && (buffersize < rc))
404 {
405 (void)fprintf(stderr, "%s length does not match wcslen: %d > %" PRIuz "\n", prefix, rc,
406 buffersize);
407 return FALSE;
408 }
409 else if ((inputlen < 0) && (welen > rc))
410 {
411 (void)fprintf(stderr, "%s length does not match wcslen: %d < %" PRIuz "\n", prefix, rc,
412 wlen);
413 return FALSE;
414 }
415 }
416
417 const size_t cmp_size = MIN(rc, test->utf16len) * sizeof(WCHAR);
418 if (memcmp(test->utf16, what, cmp_size) != 0)
419 {
420 (void)fprintf(stderr, "%s contents does not match expectations: TODO '%s' != '%s'\n",
421 prefix, test->utf8, test->utf8);
422 return FALSE;
423 }
424
425 printf("%s success\n", prefix);
426
427 return TRUE;
428}
429
430#define compare_win_utf8(what, buffersize, rc, inputlen, test) \
431 compare_win_utf8_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__)
432static BOOL compare_win_utf8_int(const char* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen,
433 const testcase_t* test, const char* fkt, size_t line)
434{
435 char prefix[8192] = WINPR_C_ARRAY_INIT;
436 create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line);
437
438 WINPR_ASSERT(what || (buffersize == 0));
439 WINPR_ASSERT(test);
440
441 BOOL isNullTerminated = TRUE;
442 if (inputlen > 0)
443 isNullTerminated = _wcsnlen(test->utf16, inputlen) < inputlen;
444
445 size_t slen = strnlen(test->utf8, test->utf8len);
446 if (isNullTerminated)
447 slen++;
448
449 if (buffersize > slen)
450 {
451 if ((inputlen >= 0) && (rc > buffersize))
452 {
453 (void)fprintf(stderr, "%s length does not match expectation: %" PRIdz " > %" PRIuz "\n",
454 prefix, rc, buffersize);
455 return FALSE;
456 }
457 else if ((inputlen < 0) && (rc != slen))
458 {
459 (void)fprintf(stderr,
460 "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n",
461 prefix, rc, slen);
462 return FALSE;
463 }
464 }
465 else
466 {
467 if (!check_short_buffer(prefix, rc, buffersize, test, TRUE))
468 return FALSE;
469 }
470
471 if ((rc > 0) && (buffersize > rc))
472 {
473 size_t wlen = strnlen(what, buffersize);
474 if (isNullTerminated)
475 wlen++;
476
477 if (wlen > rc)
478 {
479 (void)fprintf(stderr, "%s length does not match wcslen: %" PRIdz " < %" PRIuz "\n",
480 prefix, rc, wlen);
481 return FALSE;
482 }
483 }
484
485 const size_t cmp_size = MIN(test->utf8len, rc);
486 if (memcmp(test->utf8, what, cmp_size) != 0)
487 {
488 (void)fprintf(stderr, "%s contents does not match expectations: '%s' != '%s'\n", prefix,
489 what, test->utf8);
490 return FALSE;
491 }
492 printf("%s success\n", prefix);
493
494 return TRUE;
495}
496#endif
497
498#if defined(WITH_WINPR_DEPRECATED)
499static BOOL test_win_convert_to_utf16(const testcase_t* test)
500{
501 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
502 test->utf16len - 1 };
503 const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
504
505 const int rc2 = MultiByteToWideChar(CP_UTF8, 0, test->utf8, -1, nullptr, 0);
506 const size_t wlen = _wcsnlen(test->utf16, test->utf16len);
507 if (rc2 != wlen + 1)
508 {
509 char prefix[8192] = WINPR_C_ARRAY_INIT;
510 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
511 (void)fprintf(stderr,
512 "%s MultiByteToWideChar(CP_UTF8, 0, %s, [-1], nullptr, 0) expected %" PRIuz
513 ", got %d\n",
514 prefix, test->utf8, wlen + 1, rc2);
515 return FALSE;
516 }
517 for (size_t x = 0; x < max; x++)
518 {
519 WCHAR buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
520 const int rc = MultiByteToWideChar(CP_UTF8, 0, test->utf8, -1, buffer, len[x]);
521 if (!compare_win_utf16(buffer, len[x], rc, -1, test))
522 return FALSE;
523 }
524
525 return TRUE;
526}
527
528static BOOL test_win_convert_to_utf16_n(const testcase_t* test)
529{
530 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
531 test->utf16len - 1 };
532 const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
533
534 BOOL isNullTerminated = strnlen(test->utf8, test->utf8len) < test->utf8len;
535 const int rc2 = MultiByteToWideChar(CP_UTF8, 0, test->utf8, test->utf8len, nullptr, 0);
536 size_t wlen = _wcsnlen(test->utf16, test->utf16len);
537 if (isNullTerminated)
538 wlen++;
539
540 if (rc2 != wlen)
541 {
542 char prefix[8192] = WINPR_C_ARRAY_INIT;
543 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf8len, test, __func__, __LINE__);
544 (void)fprintf(stderr,
545 "%s MultiByteToWideChar(CP_UTF8, 0, %s, %" PRIuz
546 ", nullptr, 0) expected %" PRIuz ", got %d\n",
547 prefix, test->utf8, test->utf8len, wlen, rc2);
548 return FALSE;
549 }
550
551 for (size_t x = 0; x < max; x++)
552 {
553 const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
554 test->utf8len - 1 };
555 const size_t imax = test->utf8len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
556
557 for (size_t y = 0; y < imax; y++)
558 {
559 char mbuffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
560 WCHAR buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
561 strncpy(mbuffer, test->utf8, test->utf8len);
562 const int rc = MultiByteToWideChar(CP_UTF8, 0, mbuffer, ilen[x], buffer, len[x]);
563 if (!compare_win_utf16(buffer, len[x], rc, ilen[x], test))
564 return FALSE;
565 }
566 }
567 return TRUE;
568}
569#endif
570
571#if defined(WITH_WINPR_DEPRECATED)
572static BOOL test_win_convert_to_utf8(const testcase_t* test)
573{
574 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
575 test->utf8len - 1 };
576 const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
577
578 const int rc2 = WideCharToMultiByte(CP_UTF8, 0, test->utf16, -1, nullptr, 0, nullptr, nullptr);
579 const size_t wlen = strnlen(test->utf8, test->utf8len) + 1;
580 if (rc2 != wlen)
581 {
582 char prefix[8192] = WINPR_C_ARRAY_INIT;
583 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__);
584 (void)fprintf(stderr,
585 "%s WideCharToMultiByte(CP_UTF8, 0, %s, -1, nullptr, 0, nullptr, nullptr) "
586 "expected %" PRIuz ", got %d\n",
587 prefix, test->utf8, wlen, rc2);
588 return FALSE;
589 }
590
591 for (size_t x = 0; x < max; x++)
592 {
593 char buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
594 int rc = WideCharToMultiByte(CP_UTF8, 0, test->utf16, -1, buffer, len[x], nullptr, nullptr);
595 if (!compare_win_utf8(buffer, len[x], rc, -1, test))
596 return FALSE;
597 }
598
599 return TRUE;
600}
601
602static BOOL test_win_convert_to_utf8_n(const testcase_t* test)
603{
604 const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1,
605 test->utf8len - 1 };
606 const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1;
607
608 const BOOL isNullTerminated = _wcsnlen(test->utf16, test->utf16len) < test->utf16len;
609 const int rc2 =
610 WideCharToMultiByte(CP_UTF8, 0, test->utf16, test->utf16len, nullptr, 0, nullptr, nullptr);
611 size_t wlen = strnlen(test->utf8, test->utf8len);
612 if (isNullTerminated)
613 wlen++;
614
615 if (rc2 != wlen)
616 {
617 char prefix[8192] = WINPR_C_ARRAY_INIT;
618 create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf16len, test, __func__, __LINE__);
619 (void)fprintf(stderr,
620 "%s WideCharToMultiByte(CP_UTF8, 0, %s, %" PRIuz
621 ", nullptr, 0, nullptr, nullptr) expected %" PRIuz ", got %d\n",
622 prefix, test->utf8, test->utf16len, wlen, rc2);
623 return FALSE;
624 }
625
626 for (size_t x = 0; x < max; x++)
627 {
628 const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1,
629 test->utf16len - 1 };
630 const size_t imax = test->utf16len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1;
631
632 for (size_t y = 0; y < imax; y++)
633 {
634 WCHAR wbuffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
635 char buffer[TESTCASE_BUFFER_SIZE] = WINPR_C_ARRAY_INIT;
636 memcpy(wbuffer, test->utf16, test->utf16len * sizeof(WCHAR));
637 const int rc =
638 WideCharToMultiByte(CP_UTF8, 0, wbuffer, ilen[x], buffer, len[x], nullptr, nullptr);
639 if (!compare_win_utf8(buffer, len[x], rc, ilen[x], test))
640 return FALSE;
641 }
642 }
643
644 return TRUE;
645}
646
647static BOOL test_win_conversion(const testcase_t* testcases, size_t count)
648{
649 WINPR_ASSERT(testcases || (count == 0));
650 for (size_t x = 0; x < count; x++)
651 {
652 const testcase_t* test = &testcases[x];
653
654 printf("Running test case %" PRIuz " [%s]\n", x, test->utf8);
655 if (!test_win_convert_to_utf16(test))
656 return FALSE;
657 if (!test_win_convert_to_utf16_n(test))
658 return FALSE;
659 if (!test_win_convert_to_utf8(test))
660 return FALSE;
661 if (!test_win_convert_to_utf8_n(test))
662 return FALSE;
663 }
664 return TRUE;
665}
666#endif
667
668#if defined(WITH_WINPR_DEPRECATED)
669/* Letters */
670
671static BYTE c_cedilla_UTF8[] = "\xC3\xA7\x00";
672static BYTE c_cedilla_UTF16[] = "\xE7\x00\x00\x00";
673static int c_cedilla_cchWideChar = 2;
674static int c_cedilla_cbMultiByte = 3;
675
676/* English */
677
678static BYTE en_Hello_UTF8[] = "Hello\0";
679static BYTE en_Hello_UTF16[] = "\x48\x00\x65\x00\x6C\x00\x6C\x00\x6F\x00\x00\x00";
680static int en_Hello_cchWideChar = 6;
681static int en_Hello_cbMultiByte = 6;
682
683static BYTE en_HowAreYou_UTF8[] = "How are you?\0";
684static BYTE en_HowAreYou_UTF16[] =
685 "\x48\x00\x6F\x00\x77\x00\x20\x00\x61\x00\x72\x00\x65\x00\x20\x00"
686 "\x79\x00\x6F\x00\x75\x00\x3F\x00\x00\x00";
687static int en_HowAreYou_cchWideChar = 13;
688static int en_HowAreYou_cbMultiByte = 13;
689
690/* French */
691
692static BYTE fr_Hello_UTF8[] = "Allo\0";
693static BYTE fr_Hello_UTF16[] = "\x41\x00\x6C\x00\x6C\x00\x6F\x00\x00\x00";
694static int fr_Hello_cchWideChar = 5;
695static int fr_Hello_cbMultiByte = 5;
696
697static BYTE fr_HowAreYou_UTF8[] =
698 "\x43\x6F\x6D\x6D\x65\x6E\x74\x20\xC3\xA7\x61\x20\x76\x61\x3F\x00";
699static BYTE fr_HowAreYou_UTF16[] =
700 "\x43\x00\x6F\x00\x6D\x00\x6D\x00\x65\x00\x6E\x00\x74\x00\x20\x00"
701 "\xE7\x00\x61\x00\x20\x00\x76\x00\x61\x00\x3F\x00\x00\x00";
702static int fr_HowAreYou_cchWideChar = 15;
703static int fr_HowAreYou_cbMultiByte = 16;
704
705/* Russian */
706
707static BYTE ru_Hello_UTF8[] = "\xD0\x97\xD0\xB4\xD0\xBE\xD1\x80\xD0\xBE\xD0\xB2\xD0\xBE\x00";
708static BYTE ru_Hello_UTF16[] = "\x17\x04\x34\x04\x3E\x04\x40\x04\x3E\x04\x32\x04\x3E\x04\x00\x00";
709static int ru_Hello_cchWideChar = 8;
710static int ru_Hello_cbMultiByte = 15;
711
712static BYTE ru_HowAreYou_UTF8[] =
713 "\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB0\x3F\x00";
714static BYTE ru_HowAreYou_UTF16[] =
715 "\x1A\x04\x30\x04\x3A\x04\x20\x00\x34\x04\x35\x04\x3B\x04\x30\x04"
716 "\x3F\x00\x00\x00";
717static int ru_HowAreYou_cchWideChar = 10;
718static int ru_HowAreYou_cbMultiByte = 17;
719
720/* Arabic */
721
722static BYTE ar_Hello_UTF8[] = "\xD8\xA7\xD9\x84\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\x20\xD8\xB9\xD9"
723 "\x84\xD9\x8A\xD9\x83\xD9\x85\x00";
724static BYTE ar_Hello_UTF16[] = "\x27\x06\x44\x06\x33\x06\x44\x06\x27\x06\x45\x06\x20\x00\x39\x06"
725 "\x44\x06\x4A\x06\x43\x06\x45\x06\x00\x00";
726static int ar_Hello_cchWideChar = 13;
727static int ar_Hello_cbMultiByte = 24;
728
729static BYTE ar_HowAreYou_UTF8[] = "\xD9\x83\xD9\x8A\xD9\x81\x20\xD8\xAD\xD8\xA7\xD9\x84\xD9\x83\xD8"
730 "\x9F\x00";
731static BYTE ar_HowAreYou_UTF16[] =
732 "\x43\x06\x4A\x06\x41\x06\x20\x00\x2D\x06\x27\x06\x44\x06\x43\x06"
733 "\x1F\x06\x00\x00";
734static int ar_HowAreYou_cchWideChar = 10;
735static int ar_HowAreYou_cbMultiByte = 18;
736
737/* Chinese */
738
739static BYTE ch_Hello_UTF8[] = "\xE4\xBD\xA0\xE5\xA5\xBD\x00";
740static BYTE ch_Hello_UTF16[] = "\x60\x4F\x7D\x59\x00\x00";
741static int ch_Hello_cchWideChar = 3;
742static int ch_Hello_cbMultiByte = 7;
743
744static BYTE ch_HowAreYou_UTF8[] = "\xE4\xBD\xA0\xE5\xA5\xBD\xE5\x90\x97\x00";
745static BYTE ch_HowAreYou_UTF16[] = "\x60\x4F\x7D\x59\x17\x54\x00\x00";
746static int ch_HowAreYou_cchWideChar = 4;
747static int ch_HowAreYou_cbMultiByte = 10;
748
749/* Uppercasing */
750
751static BYTE ru_Administrator_lower[] = "\xd0\x90\xd0\xb4\xd0\xbc\xd0\xb8\xd0\xbd\xd0\xb8\xd1\x81"
752 "\xd1\x82\xd1\x80\xd0\xb0\xd1\x82\xd0\xbe\xd1\x80\x00";
753
754static BYTE ru_Administrator_upper[] = "\xd0\x90\xd0\x94\xd0\x9c\xd0\x98\xd0\x9d\xd0\x98\xd0\xa1"
755 "\xd0\xa2\xd0\xa0\xd0\x90\xd0\xa2\xd0\x9e\xd0\xa0\x00";
756
757static void string_hexdump(const BYTE* data, size_t length)
758{
759 size_t offset = 0;
760
761 char* str = winpr_BinToHexString(data, length, TRUE);
762 if (!str)
763 return;
764
765 while (offset < length)
766 {
767 const size_t diff = (length - offset) * 3;
768 WINPR_ASSERT(diff <= INT_MAX);
769 printf("%04" PRIxz " %.*s\n", offset, (int)diff, &str[offset]);
770 offset += 16;
771 }
772
773 free(str);
774}
775
776static int convert_utf8_to_utf16(BYTE* lpMultiByteStr, BYTE* expected_lpWideCharStr,
777 int expected_cchWideChar)
778{
779 int rc = -1;
780 int length = 0;
781 size_t cbMultiByte = 0;
782 int cchWideChar = 0;
783 LPWSTR lpWideCharStr = nullptr;
784
785 cbMultiByte = strlen((char*)lpMultiByteStr);
786 cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpMultiByteStr, -1, nullptr, 0);
787
788 printf("MultiByteToWideChar Input UTF8 String:\n");
789 string_hexdump(lpMultiByteStr, cbMultiByte + 1);
790
791 printf("MultiByteToWideChar required cchWideChar: %d\n", cchWideChar);
792
793 if (cchWideChar != expected_cchWideChar)
794 {
795 printf("MultiByteToWideChar unexpected cchWideChar: actual: %d expected: %d\n", cchWideChar,
796 expected_cchWideChar);
797 goto fail;
798 }
799
800 lpWideCharStr = (LPWSTR)calloc((size_t)cchWideChar, sizeof(WCHAR));
801 if (!lpWideCharStr)
802 {
803 printf("MultiByteToWideChar: unable to allocate memory for test\n");
804 goto fail;
805 }
806 lpWideCharStr[cchWideChar - 1] =
807 0xFFFF; /* should be overwritten if null terminator is inserted properly */
808 length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpMultiByteStr, cbMultiByte + 1, lpWideCharStr,
809 cchWideChar);
810
811 printf("MultiByteToWideChar converted length (WCHAR): %d\n", length);
812
813 if (!length)
814 {
815 DWORD error = GetLastError();
816 printf("MultiByteToWideChar error: 0x%08" PRIX32 "\n", error);
817 goto fail;
818 }
819
820 if (length != expected_cchWideChar)
821 {
822 printf("MultiByteToWideChar unexpected converted length (WCHAR): actual: %d expected: %d\n",
823 length, expected_cchWideChar);
824 goto fail;
825 }
826
827 if (_wcscmp(lpWideCharStr, (WCHAR*)expected_lpWideCharStr) != 0)
828 {
829 printf("MultiByteToWideChar unexpected string:\n");
830
831 printf("UTF8 String:\n");
832 string_hexdump(lpMultiByteStr, cbMultiByte + 1);
833
834 printf("UTF16 String (actual):\n");
835 string_hexdump((BYTE*)lpWideCharStr, length * sizeof(WCHAR));
836
837 printf("UTF16 String (expected):\n");
838 string_hexdump(expected_lpWideCharStr, expected_cchWideChar * sizeof(WCHAR));
839
840 goto fail;
841 }
842
843 printf("MultiByteToWideChar Output UTF16 String:\n");
844 string_hexdump((BYTE*)lpWideCharStr, length * sizeof(WCHAR));
845 printf("\n");
846
847 rc = length;
848fail:
849 free(lpWideCharStr);
850
851 return rc;
852}
853#endif
854
855#if defined(WITH_WINPR_DEPRECATED)
856static int convert_utf16_to_utf8(BYTE* lpWideCharStr, BYTE* expected_lpMultiByteStr,
857 int expected_cbMultiByte)
858{
859 int rc = -1;
860 int length = 0;
861 int cchWideChar = 0;
862 int cbMultiByte = 0;
863 LPSTR lpMultiByteStr = nullptr;
864
865 cchWideChar = _wcslen((WCHAR*)lpWideCharStr);
866 cbMultiByte =
867 WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpWideCharStr, -1, nullptr, 0, nullptr, nullptr);
868
869 printf("WideCharToMultiByte Input UTF16 String:\n");
870 string_hexdump(lpWideCharStr, (cchWideChar + 1) * sizeof(WCHAR));
871
872 printf("WideCharToMultiByte required cbMultiByte: %d\n", cbMultiByte);
873
874 if (cbMultiByte != expected_cbMultiByte)
875 {
876 printf("WideCharToMultiByte unexpected cbMultiByte: actual: %d expected: %d\n", cbMultiByte,
877 expected_cbMultiByte);
878 goto fail;
879 }
880
881 lpMultiByteStr = (LPSTR)malloc(cbMultiByte);
882 if (!lpMultiByteStr)
883 {
884 printf("WideCharToMultiByte: unable to allocate memory for test\n");
885 goto fail;
886 }
887 lpMultiByteStr[cbMultiByte - 1] =
888 (CHAR)0xFF; /* should be overwritten if null terminator is inserted properly */
889 length = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpWideCharStr, cchWideChar + 1,
890 lpMultiByteStr, cbMultiByte, nullptr, nullptr);
891
892 printf("WideCharToMultiByte converted length (BYTE): %d\n", length);
893
894 if (!length)
895 {
896 DWORD error = GetLastError();
897 printf("WideCharToMultiByte error: 0x%08" PRIX32 "\n", error);
898 goto fail;
899 }
900
901 if (length != expected_cbMultiByte)
902 {
903 printf("WideCharToMultiByte unexpected converted length (BYTE): actual: %d expected: %d\n",
904 length, expected_cbMultiByte);
905 goto fail;
906 }
907
908 if (strcmp(lpMultiByteStr, (char*)expected_lpMultiByteStr) != 0)
909 {
910 printf("WideCharToMultiByte unexpected string:\n");
911
912 printf("UTF16 String:\n");
913 string_hexdump(lpWideCharStr, (cchWideChar + 1) * sizeof(WCHAR));
914
915 printf("UTF8 String (actual):\n");
916 string_hexdump((BYTE*)lpMultiByteStr, cbMultiByte);
917
918 printf("UTF8 String (expected):\n");
919 string_hexdump(expected_lpMultiByteStr, expected_cbMultiByte);
920
921 goto fail;
922 }
923
924 printf("WideCharToMultiByte Output UTF8 String:\n");
925 string_hexdump((BYTE*)lpMultiByteStr, cbMultiByte);
926 printf("\n");
927
928 rc = length;
929fail:
930 free(lpMultiByteStr);
931
932 return rc;
933}
934#endif
935
936#if defined(WITH_WINPR_DEPRECATED)
937static BOOL test_unicode_uppercasing(BYTE* lower, BYTE* upper)
938{
939 WCHAR* lowerW = nullptr;
940 int lowerLength = 0;
941 WCHAR* upperW = nullptr;
942 int upperLength = 0;
943
944 lowerLength = ConvertToUnicode(CP_UTF8, 0, (LPSTR)lower, -1, &lowerW, 0);
945 upperLength = ConvertToUnicode(CP_UTF8, 0, (LPSTR)upper, -1, &upperW, 0);
946
947 CharUpperBuffW(lowerW, lowerLength);
948
949 if (_wcscmp(lowerW, upperW) != 0)
950 {
951 printf("Lowercase String:\n");
952 string_hexdump((BYTE*)lowerW, lowerLength * 2);
953
954 printf("Uppercase String:\n");
955 string_hexdump((BYTE*)upperW, upperLength * 2);
956
957 return FALSE;
958 }
959
960 free(lowerW);
961 free(upperW);
962
963 printf("success\n\n");
964 return TRUE;
965}
966#endif
967
968#if defined(WITH_WINPR_DEPRECATED)
969static BOOL test_ConvertFromUnicode_wrapper(void)
970{
971 const BYTE src1[] =
972 "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00\x20\x00\x46\x00"
973 "\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x40\x00\x40\x00\x40\x00";
974 const BYTE src2[] = "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00"
975 "\x20\x00\x46\x00\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x00\x00";
976 /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 */
977 const CHAR cmp0[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T',
978 ' ', 'F', 'O', 'R', 'M', 'A', 'T', 0 };
979 CHAR* dst = nullptr;
980 int i = 0;
981
982 /* Test unterminated unicode string:
983 * ConvertFromUnicode must always null-terminate, even if the src string isn't
984 */
985
986 printf("Input UTF16 String:\n");
987 string_hexdump((const BYTE*)src1, 19 * sizeof(WCHAR));
988
989 i = ConvertFromUnicode(CP_UTF8, 0, (const WCHAR*)src1, 16, &dst, 0, nullptr, nullptr);
990 if (i != 16)
991 {
992 (void)fprintf(stderr,
993 "ConvertFromUnicode failure A1: unexpectedly returned %d instead of 16\n", i);
994 goto fail;
995 }
996 if (dst == nullptr)
997 {
998 (void)fprintf(stderr, "ConvertFromUnicode failure A2: destination is nullptr\n");
999 goto fail;
1000 }
1001 if ((i = strlen(dst)) != 16)
1002 {
1003 (void)fprintf(stderr, "ConvertFromUnicode failure A3: dst length is %d instead of 16\n", i);
1004 goto fail;
1005 }
1006 if (strcmp(dst, cmp0))
1007 {
1008 (void)fprintf(stderr, "ConvertFromUnicode failure A4: data mismatch\n");
1009 goto fail;
1010 }
1011 printf("Output UTF8 String:\n");
1012 string_hexdump((BYTE*)dst, i + 1);
1013
1014 free(dst);
1015 dst = nullptr;
1016
1017 /* Test null-terminated string */
1018
1019 printf("Input UTF16 String:\n");
1020 string_hexdump((const BYTE*)src2, (_wcslen((const WCHAR*)src2) + 1) * sizeof(WCHAR));
1021
1022 i = ConvertFromUnicode(CP_UTF8, 0, (const WCHAR*)src2, -1, &dst, 0, nullptr, nullptr);
1023 if (i != 17)
1024 {
1025 (void)fprintf(stderr,
1026 "ConvertFromUnicode failure B1: unexpectedly returned %d instead of 17\n", i);
1027 goto fail;
1028 }
1029 if (dst == nullptr)
1030 {
1031 (void)fprintf(stderr, "ConvertFromUnicode failure B2: destination is nullptr\n");
1032 goto fail;
1033 }
1034 if ((i = strlen(dst)) != 16)
1035 {
1036 (void)fprintf(stderr, "ConvertFromUnicode failure B3: dst length is %d instead of 16\n", i);
1037 goto fail;
1038 }
1039 if (strcmp(dst, cmp0))
1040 {
1041 (void)fprintf(stderr, "ConvertFromUnicode failure B: data mismatch\n");
1042 goto fail;
1043 }
1044 printf("Output UTF8 String:\n");
1045 string_hexdump((BYTE*)dst, i + 1);
1046
1047 free(dst);
1048 dst = nullptr;
1049
1050 printf("success\n\n");
1051
1052 return TRUE;
1053
1054fail:
1055 free(dst);
1056 return FALSE;
1057}
1058
1059static BOOL test_ConvertToUnicode_wrapper(void)
1060{
1061 /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 */
1062 const CHAR src1[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T', ' ',
1063 'F', 'O', 'R', 'M', 'A', 'T', '@', '@', '@' };
1064 const CHAR src2[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T',
1065 ' ', 'F', 'O', 'R', 'M', 'A', 'T', 0 };
1066 const BYTE cmp0[] = "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00"
1067 "\x20\x00\x46\x00\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x00\x00";
1068 WCHAR* dst = nullptr;
1069 int ii = 0;
1070 size_t i = 0;
1071
1072 /* Test static string buffers of differing sizes */
1073 {
1074 char name[] = "someteststring";
1075 const BYTE cmp[] = { 's', 0, 'o', 0, 'm', 0, 'e', 0, 't', 0, 'e', 0, 's', 0, 't', 0,
1076 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, 0, 0 };
1077 WCHAR xname[128] = WINPR_C_ARRAY_INIT;
1078 LPWSTR aname = nullptr;
1079 LPWSTR wname = &xname[0];
1080 const size_t len = strnlen(name, ARRAYSIZE(name) - 1);
1081 ii = ConvertToUnicode(CP_UTF8, 0, name, len, &wname, ARRAYSIZE(xname));
1082 if (ii != (SSIZE_T)len)
1083 goto fail;
1084
1085 if (memcmp(wname, cmp, sizeof(cmp)) != 0)
1086 goto fail;
1087
1088 ii = ConvertToUnicode(CP_UTF8, 0, name, len, &aname, 0);
1089 if (ii != (SSIZE_T)len)
1090 goto fail;
1091 ii = memcmp(aname, cmp, sizeof(cmp));
1092 free(aname);
1093 if (ii != 0)
1094 goto fail;
1095 }
1096
1097 /* Test unterminated unicode string:
1098 * ConvertToUnicode must always null-terminate, even if the src string isn't
1099 */
1100
1101 printf("Input UTF8 String:\n");
1102 string_hexdump((const BYTE*)src1, 19);
1103
1104 ii = ConvertToUnicode(CP_UTF8, 0, src1, 16, &dst, 0);
1105 if (ii != 16)
1106 {
1107 (void)fprintf(stderr,
1108 "ConvertToUnicode failure A1: unexpectedly returned %d instead of 16\n", ii);
1109 goto fail;
1110 }
1111 i = (size_t)ii;
1112 if (dst == nullptr)
1113 {
1114 (void)fprintf(stderr, "ConvertToUnicode failure A2: destination is nullptr\n");
1115 goto fail;
1116 }
1117 if ((i = _wcslen(dst)) != 16)
1118 {
1119 (void)fprintf(stderr,
1120 "ConvertToUnicode failure A3: dst length is %" PRIuz " instead of 16\n", i);
1121 goto fail;
1122 }
1123 if (_wcscmp(dst, (const WCHAR*)cmp0))
1124 {
1125 (void)fprintf(stderr, "ConvertToUnicode failure A4: data mismatch\n");
1126 goto fail;
1127 }
1128 printf("Output UTF16 String:\n");
1129 string_hexdump((const BYTE*)dst, (i + 1) * sizeof(WCHAR));
1130
1131 free(dst);
1132 dst = nullptr;
1133
1134 /* Test null-terminated string */
1135
1136 printf("Input UTF8 String:\n");
1137 string_hexdump((const BYTE*)src2, strlen(src2) + 1);
1138
1139 i = ConvertToUnicode(CP_UTF8, 0, src2, -1, &dst, 0);
1140 if (i != 17)
1141 {
1142 (void)fprintf(
1143 stderr, "ConvertToUnicode failure B1: unexpectedly returned %" PRIuz " instead of 17\n",
1144 i);
1145 goto fail;
1146 }
1147 if (dst == nullptr)
1148 {
1149 (void)fprintf(stderr, "ConvertToUnicode failure B2: destination is nullptr\n");
1150 goto fail;
1151 }
1152 if ((i = _wcslen(dst)) != 16)
1153 {
1154 (void)fprintf(stderr,
1155 "ConvertToUnicode failure B3: dst length is %" PRIuz " instead of 16\n", i);
1156 goto fail;
1157 }
1158 if (_wcscmp(dst, (const WCHAR*)cmp0))
1159 {
1160 (void)fprintf(stderr, "ConvertToUnicode failure B: data mismatch\n");
1161 goto fail;
1162 }
1163 printf("Output UTF16 String:\n");
1164 string_hexdump((BYTE*)dst, (i + 1) * 2);
1165
1166 free(dst);
1167 dst = nullptr;
1168
1169 printf("success\n\n");
1170
1171 return TRUE;
1172
1173fail:
1174 free(dst);
1175 return FALSE;
1176}
1177#endif
1178
1179#if defined(BUILD_TESTING_INTERNAL)
1180typedef struct
1181{
1182 char* utf8;
1183 size_t utf8len;
1184 char* esc;
1185 size_t esclen;
1186} test_case_t;
1187
1188WINPR_ATTR_NODISCARD
1189static BOOL testEscapeCase(const test_case_t* test)
1190{
1191 WINPR_ASSERT(test);
1192
1193 BOOL rc = FALSE;
1194 size_t dlen = 0;
1195 char* cmp = nullptr;
1196 WINPR_ASSERT(test->utf8len == strlen(test->utf8));
1197 char* str = winpr_utf8ToUtfEscapedString(test->utf8, test->utf8len, &dlen);
1198 if (dlen != test->esclen)
1199 {
1200 (void)fprintf(stderr, "[%s] length 1 mismatch %" PRIuz " vs %" PRIuz, __func__, dlen,
1201 test->esclen);
1202 goto fail;
1203 }
1204 if (strncmp(test->esc, str, test->esclen + 1) != 0)
1205 {
1206 (void)fprintf(stderr, "[%s] strcmp 1 mismatch %s vs %s", __func__, str, test->esc);
1207 goto fail;
1208 }
1209
1210 cmp = strndup(str, dlen);
1211 if (!cmp)
1212 {
1213 (void)fprintf(stderr, "[%s] strndup(%s, %" PRIuz ") mismatch", __func__, str, dlen);
1214 goto fail;
1215 }
1216 WINPR_ASSERT(test->esclen == strlen(test->esc));
1217 const SSIZE_T res = winpr_utfEscapedStringToUtf8(cmp, dlen);
1218 if (res < 0)
1219 {
1220 (void)fprintf(stderr, "[%s] winpr_utfEscapedStringToUtf8(%s, %" PRIuz ") failed", __func__,
1221 cmp, dlen);
1222 goto fail;
1223 }
1224
1225 if ((size_t)res != test->utf8len)
1226 {
1227 (void)fprintf(stderr, "[%s] length 2 mismatch %" PRIdz " vs %" PRIuz, __func__, res,
1228 test->utf8len);
1229 goto fail;
1230 }
1231 if (strncmp(test->utf8, cmp, test->utf8len + 1) != 0)
1232 {
1233 (void)fprintf(stderr, "[%s] strcmp 2 mismatch %s vs %s", __func__, cmp, test->utf8);
1234 goto fail;
1235 }
1236
1237 rc = TRUE;
1238fail:
1239 free(cmp);
1240 free(str);
1241 return rc;
1242}
1243
1244WINPR_ATTR_NODISCARD
1245static BOOL testEscape(void)
1246{
1247 const test_case_t tests[] = {
1248 { "abc", 3, "abc", 3 },
1249 { "՞", 2, "\\u055e", 6 },
1250 { "⟷", 3, "\\u27f7", 6 },
1251 { "π’€€", 4, "\\ud808\\udc00", 12 },
1252 { "՞a⟷bπ’€€c", 12, "\\u055ea\\u27f7b\\ud808\\udc00c", 27 },
1253 { "ΥžβŸ·π’€€π’€€βŸ·βŸ·ΥžΥž", 23, "\\u055e\\u27f7\\ud808\\udc00\\ud808\\udc00\\u27f7\\u27f7\\u055e\\u055e",
1254 60 }
1255 };
1256
1257 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
1258 {
1259 const test_case_t* cur = &tests[x];
1260 if (!testEscapeCase(cur))
1261 {
1262 (void)fprintf(stderr, "Test case %" PRIuz " %s escaped string failed\n", x, cur->utf8);
1263 return FALSE;
1264 }
1265 }
1266 return TRUE;
1267}
1268
1269WINPR_ATTR_NODISCARD
1270static BOOL testUnescapeLiteral(void)
1271{
1272 /* Text that is not a valid \uXXXX escape must pass through unchanged */
1273 const struct
1274 {
1275 const char* in;
1276 const char* out;
1277 } tests[] = { { "C:\\users\\test", "C:\\users\\test" },
1278 { "see \\utils here", "see \\utils here" },
1279 { "end \\u12", "end \\u12" },
1280 { "\\uzzzz\\u0041", "\\uzzzzA" },
1281 { "\\u005cu0041", "\\u0041" } };
1282
1283 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
1284 {
1285 char* str = _strdup(tests[x].in);
1286 if (!str)
1287 return FALSE;
1288
1289 const SSIZE_T res = winpr_utfEscapedStringToUtf8(str, strlen(str));
1290 const BOOL ok =
1291 (res >= 0) && ((size_t)res == strlen(tests[x].out)) && (strcmp(str, tests[x].out) == 0);
1292 if (!ok)
1293 (void)fprintf(stderr, "unescape '%s': got '%s', expected '%s'\n", tests[x].in, str,
1294 tests[x].out);
1295 free(str);
1296 if (!ok)
1297 return FALSE;
1298 }
1299 return TRUE;
1300}
1301#endif
1302
1303int TestUnicodeConversion(int argc, char* argv[])
1304{
1305 WINPR_UNUSED(argc);
1306 WINPR_UNUSED(argv);
1307
1308 if (!test_conversion(unit_testcases, ARRAYSIZE(unit_testcases)))
1309 return -1;
1310
1311#if defined(WITH_WINPR_DEPRECATED)
1312 if (!test_win_conversion(unit_testcases, ARRAYSIZE(unit_testcases)))
1313 return -1;
1314
1315 /* Letters */
1316
1317 printf("Letters\n");
1318
1319 if (convert_utf8_to_utf16(c_cedilla_UTF8, c_cedilla_UTF16, c_cedilla_cchWideChar) < 1)
1320 return -1;
1321
1322 if (convert_utf16_to_utf8(c_cedilla_UTF16, c_cedilla_UTF8, c_cedilla_cbMultiByte) < 1)
1323 return -1;
1324
1325 /* English */
1326
1327 printf("English\n");
1328
1329 if (convert_utf8_to_utf16(en_Hello_UTF8, en_Hello_UTF16, en_Hello_cchWideChar) < 1)
1330 return -1;
1331 if (convert_utf8_to_utf16(en_HowAreYou_UTF8, en_HowAreYou_UTF16, en_HowAreYou_cchWideChar) < 1)
1332 return -1;
1333
1334 if (convert_utf16_to_utf8(en_Hello_UTF16, en_Hello_UTF8, en_Hello_cbMultiByte) < 1)
1335 return -1;
1336 if (convert_utf16_to_utf8(en_HowAreYou_UTF16, en_HowAreYou_UTF8, en_HowAreYou_cbMultiByte) < 1)
1337 return -1;
1338
1339 /* French */
1340
1341 printf("French\n");
1342
1343 if (convert_utf8_to_utf16(fr_Hello_UTF8, fr_Hello_UTF16, fr_Hello_cchWideChar) < 1)
1344 return -1;
1345 if (convert_utf8_to_utf16(fr_HowAreYou_UTF8, fr_HowAreYou_UTF16, fr_HowAreYou_cchWideChar) < 1)
1346 return -1;
1347
1348 if (convert_utf16_to_utf8(fr_Hello_UTF16, fr_Hello_UTF8, fr_Hello_cbMultiByte) < 1)
1349 return -1;
1350 if (convert_utf16_to_utf8(fr_HowAreYou_UTF16, fr_HowAreYou_UTF8, fr_HowAreYou_cbMultiByte) < 1)
1351 return -1;
1352
1353 /* Russian */
1354
1355 printf("Russian\n");
1356
1357 if (convert_utf8_to_utf16(ru_Hello_UTF8, ru_Hello_UTF16, ru_Hello_cchWideChar) < 1)
1358 return -1;
1359 if (convert_utf8_to_utf16(ru_HowAreYou_UTF8, ru_HowAreYou_UTF16, ru_HowAreYou_cchWideChar) < 1)
1360 return -1;
1361
1362 if (convert_utf16_to_utf8(ru_Hello_UTF16, ru_Hello_UTF8, ru_Hello_cbMultiByte) < 1)
1363 return -1;
1364 if (convert_utf16_to_utf8(ru_HowAreYou_UTF16, ru_HowAreYou_UTF8, ru_HowAreYou_cbMultiByte) < 1)
1365 return -1;
1366
1367 /* Arabic */
1368
1369 printf("Arabic\n");
1370
1371 if (convert_utf8_to_utf16(ar_Hello_UTF8, ar_Hello_UTF16, ar_Hello_cchWideChar) < 1)
1372 return -1;
1373 if (convert_utf8_to_utf16(ar_HowAreYou_UTF8, ar_HowAreYou_UTF16, ar_HowAreYou_cchWideChar) < 1)
1374 return -1;
1375
1376 if (convert_utf16_to_utf8(ar_Hello_UTF16, ar_Hello_UTF8, ar_Hello_cbMultiByte) < 1)
1377 return -1;
1378 if (convert_utf16_to_utf8(ar_HowAreYou_UTF16, ar_HowAreYou_UTF8, ar_HowAreYou_cbMultiByte) < 1)
1379 return -1;
1380
1381 /* Chinese */
1382
1383 printf("Chinese\n");
1384
1385 if (convert_utf8_to_utf16(ch_Hello_UTF8, ch_Hello_UTF16, ch_Hello_cchWideChar) < 1)
1386 return -1;
1387 if (convert_utf8_to_utf16(ch_HowAreYou_UTF8, ch_HowAreYou_UTF16, ch_HowAreYou_cchWideChar) < 1)
1388 return -1;
1389
1390 if (convert_utf16_to_utf8(ch_Hello_UTF16, ch_Hello_UTF8, ch_Hello_cbMultiByte) < 1)
1391 return -1;
1392 if (convert_utf16_to_utf8(ch_HowAreYou_UTF16, ch_HowAreYou_UTF8, ch_HowAreYou_cbMultiByte) < 1)
1393 return -1;
1394
1395#endif
1396
1397 /* Uppercasing */
1398#if defined(WITH_WINPR_DEPRECATED)
1399 printf("Uppercasing\n");
1400
1401 if (!test_unicode_uppercasing(ru_Administrator_lower, ru_Administrator_upper))
1402 return -1;
1403#endif
1404
1405 /* ConvertFromUnicode */
1406#if defined(WITH_WINPR_DEPRECATED)
1407 printf("ConvertFromUnicode\n");
1408
1409 if (!test_ConvertFromUnicode_wrapper())
1410 return -1;
1411
1412 /* ConvertToUnicode */
1413
1414 printf("ConvertToUnicode\n");
1415
1416 if (!test_ConvertToUnicode_wrapper())
1417 return -1;
1418#endif
1419 /*
1420
1421 printf("----------------------------------------------------------\n\n");
1422
1423 if (0)
1424 {
1425 BYTE src[] = { 'R',0,'I',0,'C',0,'H',0,' ',0, 'T',0,'E',0,'X',0,'T',0,'
1426 ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 };
1427 //BYTE src[] = { 'R',0,'I',0,'C',0,'H',0,' ',0, 0,0, 'T',0,'E',0,'X',0,'T',0,'
1428 ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 };
1429 //BYTE src[] = { 0,0,'R',0,'I',0,'C',0,'H',0,' ',0, 'T',0,'E',0,'X',0,'T',0,'
1430 ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 }; char* dst = nullptr; int num; num =
1431 ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) src, 16, &dst, 0, nullptr, nullptr);
1432 printf("ConvertFromUnicode returned %d dst=[%s]\n", num, dst);
1433 string_hexdump((BYTE*)dst, num+1);
1434 }
1435 if (1)
1436 {
1437 char src[] = "RICH TEXT FORMAT@@@@@@";
1438 WCHAR *dst = nullptr;
1439 int num;
1440 num = ConvertToUnicode(CP_UTF8, 0, src, 16, &dst, 0);
1441 printf("ConvertToUnicode returned %d dst=%p\n", num, (void*) dst);
1442 string_hexdump((BYTE*)dst, num * 2 + 2);
1443
1444 }
1445 */
1446
1447#if defined(BUILD_TESTING_INTERNAL)
1448 if (!testEscape())
1449 return -1;
1450 if (!testUnescapeLiteral())
1451 return -1;
1452#endif
1453
1454 return 0;
1455}