a3f6e8ff469ba9a2865e7d1996ed52ed80be0bff
[reactos.git] / modules / rostests / apitests / kernel32 / MultiByteToWideChar.c
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Test for MultiByteToWideChar
5 * PROGRAMMERS: Mike "tamlin" Nordell
6 * Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7 */
8
9 #include "precomp.h"
10
11 /* NOTE: Tested on Win10. We follow Win10 in this function.
12 Win10 might alter its design in future. */
13
14 /* TODO: Russian, French, Korean etc. codepages */
15
16 #define CP932 932 /* Japanese Shift_JIS (SJIS) codepage */
17
18 /* "Japanese" in Japanese UTF-8 */
19 static const char UTF8_Japanese[] = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E";
20 /* "Japanese" in Japanese Shift_JIS */
21 static const char SJIS_Japanese[] = "\x93\xFA\x96\x7B\x8C\xEA";
22
23 #define MAX_BUFFER 8
24
25 /* test entry */
26 typedef struct ENTRY
27 {
28 int LineNo;
29 int Return;
30 DWORD Error;
31 UINT CodePage;
32 DWORD Flags;
33 const char *Src;
34 int SrcLen;
35 int DestLen;
36 WCHAR CheckDest[MAX_BUFFER];
37 int CheckLen;
38 BOOL SamePointer;
39 } ENTRY;
40
41 static const ENTRY Entries[] =
42 {
43 /* without buffer */
44 { __LINE__, 1, 0xBEAF, CP_UTF8, 0, "a", 1 },
45 { __LINE__, 2, 0xBEAF, CP_UTF8, 0, "a", 2 },
46 { __LINE__, 2, 0xBEAF, CP_UTF8, MB_ERR_INVALID_CHARS, "a", 2 },
47 /* negative length */
48 { __LINE__, 2, 0xBEAF, CP_UTF8, 0, "a", -1 },
49 { __LINE__, 2, 0xBEAF, CP_UTF8, 0, "a", -2 },
50 { __LINE__, 2, 0xBEAF, CP_UTF8, MB_ERR_INVALID_CHARS, "a", -1 },
51 { __LINE__, 2, 0xBEAF, CP_UTF8, MB_ERR_INVALID_CHARS, "a", -3 },
52 /* with buffer */
53 { __LINE__, 1, 0xBEAF, CP_UTF8, 0, "a", 1, 1, {'a', 0x7F7F}, 2 },
54 { __LINE__, 2, 0xBEAF, CP_UTF8, 0, "a", 2, 4, {'a', 0, 0x7F7F}, 3 },
55 { __LINE__, 2, 0xBEAF, CP_UTF8, MB_ERR_INVALID_CHARS, "a", 2, 4, {'a', 0, 0x7F7F}, 3 },
56 /* short buffer */
57 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, 0, "a", 2, 1, {'a', 0x7F7F}, 2 },
58 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, 0, "a", 2, 1, {'a', 0x7F7F}, 2 },
59 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, MB_ERR_INVALID_CHARS, "a", 2, 1, {'a', 0x7F7F}, 2 },
60 /* same pointer */
61 { __LINE__, 0, ERROR_INVALID_PARAMETER, CP_UTF8, 0, "", 1, 1, { 0x7F7F }, 1, TRUE },
62 { __LINE__, 0, ERROR_INVALID_PARAMETER, CP_UTF8, MB_ERR_INVALID_CHARS, "", 1, 1, { 0x7F7F }, 1, TRUE },
63 /* invalid UTF-8 sequences without buffer */
64 { __LINE__, 2, 0xBEAF, CP_UTF8, 0, "\xC0", 2 },
65 { __LINE__, 4, 0xBEAF, CP_UTF8, 0, "\xC0\xC0\x80", 4 },
66 { __LINE__, 3, 0xBEAF, CP_UTF8, 0, "\xE0\xC0", 3 },
67 { __LINE__, 4, 0xBEAF, CP_UTF8, 0, "\xE0\x20\xC0", 4 },
68 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xC0", 2 },
69 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xC0\xC0\x80", 4 },
70 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xE0\xC0", 3 },
71 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xE0\x20\xC0", 4 },
72 /* invalid UTF-8 sequences with buffer */
73 { __LINE__, 2, 0xBEAF, CP_UTF8, 0, "\xC0", 2, 4, {0xFFFD, 0, 0x7F7F}, 3},
74 { __LINE__, 4, 0xBEAF, CP_UTF8, 0, "\xC0\xC0\x80", 4, 5, {0xFFFD, 0xFFFD, 0xFFFD, 0, 0x7F7F}, 5 },
75 { __LINE__, 3, 0xBEAF, CP_UTF8, 0, "\xE0\xC0", 3, 4, {0xFFFD, 0xFFFD, 0, 0x7F7F}, 4 },
76 { __LINE__, 4, 0xBEAF, CP_UTF8, 0, "\xE0\x20\xC0", 4, 5, {0xFFFD, 0x0020, 0xFFFD, 0, 0x7F7F}, 5 },
77 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xC0", 2, 4, {0xFFFD, 0, 0x7F7F}, 3 },
78 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xC0\xC0\x80", 4, 5, {0xFFFD, 0xFFFD, 0xFFFD, 0, 0x7F7F}, 5 },
79 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xE0\xC0", 3, 4, {0xFFFD, 0xFFFD, 0, 0x7F7F}, 4 },
80 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, "\xE0\x20\xC0", 4, 5, {0xFFFD, 0x0020, 0xFFFD, 0, 0x7F7F}, 5 },
81 /* invalid UTF-8 sequences with short buffer */
82 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, 0, "\xC0", 2, 1, {0xFFFD, 0x7F7F}, 2},
83 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, 0, "\xC0\xC0\x80", 4, 1, {0xFFFD, 0x7F7F}, 2 },
84 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, 0, "\xE0\xC0", 3, 1, {0xFFFD, 0x7F7F}, 2 },
85 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, 0, "\xE0\x20\xC0", 4, 1, {0xFFFD, 0x7F7F}, 2 },
86 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, MB_ERR_INVALID_CHARS, "\xC0", 2, 1, {0xFFFD, 0x7F7F}, 2 },
87 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, MB_ERR_INVALID_CHARS, "\xC0\xC0\x80", 4, 1, {0xFFFD, 0x7F7F}, 2 },
88 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, MB_ERR_INVALID_CHARS, "\xE0\xC0", 3, 1, {0xFFFD, 0x7F7F}, 2 },
89 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, MB_ERR_INVALID_CHARS, "\xE0\x20\xC0", 4, 1, {0xFFFD, 0x7F7F}, 2 },
90 /* Japanese UTF-8 without buffer */
91 { __LINE__, 4, 0xBEAF, CP_UTF8, 0, UTF8_Japanese, sizeof(UTF8_Japanese) },
92 { __LINE__, 4, 0xBEAF, CP_UTF8, MB_ERR_INVALID_CHARS, UTF8_Japanese, sizeof(UTF8_Japanese) },
93 /* Japanese UTF-8 with buffer */
94 { __LINE__, 4, 0xBEAF, CP_UTF8, 0, UTF8_Japanese, sizeof(UTF8_Japanese), 4, {0x65E5, 0x672C, 0x8A9E, 0, 0x7F7F}, 5 },
95 { __LINE__, 4, 0xBEAF, CP_UTF8, MB_ERR_INVALID_CHARS, UTF8_Japanese, sizeof(UTF8_Japanese), 4, {0x65E5, 0x672C, 0x8A9E, 0, 0x7F7F}, 5 },
96 /* Japanese UTF-8 with short buffer */
97 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, 0, UTF8_Japanese, sizeof(UTF8_Japanese), 1, {0x65E5, 0x7F7F}, 2 },
98 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP_UTF8, MB_ERR_INVALID_CHARS, UTF8_Japanese, sizeof(UTF8_Japanese), 1, {0x65E5, 0x7F7F}, 2 },
99 /* Japanese UTF-8 truncated source */
100 { __LINE__, 1, 0xBEAF, CP_UTF8, 0, UTF8_Japanese, 1, 4, {0xFFFD, 0x7F7F}, 2 },
101 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP_UTF8, MB_ERR_INVALID_CHARS, UTF8_Japanese, 1, 4, {0xFFFD, 0x7F7F}, 2 },
102 /* Japanese CP932 without buffer */
103 { __LINE__, 4, 0xBEAF, CP932, 0, SJIS_Japanese, sizeof(SJIS_Japanese) },
104 { __LINE__, 4, 0xBEAF, CP932, MB_ERR_INVALID_CHARS, SJIS_Japanese, sizeof(SJIS_Japanese) },
105 /* Japanese CP932 with buffer */
106 { __LINE__, 4, 0xBEAF, CP932, 0, SJIS_Japanese, sizeof(SJIS_Japanese), 4, {0x65E5, 0x672C, 0x8A9E, 0, 0x7F7F}, 5 },
107 { __LINE__, 4, 0xBEAF, CP932, MB_ERR_INVALID_CHARS, SJIS_Japanese, sizeof(SJIS_Japanese), 4, {0x65E5, 0x672C, 0x8A9E, 0, 0x7F7F}, 5 },
108 /* Japanese CP932 with short buffer */
109 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP932, 0, SJIS_Japanese, sizeof(SJIS_Japanese), 1, {0x65E5, 0x7F7F}, 2 },
110 { __LINE__, 0, ERROR_INSUFFICIENT_BUFFER, CP932, MB_ERR_INVALID_CHARS, SJIS_Japanese, sizeof(SJIS_Japanese), 1, {0x65E5, 0x7F7F}, 2 },
111 /* Japanese CP932 truncated source */
112 { __LINE__, 1, 0xBEAF, CP932, 0, SJIS_Japanese, 1, 4, {0x30FB, 0x7F7F}, 2 },
113 { __LINE__, 0, ERROR_NO_UNICODE_TRANSLATION, CP932, MB_ERR_INVALID_CHARS, SJIS_Japanese, 1, 4, {0x7F7F, 0x7F7F}, 2 },
114 };
115
116 static void TestEntry(const ENTRY *pEntry)
117 {
118 int ret, i;
119 WCHAR Buffer[MAX_BUFFER];
120 DWORD Error;
121
122 FillMemory(Buffer, sizeof(Buffer), 0x7F);
123 SetLastError(0xBEAF);
124
125 if (pEntry->DestLen == 0)
126 {
127 /* dest is NULL */
128 ret = MultiByteToWideChar(pEntry->CodePage, pEntry->Flags, pEntry->Src,
129 pEntry->SrcLen, NULL, 0);
130 }
131 else
132 {
133 ok(pEntry->DestLen >= pEntry->CheckLen - 1,
134 "Line %d: DestLen was shorter than (CheckLen - 1)\n", pEntry->LineNo);
135
136 if (pEntry->SamePointer)
137 {
138 /* src ptr == dest ptr */
139 ret = MultiByteToWideChar(pEntry->CodePage, pEntry->Flags,
140 (const char *)Buffer, pEntry->SrcLen,
141 Buffer, pEntry->DestLen);
142 }
143 else
144 {
145 /* src ptr != dest ptr */
146 ret = MultiByteToWideChar(pEntry->CodePage, pEntry->Flags,
147 pEntry->Src, pEntry->SrcLen,
148 Buffer, pEntry->DestLen);
149 }
150 }
151
152 Error = GetLastError();
153
154 /* check ret */
155 ok(ret == pEntry->Return, "Line %d: ret expected %d, got %d\n",
156 pEntry->LineNo, pEntry->Return, ret);
157
158 /* check error code */
159 ok(Error == pEntry->Error,
160 "Line %d: Wrong last error. Expected %lu, got %lu\n",
161 pEntry->LineNo, pEntry->Error, Error);
162
163 if (pEntry->DestLen)
164 {
165 /* check buffer */
166 for (i = 0; i < pEntry->CheckLen; ++i)
167 {
168 ok(Buffer[i] == pEntry->CheckDest[i], "Line %d: Buffer[%d] expected %d, got %d\n",
169 pEntry->LineNo, i, pEntry->CheckDest[i], Buffer[i]);
170 }
171 }
172 }
173
174 typedef NTSTATUS (WINAPI* RTLGETVERSION)(PRTL_OSVERSIONINFOW);
175
176 static RTL_OSVERSIONINFOW *GetRealOSVersion(void)
177 {
178 static RTL_OSVERSIONINFOW osvi = { 0 };
179 RTL_OSVERSIONINFOW *ptr = NULL;
180 HINSTANCE hNTDLL = LoadLibraryW(L"ntdll.dll");
181 RTLGETVERSION fn;
182 if (hNTDLL)
183 {
184 fn = (RTLGETVERSION)GetProcAddress(hNTDLL, "RtlGetVersion");
185 if (fn)
186 {
187 osvi.dwOSVersionInfoSize = sizeof(osvi);
188 if (fn(&osvi) == STATUS_SUCCESS)
189 {
190 ptr = &osvi;
191 }
192 }
193 FreeLibrary(hNTDLL);
194 }
195 return ptr;
196 }
197
198 static BOOL IsWin10Plus(void)
199 {
200 RTL_OSVERSIONINFOW *info = GetRealOSVersion();
201 if (!info)
202 return FALSE;
203
204 return info->dwMajorVersion >= 10;
205 }
206
207 static BOOL IsReactOS(void)
208 {
209 WCHAR szWinDir[MAX_PATH];
210 GetWindowsDirectoryW(szWinDir, _countof(szWinDir));
211 return (wcsstr(szWinDir, L"ReactOS") != NULL);
212 }
213
214 START_TEST(MultiByteToWideChar)
215 {
216 size_t i;
217
218 if (!IsWin10Plus() && !IsReactOS())
219 {
220 trace("This test is designed for Windows 10+ and ReactOS.\n"
221 "It is expected to report some failures on older Windows versions.\n");
222 #if 0
223 skip("");
224 return;
225 #endif
226 }
227
228 for (i = 0; i < _countof(Entries); ++i)
229 {
230 TestEntry(&Entries[i]);
231 }
232 }