1*c2c66affSColin Finck /*
2*c2c66affSColin Finck  * PROJECT:         ReactOS API tests
3*c2c66affSColin Finck  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4*c2c66affSColin Finck  * PURPOSE:         Test for WideCharToMultiByte
5*c2c66affSColin Finck  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6*c2c66affSColin Finck  */
7*c2c66affSColin Finck 
8*c2c66affSColin Finck #include <apitest.h>
9*c2c66affSColin Finck 
10*c2c66affSColin Finck #include <winnls.h>
11*c2c66affSColin Finck 
12*c2c66affSColin Finck #define ntv6(x) (LOBYTE(LOWORD(GetVersion())) >= 6 ? (x) : 0)
13*c2c66affSColin Finck 
14*c2c66affSColin Finck static
15*c2c66affSColin Finck VOID
16*c2c66affSColin Finck Utf8Convert_(
17*c2c66affSColin Finck     _In_ PCWSTR WideString,
18*c2c66affSColin Finck     _In_ PCSTR ExpectedUtf8_2003,
19*c2c66affSColin Finck     _In_ PCSTR ExpectedUtf8_Vista,
20*c2c66affSColin Finck     _In_ BOOL IsInvalid,
21*c2c66affSColin Finck     _In_ PCSTR File,
22*c2c66affSColin Finck     _In_ INT Line)
23*c2c66affSColin Finck {
24*c2c66affSColin Finck     size_t WideLen;
25*c2c66affSColin Finck     size_t Utf8Len;
26*c2c66affSColin Finck     char Buffer[32];
27*c2c66affSColin Finck     int Ret;
28*c2c66affSColin Finck     ULONG i;
29*c2c66affSColin Finck     ULONG Error;
30*c2c66affSColin Finck     PCSTR ExpectedUtf8;
31*c2c66affSColin Finck 
32*c2c66affSColin Finck     ExpectedUtf8 = ntv6(1) ? ExpectedUtf8_Vista : ExpectedUtf8_2003;
33*c2c66affSColin Finck     WideLen = wcslen(WideString);
34*c2c66affSColin Finck     Utf8Len = strlen(ExpectedUtf8);
35*c2c66affSColin Finck 
36*c2c66affSColin Finck     /* Get length only */
37*c2c66affSColin Finck     Ret = WideCharToMultiByte(CP_UTF8, 0, WideString, WideLen, NULL, 0, NULL, NULL);
38*c2c66affSColin Finck     ok_(File, Line)(Ret == Utf8Len, "Length check: Ret = %d\n", Ret);
39*c2c66affSColin Finck 
40*c2c66affSColin Finck     /* Get length including nul */
41*c2c66affSColin Finck     Ret = WideCharToMultiByte(CP_UTF8, 0, WideString, WideLen + 1, NULL, 0, NULL, NULL);
42*c2c66affSColin Finck     ok_(File, Line)(Ret == Utf8Len + 1, "Length check with null: Ret = %d\n", Ret);
43*c2c66affSColin Finck 
44*c2c66affSColin Finck     /* Convert, excluding null */
45*c2c66affSColin Finck     FillMemory(Buffer, sizeof(Buffer), 0x55);
46*c2c66affSColin Finck     Ret = WideCharToMultiByte(CP_UTF8, 0, WideString, WideLen, Buffer, sizeof(Buffer), NULL, NULL);
47*c2c66affSColin Finck     ok_(File, Line)(Ret == Utf8Len, "Convert: Ret = %d\n", Ret);
48*c2c66affSColin Finck     for (i = 0; i < Utf8Len; i++)
49*c2c66affSColin Finck     {
50*c2c66affSColin Finck         ok_(File, Line)(Buffer[i] == ExpectedUtf8[i], "Convert: Buffer[%lu] = 0x%x, expected 0x%x\n", i, (unsigned char)Buffer[i], (unsigned char)ExpectedUtf8[i]);
51*c2c66affSColin Finck     }
52*c2c66affSColin Finck 
53*c2c66affSColin Finck     /* Convert, including null */
54*c2c66affSColin Finck     FillMemory(Buffer, sizeof(Buffer), 0x55);
55*c2c66affSColin Finck     Ret = WideCharToMultiByte(CP_UTF8, 0, WideString, WideLen + 1, Buffer, sizeof(Buffer), NULL, NULL);
56*c2c66affSColin Finck     ok_(File, Line)(Ret == Utf8Len + 1, "Convert with null: Ret = %d\n", Ret);
57*c2c66affSColin Finck     for (i = 0; i < Utf8Len + 1; i++)
58*c2c66affSColin Finck     {
59*c2c66affSColin Finck         ok_(File, Line)(Buffer[i] == ExpectedUtf8[i], "Convert with null: Buffer[%lu] = 0x%x, expected 0x%x\n", i, (unsigned char)Buffer[i], (unsigned char)ExpectedUtf8[i]);
60*c2c66affSColin Finck     }
61*c2c66affSColin Finck 
62*c2c66affSColin Finck     /* Get length, reject invalid */
63*c2c66affSColin Finck     SetLastError(0xfeedf00d);
64*c2c66affSColin Finck     Ret = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, WideString, WideLen + 1, NULL, 0, NULL, NULL);
65*c2c66affSColin Finck     Error = GetLastError();
66*c2c66affSColin Finck     if (!ntv6(1))
67*c2c66affSColin Finck     {
68*c2c66affSColin Finck         ok_(File, Line)(Ret == 0, "Length check, reject invalid, NT5: Ret = %d\n", Ret);
69*c2c66affSColin Finck         ok_(File, Line)(Error == ERROR_INVALID_FLAGS, "Length check, reject invalid, NT5: Error = %lu\n", Error);
70*c2c66affSColin Finck     }
71*c2c66affSColin Finck     else if (IsInvalid)
72*c2c66affSColin Finck     {
73*c2c66affSColin Finck         ok_(File, Line)(Ret == 0, "Length check, reject invalid: Ret = %d\n", Ret);
74*c2c66affSColin Finck         ok_(File, Line)(Error == ERROR_NO_UNICODE_TRANSLATION, "Length check, reject invalid: Error = %lu\n", Error);
75*c2c66affSColin Finck     }
76*c2c66affSColin Finck     else
77*c2c66affSColin Finck     {
78*c2c66affSColin Finck         ok_(File, Line)(Ret == Utf8Len + 1, "Length check, reject invalid: Ret = %d\n", Ret);
79*c2c66affSColin Finck     }
80*c2c66affSColin Finck 
81*c2c66affSColin Finck     /* Convert, reject invalid */
82*c2c66affSColin Finck     FillMemory(Buffer, sizeof(Buffer), 0x55);
83*c2c66affSColin Finck     SetLastError(0xfeedf00d);
84*c2c66affSColin Finck     Ret = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, WideString, WideLen + 1, Buffer, sizeof(Buffer), NULL, NULL);
85*c2c66affSColin Finck     Error = GetLastError();
86*c2c66affSColin Finck     if (!ntv6(1))
87*c2c66affSColin Finck     {
88*c2c66affSColin Finck         ok_(File, Line)(Ret == 0, "Convert, reject invalid, NT5: Ret = %d\n", Ret);
89*c2c66affSColin Finck         ok_(File, Line)(Error == ERROR_INVALID_FLAGS, "Convert, reject invalid, NT5: Error = %lu\n", Error);
90*c2c66affSColin Finck     }
91*c2c66affSColin Finck     else if (IsInvalid)
92*c2c66affSColin Finck     {
93*c2c66affSColin Finck         ok_(File, Line)(Ret == 0, "Convert, reject invalid: Ret = %d\n", Ret);
94*c2c66affSColin Finck         ok_(File, Line)(Error == ERROR_NO_UNICODE_TRANSLATION, "Convert, reject invalid: Error = %lu\n", Error);
95*c2c66affSColin Finck         for (i = 0; i < Utf8Len + 1; i++)
96*c2c66affSColin Finck         {
97*c2c66affSColin Finck             ok_(File, Line)(Buffer[i] == ExpectedUtf8[i], "Convert, reject invalid: Buffer[%lu] = 0x%x, expected 0x%x\n", i, (unsigned char)Buffer[i], (unsigned char)ExpectedUtf8[i]);
98*c2c66affSColin Finck         }
99*c2c66affSColin Finck     }
100*c2c66affSColin Finck     else
101*c2c66affSColin Finck     {
102*c2c66affSColin Finck         ok_(File, Line)(Ret == Utf8Len + 1, "Convert, reject invalid: Ret = %d\n", Ret);
103*c2c66affSColin Finck         for (i = 0; i < Utf8Len + 1; i++)
104*c2c66affSColin Finck         {
105*c2c66affSColin Finck             ok_(File, Line)(Buffer[i] == ExpectedUtf8[i], "Convert, reject invalid: Buffer[%lu] = 0x%x, expected 0x%x\n", i, (unsigned char)Buffer[i], (unsigned char)ExpectedUtf8[i]);
106*c2c66affSColin Finck         }
107*c2c66affSColin Finck     }
108*c2c66affSColin Finck }
109*c2c66affSColin Finck #define Utf8Convert(w, e, i) Utf8Convert_(w, e, e, i, __FILE__, __LINE__)
110*c2c66affSColin Finck #define Utf8Convert_Vista(w, e, i, e2) Utf8Convert_(w, e, e2, i, __FILE__, __LINE__)
111*c2c66affSColin Finck 
112*c2c66affSColin Finck static
113*c2c66affSColin Finck VOID
114*c2c66affSColin Finck TestUtf8(VOID)
115*c2c66affSColin Finck {
116*c2c66affSColin Finck     Utf8Convert(L"", "", FALSE);
117*c2c66affSColin Finck 
118*c2c66affSColin Finck     /* Various character ranges */
119*c2c66affSColin Finck     Utf8Convert(L"A", "A", FALSE);
120*c2c66affSColin Finck     Utf8Convert(L"\x007f", "\x7f", FALSE);
121*c2c66affSColin Finck     Utf8Convert(L"\x0080", "\xc2\x80", FALSE);
122*c2c66affSColin Finck     Utf8Convert(L"\x00ff", "\xc3\xbf", FALSE);
123*c2c66affSColin Finck     Utf8Convert(L"\x0100", "\xc4\x80", FALSE);
124*c2c66affSColin Finck     Utf8Convert(L"\x07ff", "\xdf\xbf", FALSE);
125*c2c66affSColin Finck     Utf8Convert(L"\x0800", "\xe0\xa0\x80", FALSE);
126*c2c66affSColin Finck     Utf8Convert(L"\xd7ff", "\xed\x9f\xbf", FALSE);
127*c2c66affSColin Finck     Utf8Convert(L"\xe000", "\xee\x80\x80", FALSE);
128*c2c66affSColin Finck     Utf8Convert(L"\xffff", "\xef\xbf\xbf", FALSE);
129*c2c66affSColin Finck 
130*c2c66affSColin Finck     /* surrogate pairs */
131*c2c66affSColin Finck     Utf8Convert(L"\xd800\xdc00", "\xf0\x90\x80\x80", FALSE); /* U+10000 */
132*c2c66affSColin Finck     Utf8Convert(L"\xd800\xdfff", "\xf0\x90\x8f\xbf", FALSE); /* U+103ff */
133*c2c66affSColin Finck     Utf8Convert(L"\xd801\xdc00", "\xf0\x90\x90\x80", FALSE); /* U+10400 */
134*c2c66affSColin Finck     Utf8Convert(L"\xdbff\xdfff", "\xf4\x8f\xbf\xbf", FALSE); /* U+10ffff */
135*c2c66affSColin Finck 
136*c2c66affSColin Finck     /* standalone lead surrogate becomes 0xfffd on Vista, goes through verbatim on 2003 */
137*c2c66affSColin Finck     Utf8Convert_Vista(L"\xd800", "\xed\xa0\x80", TRUE,
138*c2c66affSColin Finck                                  "\xef\xbf\xbd");
139*c2c66affSColin Finck     Utf8Convert_Vista(L"\xd800-", "\xed\xa0\x80-", TRUE,
140*c2c66affSColin Finck                                   "\xef\xbf\xbd-");
141*c2c66affSColin Finck     Utf8Convert_Vista(L"\xdbff", "\xed\xaf\xbf", TRUE,
142*c2c66affSColin Finck                                  "\xef\xbf\xbd");
143*c2c66affSColin Finck     Utf8Convert_Vista(L"\xdbff-", "\xed\xaf\xbf-", TRUE,
144*c2c66affSColin Finck                                   "\xef\xbf\xbd-");
145*c2c66affSColin Finck 
146*c2c66affSColin Finck     /* standalone trail surrogate becomes 0xfffd */
147*c2c66affSColin Finck     Utf8Convert_Vista(L"\xdc00", "\xed\xb0\x80", TRUE,
148*c2c66affSColin Finck                                  "\xef\xbf\xbd");
149*c2c66affSColin Finck     Utf8Convert_Vista(L"\xdc00-", "\xed\xb0\x80-", TRUE,
150*c2c66affSColin Finck                                   "\xef\xbf\xbd-");
151*c2c66affSColin Finck     Utf8Convert_Vista(L"\xdfff", "\xed\xbf\xbf", TRUE,
152*c2c66affSColin Finck                                  "\xef\xbf\xbd");
153*c2c66affSColin Finck     Utf8Convert_Vista(L"\xdfff-", "\xed\xbf\xbf-", TRUE,
154*c2c66affSColin Finck                                   "\xef\xbf\xbd-");
155*c2c66affSColin Finck 
156*c2c66affSColin Finck     /* Reverse surrogate pair */
157*c2c66affSColin Finck     Utf8Convert_Vista(L"\xdfff\xdbff", "\xed\xbf\xbf\xed\xaf\xbf", TRUE,
158*c2c66affSColin Finck                                        "\xef\xbf\xbd\xef\xbf\xbd");
159*c2c66affSColin Finck 
160*c2c66affSColin Finck     /* Byte order marks */
161*c2c66affSColin Finck     Utf8Convert(L"\xfeff", "\xef\xbb\xbf", FALSE);
162*c2c66affSColin Finck     Utf8Convert(L"\xfffe", "\xef\xbf\xbe", FALSE);
163*c2c66affSColin Finck 
164*c2c66affSColin Finck     /* canonically equivalent representations -- no normalization should happen */
165*c2c66affSColin Finck     Utf8Convert(L"\x1e09", "\xe1\xb8\x89", FALSE);
166*c2c66affSColin Finck     Utf8Convert(L"\x0107\x0327", "\xc4\x87\xcc\xa7", FALSE);
167*c2c66affSColin Finck     Utf8Convert(L"\x00e7\x0301", "\xc3\xa7\xcc\x81", FALSE);
168*c2c66affSColin Finck     Utf8Convert(L"\x0063\x0327\x0301", "\x63\xcc\xa7\xcc\x81", FALSE);
169*c2c66affSColin Finck     Utf8Convert(L"\x0063\x0301\x0327", "\x63\xcc\x81\xcc\xa7", FALSE);
170*c2c66affSColin Finck }
171*c2c66affSColin Finck 
172*c2c66affSColin Finck START_TEST(WideCharToMultiByte)
173*c2c66affSColin Finck {
174*c2c66affSColin Finck     TestUtf8();
175*c2c66affSColin Finck }
176