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