mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / kernel32 / tests / locale.c
blob75c86bbaf216e6bc4abdb18e4865e68a6e0a37d0
1 /*
2 * Unit tests for locale functions
4 * Copyright 2002 YASAR Mehmet
5 * Copyright 2003 Dmitry Timoshkov
6 * Copyright 2003 Jon Griffiths
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * NOTES
23 * We must pass LOCALE_NOUSEROVERRIDE (NUO) to formatting functions so that
24 * even when the user has overridden their default i8n settings (e.g. in
25 * the control panel i8n page), we will still get the expected results.
28 #define _CRT_NON_CONFORMING_WCSTOK
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <stdio.h>
35 #include "ntstatus.h"
36 #define WIN32_NO_STATUS
37 #include "wine/test.h"
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winerror.h"
41 #include "winnls.h"
42 #include "winternl.h"
43 #include "winreg.h"
45 static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0};
46 static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
47 static const WCHAR title_case[] = {'\t','J','u','s','t','!',' ','A',',',' ','T','e','s','t',';',' ','S','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
48 static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
49 static const WCHAR localeW[] = {'e','n','-','U','S',0};
50 static const WCHAR fooW[] = {'f','o','o',0};
51 static const WCHAR emptyW[] = {0};
52 static const WCHAR invalidW[] = {'i','n','v','a','l','i','d',0};
54 /* Some functions are only in later versions of kernel32.dll */
55 static WORD enumCount;
57 static INT (WINAPI *pGetTimeFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT);
58 static INT (WINAPI *pGetDateFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT, LPCWSTR);
59 static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROCA, DWORD, LONG_PTR);
60 static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROCA, LGRPID, DWORD, LONG_PTR);
61 static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROCA, DWORD, LONG_PTR);
62 static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
63 static INT (WINAPI *pLCMapStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM);
64 static LCID (WINAPI *pLocaleNameToLCID)(LPCWSTR, DWORD);
65 static NTSTATUS (WINAPI *pRtlLocaleNameToLcid)(LPCWSTR, LCID *, DWORD);
66 static INT (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD);
67 static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
68 static INT (WINAPI *pIdnToNameprepUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
69 static INT (WINAPI *pIdnToAscii)(DWORD, LPCWSTR, INT, LPWSTR, INT);
70 static INT (WINAPI *pIdnToUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
71 static INT (WINAPI *pGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, INT);
72 static BOOL (WINAPI *pIsValidLocaleName)(LPCWSTR);
73 static INT (WINAPI *pCompareStringOrdinal)(const WCHAR *, INT, const WCHAR *, INT, BOOL);
74 static INT (WINAPI *pCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT,
75 LPNLSVERSIONINFO, LPVOID, LPARAM);
76 static INT (WINAPI *pGetGeoInfoA)(GEOID, GEOTYPE, LPSTR, INT, LANGID);
77 static INT (WINAPI *pGetGeoInfoW)(GEOID, GEOTYPE, LPWSTR, INT, LANGID);
78 static INT (WINAPI *pGetUserDefaultGeoName)(LPWSTR, int);
79 static BOOL (WINAPI *pSetUserGeoName)(PWSTR);
80 static BOOL (WINAPI *pEnumSystemGeoID)(GEOCLASS, GEOID, GEO_ENUMPROC);
81 static BOOL (WINAPI *pGetSystemPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
82 static BOOL (WINAPI *pGetThreadPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
83 static BOOL (WINAPI *pGetUserPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
84 static WCHAR (WINAPI *pRtlUpcaseUnicodeChar)(WCHAR);
85 static INT (WINAPI *pGetNumberFormatEx)(LPCWSTR, DWORD, LPCWSTR, const NUMBERFMTW *, LPWSTR, int);
86 static INT (WINAPI *pFindNLSStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT, LPINT, LPNLSVERSIONINFO, LPVOID, LPARAM);
87 static LANGID (WINAPI *pSetThreadUILanguage)(LANGID);
88 static LANGID (WINAPI *pGetThreadUILanguage)(VOID);
89 static INT (WINAPI *pNormalizeString)(NORM_FORM, LPCWSTR, INT, LPWSTR, INT);
90 static INT (WINAPI *pFindStringOrdinal)(DWORD, LPCWSTR lpStringSource, INT, LPCWSTR, INT, BOOL);
91 static BOOL (WINAPI *pGetNLSVersion)(NLS_FUNCTION,LCID,NLSVERSIONINFO*);
92 static BOOL (WINAPI *pGetNLSVersionEx)(NLS_FUNCTION,LPCWSTR,NLSVERSIONINFOEX*);
93 static DWORD (WINAPI *pIsValidNLSVersion)(NLS_FUNCTION,LPCWSTR,NLSVERSIONINFOEX*);
94 static NTSTATUS (WINAPI *pRtlNormalizeString)(ULONG, LPCWSTR, INT, LPWSTR, INT*);
95 static NTSTATUS (WINAPI *pRtlIsNormalizedString)(ULONG, LPCWSTR, INT, BOOLEAN*);
96 static NTSTATUS (WINAPI *pNtGetNlsSectionPtr)(ULONG,ULONG,void*,void**,SIZE_T*);
97 static void (WINAPI *pRtlInitCodePageTable)(USHORT*,CPTABLEINFO*);
98 static NTSTATUS (WINAPI *pRtlCustomCPToUnicodeN)(CPTABLEINFO*,WCHAR*,DWORD,DWORD*,const char*,DWORD);
99 static NTSTATUS (WINAPI *pRtlGetSystemPreferredUILanguages)(DWORD,ULONG,ULONG*,WCHAR*,ULONG*);
100 static NTSTATUS (WINAPI *pRtlGetThreadPreferredUILanguages)(DWORD,ULONG*,WCHAR*,ULONG*);
101 static NTSTATUS (WINAPI *pRtlGetUserPreferredUILanguages)(DWORD,ULONG,ULONG*,WCHAR*,ULONG*);
103 static void InitFunctionPointers(void)
105 HMODULE mod = GetModuleHandleA("kernel32");
107 #define X(f) p##f = (void*)GetProcAddress(mod, #f)
108 X(GetTimeFormatEx);
109 X(GetDateFormatEx);
110 X(EnumSystemLanguageGroupsA);
111 X(EnumLanguageGroupLocalesA);
112 X(LocaleNameToLCID);
113 X(LCIDToLocaleName);
114 X(LCMapStringEx);
115 X(IsValidLanguageGroup);
116 X(EnumUILanguagesA);
117 X(EnumSystemLocalesEx);
118 X(IdnToNameprepUnicode);
119 X(IdnToAscii);
120 X(IdnToUnicode);
121 X(GetLocaleInfoEx);
122 X(IsValidLocaleName);
123 X(CompareStringOrdinal);
124 X(CompareStringEx);
125 X(GetGeoInfoA);
126 X(GetGeoInfoW);
127 X(GetUserDefaultGeoName);
128 X(SetUserGeoName);
129 X(EnumSystemGeoID);
130 X(GetSystemPreferredUILanguages);
131 X(GetThreadPreferredUILanguages);
132 X(GetUserPreferredUILanguages);
133 X(GetNumberFormatEx);
134 X(FindNLSStringEx);
135 X(SetThreadUILanguage);
136 X(GetThreadUILanguage);
137 X(NormalizeString);
138 X(FindStringOrdinal);
139 X(GetNLSVersion);
140 X(GetNLSVersionEx);
141 X(IsValidNLSVersion);
143 mod = GetModuleHandleA("ntdll");
144 X(RtlUpcaseUnicodeChar);
145 X(RtlLocaleNameToLcid);
146 X(RtlNormalizeString);
147 X(RtlIsNormalizedString);
148 X(NtGetNlsSectionPtr);
149 X(RtlInitCodePageTable);
150 X(RtlCustomCPToUnicodeN);
151 X(RtlGetSystemPreferredUILanguages);
152 X(RtlGetThreadPreferredUILanguages);
153 X(RtlGetUserPreferredUILanguages);
154 #undef X
157 #define eq(received, expected, label, type) \
158 ok((received) == (expected), "%s: got " type " instead of " type "\n", \
159 (label), (received), (expected))
161 #define BUFFER_SIZE 128
163 #define STRINGSA(x,y) strcpy(input, x); strcpy(Expected, y); SetLastError(0xdeadbeef); buffer[0] = '\0'
164 #define EXPECT_LENA ok(ret == lstrlenA(Expected)+1, "Expected len %d, got %d\n", lstrlenA(Expected)+1, ret)
165 #define EXPECT_EQA ok(strncmp(buffer, Expected, strlen(Expected)) == 0, \
166 "Expected '%s', got '%s'\n", Expected, buffer)
168 #define STRINGSW(x,y) MultiByteToWideChar(CP_ACP,0,x,-1,input,ARRAY_SIZE(input)); \
169 MultiByteToWideChar(CP_ACP,0,y,-1,Expected,ARRAY_SIZE(Expected)); \
170 SetLastError(0xdeadbeef); buffer[0] = '\0'
171 #define EXPECT_LENW ok(ret == lstrlenW(Expected)+1, "Expected Len %d, got %d\n", lstrlenW(Expected)+1, ret)
172 #define EXPECT_EQW ok(wcsncmp(buffer, Expected, lstrlenW(Expected)) == 0, "Bad conversion\n")
174 #define NUO LOCALE_NOUSEROVERRIDE
176 static void test_GetLocaleInfoA(void)
178 int ret;
179 int len;
180 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
181 char buffer[BUFFER_SIZE];
182 char expected[BUFFER_SIZE];
183 DWORD val;
185 ok(lcid == 0x409, "wrong LCID calculated - %d\n", lcid);
187 ret = GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (char*)&val, sizeof(val));
188 ok(ret, "got %d\n", ret);
189 ok(val == lcid, "got 0x%08x\n", val);
191 /* en and ar use SUBLANG_NEUTRAL, but GetLocaleInfo assume SUBLANG_DEFAULT
192 Same is true for zh on pre-Vista, but on Vista and higher GetLocaleInfo
193 assumes SUBLANG_NEUTRAL for zh */
194 memset(expected, 0, ARRAY_SIZE(expected));
195 len = GetLocaleInfoA(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
196 SetLastError(0xdeadbeef);
197 memset(buffer, 0, ARRAY_SIZE(buffer));
198 ret = GetLocaleInfoA(LANG_ENGLISH, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
199 ok((ret == len) && !lstrcmpA(buffer, expected),
200 "got %d with '%s' (expected %d with '%s')\n",
201 ret, buffer, len, expected);
203 memset(expected, 0, ARRAY_SIZE(expected));
204 len = GetLocaleInfoA(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
205 if (len) {
206 SetLastError(0xdeadbeef);
207 memset(buffer, 0, ARRAY_SIZE(buffer));
208 ret = GetLocaleInfoA(LANG_ARABIC, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
209 ok((ret == len) && !lstrcmpA(buffer, expected),
210 "got %d with '%s' (expected %d with '%s')\n",
211 ret, buffer, len, expected);
213 else
214 win_skip("LANG_ARABIC not installed\n");
216 /* SUBLANG_DEFAULT is required for mlang.dll, but optional for GetLocaleInfo */
217 memset(expected, 0, ARRAY_SIZE(expected));
218 len = GetLocaleInfoA(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
219 SetLastError(0xdeadbeef);
220 memset(buffer, 0, ARRAY_SIZE(buffer));
221 ret = GetLocaleInfoA(LANG_GERMAN, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
222 ok((ret == len) && !lstrcmpA(buffer, expected),
223 "got %d with '%s' (expected %d with '%s')\n",
224 ret, buffer, len, expected);
227 /* HTMLKit and "Font xplorer lite" expect GetLocaleInfoA to
228 * partially fill the buffer even if it is too short. See bug 637.
230 SetLastError(0xdeadbeef);
231 memset(buffer, 0, ARRAY_SIZE(buffer));
232 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 0);
233 ok(ret == 7 && !buffer[0], "Expected len=7, got %d\n", ret);
235 SetLastError(0xdeadbeef);
236 memset(buffer, 0, ARRAY_SIZE(buffer));
237 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 3);
238 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
239 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
240 ok(!strcmp(buffer, "Mon"), "Expected 'Mon', got '%s'\n", buffer);
242 SetLastError(0xdeadbeef);
243 memset(buffer, 0, ARRAY_SIZE(buffer));
244 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 10);
245 ok(ret == 7, "Expected ret == 7, got %d, error %d\n", ret, GetLastError());
246 ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer);
249 struct neutralsublang_name2_t {
250 WCHAR name[3];
251 WCHAR sname[15];
252 LCID lcid;
253 LCID lcid_broken;
254 WCHAR sname_broken[15];
257 static const struct neutralsublang_name2_t neutralsublang_names2[] = {
258 { {'a','r',0}, {'a','r','-','S','A',0},
259 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
260 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0},
261 MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
262 { {'d','e',0}, {'d','e','-','D','E',0},
263 MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
264 { {'e','n',0}, {'e','n','-','U','S',0},
265 MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
266 { {'e','s',0}, {'e','s','-','E','S',0},
267 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT),
268 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), SORT_DEFAULT) /* vista */,
269 {'e','s','-','E','S','_','t','r','a','d','n','l',0} },
270 { {'g','a',0}, {'g','a','-','I','E',0},
271 MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 0, {0} },
272 { {'i','t',0}, {'i','t','-','I','T',0},
273 MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
274 { {'m','s',0}, {'m','s','-','M','Y',0},
275 MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
276 { {'n','l',0}, {'n','l','-','N','L',0},
277 MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
278 { {'p','t',0}, {'p','t','-','B','R',0},
279 MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
280 { {'s','r',0}, {'h','r','-','H','R',0},
281 MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CROATIA), SORT_DEFAULT) },
282 { {'s','v',0}, {'s','v','-','S','E',0},
283 MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
284 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0},
285 MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
286 { {'z','h',0}, {'z','h','-','C','N',0},
287 MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
288 { {0} }
291 static void test_GetLocaleInfoW(void)
293 LCID lcid_en = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
294 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
295 LCID lcid_en_neut = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT);
296 WCHAR bufferW[80], buffer2W[80];
297 CHAR bufferA[80];
298 DWORD val;
299 DWORD ret;
300 INT i;
302 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
303 if (!ret) {
304 win_skip("GetLocaleInfoW() isn't implemented\n");
305 return;
308 ret = GetLocaleInfoW(lcid_en, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
309 ok(ret, "got %d\n", ret);
310 ok(val == lcid_en, "got 0x%08x\n", val);
312 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
313 if (ret)
315 static const WCHAR slangW[] = {'E','n','g','l','i','s','h',' ','(','U','n','i','t','e','d',' ',
316 'S','t','a','t','e','s',')',0};
317 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
318 static const WCHAR enW[] = {'e','n','-','U','S',0};
319 const struct neutralsublang_name2_t *ptr = neutralsublang_names2;
321 ok(!lstrcmpW(bufferW, enW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
323 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
324 ok(ret, "got %d\n", ret);
325 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
326 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
328 skip("Non-English locale\n");
330 else
331 ok(!lstrcmpW(statesW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
333 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SLANGUAGE, bufferW, ARRAY_SIZE(bufferW));
334 ok(ret, "got %d\n", ret);
335 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
336 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
338 skip("Non-English locale\n");
340 else
341 ok(!lstrcmpW(slangW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
343 while (*ptr->name)
345 LANGID langid;
346 LCID lcid;
348 /* make neutral lcid */
349 langid = MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(ptr->lcid)), SUBLANG_NEUTRAL);
350 lcid = MAKELCID(langid, SORT_DEFAULT);
352 val = 0;
353 GetLocaleInfoW(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
354 ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
355 wine_dbgstr_w(ptr->name), val, ptr->lcid);
357 /* now check LOCALE_SNAME */
358 GetLocaleInfoW(lcid, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
359 ok(!lstrcmpW(bufferW, ptr->sname) ||
360 (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))),
361 "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
362 ptr++;
365 else
366 win_skip("English neutral locale not supported\n");
368 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
369 if (!ret) {
370 win_skip("LANG_RUSSIAN locale data unavailable\n");
371 return;
373 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
374 bufferW, ARRAY_SIZE(bufferW));
375 if (!ret) {
376 win_skip("LOCALE_RETURN_GENITIVE_NAMES isn't supported\n");
377 return;
380 /* LOCALE_RETURN_GENITIVE_NAMES isn't supported for GetLocaleInfoA */
381 bufferA[0] = 'a';
382 SetLastError(0xdeadbeef);
383 ret = GetLocaleInfoA(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
384 bufferA, ARRAY_SIZE(bufferA));
385 ok(ret == 0, "LOCALE_RETURN_GENITIVE_NAMES should fail with GetLocaleInfoA\n");
386 ok(bufferA[0] == 'a', "Expected buffer to be untouched\n");
387 ok(GetLastError() == ERROR_INVALID_FLAGS,
388 "Expected ERROR_INVALID_FLAGS, got %x\n", GetLastError());
390 bufferW[0] = 'a';
391 SetLastError(0xdeadbeef);
392 ret = GetLocaleInfoW(lcid_ru, LOCALE_RETURN_GENITIVE_NAMES, bufferW, ARRAY_SIZE(bufferW));
393 ok(ret == 0,
394 "LOCALE_RETURN_GENITIVE_NAMES itself doesn't return anything, got %d\n", ret);
395 ok(bufferW[0] == 'a', "Expected buffer to be untouched\n");
396 ok(GetLastError() == ERROR_INVALID_FLAGS,
397 "Expected ERROR_INVALID_FLAGS, got %x\n", GetLastError());
399 /* yes, test empty 13 month entry too */
400 for (i = 0; i < 12; i++) {
401 bufferW[0] = 0;
402 ret = GetLocaleInfoW(lcid_ru, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
403 bufferW, ARRAY_SIZE(bufferW));
404 ok(ret, "Expected non zero result\n");
405 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %d, length %d\n",
406 ret, lstrlenW(bufferW));
407 buffer2W[0] = 0;
408 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
409 ok(ret, "Expected non zero result\n");
410 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %d, length %d\n",
411 ret, lstrlenW(buffer2W));
413 ok(lstrcmpW(bufferW, buffer2W) != 0,
414 "Expected genitive name to differ, got the same for month %d\n", i+1);
416 /* for locale without genitive names nominative returned in both cases */
417 bufferW[0] = 0;
418 ret = GetLocaleInfoW(lcid_en, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
419 bufferW, ARRAY_SIZE(bufferW));
420 ok(ret, "Expected non zero result\n");
421 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %d, length %d\n",
422 ret, lstrlenW(bufferW));
423 buffer2W[0] = 0;
424 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
425 ok(ret, "Expected non zero result\n");
426 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %d, length %d\n",
427 ret, lstrlenW(buffer2W));
429 ok(lstrcmpW(bufferW, buffer2W) == 0,
430 "Expected same names, got different for month %d\n", i+1);
434 static void test_GetTimeFormatA(void)
436 int ret;
437 SYSTEMTIME curtime;
438 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
439 char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
441 memset(&curtime, 2, sizeof(SYSTEMTIME));
442 STRINGSA("tt HH':'mm'@'ss", ""); /* Invalid time */
443 SetLastError(0xdeadbeef);
444 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
445 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
446 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
448 curtime.wHour = 8;
449 curtime.wMinute = 56;
450 curtime.wSecond = 13;
451 curtime.wMilliseconds = 22;
452 STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
453 SetLastError(0xdeadbeef);
454 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
455 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
456 EXPECT_LENA; EXPECT_EQA;
458 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
459 SetLastError(0xdeadbeef);
460 ret = GetTimeFormatA(lcid, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
461 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
462 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
464 STRINGSA("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */
465 SetLastError(0xdeadbeef);
466 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
467 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
468 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
470 STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
471 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
472 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
473 EXPECT_LENA;
475 STRINGSA("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
476 ret = GetTimeFormatA(lcid, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
477 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
478 EXPECT_LENA; EXPECT_EQA;
480 STRINGSA("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
481 ret = GetTimeFormatA(lcid, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
482 ok(ret == strlen(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
483 ok( !strcmp( buffer, "" ) || broken( !strcmp( buffer, "4" )), /* win9x */
484 "Expected '', got '%s'\n", buffer );
486 STRINGSA("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
487 ret = GetTimeFormatA(lcid, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
488 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
489 EXPECT_LENA; EXPECT_EQA;
491 STRINGSA("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
492 strcpy(Expected, "8:56 AM");
493 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
494 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
495 EXPECT_LENA; EXPECT_EQA;
497 STRINGSA("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
498 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
499 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
500 ok( !strcmp( buffer, "8.@:56AM" ) || broken( !strcmp( buffer, "8.@:56.@:AM" )) /* win9x */,
501 "Expected '8.@:56AM', got '%s'\n", buffer );
503 STRINGSA("s1s2s3", ""); /* Duplicate tokens */
504 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
505 ok(ret == strlen(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
506 ok( !strcmp( buffer, "" ) || broken( !strcmp( buffer, "3" )), /* win9x */
507 "Expected '', got '%s'\n", buffer );
509 STRINGSA("t/tt", "A/AM"); /* AM time marker */
510 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
511 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
512 EXPECT_LENA; EXPECT_EQA;
514 curtime.wHour = 13;
515 STRINGSA("t/tt", "P/PM"); /* PM time marker */
516 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
517 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
518 EXPECT_LENA; EXPECT_EQA;
520 STRINGSA("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
521 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
522 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
523 EXPECT_LENA; EXPECT_EQA;
525 STRINGSA("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
526 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
527 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
528 EXPECT_LENA; EXPECT_EQA;
530 STRINGSA("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
531 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
532 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
533 EXPECT_LENA; EXPECT_EQA;
535 curtime.wHour = 14; /* change this to 14 or 2pm */
536 curtime.wMinute = 5;
537 curtime.wSecond = 3;
538 STRINGSA("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */
539 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
540 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
541 EXPECT_LENA; EXPECT_EQA;
543 curtime.wHour = 0;
544 STRINGSA("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
545 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
546 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
547 EXPECT_LENA; EXPECT_EQA;
549 STRINGSA("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
550 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
551 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
552 EXPECT_LENA; EXPECT_EQA;
554 /* try to convert formatting strings with more than two letters
555 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
556 * NOTE: We expect any letter for which there is an upper case value
557 * we should see a replacement. For letters that DO NOT have
558 * upper case values we should see NO REPLACEMENT.
560 curtime.wHour = 8;
561 curtime.wMinute = 56;
562 curtime.wSecond = 13;
563 curtime.wMilliseconds = 22;
564 STRINGSA("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
565 "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
566 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
567 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
568 EXPECT_LENA; EXPECT_EQA;
570 STRINGSA("h", "text"); /* Don't write to buffer if len is 0 */
571 strcpy(buffer, "text");
572 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, 0);
573 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
574 EXPECT_EQA;
576 STRINGSA("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
577 "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
578 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
579 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
580 EXPECT_LENA; EXPECT_EQA;
582 STRINGSA("'''", "'"); /* invalid quoted string */
583 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
584 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
585 EXPECT_LENA; EXPECT_EQA;
587 /* test that msdn suggested single quotation usage works as expected */
588 STRINGSA("''''", "'"); /* single quote mark */
589 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
590 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
591 EXPECT_LENA; EXPECT_EQA;
593 STRINGSA("''HHHHHH", "08"); /* Normal use */
594 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
595 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
596 EXPECT_LENA; EXPECT_EQA;
598 /* and test for normal use of the single quotation mark */
599 STRINGSA("'''HHHHHH'", "'HHHHHH"); /* Normal use */
600 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
601 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
602 EXPECT_LENA; EXPECT_EQA;
604 STRINGSA("'''HHHHHH", "'HHHHHH"); /* Odd use */
605 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
606 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
607 EXPECT_LENA; EXPECT_EQA;
609 STRINGSA("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
610 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
611 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
612 EXPECT_LENA; EXPECT_EQA;
614 curtime.wHour = 25;
615 STRINGSA("'123'tt", ""); /* Invalid time */
616 SetLastError(0xdeadbeef);
617 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
618 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
619 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
621 curtime.wHour = 12;
622 curtime.wMonth = 60; /* Invalid */
623 STRINGSA("h:m:s", "12:56:13"); /* Invalid date */
624 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
625 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
626 EXPECT_LENA; EXPECT_EQA;
629 static void test_GetTimeFormatEx(void)
631 int ret;
632 SYSTEMTIME curtime;
633 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
635 if (!pGetTimeFormatEx)
637 win_skip("GetTimeFormatEx not supported\n");
638 return;
641 memset(&curtime, 2, sizeof(SYSTEMTIME));
642 STRINGSW("tt HH':'mm'@'ss", ""); /* Invalid time */
643 SetLastError(0xdeadbeef);
644 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
645 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
646 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
648 curtime.wHour = 8;
649 curtime.wMinute = 56;
650 curtime.wSecond = 13;
651 curtime.wMilliseconds = 22;
652 STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
653 SetLastError(0xdeadbeef);
654 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
655 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
656 EXPECT_LENW; EXPECT_EQW;
658 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
659 SetLastError(0xdeadbeef);
660 ret = pGetTimeFormatEx(localeW, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
661 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
662 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
664 STRINGSW("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */
665 SetLastError(0xdeadbeef);
666 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
667 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
668 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
670 STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
671 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
672 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
673 EXPECT_LENW;
675 STRINGSW("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
676 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
677 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
678 EXPECT_LENW; EXPECT_EQW;
680 STRINGSW("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
681 ret = pGetTimeFormatEx(localeW, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
682 ok(ret == lstrlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
683 EXPECT_LENW; EXPECT_EQW;
685 STRINGSW("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
686 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
687 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
688 EXPECT_LENW; EXPECT_EQW;
690 STRINGSW("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
691 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
692 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
693 EXPECT_LENW; EXPECT_EQW;
695 STRINGSW("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
696 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
697 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
698 EXPECT_LENW; EXPECT_EQW;
700 STRINGSW("s1s2s3", ""); /* Duplicate tokens */
701 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
702 ok(ret == lstrlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
703 EXPECT_LENW; EXPECT_EQW;
705 STRINGSW("t/tt", "A/AM"); /* AM time marker */
706 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
707 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
708 EXPECT_LENW; EXPECT_EQW;
710 curtime.wHour = 13;
711 STRINGSW("t/tt", "P/PM"); /* PM time marker */
712 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
713 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
714 EXPECT_LENW; EXPECT_EQW;
716 STRINGSW("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
717 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
718 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
719 EXPECT_LENW; EXPECT_EQW;
721 STRINGSW("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
722 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
723 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
724 EXPECT_LENW; EXPECT_EQW;
726 STRINGSW("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
727 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
728 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
729 EXPECT_LENW; EXPECT_EQW;
731 curtime.wHour = 14; /* change this to 14 or 2pm */
732 curtime.wMinute = 5;
733 curtime.wSecond = 3;
734 STRINGSW("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */
735 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
736 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
737 EXPECT_LENW; EXPECT_EQW;
739 curtime.wHour = 0;
740 STRINGSW("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
741 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
742 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
743 EXPECT_LENW; EXPECT_EQW;
745 STRINGSW("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
746 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
747 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
748 EXPECT_LENW; EXPECT_EQW;
750 /* try to convert formatting strings with more than two letters
751 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
752 * NOTE: We expect any letter for which there is an upper case value
753 * we should see a replacement. For letters that DO NOT have
754 * upper case values we should see NO REPLACEMENT.
756 curtime.wHour = 8;
757 curtime.wMinute = 56;
758 curtime.wSecond = 13;
759 curtime.wMilliseconds = 22;
760 STRINGSW("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
761 "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
762 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
763 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
764 EXPECT_LENW; EXPECT_EQW;
766 STRINGSW("h", "text"); /* Don't write to buffer if len is 0 */
767 lstrcpyW(buffer, Expected);
768 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, 0);
769 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
770 EXPECT_EQW;
772 STRINGSW("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
773 "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
774 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
775 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
776 EXPECT_LENW; EXPECT_EQW;
778 STRINGSW("'''", "'"); /* invalid quoted string */
779 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
780 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
781 EXPECT_LENW; EXPECT_EQW;
783 /* test that msdn suggested single quotation usage works as expected */
784 STRINGSW("''''", "'"); /* single quote mark */
785 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
786 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
787 EXPECT_LENW; EXPECT_EQW;
789 STRINGSW("''HHHHHH", "08"); /* Normal use */
790 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
791 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
792 EXPECT_LENW; EXPECT_EQW;
794 /* and test for normal use of the single quotation mark */
795 STRINGSW("'''HHHHHH'", "'HHHHHH"); /* Normal use */
796 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
797 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
798 EXPECT_LENW; EXPECT_EQW;
800 STRINGSW("'''HHHHHH", "'HHHHHH"); /* Odd use */
801 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
802 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
803 EXPECT_LENW; EXPECT_EQW;
805 STRINGSW("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
806 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
807 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
808 EXPECT_LENW; EXPECT_EQW;
810 curtime.wHour = 25;
811 STRINGSW("'123'tt", ""); /* Invalid time */
812 SetLastError(0xdeadbeef);
813 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
814 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
815 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
817 curtime.wHour = 12;
818 curtime.wMonth = 60; /* Invalid */
819 STRINGSW("h:m:s", "12:56:13"); /* Invalid date */
820 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
821 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
822 EXPECT_LENW; EXPECT_EQW;
825 static void test_GetDateFormatA(void)
827 int ret;
828 SYSTEMTIME curtime;
829 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
830 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
831 char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
832 char Broken[BUFFER_SIZE];
833 char short_day[10], month[10], genitive_month[10];
835 memset(&curtime, 2, sizeof(SYSTEMTIME)); /* Invalid time */
836 STRINGSA("ddd',' MMM dd yy","");
837 SetLastError(0xdeadbeef);
838 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
839 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
840 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
842 curtime.wYear = 2002;
843 curtime.wMonth = 5;
844 curtime.wDay = 4;
845 curtime.wDayOfWeek = 3;
846 STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Simple case */
847 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
848 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
849 EXPECT_LENA; EXPECT_EQA;
851 /* Same as above but with LOCALE_NOUSEROVERRIDE */
852 STRINGSA("ddd',' MMM dd yy",""); /* Simple case */
853 SetLastError(0xdeadbeef);
854 ret = GetDateFormatA(lcid, NUO, &curtime, input, buffer, ARRAY_SIZE(buffer));
855 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
856 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
857 EXPECT_EQA;
859 STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Format containing "'" */
860 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
861 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
862 EXPECT_LENA; EXPECT_EQA;
864 curtime.wHour = 36; /* Invalid */
865 STRINGSA("ddd',' MMM dd ''''yy","Sat, May 04 '02"); /* Invalid time */
866 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
867 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
868 EXPECT_LENA; EXPECT_EQA;
870 STRINGSA("ddd',' MMM dd ''''yy",""); /* Get size only */
871 ret = GetDateFormatA(lcid, 0, &curtime, input, NULL, 0);
872 ok(ret == 16, "Expected ret == 16, got %d, error %d\n", ret, GetLastError());
873 EXPECT_EQA;
875 STRINGSA("ddd',' MMM dd ''''yy",""); /* Buffer too small */
876 SetLastError(0xdeadbeef);
877 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, 2);
878 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
879 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
881 STRINGSA("ddd',' MMM dd ''''yy","5/4/2002"); /* Default to DATE_SHORTDATE */
882 ret = GetDateFormatA(lcid, NUO, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
883 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
884 if (strncmp(buffer, Expected, strlen(Expected)) && strncmp(buffer, "5/4/02", strlen(Expected)) != 0)
885 ok (0, "Expected '%s' or '5/4/02', got '%s'\n", Expected, buffer);
887 SetLastError(0xdeadbeef); buffer[0] = '\0'; /* DATE_LONGDATE */
888 ret = GetDateFormatA(lcid, NUO|DATE_LONGDATE, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
889 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
890 ok(strcmp(buffer, "Saturday, May 04, 2002") == 0 ||
891 strcmp(buffer, "Saturday, May 4, 2002") == 0 /* Win 8 */,
892 "got an unexpected date string '%s'\n", buffer);
894 /* test for expected DATE_YEARMONTH behavior with null format */
895 /* NT4 returns ERROR_INVALID_FLAGS for DATE_YEARMONTH */
896 STRINGSA("ddd',' MMM dd ''''yy", ""); /* DATE_YEARMONTH */
897 SetLastError(0xdeadbeef);
898 ret = GetDateFormatA(lcid, NUO|DATE_YEARMONTH, &curtime, input, buffer, ARRAY_SIZE(buffer));
899 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
900 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
901 EXPECT_EQA;
903 /* Test that using invalid DATE_* flags results in the correct error */
904 /* and return values */
905 STRINGSA("m/d/y", ""); /* Invalid flags */
906 SetLastError(0xdeadbeef);
907 ret = GetDateFormatA(lcid, DATE_YEARMONTH|DATE_SHORTDATE|DATE_LONGDATE, &curtime, input,
908 buffer, ARRAY_SIZE(buffer));
909 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
910 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
912 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddMMMM", buffer, ARRAY_SIZE(buffer));
913 if (!ret)
915 win_skip("LANG_RUSSIAN locale data unavailable\n");
916 return;
919 /* month part should be in genitive form */
920 strcpy(genitive_month, buffer + 2);
921 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMM", buffer, ARRAY_SIZE(buffer));
922 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
923 strcpy(month, buffer);
924 ok(strcmp(genitive_month, month) != 0, "Expected different month forms\n");
926 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddd", buffer, ARRAY_SIZE(buffer));
927 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
928 strcpy(short_day, buffer);
930 STRINGSA("dd MMMMddd dd", "");
931 sprintf(Expected, "04 %s%s 04", genitive_month, short_day);
932 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
933 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
934 EXPECT_EQA;
936 STRINGSA("MMMMddd dd", "");
937 sprintf(Expected, "%s%s 04", month, short_day);
938 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
939 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
940 EXPECT_EQA;
942 STRINGSA("MMMMddd", "");
943 sprintf(Expected, "%s%s", month, short_day);
944 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
945 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
946 EXPECT_EQA;
948 STRINGSA("MMMMdd", "");
949 sprintf(Expected, "%s04", genitive_month);
950 sprintf(Broken, "%s04", month);
951 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
952 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
953 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
954 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
955 "Expected '%s', got '%s'\n", Expected, buffer);
957 STRINGSA("MMMMdd ddd", "");
958 sprintf(Expected, "%s04 %s", genitive_month, short_day);
959 sprintf(Broken, "%s04 %s", month, short_day);
960 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
961 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
962 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
963 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
964 "Expected '%s', got '%s'\n", Expected, buffer);
966 STRINGSA("dd dddMMMM", "");
967 sprintf(Expected, "04 %s%s", short_day, month);
968 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
969 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
970 EXPECT_EQA;
972 STRINGSA("dd dddMMMM ddd MMMMdd", "");
973 sprintf(Expected, "04 %s%s %s %s04", short_day, month, short_day, genitive_month);
974 sprintf(Broken, "04 %s%s %s %s04", short_day, month, short_day, month);
975 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
976 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
977 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
978 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
979 "Expected '%s', got '%s'\n", Expected, buffer);
981 /* with literal part */
982 STRINGSA("ddd',' MMMM dd", "");
983 sprintf(Expected, "%s, %s 04", short_day, genitive_month);
984 sprintf(Broken, "%s, %s 04", short_day, month);
985 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
986 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
987 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
988 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
989 "Expected '%s', got '%s'\n", Expected, buffer);
992 static void test_GetDateFormatEx(void)
994 int ret;
995 SYSTEMTIME curtime;
996 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
998 if (!pGetDateFormatEx)
1000 win_skip("GetDateFormatEx not supported\n");
1001 return;
1004 STRINGSW("",""); /* If flags are set, then format must be NULL */
1005 SetLastError(0xdeadbeef);
1006 ret = pGetDateFormatEx(localeW, DATE_LONGDATE, NULL, input, buffer, ARRAY_SIZE(buffer), NULL);
1007 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1008 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1009 EXPECT_EQW;
1011 STRINGSW("",""); /* NULL buffer, len > 0 */
1012 SetLastError(0xdeadbeef);
1013 ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, ARRAY_SIZE(buffer), NULL);
1014 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1015 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1017 STRINGSW("",""); /* NULL buffer, len == 0 */
1018 ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, 0, NULL);
1019 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1020 EXPECT_LENW; EXPECT_EQW;
1022 STRINGSW("",""); /* Invalid flag combination */
1023 SetLastError(0xdeadbeef);
1024 ret = pGetDateFormatEx(localeW, DATE_LONGDATE|DATE_SHORTDATE, NULL,
1025 input, NULL, 0, NULL);
1026 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1027 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1028 EXPECT_EQW;
1030 curtime.wYear = 2002;
1031 curtime.wMonth = 10;
1032 curtime.wDay = 23;
1033 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1034 curtime.wHour = 65432; /* Invalid */
1035 curtime.wMinute = 34512; /* Invalid */
1036 curtime.wSecond = 65535; /* Invalid */
1037 curtime.wMilliseconds = 12345;
1038 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
1039 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1040 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1041 EXPECT_LENW; EXPECT_EQW;
1043 curtime.wYear = 2002;
1044 curtime.wMonth = 10;
1045 curtime.wDay = 23;
1046 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1047 curtime.wHour = 65432; /* Invalid */
1048 curtime.wMinute = 34512; /* Invalid */
1049 curtime.wSecond = 65535; /* Invalid */
1050 curtime.wMilliseconds = 12345;
1051 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002");
1052 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), emptyW); /* Use reserved arg */
1053 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1054 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1056 /* Limit tests */
1058 curtime.wYear = 1601;
1059 curtime.wMonth = 1;
1060 curtime.wDay = 1;
1061 curtime.wDayOfWeek = 0; /* Irrelevant */
1062 curtime.wHour = 0;
1063 curtime.wMinute = 0;
1064 curtime.wSecond = 0;
1065 curtime.wMilliseconds = 0;
1066 STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601");
1067 SetLastError(0xdeadbeef);
1068 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1069 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1070 EXPECT_LENW; EXPECT_EQW;
1072 curtime.wYear = 1600;
1073 curtime.wMonth = 12;
1074 curtime.wDay = 31;
1075 curtime.wDayOfWeek = 0; /* Irrelevant */
1076 curtime.wHour = 23;
1077 curtime.wMinute = 59;
1078 curtime.wSecond = 59;
1079 curtime.wMilliseconds = 999;
1080 STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600");
1081 SetLastError(0xdeadbeef);
1082 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1083 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1084 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1087 static void test_GetDateFormatW(void)
1089 int ret;
1090 SYSTEMTIME curtime;
1091 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
1092 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1094 STRINGSW("",""); /* If flags is not zero then format must be NULL */
1095 ret = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, NULL, input, buffer, ARRAY_SIZE(buffer));
1096 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1098 win_skip("GetDateFormatW is not implemented\n");
1099 return;
1101 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1102 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1103 EXPECT_EQW;
1105 STRINGSW("",""); /* NULL buffer, len > 0 */
1106 SetLastError(0xdeadbeef);
1107 ret = GetDateFormatW (lcid, 0, NULL, input, NULL, ARRAY_SIZE(buffer));
1108 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1109 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1111 STRINGSW("",""); /* NULL buffer, len == 0 */
1112 ret = GetDateFormatW (lcid, 0, NULL, input, NULL, 0);
1113 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1114 EXPECT_LENW; EXPECT_EQW;
1116 curtime.wYear = 2002;
1117 curtime.wMonth = 10;
1118 curtime.wDay = 23;
1119 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1120 curtime.wHour = 65432; /* Invalid */
1121 curtime.wMinute = 34512; /* Invalid */
1122 curtime.wSecond = 65535; /* Invalid */
1123 curtime.wMilliseconds = 12345;
1124 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
1125 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1126 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1127 EXPECT_LENW; EXPECT_EQW;
1129 /* Limit tests */
1131 curtime.wYear = 1601;
1132 curtime.wMonth = 1;
1133 curtime.wDay = 1;
1134 curtime.wDayOfWeek = 0; /* Irrelevant */
1135 curtime.wHour = 0;
1136 curtime.wMinute = 0;
1137 curtime.wSecond = 0;
1138 curtime.wMilliseconds = 0;
1139 STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601");
1140 SetLastError(0xdeadbeef);
1141 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1142 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1143 EXPECT_LENW; EXPECT_EQW;
1145 curtime.wYear = 1600;
1146 curtime.wMonth = 12;
1147 curtime.wDay = 31;
1148 curtime.wDayOfWeek = 0; /* Irrelevant */
1149 curtime.wHour = 23;
1150 curtime.wMinute = 59;
1151 curtime.wSecond = 59;
1152 curtime.wMilliseconds = 999;
1153 STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600");
1154 SetLastError(0xdeadbeef);
1155 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1156 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1157 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1161 #define CY_POS_LEFT 0
1162 #define CY_POS_RIGHT 1
1163 #define CY_POS_LEFT_SPACE 2
1164 #define CY_POS_RIGHT_SPACE 3
1166 static void test_GetCurrencyFormatA(void)
1168 static char szDot[] = { '.', '\0' };
1169 static char szComma[] = { ',', '\0' };
1170 static char szDollar[] = { '$', '\0' };
1171 int ret;
1172 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1173 char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
1174 CURRENCYFMTA format;
1176 memset(&format, 0, sizeof(format));
1178 STRINGSA("23",""); /* NULL output, length > 0 --> Error */
1179 SetLastError(0xdeadbeef);
1180 ret = GetCurrencyFormatA(lcid, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1181 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1182 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1184 STRINGSA("23,53",""); /* Invalid character --> Error */
1185 SetLastError(0xdeadbeef);
1186 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1187 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1188 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1190 STRINGSA("--",""); /* Double '-' --> Error */
1191 SetLastError(0xdeadbeef);
1192 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1193 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1194 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1196 STRINGSA("0-",""); /* Trailing '-' --> Error */
1197 SetLastError(0xdeadbeef);
1198 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1199 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1200 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1202 STRINGSA("0..",""); /* Double '.' --> Error */
1203 SetLastError(0xdeadbeef);
1204 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1205 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1206 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1208 STRINGSA(" 0.1",""); /* Leading space --> Error */
1209 SetLastError(0xdeadbeef);
1210 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1211 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1212 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1214 STRINGSA("1234","$"); /* Length too small --> Write up to length chars */
1215 SetLastError(0xdeadbeef);
1216 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, 2);
1217 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1218 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1220 STRINGSA("2353",""); /* Format and flags given --> Error */
1221 SetLastError(0xdeadbeef);
1222 ret = GetCurrencyFormatA(lcid, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1223 ok( !ret, "Expected ret == 0, got %d\n", ret);
1224 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1225 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1227 STRINGSA("2353",""); /* Invalid format --> Error */
1228 SetLastError(0xdeadbeef);
1229 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1230 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1231 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1233 STRINGSA("2353","$2,353.00"); /* Valid number */
1234 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1235 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1236 EXPECT_LENA; EXPECT_EQA;
1238 STRINGSA("-2353","($2,353.00)"); /* Valid negative number */
1239 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1240 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1241 EXPECT_LENA; EXPECT_EQA;
1243 STRINGSA("2353.1","$2,353.10"); /* Valid real number */
1244 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1245 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1246 EXPECT_LENA; EXPECT_EQA;
1248 STRINGSA("2353.111","$2,353.11"); /* Too many DP --> Truncated */
1249 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1250 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1251 EXPECT_LENA; EXPECT_EQA;
1253 STRINGSA("2353.119","$2,353.12"); /* Too many DP --> Rounded */
1254 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1255 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1256 EXPECT_LENA; EXPECT_EQA;
1258 format.NumDigits = 0; /* No decimal separator */
1259 format.LeadingZero = 0;
1260 format.Grouping = 0; /* No grouping char */
1261 format.NegativeOrder = 0;
1262 format.PositiveOrder = CY_POS_LEFT;
1263 format.lpDecimalSep = szDot;
1264 format.lpThousandSep = szComma;
1265 format.lpCurrencySymbol = szDollar;
1267 STRINGSA("2353","$2353"); /* No decimal or grouping chars expected */
1268 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1269 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1270 EXPECT_LENA; EXPECT_EQA;
1272 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1273 STRINGSA("2353","$2353.0");
1274 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1275 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1276 EXPECT_LENA; EXPECT_EQA;
1278 format.Grouping = 2; /* Group by 100's */
1279 STRINGSA("2353","$23,53.0");
1280 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1281 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1282 EXPECT_LENA; EXPECT_EQA;
1284 STRINGSA("235","$235.0"); /* Grouping of a positive number */
1285 format.Grouping = 3;
1286 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1287 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1288 EXPECT_LENA; EXPECT_EQA;
1290 STRINGSA("-235","$-235.0"); /* Grouping of a negative number */
1291 format.NegativeOrder = 2;
1292 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1293 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1294 EXPECT_LENA; EXPECT_EQA;
1296 format.LeadingZero = 1; /* Always provide leading zero */
1297 STRINGSA(".5","$0.5");
1298 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1299 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1300 EXPECT_LENA; EXPECT_EQA;
1302 format.PositiveOrder = CY_POS_RIGHT;
1303 STRINGSA("1","1.0$");
1304 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1305 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1306 EXPECT_LENA; EXPECT_EQA;
1308 format.PositiveOrder = CY_POS_LEFT_SPACE;
1309 STRINGSA("1","$ 1.0");
1310 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1311 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1312 EXPECT_LENA; EXPECT_EQA;
1314 format.PositiveOrder = CY_POS_RIGHT_SPACE;
1315 STRINGSA("1","1.0 $");
1316 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1317 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1318 EXPECT_LENA; EXPECT_EQA;
1320 format.NegativeOrder = 0;
1321 STRINGSA("-1","($1.0)");
1322 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1323 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1324 EXPECT_LENA; EXPECT_EQA;
1326 format.NegativeOrder = 1;
1327 STRINGSA("-1","-$1.0");
1328 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1329 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1330 EXPECT_LENA; EXPECT_EQA;
1332 format.NegativeOrder = 2;
1333 STRINGSA("-1","$-1.0");
1334 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1335 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1336 EXPECT_LENA; EXPECT_EQA;
1338 format.NegativeOrder = 3;
1339 STRINGSA("-1","$1.0-");
1340 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1341 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1342 EXPECT_LENA; EXPECT_EQA;
1344 format.NegativeOrder = 4;
1345 STRINGSA("-1","(1.0$)");
1346 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1347 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1348 EXPECT_LENA; EXPECT_EQA;
1350 format.NegativeOrder = 5;
1351 STRINGSA("-1","-1.0$");
1352 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1353 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1354 EXPECT_LENA; EXPECT_EQA;
1356 format.NegativeOrder = 6;
1357 STRINGSA("-1","1.0-$");
1358 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1359 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1360 EXPECT_LENA; EXPECT_EQA;
1362 format.NegativeOrder = 7;
1363 STRINGSA("-1","1.0$-");
1364 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1365 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1366 EXPECT_LENA; EXPECT_EQA;
1368 format.NegativeOrder = 8;
1369 STRINGSA("-1","-1.0 $");
1370 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1371 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1372 EXPECT_LENA; EXPECT_EQA;
1374 format.NegativeOrder = 9;
1375 STRINGSA("-1","-$ 1.0");
1376 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1377 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1378 EXPECT_LENA; EXPECT_EQA;
1380 format.NegativeOrder = 10;
1381 STRINGSA("-1","1.0 $-");
1382 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1383 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1384 EXPECT_LENA; EXPECT_EQA;
1386 format.NegativeOrder = 11;
1387 STRINGSA("-1","$ 1.0-");
1388 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1389 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1390 EXPECT_LENA; EXPECT_EQA;
1392 format.NegativeOrder = 12;
1393 STRINGSA("-1","$ -1.0");
1394 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1395 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1396 EXPECT_LENA; EXPECT_EQA;
1398 format.NegativeOrder = 13;
1399 STRINGSA("-1","1.0- $");
1400 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1401 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1402 EXPECT_LENA; EXPECT_EQA;
1404 format.NegativeOrder = 14;
1405 STRINGSA("-1","($ 1.0)");
1406 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1407 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1408 EXPECT_LENA; EXPECT_EQA;
1410 format.NegativeOrder = 15;
1411 STRINGSA("-1","(1.0 $)");
1412 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1413 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1414 EXPECT_LENA; EXPECT_EQA;
1417 #define NEG_PARENS 0 /* "(1.1)" */
1418 #define NEG_LEFT 1 /* "-1.1" */
1419 #define NEG_LEFT_SPACE 2 /* "- 1.1" */
1420 #define NEG_RIGHT 3 /* "1.1-" */
1421 #define NEG_RIGHT_SPACE 4 /* "1.1 -" */
1423 static void test_GetNumberFormatA(void)
1425 static char szDot[] = { '.', '\0' };
1426 static char szComma[] = { ',', '\0' };
1427 int ret;
1428 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1429 char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
1430 NUMBERFMTA format;
1432 memset(&format, 0, sizeof(format));
1434 STRINGSA("23",""); /* NULL output, length > 0 --> Error */
1435 SetLastError(0xdeadbeef);
1436 ret = GetNumberFormatA(lcid, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1437 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1438 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1440 STRINGSA("23,53",""); /* Invalid character --> Error */
1441 SetLastError(0xdeadbeef);
1442 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1443 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1444 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1446 STRINGSA("--",""); /* Double '-' --> Error */
1447 SetLastError(0xdeadbeef);
1448 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1449 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1450 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1452 STRINGSA("0-",""); /* Trailing '-' --> Error */
1453 SetLastError(0xdeadbeef);
1454 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1455 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1456 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1458 STRINGSA("0..",""); /* Double '.' --> Error */
1459 SetLastError(0xdeadbeef);
1460 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1461 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1462 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1464 STRINGSA(" 0.1",""); /* Leading space --> Error */
1465 SetLastError(0xdeadbeef);
1466 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1467 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1468 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1470 STRINGSA("1234","1"); /* Length too small --> Write up to length chars */
1471 SetLastError(0xdeadbeef);
1472 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, 2);
1473 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1474 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1476 STRINGSA("2353",""); /* Format and flags given --> Error */
1477 SetLastError(0xdeadbeef);
1478 ret = GetNumberFormatA(lcid, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1479 ok( !ret, "Expected ret == 0, got %d\n", ret);
1480 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1481 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1483 STRINGSA("2353",""); /* Invalid format --> Error */
1484 SetLastError(0xdeadbeef);
1485 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1486 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1487 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1489 STRINGSA("2353","2,353.00"); /* Valid number */
1490 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1491 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1492 EXPECT_LENA; EXPECT_EQA;
1494 STRINGSA("-2353","-2,353.00"); /* Valid negative number */
1495 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1496 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1497 EXPECT_LENA; EXPECT_EQA;
1499 STRINGSA("-353","-353.00"); /* test for off by one error in grouping */
1500 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1501 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1502 EXPECT_LENA; EXPECT_EQA;
1504 STRINGSA("2353.1","2,353.10"); /* Valid real number */
1505 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1506 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1507 EXPECT_LENA; EXPECT_EQA;
1509 STRINGSA("2353.111","2,353.11"); /* Too many DP --> Truncated */
1510 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1511 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1512 EXPECT_LENA; EXPECT_EQA;
1514 STRINGSA("2353.119","2,353.12"); /* Too many DP --> Rounded */
1515 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1516 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1517 EXPECT_LENA; EXPECT_EQA;
1519 format.NumDigits = 0; /* No decimal separator */
1520 format.LeadingZero = 0;
1521 format.Grouping = 0; /* No grouping char */
1522 format.NegativeOrder = 0;
1523 format.lpDecimalSep = szDot;
1524 format.lpThousandSep = szComma;
1526 STRINGSA("2353","2353"); /* No decimal or grouping chars expected */
1527 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1528 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1529 EXPECT_LENA; EXPECT_EQA;
1531 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1532 STRINGSA("2353","2353.0");
1533 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1534 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1535 EXPECT_LENA; EXPECT_EQA;
1537 format.Grouping = 2; /* Group by 100's */
1538 STRINGSA("2353","23,53.0");
1539 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1540 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1541 EXPECT_LENA; EXPECT_EQA;
1543 STRINGSA("235","235.0"); /* Grouping of a positive number */
1544 format.Grouping = 3;
1545 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1546 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1547 EXPECT_LENA; EXPECT_EQA;
1549 STRINGSA("-235","-235.0"); /* Grouping of a negative number */
1550 format.NegativeOrder = NEG_LEFT;
1551 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1552 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1553 EXPECT_LENA; EXPECT_EQA;
1555 format.LeadingZero = 1; /* Always provide leading zero */
1556 STRINGSA(".5","0.5");
1557 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1558 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1559 EXPECT_LENA; EXPECT_EQA;
1561 format.NegativeOrder = NEG_PARENS;
1562 STRINGSA("-1","(1.0)");
1563 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1564 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1565 EXPECT_LENA; EXPECT_EQA;
1567 format.NegativeOrder = NEG_LEFT;
1568 STRINGSA("-1","-1.0");
1569 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1570 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1571 EXPECT_LENA; EXPECT_EQA;
1573 format.NegativeOrder = NEG_LEFT_SPACE;
1574 STRINGSA("-1","- 1.0");
1575 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1576 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1577 EXPECT_LENA; EXPECT_EQA;
1579 format.NegativeOrder = NEG_RIGHT;
1580 STRINGSA("-1","1.0-");
1581 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1582 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1583 EXPECT_LENA; EXPECT_EQA;
1585 format.NegativeOrder = NEG_RIGHT_SPACE;
1586 STRINGSA("-1","1.0 -");
1587 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1588 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1589 EXPECT_LENA; EXPECT_EQA;
1591 lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1593 if (IsValidLocale(lcid, 0))
1595 STRINGSA("-12345","-12 345,00"); /* Try French formatting */
1596 Expected[3] = (char)160; /* Non breaking space */
1597 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1598 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1599 EXPECT_LENA; EXPECT_EQA;
1603 static void test_GetNumberFormatEx(void)
1605 int ret;
1606 NUMBERFMTW format;
1607 static WCHAR dotW[] = {'.',0};
1608 static WCHAR commaW[] = {',',0};
1609 static const WCHAR enW[] = {'e','n','-','U','S',0};
1610 static const WCHAR frW[] = {'f','r','-','F','R',0};
1611 static const WCHAR bogusW[] = {'b','o','g','u','s',0};
1612 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
1614 if (!pGetNumberFormatEx)
1616 win_skip("GetNumberFormatEx is not available.\n");
1617 return;
1620 STRINGSW("23",""); /* NULL output, length > 0 --> Error */
1621 ret = pGetNumberFormatEx(enW, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1622 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1623 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1625 STRINGSW("23,53",""); /* Invalid character --> Error */
1626 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1627 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1628 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1630 STRINGSW("--",""); /* Double '-' --> Error */
1631 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1632 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1633 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1635 STRINGSW("0-",""); /* Trailing '-' --> Error */
1636 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1637 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1638 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1640 STRINGSW("0..",""); /* Double '.' --> Error */
1641 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1642 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1643 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1645 STRINGSW(" 0.1",""); /* Leading space --> Error */
1646 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1647 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1648 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1650 STRINGSW("1234","1"); /* Length too small --> Write up to length chars */
1651 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, 2);
1652 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1653 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1655 STRINGSW("23",""); /* Bogus locale --> Error */
1656 ret = pGetNumberFormatEx(bogusW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1657 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1658 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1660 memset(&format, 0, sizeof(format));
1662 STRINGSW("2353",""); /* Format and flags given --> Error */
1663 ret = pGetNumberFormatEx(enW, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1664 ok( !ret, "Expected ret == 0, got %d\n", ret);
1665 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1666 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1668 STRINGSW("2353",""); /* Invalid format --> Error */
1669 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1670 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1671 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1673 STRINGSW("2353","2,353.00"); /* Valid number */
1674 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1675 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1676 EXPECT_LENW; EXPECT_EQW;
1678 STRINGSW("-2353","-2,353.00"); /* Valid negative number */
1679 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1680 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1681 EXPECT_LENW; EXPECT_EQW;
1683 STRINGSW("-353","-353.00"); /* test for off by one error in grouping */
1684 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1685 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1686 EXPECT_LENW; EXPECT_EQW;
1688 STRINGSW("2353.1","2,353.10"); /* Valid real number */
1689 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1690 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1691 EXPECT_LENW; EXPECT_EQW;
1693 STRINGSW("2353.111","2,353.11"); /* Too many DP --> Truncated */
1694 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1695 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1696 EXPECT_LENW; EXPECT_EQW;
1698 STRINGSW("2353.119","2,353.12"); /* Too many DP --> Rounded */
1699 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1700 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1701 EXPECT_LENW; EXPECT_EQW;
1703 format.NumDigits = 0; /* No decimal separator */
1704 format.LeadingZero = 0;
1705 format.Grouping = 0; /* No grouping char */
1706 format.NegativeOrder = 0;
1707 format.lpDecimalSep = dotW;
1708 format.lpThousandSep = commaW;
1710 STRINGSW("2353","2353"); /* No decimal or grouping chars expected */
1711 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1712 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1713 EXPECT_LENW; EXPECT_EQW;
1715 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1716 STRINGSW("2353","2353.0");
1717 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1718 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1719 EXPECT_LENW; EXPECT_EQW;
1721 format.Grouping = 2; /* Group by 100's */
1722 STRINGSW("2353","23,53.0");
1723 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1724 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1725 EXPECT_LENW; EXPECT_EQW;
1727 STRINGSW("235","235.0"); /* Grouping of a positive number */
1728 format.Grouping = 3;
1729 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1730 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1731 EXPECT_LENW; EXPECT_EQW;
1733 STRINGSW("-235","-235.0"); /* Grouping of a negative number */
1734 format.NegativeOrder = NEG_LEFT;
1735 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1736 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1737 EXPECT_LENW; EXPECT_EQW;
1739 format.LeadingZero = 1; /* Always provide leading zero */
1740 STRINGSW(".5","0.5");
1741 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1742 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1743 EXPECT_LENW; EXPECT_EQW;
1745 format.NegativeOrder = NEG_PARENS;
1746 STRINGSW("-1","(1.0)");
1747 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1748 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1749 EXPECT_LENW; EXPECT_EQW;
1751 format.NegativeOrder = NEG_LEFT;
1752 STRINGSW("-1","-1.0");
1753 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1754 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1755 EXPECT_LENW; EXPECT_EQW;
1757 format.NegativeOrder = NEG_LEFT_SPACE;
1758 STRINGSW("-1","- 1.0");
1759 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1760 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1761 EXPECT_LENW; EXPECT_EQW;
1763 format.NegativeOrder = NEG_RIGHT;
1764 STRINGSW("-1","1.0-");
1765 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1766 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1767 EXPECT_LENW; EXPECT_EQW;
1769 format.NegativeOrder = NEG_RIGHT_SPACE;
1770 STRINGSW("-1","1.0 -");
1771 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1772 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1773 EXPECT_LENW; EXPECT_EQW;
1775 if (pIsValidLocaleName(frW))
1777 STRINGSW("-12345","-12 345,00"); /* Try French formatting */
1778 Expected[3] = 160; /* Non breaking space */
1779 ret = pGetNumberFormatEx(frW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1780 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1781 EXPECT_LENW; EXPECT_EQW;
1785 struct comparestringa_entry {
1786 LCID lcid;
1787 DWORD flags;
1788 const char *first;
1789 int first_len;
1790 const char *second;
1791 int second_len;
1792 int ret;
1793 DWORD le;
1796 static const struct comparestringa_entry comparestringa_data[] = {
1797 { LOCALE_SYSTEM_DEFAULT, 0, "EndDialog", -1, "_Property", -1, CSTR_GREATER_THAN },
1798 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0070", -1, "_IEWWBrowserComp", -1, CSTR_GREATER_THAN },
1799 { LOCALE_SYSTEM_DEFAULT, 0, "r", -1, "\\", -1, CSTR_GREATER_THAN },
1800 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0031", -1, "OriginalDatabase", -1, CSTR_GREATER_THAN },
1801 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1, CSTR_GREATER_THAN },
1802 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1, CSTR_LESS_THAN },
1803 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1804 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1805 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1, CSTR_LESS_THAN },
1806 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1, CSTR_LESS_THAN },
1807 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1, CSTR_LESS_THAN },
1808 { LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1, CSTR_GREATER_THAN },
1809 { LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1, CSTR_LESS_THAN },
1810 { LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1, CSTR_LESS_THAN },
1811 { LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1, CSTR_GREATER_THAN },
1812 { LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1, CSTR_GREATER_THAN },
1813 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1, CSTR_LESS_THAN },
1814 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1, CSTR_LESS_THAN },
1815 { LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1, CSTR_LESS_THAN },
1816 /* hyphen and apostrophe are treated differently depending on whether SORT_STRINGSORT specified or not */
1817 { LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1, CSTR_GREATER_THAN },
1818 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1, CSTR_LESS_THAN },
1819 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1, CSTR_LESS_THAN },
1820 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1, CSTR_GREATER_THAN },
1821 { LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1, CSTR_GREATER_THAN },
1822 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1, CSTR_LESS_THAN },
1823 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1, CSTR_LESS_THAN },
1824 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1, CSTR_GREATER_THAN },
1825 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9, CSTR_EQUAL },
1826 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10, CSTR_LESS_THAN },
1827 { LOCALE_SYSTEM_DEFAULT, 0, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1828 { LOCALE_SYSTEM_DEFAULT, 0, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1829 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1830 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1831 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a.", 3, "a\0", 3, CSTR_EQUAL },
1832 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a ", 3, "a\0", 3, CSTR_EQUAL },
1833 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0\0", 4, CSTR_EQUAL },
1834 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0\0", 4, CSTR_EQUAL },
1835 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 1, CSTR_EQUAL },
1836 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 2, CSTR_EQUAL },
1837 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0x", 4, CSTR_LESS_THAN },
1838 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0x", 4, CSTR_LESS_THAN },
1839 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 1, CSTR_GREATER_THAN },
1840 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 2, CSTR_GREATER_THAN },
1841 /* flag tests */
1842 { LOCALE_SYSTEM_DEFAULT, LOCALE_USE_CP_ACP, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1843 { LOCALE_SYSTEM_DEFAULT, LINGUISTIC_IGNORECASE, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1844 { LOCALE_SYSTEM_DEFAULT, LINGUISTIC_IGNOREDIACRITIC, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1845 { LOCALE_SYSTEM_DEFAULT, NORM_IGNOREKANATYPE, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1846 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORENONSPACE, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1847 { LOCALE_SYSTEM_DEFAULT, NORM_IGNOREWIDTH, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1848 { LOCALE_SYSTEM_DEFAULT, NORM_LINGUISTIC_CASING, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1849 { LOCALE_SYSTEM_DEFAULT, SORT_DIGITSASNUMBERS, "NULL", -1, "NULL", -1, 0, ERROR_INVALID_FLAGS }
1852 static void test_CompareStringA(void)
1854 static const char ABC_EE[] = {'A','B','C',0,0xEE};
1855 static const char ABC_FF[] = {'A','B','C',0,0xFF};
1856 int ret, i;
1857 char a[256];
1858 LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1860 for (i = 0; i < ARRAY_SIZE(comparestringa_data); i++)
1862 const struct comparestringa_entry *entry = &comparestringa_data[i];
1864 SetLastError(0xdeadbeef);
1865 ret = CompareStringA(entry->lcid, entry->flags, entry->first, entry->first_len,
1866 entry->second, entry->second_len);
1867 ok(ret == entry->ret, "%d: got %d, expected %d\n", i, ret, entry->ret);
1868 ok(GetLastError() == (ret ? 0xdeadbeef : entry->le), "%d: got last error %d, expected %d\n",
1869 i, GetLastError(), (ret ? 0xdeadbeef : entry->le));
1872 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
1873 ok (ret == CSTR_LESS_THAN, "(Salut/Salute) Expected CSTR_LESS_THAN, got %d\n", ret);
1875 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
1876 ok (ret == CSTR_EQUAL, "(Salut/SaLuT) Expected CSTR_EQUAL, got %d\n", ret);
1878 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
1879 ok (ret == CSTR_GREATER_THAN, "(Salut/hola) Expected CSTR_GREATER_THAN, got %d\n", ret);
1881 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1882 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1884 lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1886 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1887 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1889 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
1890 ok (ret == CSTR_GREATER_THAN, "(haha/hoho) Expected CSTR_GREATER_THAN, got %d\n", ret);
1892 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1);
1893 ok (ret == CSTR_EQUAL, "(Salut/saLuT) Expected CSTR_EQUAL, got %d\n", ret);
1895 ret = lstrcmpA("", "");
1896 ok (ret == 0, "lstrcmpA(\"\", \"\") should return 0, got %d\n", ret);
1898 ret = lstrcmpA(NULL, NULL);
1899 ok (ret == 0 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, NULL) should return 0, got %d\n", ret);
1901 ret = lstrcmpA("", NULL);
1902 ok (ret == 1 || broken(ret == -2) /* win9x */, "lstrcmpA(\"\", NULL) should return 1, got %d\n", ret);
1904 ret = lstrcmpA(NULL, "");
1905 ok (ret == -1 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, \"\") should return -1, got %d\n", ret);
1908 if (0) { /* this requires collation table patch to make it MS compatible */
1909 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
1910 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1912 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 );
1913 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1915 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 );
1916 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1918 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 );
1919 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1921 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 );
1922 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1924 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 );
1925 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1927 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 );
1928 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1930 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 );
1931 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1933 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 );
1934 ok(ret == CSTR_LESS_THAN, "`o vs -m expected CSTR_LESS_THAN, got %d\n", ret);
1936 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 );
1937 ok(ret == CSTR_GREATER_THAN, "-m vs `o CSTR_GREATER_THAN got %d\n", ret);
1939 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 );
1940 ok(ret == CSTR_GREATER_THAN, "`o vs -m CSTR_GREATER_THAN got %d\n", ret);
1942 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 );
1943 ok(ret == CSTR_LESS_THAN, "-m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1947 /* WinXP handles embedded NULLs differently than earlier versions */
1948 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
1949 ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLuZkUtZ vs aLuZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret);
1951 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10);
1952 ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLu\\0ZkUtZ vs aLu\\0ZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret);
1954 ret = CompareStringA(lcid, 0, "a\0b", -1, "a", -1);
1955 ok(ret == CSTR_EQUAL, "a vs a expected CSTR_EQUAL, got %d\n", ret);
1957 ret = CompareStringA(lcid, 0, "a\0b", 4, "a", 2);
1958 ok(ret == CSTR_EQUAL || /* win2k */
1959 ret == CSTR_GREATER_THAN,
1960 "a\\0b vs a expected CSTR_EQUAL or CSTR_GREATER_THAN, got %d\n", ret);
1962 ret = CompareStringA(lcid, 0, "\2", 2, "\1", 2);
1963 todo_wine ok(ret != CSTR_EQUAL, "\\2 vs \\1 expected unequal\n");
1965 ret = CompareStringA(lcid, NORM_IGNORECASE | LOCALE_USE_CP_ACP, "#", -1, ".", -1);
1966 todo_wine ok(ret == CSTR_LESS_THAN, "\"#\" vs \".\" expected CSTR_LESS_THAN, got %d\n", ret);
1968 ret = CompareStringA(lcid, NORM_IGNORECASE, "_", -1, ".", -1);
1969 todo_wine ok(ret == CSTR_GREATER_THAN, "\"_\" vs \".\" expected CSTR_GREATER_THAN, got %d\n", ret);
1971 ret = lstrcmpiA("#", ".");
1972 todo_wine ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret);
1974 lcid = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT);
1976 /* \xB9 character lies between a and b */
1977 ret = CompareStringA(lcid, 0, "a", 1, "\xB9", 1);
1978 ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be greater than \'a\'\n");
1979 ret = CompareStringA(lcid, 0, "\xB9", 1, "b", 1);
1980 ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be smaller than \'b\'\n");
1982 memset(a, 'a', sizeof(a));
1983 SetLastError(0xdeadbeef);
1984 ret = CompareStringA(lcid, 0, a, sizeof(a), a, sizeof(a));
1985 ok (GetLastError() == 0xdeadbeef && ret == CSTR_EQUAL,
1986 "ret %d, error %d, expected value %d\n", ret, GetLastError(), CSTR_EQUAL);
1988 ret = CompareStringA(CP_ACP, 0, ABC_EE, 3, ABC_FF, 3);
1989 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
1990 ret = CompareStringA(CP_ACP, 0, ABC_EE, 5, ABC_FF, 3);
1991 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
1992 ret = CompareStringA(CP_ACP, 0, ABC_EE, 3, ABC_FF, 5);
1993 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
1994 ret = CompareStringA(CP_ACP, 0, ABC_EE, 5, ABC_FF, 5);
1995 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
1996 ret = CompareStringA(CP_ACP, 0, ABC_FF, 5, ABC_EE, 5);
1997 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
2000 static void test_CompareStringW(void)
2002 static const WCHAR ABC_EE[] = {'A','B','C',0,0xEE};
2003 static const WCHAR ABC_FF[] = {'A','B','C',0,0xFF};
2004 static const WCHAR A_ACUTE_BC[] = {0xc1,'B','C',0};
2005 static const WCHAR A_ACUTE_BC_DECOMP[] = {'A',0x301,'B','C',0};
2006 static const WCHAR A_NULL_BC[] = {'A',0,'B','C',0};
2007 WCHAR *str1, *str2;
2008 SYSTEM_INFO si;
2009 DWORD old_prot;
2010 BOOL success;
2011 char *buf;
2012 int ret;
2014 GetSystemInfo(&si);
2015 buf = VirtualAlloc(NULL, si.dwPageSize * 4, MEM_COMMIT, PAGE_READWRITE);
2016 ok(buf != NULL, "VirtualAlloc failed with %u\n", GetLastError());
2017 success = VirtualProtect(buf + si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
2018 ok(success, "VirtualProtect failed with %u\n", GetLastError());
2019 success = VirtualProtect(buf + 3 * si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
2020 ok(success, "VirtualProtect failed with %u\n", GetLastError());
2022 str1 = (WCHAR *)(buf + si.dwPageSize - sizeof(WCHAR));
2023 str2 = (WCHAR *)(buf + 3 * si.dwPageSize - sizeof(WCHAR));
2024 *str1 = 'A';
2025 *str2 = 'B';
2027 /* CompareStringW should abort on the first non-matching character */
2028 ret = CompareStringW(CP_ACP, 0, str1, 100, str2, 100);
2029 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2031 success = VirtualFree(buf, 0, MEM_RELEASE);
2032 ok(success, "VirtualFree failed with %u\n", GetLastError());
2034 SetLastError(0xdeadbeef);
2035 ret = CompareStringW(CP_ACP, SORT_DIGITSASNUMBERS, L"NULL", -1, L"NULL", -1);
2036 ok(ret == CSTR_EQUAL || broken(!ret && GetLastError() == ERROR_INVALID_FLAGS) /* <Win7 */,
2037 "expected CSTR_EQUAL, got %d, last error %d\n", ret, GetLastError());
2039 ret = CompareStringW(CP_ACP, 0, ABC_EE, 3, ABC_FF, 3);
2040 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2041 ret = CompareStringW(CP_ACP, 0, ABC_EE, 5, ABC_FF, 3);
2042 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
2043 ret = CompareStringW(CP_ACP, 0, ABC_EE, 3, ABC_FF, 5);
2044 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2045 ret = CompareStringW(CP_ACP, 0, ABC_EE, 5, ABC_FF, 5);
2046 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2047 ret = CompareStringW(CP_ACP, 0, ABC_FF, 5, ABC_EE, 5);
2048 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
2050 ret = CompareStringW(CP_ACP, 0, ABC_EE, 4, A_ACUTE_BC, 4);
2051 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2052 ret = CompareStringW(CP_ACP, 0, ABC_EE, 4, A_ACUTE_BC_DECOMP, 5);
2053 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2054 ret = CompareStringW(CP_ACP, 0, A_ACUTE_BC, 4, A_ACUTE_BC_DECOMP, 5);
2055 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2057 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, ABC_EE, 3, A_ACUTE_BC, 4);
2058 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2059 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, ABC_EE, 4, A_ACUTE_BC_DECOMP, 5);
2060 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2061 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, A_ACUTE_BC, 4, A_ACUTE_BC_DECOMP, 5);
2062 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2064 ret = CompareStringW(CP_ACP, 0, ABC_EE, 4, A_NULL_BC, 4);
2065 ok(ret == CSTR_EQUAL, "expected CSTR_LESS_THAN, got %d\n", ret);
2066 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, ABC_EE, 4, A_NULL_BC, 4);
2067 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2069 ret = CompareStringW(CP_ACP, 0, A_NULL_BC, 4, A_ACUTE_BC, 4);
2070 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2071 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, A_NULL_BC, 4, A_ACUTE_BC, 4);
2072 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2074 ret = CompareStringW(CP_ACP, 0, A_NULL_BC, 4, A_ACUTE_BC_DECOMP, 5);
2075 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2076 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, A_NULL_BC, 4, A_ACUTE_BC_DECOMP, 5);
2077 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2080 struct comparestringex_test {
2081 const char *locale;
2082 DWORD flags;
2083 const WCHAR first[2];
2084 const WCHAR second[2];
2085 INT ret;
2086 INT broken;
2087 BOOL todo;
2090 static const struct comparestringex_test comparestringex_tests[] = {
2091 /* default behavior */
2092 { /* 0 */
2093 "tr-TR", 0,
2094 {'i',0}, {'I',0}, CSTR_LESS_THAN, -1, FALSE
2096 { /* 1 */
2097 "tr-TR", 0,
2098 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2100 { /* 2 */
2101 "tr-TR", 0,
2102 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2104 { /* 3 */
2105 "tr-TR", 0,
2106 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2108 { /* 4 */
2109 "tr-TR", 0,
2110 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2112 { /* 5 */
2113 "tr-TR", 0,
2114 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2116 /* with NORM_IGNORECASE */
2117 { /* 6 */
2118 "tr-TR", NORM_IGNORECASE,
2119 {'i',0}, {'I',0}, CSTR_EQUAL, -1, FALSE
2121 { /* 7 */
2122 "tr-TR", NORM_IGNORECASE,
2123 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2125 { /* 8 */
2126 "tr-TR", NORM_IGNORECASE,
2127 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2129 { /* 9 */
2130 "tr-TR", NORM_IGNORECASE,
2131 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2133 { /* 10 */
2134 "tr-TR", NORM_IGNORECASE,
2135 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2137 { /* 11 */
2138 "tr-TR", NORM_IGNORECASE,
2139 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2141 /* with NORM_LINGUISTIC_CASING */
2142 { /* 12 */
2143 "tr-TR", NORM_LINGUISTIC_CASING,
2144 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2146 { /* 13 */
2147 "tr-TR", NORM_LINGUISTIC_CASING,
2148 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2150 { /* 14 */
2151 "tr-TR", NORM_LINGUISTIC_CASING,
2152 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2154 { /* 15 */
2155 "tr-TR", NORM_LINGUISTIC_CASING,
2156 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2158 { /* 16 */
2159 "tr-TR", NORM_LINGUISTIC_CASING,
2160 {'I',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2162 { /* 17 */
2163 "tr-TR", NORM_LINGUISTIC_CASING,
2164 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2166 /* with LINGUISTIC_IGNORECASE */
2167 { /* 18 */
2168 "tr-TR", LINGUISTIC_IGNORECASE,
2169 {'i',0}, {'I',0}, CSTR_EQUAL, -1, TRUE
2171 { /* 19 */
2172 "tr-TR", LINGUISTIC_IGNORECASE,
2173 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2175 { /* 20 */
2176 "tr-TR", LINGUISTIC_IGNORECASE,
2177 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2179 { /* 21 */
2180 "tr-TR", LINGUISTIC_IGNORECASE,
2181 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2183 { /* 22 */
2184 "tr-TR", LINGUISTIC_IGNORECASE,
2185 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2187 { /* 23 */
2188 "tr-TR", LINGUISTIC_IGNORECASE,
2189 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2191 /* with NORM_LINGUISTIC_CASING | NORM_IGNORECASE */
2192 { /* 24 */
2193 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2194 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL, TRUE
2196 { /* 25 */
2197 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2198 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2200 { /* 26 */
2201 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2202 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2204 { /* 27 */
2205 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2206 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2208 { /* 28 */
2209 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2210 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2212 { /* 29 */
2213 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2214 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2216 /* with NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE */
2217 { /* 30 */
2218 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2219 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL, TRUE
2221 { /* 31 */
2222 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2223 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2225 { /* 32 */
2226 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2227 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2229 { /* 33 */
2230 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2231 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2233 { /* 34 */
2234 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2235 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2237 { /* 35 */
2238 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2239 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2243 static void test_CompareStringEx(void)
2245 const char *op[] = {"ERROR", "CSTR_LESS_THAN", "CSTR_EQUAL", "CSTR_GREATER_THAN"};
2246 WCHAR locale[6];
2247 INT ret, i;
2249 /* CompareStringEx is only available on Vista+ */
2250 if (!pCompareStringEx)
2252 win_skip("CompareStringEx not supported\n");
2253 return;
2256 for (i = 0; i < ARRAY_SIZE(comparestringex_tests); i++)
2258 const struct comparestringex_test *e = &comparestringex_tests[i];
2260 MultiByteToWideChar(CP_ACP, 0, e->locale, -1, locale, ARRAY_SIZE(locale));
2261 ret = pCompareStringEx(locale, e->flags, e->first, -1, e->second, -1, NULL, NULL, 0);
2262 todo_wine_if (e->todo)
2263 ok(ret == e->ret || broken(ret == e->broken),
2264 "%d: got %s, expected %s\n", i, op[ret], op[e->ret]);
2269 static const DWORD lcmap_invalid_flags[] = {
2271 LCMAP_HIRAGANA | LCMAP_KATAKANA,
2272 LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
2273 LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
2274 LCMAP_LOWERCASE | SORT_STRINGSORT,
2275 LCMAP_UPPERCASE | NORM_IGNORESYMBOLS,
2276 LCMAP_LOWERCASE | NORM_IGNORESYMBOLS,
2277 LCMAP_UPPERCASE | NORM_IGNORENONSPACE,
2278 LCMAP_LOWERCASE | NORM_IGNORENONSPACE,
2279 LCMAP_HIRAGANA | NORM_IGNORENONSPACE,
2280 LCMAP_HIRAGANA | NORM_IGNORESYMBOLS,
2281 LCMAP_HIRAGANA | LCMAP_SIMPLIFIED_CHINESE,
2282 LCMAP_HIRAGANA | LCMAP_TRADITIONAL_CHINESE,
2283 LCMAP_KATAKANA | NORM_IGNORENONSPACE,
2284 LCMAP_KATAKANA | NORM_IGNORESYMBOLS,
2285 LCMAP_KATAKANA | LCMAP_SIMPLIFIED_CHINESE,
2286 LCMAP_KATAKANA | LCMAP_TRADITIONAL_CHINESE,
2287 LCMAP_FULLWIDTH | NORM_IGNORENONSPACE,
2288 LCMAP_FULLWIDTH | NORM_IGNORESYMBOLS,
2289 LCMAP_FULLWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2290 LCMAP_FULLWIDTH | LCMAP_TRADITIONAL_CHINESE,
2291 LCMAP_HALFWIDTH | NORM_IGNORENONSPACE,
2292 LCMAP_HALFWIDTH | NORM_IGNORESYMBOLS,
2293 LCMAP_HALFWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2294 LCMAP_HALFWIDTH | LCMAP_TRADITIONAL_CHINESE,
2297 static void test_LCMapStringA(void)
2299 int ret, ret2, i;
2300 char buf[256], buf2[256];
2301 static const char upper_case[] = "\tJUST! A, TEST; STRING 1/*+-.\r\n";
2302 static const char lower_case[] = "\tjust! a, test; string 1/*+-.\r\n";
2303 static const char symbols_stripped[] = "justateststring1";
2305 SetLastError(0xdeadbeef);
2306 ret = LCMapStringA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP | LCMAP_LOWERCASE,
2307 lower_case, -1, buf, sizeof(buf));
2308 ok(ret == lstrlenA(lower_case) + 1,
2309 "ret %d, error %d, expected value %d\n",
2310 ret, GetLastError(), lstrlenA(lower_case) + 1);
2311 ok(!memcmp(buf, lower_case, ret), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2313 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
2314 upper_case, -1, buf, sizeof(buf));
2315 ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
2316 ok(GetLastError() == ERROR_INVALID_FLAGS,
2317 "unexpected error code %d\n", GetLastError());
2319 /* test invalid flag combinations */
2320 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2321 lstrcpyA(buf, "foo");
2322 SetLastError(0xdeadbeef);
2323 ret = LCMapStringA(LOCALE_USER_DEFAULT, lcmap_invalid_flags[i],
2324 lower_case, -1, buf, sizeof(buf));
2325 ok(GetLastError() == ERROR_INVALID_FLAGS,
2326 "LCMapStringA (flag %08x) unexpected error code %d\n",
2327 lcmap_invalid_flags[i], GetLastError());
2328 ok(!ret, "LCMapStringA (flag %08x) should return 0, got %d\n",
2329 lcmap_invalid_flags[i], ret);
2332 /* test LCMAP_LOWERCASE */
2333 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2334 upper_case, -1, buf, sizeof(buf));
2335 ok(ret == lstrlenA(upper_case) + 1,
2336 "ret %d, error %d, expected value %d\n",
2337 ret, GetLastError(), lstrlenA(upper_case) + 1);
2338 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2340 /* test LCMAP_UPPERCASE */
2341 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2342 lower_case, -1, buf, sizeof(buf));
2343 ok(ret == lstrlenA(lower_case) + 1,
2344 "ret %d, error %d, expected value %d\n",
2345 ret, GetLastError(), lstrlenA(lower_case) + 1);
2346 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2348 /* test buffer overflow */
2349 SetLastError(0xdeadbeef);
2350 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2351 lower_case, -1, buf, 4);
2352 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2353 "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
2355 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2356 lstrcpyA(buf, lower_case);
2357 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2358 buf, -1, buf, sizeof(buf));
2359 if (!ret) /* Win9x */
2360 trace("Ignoring LCMapStringA(LCMAP_UPPERCASE, buf, buf) error on Win9x\n");
2361 else
2363 ok(ret == lstrlenA(lower_case) + 1,
2364 "ret %d, error %d, expected value %d\n",
2365 ret, GetLastError(), lstrlenA(lower_case) + 1);
2366 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2368 lstrcpyA(buf, upper_case);
2369 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2370 buf, -1, buf, sizeof(buf));
2371 if (!ret) /* Win9x */
2372 trace("Ignoring LCMapStringA(LCMAP_LOWERCASE, buf, buf) error on Win9x\n");
2373 else
2375 ok(ret == lstrlenA(upper_case) + 1,
2376 "ret %d, error %d, expected value %d\n",
2377 ret, GetLastError(), lstrlenA(lower_case) + 1);
2378 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2381 /* otherwise src == dst should fail */
2382 SetLastError(0xdeadbeef);
2383 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
2384 buf, 10, buf, sizeof(buf));
2385 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2386 GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
2387 "unexpected error code %d\n", GetLastError());
2388 ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
2390 /* test whether '\0' is always appended */
2391 memset(buf, 0xff, sizeof(buf));
2392 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2393 upper_case, -1, buf, sizeof(buf));
2394 ok(ret, "LCMapStringA must succeed\n");
2395 ok(buf[ret-1] == 0, "LCMapStringA not null-terminated\n");
2396 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2397 upper_case, lstrlenA(upper_case), buf2, sizeof(buf2));
2398 ok(ret2, "LCMapStringA must succeed\n");
2399 ok(buf2[ret2-1] == 0, "LCMapStringA not null-terminated\n" );
2400 ok(ret == ret2, "lengths of sort keys must be equal\n");
2401 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2403 /* test we get the same length when no dest buffer is provided */
2404 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2405 upper_case, lstrlenA(upper_case), NULL, 0);
2406 ok(ret2, "LCMapStringA must succeed\n");
2407 ok(ret == ret2, "lengths of sort keys must be equal (%d vs %d)\n", ret, ret2);
2409 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2410 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
2411 upper_case, -1, buf, sizeof(buf));
2412 ok(ret, "LCMapStringA must succeed\n");
2413 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2414 lower_case, -1, buf2, sizeof(buf2));
2415 ok(ret2, "LCMapStringA must succeed\n");
2416 ok(ret == ret2, "lengths of sort keys must be equal\n");
2417 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2419 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2420 results from plain LCMAP_SORTKEY on Vista */
2422 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2423 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2424 lower_case, -1, buf, sizeof(buf));
2425 ok(ret, "LCMapStringA must succeed\n");
2426 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2427 symbols_stripped, -1, buf2, sizeof(buf2));
2428 ok(ret2, "LCMapStringA must succeed\n");
2429 ok(ret == ret2, "lengths of sort keys must be equal\n");
2430 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2432 /* test NORM_IGNORENONSPACE */
2433 lstrcpyA(buf, "foo");
2434 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
2435 lower_case, -1, buf, sizeof(buf));
2436 ok(ret == lstrlenA(lower_case) + 1, "LCMapStringA should return %d, ret = %d\n",
2437 lstrlenA(lower_case) + 1, ret);
2438 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2440 /* test NORM_IGNORESYMBOLS */
2441 lstrcpyA(buf, "foo");
2442 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
2443 lower_case, -1, buf, sizeof(buf));
2444 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2445 lstrlenA(symbols_stripped) + 1, ret);
2446 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2448 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2449 lstrcpyA(buf, "foo");
2450 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE,
2451 lower_case, -1, buf, sizeof(buf));
2452 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2453 lstrlenA(symbols_stripped) + 1, ret);
2454 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2456 /* test srclen = 0 */
2457 SetLastError(0xdeadbeef);
2458 ret = LCMapStringA(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
2459 ok(!ret, "LCMapStringA should fail with srclen = 0\n");
2460 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2461 "unexpected error code %d\n", GetLastError());
2464 typedef INT (*lcmapstring_wrapper)(DWORD, LPCWSTR, INT, LPWSTR, INT);
2466 static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *func_name)
2468 const static WCHAR japanese_text[] = {
2469 0x3044, 0x309d, 0x3084, 0x3001, 0x30a4, 0x30fc, 0x30cf, 0x30c8,
2470 0x30fc, 0x30f4, 0x30a9, 0x306e, 0x91ce, 0x539f, 0x306f, 0x5e83,
2471 0x3044, 0x3093, 0x3060, 0x3088, 0x3002, 0
2473 const static WCHAR hiragana_text[] = {
2474 0x3044, 0x309d, 0x3084, 0x3001, 0x3044, 0x30fc, 0x306f, 0x3068,
2475 0x30fc, 0x3094, 0x3049, 0x306e, 0x91ce, 0x539f, 0x306f, 0x5e83,
2476 0x3044, 0x3093, 0x3060, 0x3088, 0x3002, 0
2478 const static WCHAR katakana_text[] = {
2479 0x30a4, 0x30fd, 0x30e4, 0x3001, 0x30a4, 0x30fc, 0x30cf, 0x30c8,
2480 0x30fc, 0x30f4, 0x30a9, 0x30ce, 0x91ce, 0x539f, 0x30cf, 0x5e83,
2481 0x30a4, 0x30f3, 0x30c0, 0x30e8, 0x3002, 0
2483 const static WCHAR halfwidth_text[] = {
2484 0x3044, 0x309d, 0x3084, 0xff64, 0xff72, 0xff70, 0xff8a, 0xff84,
2485 0xff70, 0xff73, 0xff9e, 0xff6b, 0x306e, 0x91ce, 0x539f, 0x306f,
2486 0x5e83, 0x3044, 0x3093, 0x3060, 0x3088, 0xff61, 0
2488 const static WCHAR halfwidth_text2[] = {
2489 0xff72, 0x30fd, 0xff94, 0xff64, 0xff72, 0xff70, 0xff8a, 0xff84,
2490 0xff70, 0xff73, 0xff9e, 0xff6b, 0xff89, 0x91ce, 0x539f, 0xff8a,
2491 0x5e83, 0xff72, 0xff9d, 0xff80, 0xff9e, 0xff96, 0xff61, 0
2493 int ret, ret2, i;
2494 WCHAR buf[256], buf2[256];
2495 char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
2497 /* LCMAP_LOWERCASE | LCMAP_UPPERCASE makes LCMAP_TITLECASE, so it's valid now. */
2498 ret = func_ptr(LCMAP_LOWERCASE | LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2499 todo_wine ok(ret == lstrlenW(title_case) + 1 || broken(!ret),
2500 "%s ret %d, error %d, expected value %d\n", func_name,
2501 ret, GetLastError(), lstrlenW(title_case) + 1);
2502 todo_wine ok(lstrcmpW(buf, title_case) == 0 || broken(!ret),
2503 "Expected title case string\n");
2505 /* test invalid flag combinations */
2506 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2507 lstrcpyW(buf, fooW);
2508 SetLastError(0xdeadbeef);
2509 ret = func_ptr(lcmap_invalid_flags[i],
2510 lower_case, -1, buf, sizeof(buf));
2511 ok(GetLastError() == ERROR_INVALID_FLAGS,
2512 "%s (flag %08x) unexpected error code %d\n",
2513 func_name, lcmap_invalid_flags[i], GetLastError());
2514 ok(!ret, "%s (flag %08x) should return 0, got %d\n",
2515 func_name, lcmap_invalid_flags[i], ret);
2518 /* test LCMAP_LOWERCASE */
2519 ret = func_ptr(LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2520 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2521 ret, GetLastError(), lstrlenW(upper_case) + 1);
2522 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2524 /* test LCMAP_UPPERCASE */
2525 ret = func_ptr(LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2526 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2527 ret, GetLastError(), lstrlenW(lower_case) + 1);
2528 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2530 /* test LCMAP_HIRAGANA */
2531 ret = func_ptr(LCMAP_HIRAGANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2532 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2533 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2534 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2536 buf[0] = 0x30f5; /* KATAKANA LETTER SMALL KA */
2537 ret = func_ptr(LCMAP_HIRAGANA, buf, 1, buf2, 1);
2538 ok(ret == 1, "%s ret %d, error %d, expected value 1\n", func_name,
2539 ret, GetLastError());
2540 /* U+3095: HIRAGANA LETTER SMALL KA was added in Unicode 3.2 */
2541 ok(buf2[0] == 0x3095 || broken(buf2[0] == 0x30f5 /* Vista and earlier versions */),
2542 "%s expected %04x, got %04x\n", func_name, 0x3095, buf2[0]);
2544 /* test LCMAP_KATAKANA | LCMAP_LOWERCASE */
2545 ret = func_ptr(LCMAP_KATAKANA | LCMAP_LOWERCASE, japanese_text, -1, buf, ARRAY_SIZE(buf));
2546 ok(ret == lstrlenW(katakana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2547 ret, GetLastError(), lstrlenW(katakana_text) + 1);
2548 ok(!lstrcmpW(buf, katakana_text), "%s string compare mismatch\n", func_name);
2550 /* test LCMAP_FULLWIDTH */
2551 ret = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2552 ok(ret == lstrlenW(japanese_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2553 ret, GetLastError(), lstrlenW(japanese_text) + 1);
2554 ok(!lstrcmpW(buf, japanese_text), "%s string compare mismatch\n", func_name);
2556 ret2 = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, NULL, 0);
2557 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret2, ret);
2559 /* test LCMAP_FULLWIDTH | LCMAP_HIRAGANA
2560 (half-width katakana is converted into full-width hiragana) */
2561 ret = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2562 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2563 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2564 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2566 ret2 = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, NULL, 0);
2567 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2569 /* test LCMAP_HALFWIDTH */
2570 ret = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, buf, ARRAY_SIZE(buf));
2571 ok(ret == lstrlenW(halfwidth_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2572 ret, GetLastError(), lstrlenW(halfwidth_text) + 1);
2573 ok(!lstrcmpW(buf, halfwidth_text), "%s string compare mismatch\n", func_name);
2575 ret2 = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, NULL, 0);
2576 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2578 /* test LCMAP_HALFWIDTH | LCMAP_KATAKANA
2579 (hiragana character is converted into half-width katakana) */
2580 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_KATAKANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2581 ok(ret == lstrlenW(halfwidth_text2) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2582 ret, GetLastError(), lstrlenW(halfwidth_text2) + 1);
2583 ok(!lstrcmpW(buf, halfwidth_text2), "%s string compare mismatch\n", func_name);
2585 ret2 = func_ptr(LCMAP_HALFWIDTH | LCMAP_KATAKANA, japanese_text, -1, NULL, 0);
2586 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2588 /* test buffer overflow */
2589 SetLastError(0xdeadbeef);
2590 ret = func_ptr(LCMAP_UPPERCASE,
2591 lower_case, -1, buf, 4);
2592 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2593 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2595 /* KATAKANA LETTER GA (U+30AC) is converted into two half-width characters.
2596 Thus, it requires two WCHARs. */
2597 buf[0] = 0x30ac;
2598 SetLastError(0xdeadbeef);
2599 ret = func_ptr(LCMAP_HALFWIDTH, buf, 1, buf2, 1);
2600 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2601 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2603 buf[0] = 'a';
2604 buf[1] = 0x30ac;
2605 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 0);
2606 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2608 SetLastError(0xdeadbeef);
2609 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 1);
2610 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2611 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2613 SetLastError(0xdeadbeef);
2614 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 2);
2615 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2616 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2618 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 3);
2619 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2621 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 4);
2622 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2624 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2625 lstrcpyW(buf, lower_case);
2626 ret = func_ptr(LCMAP_UPPERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2627 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2628 ret, GetLastError(), lstrlenW(lower_case) + 1);
2629 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2631 lstrcpyW(buf, upper_case);
2632 ret = func_ptr(LCMAP_LOWERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2633 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2634 ret, GetLastError(), lstrlenW(lower_case) + 1);
2635 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2637 /* otherwise src == dst should fail */
2638 SetLastError(0xdeadbeef);
2639 ret = func_ptr(LCMAP_SORTKEY | LCMAP_UPPERCASE,
2640 buf, 10, buf, sizeof(buf));
2641 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2642 GetLastError() == ERROR_INVALID_PARAMETER /* Win7+ */,
2643 "%s unexpected error code %d\n", func_name, GetLastError());
2644 ok(!ret, "%s src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n", func_name);
2646 /* test whether '\0' is always appended */
2647 ret = func_ptr(LCMAP_SORTKEY,
2648 upper_case, -1, buf, sizeof(buf));
2649 ok(ret, "%s func_ptr must succeed\n", func_name);
2650 ret2 = func_ptr(LCMAP_SORTKEY,
2651 upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
2652 ok(ret, "%s func_ptr must succeed\n", func_name);
2653 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2654 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2656 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2657 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORECASE,
2658 upper_case, -1, buf, sizeof(buf));
2659 ok(ret, "%s func_ptr must succeed\n", func_name);
2660 ret2 = func_ptr(LCMAP_SORTKEY,
2661 lower_case, -1, buf2, sizeof(buf2));
2662 ok(ret2, "%s func_ptr must succeed\n", func_name);
2663 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2664 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2666 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2667 results from plain LCMAP_SORTKEY on Vista */
2669 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2670 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2671 lower_case, -1, buf, sizeof(buf));
2672 ok(ret, "%s func_ptr must succeed\n", func_name);
2673 ret2 = func_ptr(LCMAP_SORTKEY,
2674 symbols_stripped, -1, buf2, sizeof(buf2));
2675 ok(ret2, "%s func_ptr must succeed\n", func_name);
2676 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2677 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2679 /* test NORM_IGNORENONSPACE */
2680 lstrcpyW(buf, fooW);
2681 ret = func_ptr(NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2682 ok(ret == lstrlenW(lower_case) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2683 lstrlenW(lower_case) + 1, ret);
2684 ok(!lstrcmpW(buf, lower_case), "%s string comparison mismatch\n", func_name);
2686 /* test NORM_IGNORESYMBOLS */
2687 lstrcpyW(buf, fooW);
2688 ret = func_ptr(NORM_IGNORESYMBOLS, lower_case, -1, buf, ARRAY_SIZE(buf));
2689 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2690 lstrlenW(symbols_stripped) + 1, ret);
2691 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2693 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2694 lstrcpyW(buf, fooW);
2695 ret = func_ptr(NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2696 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2697 lstrlenW(symbols_stripped) + 1, ret);
2698 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2700 /* test srclen = 0 */
2701 SetLastError(0xdeadbeef);
2702 ret = func_ptr(0, upper_case, 0, buf, ARRAY_SIZE(buf));
2703 ok(!ret, "%s func_ptr should fail with srclen = 0\n", func_name);
2704 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2705 "%s unexpected error code %d\n", func_name, GetLastError());
2708 static INT LCMapStringW_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2710 return LCMapStringW(LOCALE_USER_DEFAULT, flags, src, srclen, dst, dstlen);
2713 static void test_LCMapStringW(void)
2715 int ret;
2716 WCHAR buf[256];
2718 trace("testing LCMapStringW\n");
2720 SetLastError(0xdeadbeef);
2721 ret = LCMapStringW((LCID)-1, LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2722 todo_wine {
2723 ok(!ret, "LCMapStringW should fail with bad lcid\n");
2724 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
2727 test_lcmapstring_unicode(LCMapStringW_wrapper, "LCMapStringW:");
2730 static INT LCMapStringEx_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2732 return pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2735 static void test_LCMapStringEx(void)
2737 int ret;
2738 WCHAR buf[256];
2740 if (!pLCMapStringEx)
2742 win_skip( "LCMapStringEx not available\n" );
2743 return;
2746 trace("testing LCMapStringEx\n");
2748 SetLastError(0xdeadbeef);
2749 ret = pLCMapStringEx(invalidW, LCMAP_LOWERCASE,
2750 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 0);
2751 todo_wine {
2752 ok(!ret, "LCMapStringEx should fail with bad locale name\n");
2753 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
2756 /* test reserved parameters */
2757 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2758 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 1);
2759 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
2760 ret, GetLastError(), lstrlenW(upper_case) + 1);
2761 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2763 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2764 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, (void*)1, 0);
2765 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
2766 ret, GetLastError(), lstrlenW(upper_case) + 1);
2767 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2769 /* crashes on native */
2770 if(0)
2771 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2772 upper_case, -1, buf, ARRAY_SIZE(buf), (void*)1, NULL, 0);
2774 test_lcmapstring_unicode(LCMapStringEx_wrapper, "LCMapStringEx:");
2777 struct neutralsublang_name_t {
2778 WCHAR name[3];
2779 WCHAR sname[16];
2780 LCID lcid;
2783 static const struct neutralsublang_name_t neutralsublang_names[] = {
2784 { {'a','r',0}, {'a','r','-','S','A',0}, MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
2785 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0}, MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
2786 { {'d','e',0}, {'d','e','-','D','E',0}, MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
2787 { {'e','n',0}, {'e','n','-','U','S',0}, MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
2788 { {'e','s',0}, {'e','s','-','E','S',0}, MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT) },
2789 { {'g','a',0}, {'g','a','-','I','E',0}, MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT) },
2790 { {'i','t',0}, {'i','t','-','I','T',0}, MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
2791 { {'m','s',0}, {'m','s','-','M','Y',0}, MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
2792 { {'n','l',0}, {'n','l','-','N','L',0}, MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
2793 { {'p','t',0}, {'p','t','-','B','R',0}, MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
2794 { {'s','r',0}, {'s','r','-','L','a','t','n','-','R','S',0}, MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN), SORT_DEFAULT) },
2795 { {'s','v',0}, {'s','v','-','S','E',0}, MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
2796 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0}, MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
2797 { {'z','h',0}, {'z','h','-','C','N',0}, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
2798 { {0} }
2801 static void test_LocaleNameToLCID(void)
2803 LCID lcid, expect;
2804 NTSTATUS status;
2805 INT ret;
2806 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
2807 const struct neutralsublang_name_t *ptr;
2809 static const WCHAR enW[] = {'e','n',0};
2810 static const WCHAR esesW[] = {'e','s','-','e','s',0};
2811 static const WCHAR zhHansW[] = {'z','h','-','H','a','n','s',0};
2812 static const WCHAR zhhansW[] = {'z','h','-','h','a','n','s',0};
2813 static const WCHAR zhHantW[] = {'z','h','-','H','a','n','t',0};
2814 static const WCHAR zhhantW[] = {'z','h','-','h','a','n','t',0};
2815 static const WCHAR zhcnW[] = {'z','h','-','C','N',0};
2816 static const WCHAR zhhkW[] = {'z','h','-','H','K',0};
2818 if (!pLocaleNameToLCID)
2820 win_skip( "LocaleNameToLCID not available\n" );
2821 return;
2824 /* special cases */
2825 buffer[0] = 0;
2826 SetLastError(0xdeadbeef);
2827 lcid = pLocaleNameToLCID(LOCALE_NAME_USER_DEFAULT, 0);
2828 ok(lcid == GetUserDefaultLCID() || broken(GetLastError() == ERROR_INVALID_PARAMETER /* Vista */),
2829 "Expected lcid == %08x, got %08x, error %d\n", GetUserDefaultLCID(), lcid, GetLastError());
2830 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2831 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2832 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2834 buffer[0] = 0;
2835 SetLastError(0xdeadbeef);
2836 lcid = pLocaleNameToLCID(LOCALE_NAME_SYSTEM_DEFAULT, 0);
2837 ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
2838 "Expected lcid == 0, got %08x, error %d\n", lcid, GetLastError());
2839 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2840 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2841 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2843 buffer[0] = 0;
2844 SetLastError(0xdeadbeef);
2845 lcid = pLocaleNameToLCID(LOCALE_NAME_INVARIANT, 0);
2846 ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08x, error %d\n", lcid, GetLastError());
2847 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2848 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2849 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2851 /* bad name */
2852 SetLastError(0xdeadbeef);
2853 lcid = pLocaleNameToLCID(invalidW, 0);
2854 ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
2855 "Expected lcid == 0, got %08x, error %d\n", lcid, GetLastError());
2857 /* lower-case */
2858 lcid = pLocaleNameToLCID(esesW, 0);
2859 ok(lcid == MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT), "Got wrong lcid for es-es: 0x%x\n", lcid);
2861 /* english neutral name */
2862 lcid = pLocaleNameToLCID(enW, LOCALE_ALLOW_NEUTRAL_NAMES);
2863 ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT) ||
2864 broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid);
2865 lcid = pLocaleNameToLCID(enW, 0);
2866 ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) ||
2867 broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid);
2868 if (lcid)
2870 for (ptr = neutralsublang_names; *ptr->name; ptr++)
2872 lcid = pLocaleNameToLCID(ptr->name, 0);
2873 ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
2874 wine_dbgstr_w(ptr->name), lcid, ptr->lcid);
2876 *buffer = 0;
2877 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2878 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(ptr->name), ret);
2879 ok(!lstrcmpW(ptr->sname, buffer), "%s: got wrong locale name %s\n",
2880 wine_dbgstr_w(ptr->name), wine_dbgstr_w(buffer));
2884 /* zh-Hant has LCID 0x7c04, but LocaleNameToLCID actually returns 0x0c04, which is the LCID of zh-HK */
2885 lcid = pLocaleNameToLCID(zhHantW, 0);
2886 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
2887 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhHantW), lcid);
2888 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2889 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
2890 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2891 wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
2892 /* check that 0x7c04 also works and is mapped to zh-HK */
2893 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE_TRADITIONAL, SUBLANG_CHINESE_TRADITIONAL),
2894 buffer, ARRAY_SIZE(buffer), 0);
2895 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
2896 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2897 wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
2899 /* zh-hant */
2900 lcid = pLocaleNameToLCID(zhhantW, 0);
2901 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
2902 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhhantW), lcid);
2903 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2904 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhantW), ret);
2905 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2906 wine_dbgstr_w(zhhantW), wine_dbgstr_w(buffer));
2908 /* zh-Hans has LCID 0x0004, but LocaleNameToLCID actually returns 0x0804, which is the LCID of zh-CN */
2909 lcid = pLocaleNameToLCID(zhHansW, 0);
2910 /* check that LocaleNameToLCID actually returns 0x0804 */
2911 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
2912 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhHansW), lcid);
2913 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2914 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
2915 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2916 wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
2917 /* check that 0x0004 also works and is mapped to zh-CN */
2918 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE, SUBLANG_NEUTRAL), buffer, ARRAY_SIZE(buffer), 0);
2919 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
2920 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2921 wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
2923 /* zh-hans */
2924 lcid = pLocaleNameToLCID(zhhansW, 0);
2925 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
2926 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhhansW), lcid);
2927 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2928 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhansW), ret);
2929 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2930 wine_dbgstr_w(zhhansW), wine_dbgstr_w(buffer));
2933 if (pRtlLocaleNameToLcid)
2935 status = pRtlLocaleNameToLcid( LOCALE_NAME_USER_DEFAULT, &lcid, 0 );
2936 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2937 status = pRtlLocaleNameToLcid( LOCALE_NAME_SYSTEM_DEFAULT, &lcid, 0 );
2938 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2939 status = pRtlLocaleNameToLcid( invalidW, &lcid, 0 );
2940 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2942 lcid = 0;
2943 status = pRtlLocaleNameToLcid( LOCALE_NAME_INVARIANT, &lcid, 0 );
2944 ok( !status, "failed error %x\n", status );
2945 ok( lcid == LANG_INVARIANT, "got %08x\n", lcid );
2947 lcid = 0;
2948 status = pRtlLocaleNameToLcid( localeW, &lcid, 0 );
2949 ok( !status, "failed error %x\n", status );
2950 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), "got %08x\n", lcid );
2952 lcid = 0;
2953 status = pRtlLocaleNameToLcid( esesW, &lcid, 0 );
2954 ok( !status, "failed error %x\n", status );
2955 ok( lcid == MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), "got %08x\n", lcid );
2957 lcid = 0;
2958 status = pRtlLocaleNameToLcid( enW, &lcid, 0 );
2959 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2960 status = pRtlLocaleNameToLcid( enW, &lcid, 1 );
2961 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2962 status = pRtlLocaleNameToLcid( enW, &lcid, 2 );
2963 ok( !status, "failed error %x\n", status );
2964 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), "got %08x\n", lcid );
2965 status = pRtlLocaleNameToLcid( L"en-RR", &lcid, 2 );
2966 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2967 status = pRtlLocaleNameToLcid( L"en-Latn-RR", &lcid, 2 );
2968 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2970 for (ptr = neutralsublang_names; *ptr->name; ptr++)
2972 switch (LANGIDFROMLCID(ptr->lcid))
2974 case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN): expect = LANG_SERBIAN_NEUTRAL; break;
2975 case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_CYRILLIC): expect = 0x6c1a; break;
2976 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ): expect = 0x7804; break;
2977 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ): expect = LANG_CHINESE_TRADITIONAL; break;
2978 default: expect = MAKELANGID( PRIMARYLANGID(ptr->lcid), SUBLANG_NEUTRAL ); break;
2981 status = pRtlLocaleNameToLcid( ptr->name, &lcid, 2 );
2982 ok( !status || broken(ptr->lcid == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)), /* vista */
2983 "%s failed error %x\n", wine_dbgstr_w(ptr->name), status );
2984 if (!status) ok( lcid == expect, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
2985 wine_dbgstr_w(ptr->name), lcid, expect );
2986 status = pRtlLocaleNameToLcid( ptr->sname, &lcid, 0 );
2987 ok( !status || broken(ptr->lcid == MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN)), /* vista */
2988 "%s failed error %x\n", wine_dbgstr_w(ptr->name), status );
2989 if (!status) ok( lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
2990 wine_dbgstr_w(ptr->name), lcid, ptr->lcid );
2993 else win_skip( "RtlLocaleNameToLcid not available\n" );
2996 /* this requires collation table patch to make it MS compatible */
2997 static const char * const strings_sorted[] =
2999 "'",
3000 "-",
3001 "!",
3002 "\"",
3003 ".",
3004 ":",
3005 "\\",
3006 "_",
3007 "`",
3008 "{",
3009 "}",
3010 "+",
3011 "0",
3012 "1",
3013 "2",
3014 "3",
3015 "4",
3016 "5",
3017 "6",
3018 "7",
3019 "8",
3020 "9",
3021 "a",
3022 "A",
3023 "b",
3024 "B",
3025 "c",
3029 static const char * const strings[] =
3031 "C",
3032 "\"",
3033 "9",
3034 "'",
3035 "}",
3036 "-",
3037 "7",
3038 "+",
3039 "`",
3040 "1",
3041 "a",
3042 "5",
3043 "\\",
3044 "8",
3045 "B",
3046 "3",
3047 "_",
3048 "6",
3049 "{",
3050 "2",
3051 "c",
3052 "4",
3053 "!",
3054 "0",
3055 "A",
3056 ":",
3057 "b",
3061 static int compare_string1(const void *e1, const void *e2)
3063 const char *s1 = *(const char *const *)e1;
3064 const char *s2 = *(const char *const *)e2;
3066 return lstrcmpA(s1, s2);
3069 static int compare_string2(const void *e1, const void *e2)
3071 const char *s1 = *(const char *const *)e1;
3072 const char *s2 = *(const char *const *)e2;
3074 return CompareStringA(0, 0, s1, -1, s2, -1) - 2;
3077 static int compare_string3(const void *e1, const void *e2)
3079 const char *s1 = *(const char *const *)e1;
3080 const char *s2 = *(const char *const *)e2;
3081 char key1[256], key2[256];
3083 LCMapStringA(0, LCMAP_SORTKEY, s1, -1, key1, sizeof(key1));
3084 LCMapStringA(0, LCMAP_SORTKEY, s2, -1, key2, sizeof(key2));
3085 return strcmp(key1, key2);
3088 static void test_sorting(void)
3090 char buf[256];
3091 char **str_buf = (char **)buf;
3092 int i;
3094 assert(sizeof(buf) >= sizeof(strings));
3096 /* 1. sort using lstrcmpA */
3097 memcpy(buf, strings, sizeof(strings));
3098 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string1);
3099 for (i = 0; i < ARRAY_SIZE(strings); i++)
3101 ok(!strcmp(strings_sorted[i], str_buf[i]),
3102 "qsort using lstrcmpA failed for element %d\n", i);
3104 /* 2. sort using CompareStringA */
3105 memcpy(buf, strings, sizeof(strings));
3106 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string2);
3107 for (i = 0; i < ARRAY_SIZE(strings); i++)
3109 ok(!strcmp(strings_sorted[i], str_buf[i]),
3110 "qsort using CompareStringA failed for element %d\n", i);
3112 /* 3. sort using sort keys */
3113 memcpy(buf, strings, sizeof(strings));
3114 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string3);
3115 for (i = 0; i < ARRAY_SIZE(strings); i++)
3117 ok(!strcmp(strings_sorted[i], str_buf[i]),
3118 "qsort using sort keys failed for element %d\n", i);
3122 static void test_FoldStringA(void)
3124 int ret, i, j;
3125 BOOL is_special;
3126 char src[256], dst[256];
3127 static const char digits_src[] = { 0xB9,0xB2,0xB3,'\0' };
3128 static const char digits_dst[] = { '1','2','3','\0' };
3129 static const char composite_src[] =
3131 0x8a,0x8e,0x9a,0x9e,0x9f,0xc0,0xc1,0xc2,
3132 0xc3,0xc4,0xc5,0xc7,0xc8,0xc9,0xca,0xcb,
3133 0xcc,0xcd,0xce,0xcf,0xd1,0xd2,0xd3,0xd4,
3134 0xd5,0xd6,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,
3135 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe7,0xe8,
3136 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf1,
3137 0xf2,0xf3,0xf4,0xf5,0xf6,0xf8,0xf9,0xfa,
3138 0xfb,0xfc,0xfd,0xff,'\0'
3140 static const char composite_dst[] =
3142 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
3143 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
3144 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
3145 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
3146 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
3147 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
3148 0x4f,0x7e,0x4f,0xa8,0x4f,0x3f,0x55,0x60,
3149 0x55,0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,
3150 0x61,0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,
3151 0x61,0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,
3152 0x65,0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,
3153 0x69,0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,
3154 0x6f,0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,
3155 0x6f,0xa8,0x6f,0x3f,0x75,0x60,0x75,0xb4,
3156 0x75,0x5e,0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
3158 static const char composite_dst_alt[] =
3160 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
3161 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
3162 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
3163 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
3164 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
3165 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
3166 0x4f,0x7e,0x4f,0xa8,0xd8,0x55,0x60,0x55,
3167 0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,0x61,
3168 0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,0x61,
3169 0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,0x65,
3170 0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,0x69,
3171 0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,0x6f,
3172 0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,0x6f,
3173 0xa8,0xf8,0x75,0x60,0x75,0xb4,0x75,0x5e,
3174 0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
3176 static const char ligatures_src[] =
3178 0x8C,0x9C,0xC6,0xDE,0xDF,0xE6,0xFE,'\0'
3180 static const char ligatures_dst[] =
3182 'O','E','o','e','A','E','T','H','s','s','a','e','t','h','\0'
3184 static const struct special
3186 char src;
3187 char dst[4];
3188 } foldczone_special[] =
3190 /* src dst */
3191 { 0x85, { 0x2e, 0x2e, 0x2e, 0x00 } },
3192 { 0x98, { 0x20, 0x7e, 0x00 } },
3193 { 0x99, { 0x54, 0x4d, 0x00 } },
3194 { 0xa0, { 0x20, 0x00 } },
3195 { 0xa8, { 0x20, 0xa8, 0x00 } },
3196 { 0xaa, { 0x61, 0x00 } },
3197 { 0xaf, { 0x20, 0xaf, 0x00 } },
3198 { 0xb2, { 0x32, 0x00 } },
3199 { 0xb3, { 0x33, 0x00 } },
3200 { 0xb4, { 0x20, 0xb4, 0x00 } },
3201 { 0xb8, { 0x20, 0xb8, 0x00 } },
3202 { 0xb9, { 0x31, 0x00 } },
3203 { 0xba, { 0x6f, 0x00 } },
3204 { 0xbc, { 0x31, 0x2f, 0x34, 0x00 } },
3205 { 0xbd, { 0x31, 0x2f, 0x32, 0x00 } },
3206 { 0xbe, { 0x33, 0x2f, 0x34, 0x00 } },
3207 { 0x00 }
3210 /* these tests are locale specific */
3211 if (GetACP() != 1252)
3213 trace("Skipping FoldStringA tests for a not Latin 1 locale\n");
3214 return;
3217 /* MAP_FOLDDIGITS */
3218 SetLastError(0xdeadbeef);
3219 ret = FoldStringA(MAP_FOLDDIGITS, digits_src, -1, dst, 256);
3220 ok(ret == 4, "Expected ret == 4, got %d, error %d\n", ret, GetLastError());
3221 ok(strcmp(dst, digits_dst) == 0,
3222 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", digits_dst, dst);
3223 for (i = 1; i < 256; i++)
3225 if (!strchr(digits_src, i))
3227 src[0] = i;
3228 src[1] = '\0';
3229 ret = FoldStringA(MAP_FOLDDIGITS, src, -1, dst, 256);
3230 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3231 ok(dst[0] == src[0],
3232 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", src, dst);
3236 /* MAP_EXPAND_LIGATURES */
3237 SetLastError(0xdeadbeef);
3238 ret = FoldStringA(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
3239 ok(ret == sizeof(ligatures_dst), "Got %d, error %d\n", ret, GetLastError());
3240 ok(strcmp(dst, ligatures_dst) == 0,
3241 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", ligatures_dst, dst);
3242 for (i = 1; i < 256; i++)
3244 if (!strchr(ligatures_src, i))
3246 src[0] = i;
3247 src[1] = '\0';
3248 ret = FoldStringA(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
3249 if (ret == 3)
3251 /* Vista */
3252 ok((i == 0xDC && lstrcmpA(dst, "UE") == 0) ||
3253 (i == 0xFC && lstrcmpA(dst, "ue") == 0),
3254 "Got %s for %d\n", dst, i);
3256 else
3258 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3259 ok(dst[0] == src[0],
3260 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", src, dst);
3265 /* MAP_COMPOSITE */
3266 SetLastError(0xdeadbeef);
3267 ret = FoldStringA(MAP_COMPOSITE, composite_src, -1, dst, 256);
3268 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3269 ok( GetLastError() == 0xdeadbeef || broken(!GetLastError()), /* vista */
3270 "wrong error %u\n", GetLastError());
3271 ok(ret == 121 || ret == 119, "Expected 121 or 119, got %d\n", ret);
3272 ok(strcmp(dst, composite_dst) == 0 || strcmp(dst, composite_dst_alt) == 0,
3273 "MAP_COMPOSITE: Mismatch, got '%s'\n", dst);
3275 for (i = 1; i < 256; i++)
3277 if (!strchr(composite_src, i))
3279 src[0] = i;
3280 src[1] = '\0';
3281 ret = FoldStringA(MAP_COMPOSITE, src, -1, dst, 256);
3282 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3283 ok(dst[0] == src[0],
3284 "0x%02x, 0x%02x,0x%02x,0x%02x,\n", (unsigned char)src[0],
3285 (unsigned char)dst[0],(unsigned char)dst[1],(unsigned char)dst[2]);
3289 /* MAP_FOLDCZONE */
3290 for (i = 1; i < 256; i++)
3292 src[0] = i;
3293 src[1] = '\0';
3294 SetLastError(0xdeadbeef);
3295 ret = FoldStringA(MAP_FOLDCZONE, src, -1, dst, 256);
3296 is_special = FALSE;
3297 for (j = 0; foldczone_special[j].src != 0 && ! is_special; j++)
3299 if (foldczone_special[j].src == src[0])
3301 ok(ret == 2 || ret == lstrlenA(foldczone_special[j].dst) + 1,
3302 "Expected ret == 2 or %d, got %d, error %d\n",
3303 lstrlenA(foldczone_special[j].dst) + 1, ret, GetLastError());
3304 ok(src[0] == dst[0] || lstrcmpA(foldczone_special[j].dst, dst) == 0,
3305 "MAP_FOLDCZONE: string mismatch for 0x%02x\n",
3306 (unsigned char)src[0]);
3307 is_special = TRUE;
3310 if (! is_special)
3312 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3313 ok(src[0] == dst[0],
3314 "MAP_FOLDCZONE: Expected 0x%02x, got 0x%02x\n",
3315 (unsigned char)src[0], (unsigned char)dst[0]);
3319 /* MAP_PRECOMPOSED */
3320 for (i = 1; i < 256; i++)
3322 src[0] = i;
3323 src[1] = '\0';
3324 ret = FoldStringA(MAP_PRECOMPOSED, src, -1, dst, 256);
3325 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3326 ok(src[0] == dst[0],
3327 "MAP_PRECOMPOSED: Expected 0x%02x, got 0x%02x\n",
3328 (unsigned char)src[0], (unsigned char)dst[0]);
3332 static void test_FoldStringW(void)
3334 int ret;
3335 WORD type;
3336 unsigned int i, j;
3337 WCHAR src[256], dst[256], ch, prev_ch = 1;
3338 static const DWORD badFlags[] =
3341 MAP_PRECOMPOSED|MAP_COMPOSITE,
3342 MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES,
3343 MAP_COMPOSITE|MAP_EXPAND_LIGATURES
3345 /* Ranges of digits 0-9 : Must be sorted! */
3346 static const WCHAR digitRanges[] =
3348 0x0030, /* '0'-'9' */
3349 0x0660, /* Eastern Arabic */
3350 0x06F0, /* Arabic - Hindu */
3351 0x07C0, /* Nko */
3352 0x0966, /* Devengari */
3353 0x09E6, /* Bengalii */
3354 0x0A66, /* Gurmukhi */
3355 0x0AE6, /* Gujarati */
3356 0x0B66, /* Oriya */
3357 0x0BE6, /* Tamil - No 0 */
3358 0x0C66, /* Telugu */
3359 0x0CE6, /* Kannada */
3360 0x0D66, /* Maylayalam */
3361 0x0DE6, /* Sinhala Lith */
3362 0x0E50, /* Thai */
3363 0x0ED0, /* Laos */
3364 0x0F20, /* Tibet */
3365 0x0F29, /* Tibet half - 0 is out of sequence */
3366 0x1040, /* Myanmar */
3367 0x1090, /* Myanmar Shan */
3368 0x1368, /* Ethiopic - no 0 */
3369 0x17E0, /* Khmer */
3370 0x1810, /* Mongolian */
3371 0x1946, /* Limbu */
3372 0x19D0, /* New Tai Lue */
3373 0x1A80, /* Tai Tham Hora */
3374 0x1A90, /* Tai Tham Tham */
3375 0x1B50, /* Balinese */
3376 0x1BB0, /* Sundanese */
3377 0x1C40, /* Lepcha */
3378 0x1C50, /* Ol Chiki */
3379 0x2070, /* Superscript - 1, 2, 3 are out of sequence */
3380 0x2080, /* Subscript */
3381 0x245F, /* Circled - 0 is out of sequence */
3382 0x2473, /* Bracketed */
3383 0x2487, /* Full stop */
3384 0x24F4, /* Double Circled */
3385 0x2775, /* Inverted circled - No 0 */
3386 0x277F, /* Patterned circled - No 0 */
3387 0x2789, /* Inverted Patterned circled - No 0 */
3388 0x3020, /* Hangzhou */
3389 0xA620, /* Vai */
3390 0xA8D0, /* Saurashtra */
3391 0xA900, /* Kayah Li */
3392 0xA9D0, /* Javanese */
3393 0xA9F0, /* Myanmar Tai Laing */
3394 0xAA50, /* Cham */
3395 0xABF0, /* Meetei Mayek */
3396 0xff10, /* Pliene chasse (?) */
3397 0xffff /* Terminator */
3399 /* Digits which are represented, but out of sequence */
3400 static const WCHAR outOfSequenceDigits[] =
3402 0xB9, /* Superscript 1 */
3403 0xB2, /* Superscript 2 */
3404 0xB3, /* Superscript 3 */
3405 0x0C78, /* Telugu Fraction 0 */
3406 0x0C79, /* Telugu Fraction 1 */
3407 0x0C7A, /* Telugu Fraction 2 */
3408 0x0C7B, /* Telugu Fraction 3 */
3409 0x0C7C, /* Telugu Fraction 1 */
3410 0x0C7D, /* Telugu Fraction 2 */
3411 0x0C7E, /* Telugu Fraction 3 */
3412 0x0F33, /* Tibetan half zero */
3413 0x19DA, /* New Tai Lue Tham 1 */
3414 0x24EA, /* Circled 0 */
3415 0x24FF, /* Negative Circled 0 */
3416 0x3007, /* Ideographic number zero */
3417 '\0' /* Terminator */
3419 /* Digits in digitRanges for which no representation is available */
3420 static const WCHAR noDigitAvailable[] =
3422 0x0BE6, /* No Tamil 0 */
3423 0x0F29, /* No Tibetan half zero (out of sequence) */
3424 0x1368, /* No Ethiopic 0 */
3425 0x2473, /* No Bracketed 0 */
3426 0x2487, /* No 0 Full stop */
3427 0x24F4, /* No double circled 0 */
3428 0x2775, /* No inverted circled 0 */
3429 0x277F, /* No patterned circled */
3430 0x2789, /* No inverted Patterned circled */
3431 0x3020, /* No Hangzhou 0 */
3432 '\0' /* Terminator */
3434 static const WCHAR foldczone_src[] =
3436 'W', 'i', 'n', 'e', 0x0348, 0x0551, 0x1323, 0x280d,
3437 0xff37, 0xff49, 0xff4e, 0xff45, 0x3c5, 0x308, 0x6a, 0x30c, 0xa0, 0xaa, 0
3439 static const WCHAR foldczone_dst[] =
3441 'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x3cb,0x1f0,' ','a',0
3443 static const WCHAR foldczone_broken_dst[] =
3445 'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x03c5,0x0308,'j',0x030c,0x00a0,0x00aa,0
3447 static const WCHAR ligatures_src[] =
3449 'W', 'i', 'n', 'e', 0x03a6, 0x03b9, 0x03bd, 0x03b5,
3450 0x00c6, 0x00de, 0x00df, 0x00e6, 0x00fe, 0x0132, 0x0133, 0x0152,
3451 0x0153, 0x01c4, 0x01c5, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01ca,
3452 0x01cb, 0x01cc, 0x01e2, 0x01e3, 0x01f1, 0x01f2, 0x01f3, 0x01fc,
3453 0x01fd, 0x05f0, 0x05f1, 0x05f2, 0xfb00, 0xfb01, 0xfb02, 0xfb03,
3454 0xfb04, 0xfb05, 0xfb06, '\0'
3456 static const WCHAR ligatures_dst[] =
3458 'W','i','n','e',0x03a6,0x03b9,0x03bd,0x03b5,
3459 'A','E','T','H','s','s','a','e','t','h','I','J','i','j','O','E','o','e',
3460 'D',0x017d,'D',0x017e,'d',0x017e,'L','J','L','j','l','j','N','J','N','j',
3461 'n','j',0x0100,0x0112,0x0101,0x0113,'D','Z','D','z','d','z',0x00c1,0x00c9,
3462 0x00e1,0x00e9,0x05d5,0x05d5,0x05d5,0x05d9,0x05d9,0x05d9,'f','f','f','i',
3463 'f','l','f','f','i','f','f','l',0x017f,'t','s','t','\0'
3466 /* Invalid flag combinations */
3467 for (i = 0; i < ARRAY_SIZE(badFlags); i++)
3469 src[0] = dst[0] = '\0';
3470 SetLastError(0xdeadbeef);
3471 ret = FoldStringW(badFlags[i], src, 256, dst, 256);
3472 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
3473 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3476 /* src & dst cannot be the same */
3477 SetLastError(0xdeadbeef);
3478 ret = FoldStringW(MAP_FOLDCZONE, src, -1, src, 256);
3479 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3480 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3482 /* src can't be NULL */
3483 SetLastError(0xdeadbeef);
3484 ret = FoldStringW(MAP_FOLDCZONE, NULL, -1, dst, 256);
3485 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3486 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3488 /* srclen can't be 0 */
3489 SetLastError(0xdeadbeef);
3490 ret = FoldStringW(MAP_FOLDCZONE, src, 0, dst, 256);
3491 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3492 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3494 /* dstlen can't be < 0 */
3495 SetLastError(0xdeadbeef);
3496 ret = FoldStringW(MAP_FOLDCZONE, src, -1, dst, -1);
3497 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3498 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3500 /* Ret includes terminating NUL which is appended if srclen = -1 */
3501 SetLastError(0xdeadbeef);
3502 src[0] = 'A';
3503 src[1] = '\0';
3504 dst[0] = '\0';
3505 ret = FoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
3506 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3507 ok(dst[0] == 'A' && dst[1] == '\0',
3508 "srclen=-1: Expected ret=2 [%d,%d], got ret=%d [%d,%d], err=%d\n",
3509 'A', '\0', ret, dst[0], dst[1], GetLastError());
3511 /* If size is given, result is not NUL terminated */
3512 SetLastError(0xdeadbeef);
3513 src[0] = 'A';
3514 src[1] = 'A';
3515 dst[0] = 'X';
3516 dst[1] = 'X';
3517 ret = FoldStringW(MAP_FOLDCZONE, src, 1, dst, 256);
3518 ok(ret == 1, "Expected ret == 1, got %d, error %d\n", ret, GetLastError());
3519 ok(dst[0] == 'A' && dst[1] == 'X',
3520 "srclen=1: Expected ret=1, [%d,%d], got ret=%d,[%d,%d], err=%d\n",
3521 'A','X', ret, dst[0], dst[1], GetLastError());
3523 /* MAP_FOLDDIGITS */
3524 for (j = 0; j < ARRAY_SIZE(digitRanges); j++)
3526 /* Check everything before this range */
3527 for (ch = prev_ch; ch < digitRanges[j]; ch++)
3529 SetLastError(0xdeadbeef);
3530 src[0] = ch;
3531 src[1] = dst[0] = '\0';
3532 ret = FoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
3533 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3535 ok(dst[0] == ch || wcschr(outOfSequenceDigits, ch) ||
3536 (ch >= 0xa8e0 && ch <= 0xa8e9), /* combining Devanagari on Win8 */
3537 "MAP_FOLDDIGITS: ch 0x%04x Expected unchanged got %04x\n", ch, dst[0]);
3538 GetStringTypeW( CT_CTYPE1, &ch, 1, &type );
3539 ok(!(type & C1_DIGIT) || wcschr(outOfSequenceDigits, ch) ||
3540 broken( ch >= 0xbf0 && ch <= 0xbf2 ), /* win2k */
3541 "char %04x should not be a digit\n", ch );
3544 if (digitRanges[j] == 0xffff)
3545 break; /* Finished the whole code point space */
3547 for (ch = digitRanges[j]; ch < digitRanges[j] + 10; ch++)
3549 WCHAR c;
3551 /* Map out of sequence characters */
3552 if (ch == 0x2071) c = 0x00B9; /* Superscript 1 */
3553 else if (ch == 0x2072) c = 0x00B2; /* Superscript 2 */
3554 else if (ch == 0x2073) c = 0x00B3; /* Superscript 3 */
3555 else if (ch == 0x245F) c = 0x24EA; /* Circled 0 */
3556 else c = ch;
3557 SetLastError(0xdeadbeef);
3558 src[0] = c;
3559 src[1] = dst[0] = '\0';
3560 ret = FoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
3561 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3563 ok((dst[0] == '0' + ch - digitRanges[j] && dst[1] == '\0') ||
3564 broken( dst[0] == ch ) || /* old Windows versions don't have all mappings */
3565 (digitRanges[j] == 0x3020 && dst[0] == ch) || /* Hangzhou not present in all Windows versions */
3566 (digitRanges[j] == 0x0F29 && dst[0] == ch) || /* Tibetan not present in all Windows versions */
3567 wcschr(noDigitAvailable, c),
3568 "MAP_FOLDDIGITS: ch %04x Expected %04x got %04x\n",
3569 ch, '0' + digitRanges[j] - ch, dst[0]);
3571 prev_ch = ch;
3574 /* MAP_FOLDCZONE */
3575 SetLastError(0xdeadbeef);
3576 ret = FoldStringW(MAP_FOLDCZONE, foldczone_src, -1, dst, 256);
3577 ok(ret == ARRAY_SIZE(foldczone_dst)
3578 || broken(ret == ARRAY_SIZE(foldczone_broken_dst)), /* winxp, win2003 */
3579 "Got %d, error %d.\n", ret, GetLastError());
3580 ok(!memcmp(dst, foldczone_dst, sizeof(foldczone_dst))
3581 || broken(!memcmp(dst, foldczone_broken_dst, sizeof(foldczone_broken_dst))), /* winxp, win2003 */
3582 "Got unexpected string %s.\n", wine_dbgstr_w(dst));
3584 /* MAP_EXPAND_LIGATURES */
3585 SetLastError(0xdeadbeef);
3586 ret = FoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
3587 ok(ret == ARRAY_SIZE(ligatures_dst), "Got %d, error %d\n", ret, GetLastError());
3588 ok(!memcmp(dst, ligatures_dst, sizeof(ligatures_dst)),
3589 "Got unexpected string %s.\n", wine_dbgstr_w(dst));
3591 /* FIXME: MAP_PRECOMPOSED : MAP_COMPOSITE */
3596 #define LCID_OK(l) \
3597 ok(lcid == l, "Expected lcid = %08x, got %08x\n", l, lcid)
3598 #define MKLCID(x,y,z) MAKELCID(MAKELANGID(x, y), z)
3599 #define LCID_RES(src, res) do { lcid = ConvertDefaultLocale(src); LCID_OK(res); } while (0)
3600 #define TEST_LCIDLANG(a,b) LCID_RES(MAKELCID(a,b), MAKELCID(a,b))
3601 #define TEST_LCID(a,b,c) LCID_RES(MKLCID(a,b,c), MKLCID(a,b,c))
3603 static void test_ConvertDefaultLocale(void)
3605 /* some languages use a different default than SUBLANG_DEFAULT */
3606 static const struct { WORD lang, sublang; } nondefault_langs[] =
3608 { LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED },
3609 { LANG_SPANISH, SUBLANG_SPANISH_MODERN },
3610 { LANG_IRISH, SUBLANG_IRISH_IRELAND },
3611 { LANG_BENGALI, SUBLANG_BENGALI_BANGLADESH },
3612 { LANG_SINDHI, SUBLANG_SINDHI_AFGHANISTAN },
3613 { LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA_LATIN },
3614 { LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN },
3615 { LANG_FULAH, SUBLANG_FULAH_SENEGAL },
3616 { LANG_TIGRINYA, SUBLANG_TIGRINYA_ERITREA }
3618 LCID lcid;
3619 unsigned int i;
3621 /* Doesn't change lcid, even if non default sublang/sort used */
3622 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_US, SORT_DEFAULT);
3623 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_UK, SORT_DEFAULT);
3624 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT);
3625 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE);
3626 lcid = ConvertDefaultLocale( MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ));
3627 ok( lcid == MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ) ||
3628 broken( lcid == MKLCID( LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE )), /* <= vista */
3629 "Expected lcid = %08x got %08x\n",
3630 MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ), lcid );
3631 lcid = ConvertDefaultLocale( MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ));
3632 ok( lcid == MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ) ||
3633 broken( lcid == MKLCID( LANG_IRISH, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE )), /* <= vista */
3634 "Expected lcid = %08x got %08x\n",
3635 MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ), lcid );
3637 /* SUBLANG_NEUTRAL -> SUBLANG_DEFAULT */
3638 LCID_RES(MKLCID(LANG_ENGLISH, SUBLANG_NEUTRAL, SORT_DEFAULT),
3639 MKLCID(LANG_ENGLISH, SUBLANG_DEFAULT, SORT_DEFAULT));
3640 LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_DEFAULT),
3641 MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT));
3642 for (i = 0; i < ARRAY_SIZE(nondefault_langs); i++)
3644 lcid = ConvertDefaultLocale( MAKELANGID( nondefault_langs[i].lang, SUBLANG_NEUTRAL ));
3645 ok( lcid == MAKELANGID( nondefault_langs[i].lang, nondefault_langs[i].sublang ) ||
3646 broken( lcid == MAKELANGID( nondefault_langs[i].lang, SUBLANG_DEFAULT )) || /* <= vista */
3647 broken( lcid == MAKELANGID( nondefault_langs[i].lang, SUBLANG_NEUTRAL )), /* w7 */
3648 "Expected lcid = %08x got %08x\n",
3649 MAKELANGID( nondefault_langs[i].lang, nondefault_langs[i].sublang ), lcid );
3651 lcid = ConvertDefaultLocale( 0x7804 );
3652 ok( lcid == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ) ||
3653 broken( lcid == 0x7804 ), /* <= vista */
3654 "Expected lcid = %08x got %08x\n", MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ), lcid );
3655 lcid = ConvertDefaultLocale( 0x7c04 );
3656 ok( lcid == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ) ||
3657 broken( lcid == 0x7c04 ) || /* winxp */
3658 broken( lcid == 0x0404 ), /* vista */
3659 "Expected lcid = %08x got %08x\n", MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ), lcid );
3660 lcid = ConvertDefaultLocale( LANG_SERBIAN_NEUTRAL );
3661 ok( lcid == MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN ) ||
3662 broken( lcid == MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_LATIN ) ), /* <= vista */
3663 "Expected lcid = %08x got %08x\n", MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN ), lcid );
3665 /* Invariant language is not treated specially */
3666 TEST_LCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT);
3668 /* User/system default languages alone are not mapped */
3669 TEST_LCIDLANG(LANG_SYSTEM_DEFAULT, SORT_JAPANESE_UNICODE);
3670 TEST_LCIDLANG(LANG_USER_DEFAULT, SORT_JAPANESE_UNICODE);
3672 /* Default lcids */
3673 LCID_RES(LOCALE_SYSTEM_DEFAULT, GetSystemDefaultLCID());
3674 LCID_RES(LOCALE_USER_DEFAULT, GetUserDefaultLCID());
3675 LCID_RES(LOCALE_NEUTRAL, GetUserDefaultLCID());
3676 lcid = ConvertDefaultLocale(LOCALE_INVARIANT);
3677 ok(lcid == LOCALE_INVARIANT || broken(lcid == 0x47f) /* win2k[3]/winxp */,
3678 "Expected lcid = %08x, got %08x\n", LOCALE_INVARIANT, lcid);
3681 static BOOL CALLBACK langgrp_procA(LGRPID lgrpid, LPSTR lpszNum, LPSTR lpszName,
3682 DWORD dwFlags, LONG_PTR lParam)
3684 if (winetest_debug > 1)
3685 trace("%08x, %s, %s, %08x, %08lx\n",
3686 lgrpid, lpszNum, lpszName, dwFlags, lParam);
3688 ok(pIsValidLanguageGroup(lgrpid, dwFlags) == TRUE,
3689 "Enumerated grp %d not valid (flags %d)\n", lgrpid, dwFlags);
3691 /* If lParam is one, we are calling with flags defaulted from 0 */
3692 ok(!lParam || (dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED),
3693 "Expected dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED, got %d\n", dwFlags);
3695 return TRUE;
3698 static void test_EnumSystemLanguageGroupsA(void)
3700 BOOL ret;
3702 if (!pEnumSystemLanguageGroupsA || !pIsValidLanguageGroup)
3704 win_skip("EnumSystemLanguageGroupsA and/or IsValidLanguageGroup are not available\n");
3705 return;
3708 /* No enumeration proc */
3709 SetLastError(0);
3710 ret = pEnumSystemLanguageGroupsA(0, LGRPID_INSTALLED, 0);
3711 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3713 win_skip("EnumSystemLanguageGroupsA is not implemented\n");
3714 return;
3716 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3717 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3719 /* Invalid flags */
3720 SetLastError(0);
3721 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED|LGRPID_SUPPORTED, 0);
3722 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3724 /* No flags - defaults to LGRPID_INSTALLED */
3725 SetLastError(0xdeadbeef);
3726 pEnumSystemLanguageGroupsA(langgrp_procA, 0, 1);
3727 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
3729 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED, 0);
3730 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
3733 static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam )
3735 if (winetest_debug > 1)
3736 trace( "%s %x\n", wine_dbgstr_w(name), flags );
3737 return TRUE;
3740 static void test_EnumSystemLocalesEx(void)
3742 BOOL ret;
3744 if (!pEnumSystemLocalesEx)
3746 win_skip( "EnumSystemLocalesEx not available\n" );
3747 return;
3749 SetLastError( 0xdeadbeef );
3750 ret = pEnumSystemLocalesEx( enum_func, LOCALE_ALL, 0, (void *)1 );
3751 ok( !ret, "should have failed\n" );
3752 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
3753 SetLastError( 0xdeadbeef );
3754 ret = pEnumSystemLocalesEx( enum_func, 0, 0, NULL );
3755 ok( ret, "failed err %u\n", GetLastError() );
3758 static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
3759 LONG_PTR lParam)
3761 if (winetest_debug > 1)
3762 trace("%08x, %08x, %s, %08lx\n", lgrpid, lcid, lpszNum, lParam);
3764 /* invalid locale enumerated on some platforms */
3765 if (lcid == 0)
3766 return TRUE;
3768 ok(pIsValidLanguageGroup(lgrpid, LGRPID_SUPPORTED) == TRUE,
3769 "Enumerated grp %d not valid\n", lgrpid);
3770 ok(IsValidLocale(lcid, LCID_SUPPORTED) == TRUE,
3771 "Enumerated grp locale %04x not valid\n", lcid);
3772 return TRUE;
3775 static void test_EnumLanguageGroupLocalesA(void)
3777 BOOL ret;
3779 if (!pEnumLanguageGroupLocalesA || !pIsValidLanguageGroup)
3781 win_skip("EnumLanguageGroupLocalesA and/or IsValidLanguageGroup are not available\n");
3782 return;
3785 /* No enumeration proc */
3786 SetLastError(0);
3787 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0, 0);
3788 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3790 win_skip("EnumLanguageGroupLocalesA is not implemented\n");
3791 return;
3793 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3794 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3796 /* lgrpid too small */
3797 SetLastError(0);
3798 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, 0, 0, 0);
3799 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3800 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3802 /* lgrpid too big */
3803 SetLastError(0);
3804 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_ARMENIAN + 1, 0, 0);
3805 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3806 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3808 /* dwFlags is reserved */
3809 SetLastError(0);
3810 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0x1, 0);
3811 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3812 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3814 pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_WESTERN_EUROPE, 0, 0);
3817 static void test_SetLocaleInfoA(void)
3819 BOOL bRet;
3820 LCID lcid = GetUserDefaultLCID();
3822 /* Null data */
3823 SetLastError(0);
3824 bRet = SetLocaleInfoA(lcid, LOCALE_SDATE, 0);
3825 ok( !bRet && GetLastError() == ERROR_INVALID_PARAMETER,
3826 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3828 /* IDATE */
3829 SetLastError(0);
3830 bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, "test_SetLocaleInfoA");
3831 ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
3832 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3834 /* ILDATE */
3835 SetLastError(0);
3836 bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, "test_SetLocaleInfoA");
3837 ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
3838 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3841 static BOOL CALLBACK luilocale_proc1A(LPSTR value, LONG_PTR lParam)
3843 if (winetest_debug > 1)
3844 trace("%s %08lx\n", value, lParam);
3845 return(TRUE);
3848 static BOOL CALLBACK luilocale_proc2A(LPSTR value, LONG_PTR lParam)
3850 ok(!enumCount, "callback called again unexpected\n");
3851 enumCount++;
3852 return(FALSE);
3855 static BOOL CALLBACK luilocale_proc3A(LPSTR value, LONG_PTR lParam)
3857 ok(0,"callback called unexpected\n");
3858 return(FALSE);
3861 static void test_EnumUILanguageA(void)
3863 BOOL ret;
3864 if (!pEnumUILanguagesA) {
3865 win_skip("EnumUILanguagesA is not available on Win9x or NT4\n");
3866 return;
3869 SetLastError(ERROR_SUCCESS);
3870 ret = pEnumUILanguagesA(luilocale_proc1A, 0, 0);
3871 if (ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3873 win_skip("EnumUILanguagesA is not implemented\n");
3874 return;
3876 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3878 enumCount = 0;
3879 SetLastError(ERROR_SUCCESS);
3880 ret = pEnumUILanguagesA(luilocale_proc2A, 0, 0);
3881 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3882 ok(enumCount == 1, "enumCount = %u\n", enumCount);
3884 enumCount = 0;
3885 SetLastError(ERROR_SUCCESS);
3886 ret = pEnumUILanguagesA(luilocale_proc2A, MUI_LANGUAGE_ID, 0);
3887 ok(ret || broken(!ret && GetLastError() == ERROR_INVALID_FLAGS), /* winxp */
3888 "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3889 if (ret) ok(enumCount == 1, "enumCount = %u\n", enumCount);
3891 SetLastError(ERROR_SUCCESS);
3892 ret = pEnumUILanguagesA(NULL, 0, 0);
3893 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3894 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3895 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3897 SetLastError(ERROR_SUCCESS);
3898 ret = pEnumUILanguagesA(luilocale_proc3A, 0x5a5a5a5a, 0);
3899 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3900 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3902 SetLastError(ERROR_SUCCESS);
3903 ret = pEnumUILanguagesA(NULL, 0x5a5a5a5a, 0);
3904 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3905 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3906 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3909 static char date_fmt_buf[1024];
3910 static WCHAR date_fmt_bufW[1024];
3912 static BOOL CALLBACK enum_datetime_procA(LPSTR fmt)
3914 lstrcatA(date_fmt_buf, fmt);
3915 lstrcatA(date_fmt_buf, "\n");
3916 return TRUE;
3919 static BOOL CALLBACK enum_datetime_procW(WCHAR *fmt)
3921 lstrcatW(date_fmt_bufW, fmt);
3922 return FALSE;
3925 static void test_EnumDateFormatsA(void)
3927 char *p, buf[256];
3928 BOOL ret;
3929 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
3931 date_fmt_buf[0] = 0;
3932 SetLastError(0xdeadbeef);
3933 ret = EnumDateFormatsA(enum_datetime_procA, lcid, 0);
3934 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3936 win_skip("0 for dwFlags is not supported\n");
3938 else
3940 ok(ret, "EnumDateFormatsA(0) error %d\n", GetLastError());
3941 trace("EnumDateFormatsA(0): %s\n", date_fmt_buf);
3942 /* test the 1st enumerated format */
3943 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3944 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3945 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3946 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3949 date_fmt_buf[0] = 0;
3950 SetLastError(0xdeadbeef);
3951 ret = EnumDateFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
3952 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3954 win_skip("LOCALE_USE_CP_ACP is not supported\n");
3956 else
3958 ok(ret, "EnumDateFormatsA(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
3959 trace("EnumDateFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
3960 /* test the 1st enumerated format */
3961 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3962 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3963 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3964 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3967 date_fmt_buf[0] = 0;
3968 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_SHORTDATE);
3969 ok(ret, "EnumDateFormatsA(DATE_SHORTDATE) error %d\n", GetLastError());
3970 trace("EnumDateFormatsA(DATE_SHORTDATE): %s\n", date_fmt_buf);
3971 /* test the 1st enumerated format */
3972 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3973 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3974 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3975 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3977 date_fmt_buf[0] = 0;
3978 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_LONGDATE);
3979 ok(ret, "EnumDateFormatsA(DATE_LONGDATE) error %d\n", GetLastError());
3980 trace("EnumDateFormatsA(DATE_LONGDATE): %s\n", date_fmt_buf);
3981 /* test the 1st enumerated format */
3982 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3983 ret = GetLocaleInfoA(lcid, LOCALE_SLONGDATE, buf, sizeof(buf));
3984 ok(ret, "GetLocaleInfoA(LOCALE_SLONGDATE) error %d\n", GetLastError());
3985 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3987 date_fmt_buf[0] = 0;
3988 SetLastError(0xdeadbeef);
3989 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_YEARMONTH);
3990 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3992 win_skip("DATE_YEARMONTH is only present on W2K and later\n");
3993 return;
3995 ok(ret, "EnumDateFormatsA(DATE_YEARMONTH) error %d\n", GetLastError());
3996 trace("EnumDateFormatsA(DATE_YEARMONTH): %s\n", date_fmt_buf);
3997 /* test the 1st enumerated format */
3998 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3999 ret = GetLocaleInfoA(lcid, LOCALE_SYEARMONTH, buf, sizeof(buf));
4000 ok(ret, "GetLocaleInfoA(LOCALE_SYEARMONTH) error %d\n", GetLastError());
4001 ok(!lstrcmpA(date_fmt_buf, buf) || broken(!buf[0]) /* win9x */,
4002 "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4005 static void test_EnumTimeFormatsA(void)
4007 char *p, buf[256];
4008 BOOL ret;
4009 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
4011 date_fmt_buf[0] = 0;
4012 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, 0);
4013 ok(ret, "EnumTimeFormatsA(0) error %d\n", GetLastError());
4014 trace("EnumTimeFormatsA(0): %s\n", date_fmt_buf);
4015 /* test the 1st enumerated format */
4016 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4017 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
4018 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
4019 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4021 date_fmt_buf[0] = 0;
4022 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
4023 ok(ret, "EnumTimeFormatsA(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
4024 trace("EnumTimeFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
4025 /* test the 1st enumerated format */
4026 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4027 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
4028 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
4029 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4032 static void test_EnumTimeFormatsW(void)
4034 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
4035 WCHAR bufW[256];
4036 BOOL ret;
4038 date_fmt_bufW[0] = 0;
4039 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, 0);
4040 ok(ret, "EnumTimeFormatsW(0) error %d\n", GetLastError());
4041 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
4042 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
4043 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4044 wine_dbgstr_w(bufW));
4046 date_fmt_bufW[0] = 0;
4047 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, LOCALE_USE_CP_ACP);
4048 ok(ret, "EnumTimeFormatsW(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
4049 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
4050 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
4051 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4052 wine_dbgstr_w(bufW));
4054 /* TIME_NOSECONDS is Win7+ feature */
4055 date_fmt_bufW[0] = 0;
4056 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, TIME_NOSECONDS);
4057 if (!ret && GetLastError() == ERROR_INVALID_FLAGS)
4058 win_skip("EnumTimeFormatsW doesn't support TIME_NOSECONDS\n");
4059 else {
4060 char buf[256];
4062 ok(ret, "EnumTimeFormatsW(TIME_NOSECONDS) error %d\n", GetLastError());
4063 ret = GetLocaleInfoW(lcid, LOCALE_SSHORTTIME, bufW, ARRAY_SIZE(bufW));
4064 ok(ret, "GetLocaleInfoW(LOCALE_SSHORTTIME) error %d\n", GetLastError());
4065 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4066 wine_dbgstr_w(bufW));
4068 /* EnumTimeFormatsA doesn't support this flag */
4069 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, TIME_NOSECONDS);
4070 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
4071 GetLastError());
4073 ret = EnumTimeFormatsA(NULL, lcid, TIME_NOSECONDS);
4074 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
4075 GetLastError());
4077 /* And it's not supported by GetLocaleInfoA either */
4078 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTTIME, buf, ARRAY_SIZE(buf));
4079 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "GetLocaleInfoA(LOCALE_SSHORTTIME) ret %d, error %d\n", ret,
4080 GetLastError());
4084 static void test_GetCPInfo(void)
4086 BOOL ret;
4087 CPINFO cpinfo;
4089 SetLastError(0xdeadbeef);
4090 ret = GetCPInfo(CP_SYMBOL, &cpinfo);
4091 ok(!ret, "GetCPInfo(CP_SYMBOL) should fail\n");
4092 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4093 "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
4095 SetLastError(0xdeadbeef);
4096 ret = GetCPInfo(CP_UTF7, &cpinfo);
4097 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
4099 win_skip("Codepage CP_UTF7 is not installed/available\n");
4101 else
4103 ok(ret, "GetCPInfo(CP_UTF7) error %u\n", GetLastError());
4104 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
4105 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
4106 ok(cpinfo.LeadByte[0] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[0]);
4107 ok(cpinfo.LeadByte[1] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[1]);
4108 ok(cpinfo.MaxCharSize == 5, "expected 5, got 0x%x\n", cpinfo.MaxCharSize);
4111 SetLastError(0xdeadbeef);
4112 ret = GetCPInfo(CP_UTF8, &cpinfo);
4113 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
4115 win_skip("Codepage CP_UTF8 is not installed/available\n");
4117 else
4119 ok(ret, "GetCPInfo(CP_UTF8) error %u\n", GetLastError());
4120 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
4121 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
4122 ok(cpinfo.LeadByte[0] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[0]);
4123 ok(cpinfo.LeadByte[1] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[1]);
4124 ok(cpinfo.MaxCharSize == 4 || broken(cpinfo.MaxCharSize == 3) /* win9x */,
4125 "expected 4, got %u\n", cpinfo.MaxCharSize);
4128 if (pNtGetNlsSectionPtr)
4130 CPTABLEINFO table;
4131 NTSTATUS status;
4132 void *ptr, *ptr2;
4133 SIZE_T size;
4134 int i;
4136 for (i = 0; i < 100; i++)
4138 ptr = NULL;
4139 size = 0;
4140 status = pNtGetNlsSectionPtr( i, 9999, NULL, &ptr, &size );
4141 switch (i)
4143 case 9: /* sortkeys */
4144 case 13: /* unknown */
4145 ok( status == STATUS_INVALID_PARAMETER_1 || status == STATUS_INVALID_PARAMETER_3, /* vista */
4146 "%u: failed %x\n", i, status );
4147 break;
4148 case 10: /* casemap */
4149 ok( status == STATUS_INVALID_PARAMETER_1 || status == STATUS_UNSUCCESSFUL,
4150 "%u: failed %x\n", i, status );
4151 break;
4152 case 11: /* codepage */
4153 case 12: /* normalization */
4154 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%u: failed %x\n", i, status );
4155 break;
4156 default:
4157 ok( status == STATUS_INVALID_PARAMETER_1, "%u: failed %x\n", i, status );
4158 break;
4162 /* casemap table */
4164 status = pNtGetNlsSectionPtr( 10, 0, NULL, &ptr, &size );
4165 if (status != STATUS_INVALID_PARAMETER_1)
4167 ok( !status, "failed %x\n", status );
4168 ok( size > 0x1000 && size <= 0x8000 , "wrong size %lx\n", size );
4169 status = pNtGetNlsSectionPtr( 10, 0, NULL, &ptr2, &size );
4170 ok( ptr != ptr2, "got same pointer\n" );
4171 ret = UnmapViewOfFile( ptr );
4172 ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4173 ret = UnmapViewOfFile( ptr2 );
4174 ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4177 /* codepage tables */
4179 ptr = (void *)0xdeadbeef;
4180 size = 0xdeadbeef;
4181 status = pNtGetNlsSectionPtr( 11, 437, NULL, &ptr, &size );
4182 ok( !status, "failed %x\n", status );
4183 ok( size > 0x10000 && size <= 0x20000, "wrong size %lx\n", size );
4184 memset( &table, 0xcc, sizeof(table) );
4185 if (pRtlInitCodePageTable)
4187 pRtlInitCodePageTable( ptr, &table );
4188 ok( table.CodePage == 437, "wrong codepage %u\n", table.CodePage );
4189 ok( table.MaximumCharacterSize == 1, "wrong char size %u\n", table.MaximumCharacterSize );
4190 ok( table.DefaultChar == '?', "wrong default char %x\n", table.DefaultChar );
4191 ok( !table.DBCSCodePage, "wrong dbcs %u\n", table.DBCSCodePage );
4193 ret = UnmapViewOfFile( ptr );
4194 ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4196 status = pNtGetNlsSectionPtr( 11, 936, NULL, &ptr, &size );
4197 ok( !status, "failed %x\n", status );
4198 ok( size > 0x30000 && size <= 0x40000, "wrong size %lx\n", size );
4199 memset( &table, 0xcc, sizeof(table) );
4200 if (pRtlInitCodePageTable)
4202 pRtlInitCodePageTable( ptr, &table );
4203 ok( table.CodePage == 936, "wrong codepage %u\n", table.CodePage );
4204 ok( table.MaximumCharacterSize == 2, "wrong char size %u\n", table.MaximumCharacterSize );
4205 ok( table.DefaultChar == '?', "wrong default char %x\n", table.DefaultChar );
4206 ok( table.DBCSCodePage == TRUE, "wrong dbcs %u\n", table.DBCSCodePage );
4208 if (pRtlCustomCPToUnicodeN)
4210 static const unsigned char buf[] = { 0xbf, 0xb4, 0xc7, 0, 0x78 };
4211 static const WCHAR expect[][4] = { { 0xcccc, 0xcccc, 0xcccc, 0xcccc },
4212 { 0x0000, 0xcccc, 0xcccc, 0xcccc },
4213 { 0x770b, 0xcccc, 0xcccc, 0xcccc },
4214 { 0x770b, 0x0000, 0xcccc, 0xcccc },
4215 { 0x770b, 0x003f, 0xcccc, 0xcccc },
4216 { 0x770b, 0x003f, 0x0078, 0xcccc } };
4217 WCHAR wbuf[5];
4218 DWORD i, j, reslen;
4220 for (i = 0; i <= sizeof(buf); i++)
4222 memset( wbuf, 0xcc, sizeof(wbuf) );
4223 pRtlCustomCPToUnicodeN( &table, wbuf, sizeof(wbuf), &reslen, (char *)buf, i );
4224 for (j = 0; j < 4; j++) if (expect[i][j] == 0xcccc) break;
4225 ok( reslen == j * sizeof(WCHAR), "%u: wrong len %u\n", i, reslen );
4226 for (j = 0; j < 4; j++)
4227 ok( wbuf[j] == expect[i][j], "%u: char %u got %04x\n", i, j, wbuf[j] );
4231 ret = UnmapViewOfFile( ptr );
4232 ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4234 /* normalization tables */
4236 for (i = 0; i < 100; i++)
4238 status = pNtGetNlsSectionPtr( 12, i, NULL, &ptr, &size );
4239 switch (i)
4241 case NormalizationC:
4242 case NormalizationD:
4243 case NormalizationKC:
4244 case NormalizationKD:
4245 case 13: /* IDN */
4246 ok( !status, "%u: failed %x\n", i, status );
4247 if (status) break;
4248 ok( size > 0x8000 && size <= 0x30000 , "wrong size %lx\n", size );
4249 ret = UnmapViewOfFile( ptr );
4250 ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4251 break;
4252 default:
4253 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%u: failed %x\n", i, status );
4254 break;
4258 else win_skip( "NtGetNlsSectionPtr not supported\n" );
4262 * The CT_TYPE1 has varied over windows version.
4263 * The current target for correct behavior is windows 7.
4264 * There was a big shift between windows 2000 (first introduced) and windows Xp
4265 * Most of the old values below are from windows 2000.
4266 * A smaller subset of changes happened between windows Xp and Window vista/7
4268 static void test_GetStringTypeW(void)
4270 static const WCHAR blanks[] = {0x9, 0x20, 0xa0, 0x3000, 0xfeff};
4271 static const WORD blanks_new[] = {C1_SPACE | C1_CNTRL | C1_BLANK | C1_DEFINED,
4272 C1_SPACE | C1_BLANK | C1_DEFINED,
4273 C1_SPACE | C1_BLANK | C1_DEFINED,
4274 C1_SPACE | C1_BLANK | C1_DEFINED,
4275 C1_CNTRL | C1_BLANK | C1_DEFINED};
4276 static const WORD blanks_old[] ={C1_SPACE | C1_CNTRL | C1_BLANK,
4277 C1_SPACE | C1_BLANK,
4278 C1_SPACE | C1_BLANK,
4279 C1_SPACE | C1_BLANK,
4280 C1_SPACE | C1_BLANK};
4282 static const WCHAR undefined[] = {0x378, 0x379, 0x5ff, 0xfff8, 0xfffe};
4284 /* Lu, Ll, Lt */
4285 static const WCHAR alpha[] = {0x47, 0x67, 0x1c5};
4286 static const WORD alpha_old[] = {C1_UPPER | C1_ALPHA,
4287 C1_LOWER | C1_ALPHA,
4288 C1_UPPER | C1_LOWER | C1_ALPHA,
4289 C1_ALPHA};
4291 /* Sk, Sk, Mn, So, Me */
4292 static const WCHAR oldpunc[] = { 0x2c2, 0x2e5, 0x322, 0x482, 0x6de,
4293 /* Sc, Sm, No,*/
4294 0xffe0, 0xffe9, 0x2153};
4296 /* Lm, Nl, Cf, 0xad(Cf), 0x1f88 (Lt), Lo, Mc */
4297 static const WCHAR changed[] = {0x2b0, 0x2160, 0x600, 0xad, 0x1f88, 0x294, 0x903};
4298 static const WORD changed_old[] = { C1_PUNCT, C1_PUNCT, 0, C1_PUNCT, C1_UPPER | C1_ALPHA, C1_ALPHA, C1_PUNCT };
4299 static const WORD changed_xp[] = {C1_ALPHA | C1_DEFINED,
4300 C1_ALPHA | C1_DEFINED,
4301 C1_CNTRL | C1_DEFINED,
4302 C1_PUNCT | C1_DEFINED,
4303 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
4304 C1_ALPHA | C1_LOWER | C1_DEFINED,
4305 C1_ALPHA | C1_DEFINED };
4306 static const WORD changed_new[] = { C1_ALPHA | C1_DEFINED,
4307 C1_ALPHA | C1_DEFINED,
4308 C1_CNTRL | C1_DEFINED,
4309 C1_PUNCT | C1_CNTRL | C1_DEFINED,
4310 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
4311 C1_ALPHA | C1_DEFINED,
4312 C1_DEFINED
4314 /* Pc, Pd, Ps, Pe, Pi, Pf, Po*/
4315 static const WCHAR punct[] = { 0x5f, 0x2d, 0x28, 0x29, 0xab, 0xbb, 0x21 };
4317 static const WCHAR punct_special[] = {0x24, 0x2b, 0x3c, 0x3e, 0x5e, 0x60,
4318 0x7c, 0x7e, 0xa2, 0xbe, 0xd7, 0xf7};
4319 static const WCHAR digit_special[] = {0xb2, 0xb3, 0xb9};
4320 static const WCHAR lower_special[] = {0x2071, 0x207f};
4321 static const WCHAR cntrl_special[] = {0x070f, 0x200c, 0x200d,
4322 0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
4323 0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
4324 0xfff9, 0xfffa, 0xfffb};
4325 static const WCHAR space_special[] = {0x09, 0x0d, 0x85};
4327 WORD types[20];
4328 WCHAR ch[2];
4329 BOOL ret;
4330 int i;
4332 /* NULL src */
4333 SetLastError(0xdeadbeef);
4334 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, NULL);
4335 ok(!ret, "got %d\n", ret);
4336 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4338 SetLastError(0xdeadbeef);
4339 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, types);
4340 ok(!ret, "got %d\n", ret);
4341 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4343 SetLastError(0xdeadbeef);
4344 ret = GetStringTypeW(CT_CTYPE1, NULL, 5, types);
4345 ok(!ret, "got %d\n", ret);
4346 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4348 memset(types,0,sizeof(types));
4349 GetStringTypeW(CT_CTYPE1, blanks, 5, types);
4350 for (i = 0; i < 5; i++)
4351 ok(types[i] == blanks_new[i] || broken(types[i] == blanks_old[i] || broken(types[i] == 0)), "incorrect type1 returned for %x -> (%x != %x)\n",blanks[i],types[i],blanks_new[i]);
4353 memset(types,0,sizeof(types));
4354 GetStringTypeW(CT_CTYPE1, alpha, 3, types);
4355 for (i = 0; i < 3; i++)
4356 ok(types[i] == (C1_DEFINED | alpha_old[i]) || broken(types[i] == alpha_old[i]) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",alpha[i], types[i],(C1_DEFINED | alpha_old[i]));
4357 memset(types,0,sizeof(types));
4358 GetStringTypeW(CT_CTYPE1, undefined, 5, types);
4359 for (i = 0; i < 5; i++)
4360 ok(types[i] == 0, "incorrect types returned for %x -> (%x != 0)\n",undefined[i], types[i]);
4362 memset(types,0,sizeof(types));
4363 GetStringTypeW(CT_CTYPE1, oldpunc, 8, types);
4364 for (i = 0; i < 8; i++)
4365 ok(types[i] == C1_DEFINED || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",oldpunc[i], types[i], C1_DEFINED);
4367 memset(types,0,sizeof(types));
4368 GetStringTypeW(CT_CTYPE1, changed, 7, types);
4369 for (i = 0; i < 7; i++)
4370 ok(types[i] == changed_new[i] || broken(types[i] == changed_old[i]) || broken(types[i] == changed_xp[i]) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",changed[i], types[i], changed_new[i]);
4372 memset(types,0,sizeof(types));
4373 GetStringTypeW(CT_CTYPE1, punct, 7, types);
4374 for (i = 0; i < 7; i++)
4375 ok(types[i] == (C1_PUNCT | C1_DEFINED) || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",punct[i], types[i], (C1_PUNCT | C1_DEFINED));
4378 memset(types,0,sizeof(types));
4379 GetStringTypeW(CT_CTYPE1, punct_special, 12, types);
4380 for (i = 0; i < 12; i++)
4381 ok(types[i] & C1_PUNCT || broken(types[i] == 0), "incorrect types returned for %x -> (%x doest not have %x)\n",punct_special[i], types[i], C1_PUNCT);
4383 memset(types,0,sizeof(types));
4384 GetStringTypeW(CT_CTYPE1, digit_special, 3, types);
4385 for (i = 0; i < 3; i++)
4386 ok(types[i] & C1_DIGIT || broken(types[i] == 0), "incorrect types returned for %x -> (%x doest not have = %x)\n",digit_special[i], types[i], C1_DIGIT);
4388 memset(types,0,sizeof(types));
4389 GetStringTypeW(CT_CTYPE1, lower_special, 2, types);
4390 for (i = 0; i < 2; i++)
4391 ok(types[i] & C1_LOWER || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",lower_special[i], types[i], C1_LOWER);
4393 memset(types,0,sizeof(types));
4394 GetStringTypeW(CT_CTYPE1, cntrl_special, 20, types);
4395 for (i = 0; i < 20; i++)
4396 ok(types[i] & C1_CNTRL || broken(types[i] == (C1_BLANK|C1_SPACE)) || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",cntrl_special[i], types[i], C1_CNTRL);
4398 memset(types,0,sizeof(types));
4399 GetStringTypeW(CT_CTYPE1, space_special, 3, types);
4400 for (i = 0; i < 3; i++)
4401 ok(types[i] & C1_SPACE || broken(types[i] == C1_CNTRL) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",space_special[i], types[i], C1_SPACE );
4403 /* surrogate pairs */
4404 ch[0] = 0xd800;
4405 memset(types, 0, sizeof(types));
4406 GetStringTypeW(CT_CTYPE3, ch, 1, types);
4407 if (types[0] == C3_NOTAPPLICABLE)
4408 win_skip("C3_HIGHSURROGATE/C3_LOWSURROGATE are not supported.\n");
4409 else {
4410 ok(types[0] == C3_HIGHSURROGATE, "got %x\n", types[0]);
4412 ch[0] = 0xdc00;
4413 memset(types, 0, sizeof(types));
4414 GetStringTypeW(CT_CTYPE3, ch, 1, types);
4415 ok(types[0] == C3_LOWSURROGATE, "got %x\n", types[0]);
4418 /* Zl, Zp categories */
4419 ch[0] = 0x2028;
4420 ch[1] = 0x2029;
4421 memset(types, 0, sizeof(types));
4422 GetStringTypeW(CT_CTYPE1, ch, 2, types);
4423 ok(types[0] == (C1_DEFINED|C1_SPACE), "got %x\n", types[0]);
4424 ok(types[1] == (C1_DEFINED|C1_SPACE), "got %x\n", types[1]);
4426 /* check Arabic range for kashida flag */
4427 for (ch[0] = 0x600; ch[0] <= 0x6ff; ch[0] += 1)
4429 types[0] = 0;
4430 ret = GetStringTypeW(CT_CTYPE3, ch, 1, types);
4431 ok(ret, "%#x: failed %d\n", ch[0], ret);
4432 if (ch[0] == 0x640) /* ARABIC TATWEEL (Kashida) */
4433 ok(types[0] & C3_KASHIDA, "%#x: type %#x\n", ch[0], types[0]);
4434 else
4435 ok(!(types[0] & C3_KASHIDA), "%#x: type %#x\n", ch[0], types[0]);
4439 static void test_IdnToNameprepUnicode(void)
4441 struct {
4442 int in_len;
4443 const WCHAR in[80];
4444 DWORD flags;
4445 DWORD ret;
4446 DWORD broken_ret;
4447 const WCHAR out[80];
4448 NTSTATUS status;
4449 NTSTATUS broken_status;
4450 } test_data[] = {
4451 /* 0 */
4452 { 5, L"test", 0, 5, 5, L"test" },
4453 { 3, L"a\xe111z", 0, 0, 0, L"a\xe111z", 0, STATUS_NO_UNICODE_TRANSLATION },
4454 { 4, L"t\0e", 0, 0, 0, {0}, STATUS_NO_UNICODE_TRANSLATION, STATUS_NO_UNICODE_TRANSLATION },
4455 { 1, L"T", 0, 1, 1, L"T" },
4456 { 1, {0}, 0, 0 },
4457 /* 5 */
4458 { 6, L" -/[]", 0, 6, 6, L" -/[]" },
4459 { 3, L"a-a", IDN_USE_STD3_ASCII_RULES, 3, 3, L"a-a" },
4460 { 3, L"aa-", IDN_USE_STD3_ASCII_RULES, 0, 0, L"aa-" },
4461 { -1, L"T\xdf\x130\x143\x37a\x6a\x30c \xaa", 0, 12, 12, L"tssi\x307\x144 \x3b9\x1f0 a" },
4462 { 11, L"t\xad\x34f\x1806\x180b\x180c\x180d\x200b\x200c\x200d", 0, 0, 2, L"t",
4463 STATUS_NO_UNICODE_TRANSLATION },
4464 /* 10 */
4465 { 2, {0x3b0}, 0, 2, 2, {0x3b0} },
4466 { 2, {0x380}, 0, 0, 2, {0x380} },
4467 { 2, {0x380}, IDN_ALLOW_UNASSIGNED, 2, 2, {0x380} },
4468 { 5, L"a..a", 0, 0, 0, L"a..a" },
4469 { 3, L"a.", 0, 3, 3, L"a." },
4470 /* 15 */
4471 { 5, L"T.\x105.A", 0, 5, 5, L"t.\x105.a" },
4472 { 5, L"T.*.A", 0, 5, 5, L"T.*.A" },
4473 { 5, L"X\xff0e.Z", 0, 0, 0, L"x..z" },
4474 { 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
4475 63, 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
4476 { 64, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
4477 0, 0, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
4480 WCHAR buf[1024];
4481 DWORD i, ret, err;
4483 ret = pIdnToNameprepUnicode(0, test_data[0].in,
4484 test_data[0].in_len, NULL, 0);
4485 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4487 SetLastError(0xdeadbeef);
4488 ret = pIdnToNameprepUnicode(0, test_data[1].in,
4489 test_data[1].in_len, NULL, 0);
4490 err = GetLastError();
4491 ok(ret == test_data[1].ret, "ret = %d\n", ret);
4492 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "err = %d\n", err);
4494 SetLastError(0xdeadbeef);
4495 ret = pIdnToNameprepUnicode(0, test_data[0].in, -1, buf, ARRAY_SIZE(buf));
4496 err = GetLastError();
4497 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4498 ok(err == 0xdeadbeef, "err = %d\n", err);
4500 SetLastError(0xdeadbeef);
4501 ret = pIdnToNameprepUnicode(0, test_data[0].in, -2, buf, ARRAY_SIZE(buf));
4502 err = GetLastError();
4503 ok(ret == 0, "ret = %d\n", ret);
4504 ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
4506 SetLastError(0xdeadbeef);
4507 ret = pIdnToNameprepUnicode(0, test_data[0].in, 0, buf, ARRAY_SIZE(buf));
4508 err = GetLastError();
4509 ok(ret == 0, "ret = %d\n", ret);
4510 ok(err == ERROR_INVALID_NAME, "err = %d\n", err);
4512 ret = pIdnToNameprepUnicode(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES,
4513 test_data[0].in, -1, buf, ARRAY_SIZE(buf));
4514 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4516 SetLastError(0xdeadbeef);
4517 ret = pIdnToNameprepUnicode(0, NULL, 0, NULL, 0);
4518 err = GetLastError();
4519 ok(ret == 0, "ret = %d\n", ret);
4520 ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
4522 SetLastError(0xdeadbeef);
4523 ret = pIdnToNameprepUnicode(4, NULL, 0, NULL, 0);
4524 err = GetLastError();
4525 ok(ret == 0, "ret = %d\n", ret);
4526 ok(err == ERROR_INVALID_FLAGS || err == ERROR_INVALID_PARAMETER /* Win8 */,
4527 "err = %d\n", err);
4529 for (i=0; i<ARRAY_SIZE(test_data); i++)
4531 SetLastError(0xdeadbeef);
4532 memset( buf, 0xcc, sizeof(buf) );
4533 ret = pIdnToNameprepUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len,
4534 buf, ARRAY_SIZE(buf));
4535 err = GetLastError();
4537 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%d: ret = %d\n", i, ret);
4539 if (ret == test_data[i].ret)
4541 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "%d: err = %d\n", i, err);
4542 ok(!wcsncmp(test_data[i].out, buf, ret), "%d: buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4544 if (pRtlNormalizeString)
4546 NTSTATUS status;
4547 int len = ARRAY_SIZE(buf);
4548 memset( buf, 0xcc, sizeof(buf) );
4549 status = pRtlNormalizeString( 13, test_data[i].in, test_data[i].in_len, buf, &len );
4550 ok( status == test_data[i].status || broken(status == test_data[i].broken_status),
4551 "%d: failed %x\n", i, status );
4552 if (!status) ok( !wcsnicmp(test_data[i].out, buf, len), "%d: buf = %s\n", i, wine_dbgstr_wn(buf, len));
4557 static void test_IdnToAscii(void)
4559 struct {
4560 int in_len;
4561 const WCHAR in[80];
4562 DWORD flags;
4563 DWORD ret;
4564 DWORD broken_ret;
4565 const WCHAR out[80];
4566 } test_data[] = {
4567 /* 0 */
4568 { 5, L"Test", 0, 5, 5, L"Test" },
4569 { 5, L"Te\x017cst", 0, 12, 12, L"xn--test-cbb" },
4570 { 12, L"te\x0105st.te\x017cst", 0, 26, 26, L"xn--test-cta.xn--test-cbb" },
4571 { 3, {0x0105,'.',0}, 0, 9, 9, L"xn--2da." },
4572 { 10, L"http://t\x106", 0, 17, 17, L"xn--http://t-78a" },
4573 /* 5 */
4574 { -1, L"\x4e3a\x8bf4\x4e0d\x4ed6\x5011\x10d\x11b\x305c\x306a", 0,
4575 35, 35, L"xn--bea2a1631avbav44tyha32b91egs2t" },
4576 { 2, L"\x380", IDN_ALLOW_UNASSIGNED, 8, 8, L"xn--7va" },
4577 { 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
4578 63, 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
4579 { 64, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 0 },
4580 { -1, L"\xe4z123456789012345678901234567890123456789012345678901234", 0,
4581 64, 64, L"xn--z123456789012345678901234567890123456789012345678901234-9te" },
4582 /* 10 */
4583 { -1, L"\xd803\xde78\x46b5-\xa861.\x2e87", 0, 28, 0, L"xn----bm3an932a1l5d.xn--xvj" },
4584 { -1, L"\x06ef\x06ef", 0, 9, 0, L"xn--cmba" },
4585 { -1, L"-\x07e1\xff61\x2184", 0, 18, 0, L"xn----8cd.xn--r5g" },
4588 WCHAR buf[1024];
4589 DWORD i, ret, err;
4591 for (i=0; i<ARRAY_SIZE(test_data); i++)
4593 SetLastError(0xdeadbeef);
4594 ret = pIdnToAscii(test_data[i].flags, test_data[i].in, test_data[i].in_len, buf, ARRAY_SIZE(buf));
4595 err = GetLastError();
4596 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%d: ret = %d\n", i, ret);
4597 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "%d: err = %d\n", i, err);
4598 ok(!wcsnicmp(test_data[i].out, buf, ret), "%d: buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4602 static void test_IdnToUnicode(void)
4604 struct {
4605 int in_len;
4606 const WCHAR in[80];
4607 DWORD flags;
4608 DWORD ret;
4609 DWORD broken_ret;
4610 const WCHAR out[80];
4611 } test_data[] = {
4612 /* 0 */
4613 { 5, L"Tes.", 0, 5, 5, L"Tes." },
4614 { 2, L"\x105", 0, 0 },
4615 { 33, L"xn--4dbcagdahymbxekheh6e0a7fei0b", 0,
4616 23, 23, L"\x05dc\x05de\x05d4\x05d4\x05dd\x05e4\x05e9\x05d5\x05d8\x05dc\x05d0\x05de\x05d3\x05d1\x05e8\x05d9\x05dd\x05e2\x05d1\x05e8\x05d9\x05ea" },
4617 { 34, L"test.xn--kda9ag5e9jnfsj.xn--pz-fna", 0,
4618 16, 16, L"test.\x0105\x0119\x015b\x0107\x0142\x00f3\x017c.p\x0119z" },
4619 { 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
4620 63, 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
4621 /* 5 */
4622 { 64, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 0 },
4623 { 8, L"xn--7va", IDN_ALLOW_UNASSIGNED, 2, 2, L"\x380" },
4624 { 8, L"xn--7va", 0, 0, 0, L"\x380" },
4625 { -1, L"xn----bm3an932a1l5d.xn--xvj", 0, 8, 0, L"\xd803\xde78\x46b5-\xa861.\x2e87" },
4626 { -1, L"xn--z123456789012345678901234567890123456789012345678901234-9te", 0,
4627 57, 57, L"\xe4z123456789012345678901234567890123456789012345678901234" },
4628 /* 10 */
4629 { -1, L"foo.bar", 0, 8, 8, L"foo.bar" },
4630 { -1, L"d.xn----dha", 0, 5, 5, L"d.\x00fc-" },
4633 WCHAR buf[1024];
4634 DWORD i, ret, err;
4636 for (i=0; i<ARRAY_SIZE(test_data); i++)
4638 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len, NULL, 0);
4639 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%d: ret = %d\n", i, ret);
4641 SetLastError(0xdeadbeef);
4642 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len, buf, ARRAY_SIZE(buf));
4643 err = GetLastError();
4644 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%d: ret = %d\n", i, ret);
4645 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "%d: err = %d\n", i, err);
4646 ok(!wcsncmp(test_data[i].out, buf, ret), "%d: buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4650 static BOOL is_idn_error( const WCHAR *str )
4652 WCHAR *p, err[256];
4653 lstrcpyW( err, str );
4654 for (p = wcstok( err, L" []" ); p; p = wcstok( NULL, L" []" ) )
4656 if (*p == 'B' || !wcscmp( p, L"V8" )) continue; /* BiDi */
4657 if (!wcscmp( p, L"V2" )) continue; /* CheckHyphens */
4658 if (!wcscmp( p, L"V5" )) continue; /* Combining marks */
4659 return TRUE;
4661 return FALSE;
4664 static void test_Idn(void)
4666 FILE *f;
4668 if (!pIdnToAscii || !pIdnToUnicode || !pIdnToNameprepUnicode)
4670 win_skip("Idn support is not available\n");
4671 return;
4674 test_IdnToNameprepUnicode();
4675 test_IdnToAscii();
4676 test_IdnToUnicode();
4678 /* optionally run the full test file from Unicode.org
4679 * available at https://www.unicode.org/Public/idna/latest/IdnaTestV2.txt
4681 if ((f = fopen( "IdnaTestV2.txt", "r" )))
4683 char *p, *end, buffer[2048];
4684 WCHAR columns[7][256], dst[256], *expect, *error;
4685 int i, ret, line = 0;
4687 while (fgets( buffer, sizeof(buffer), f ))
4689 line++;
4690 if ((p = strchr( buffer, '#' ))) *p = 0;
4691 if (!(p = strtok( buffer, ";" ))) continue;
4692 for (i = 0; i < 7 && p; i++)
4694 while (*p == ' ') p++;
4695 for (end = p + strlen(p); end > p; end--) if (end[-1] != ' ') break;
4696 *end = 0;
4697 MultiByteToWideChar( CP_UTF8, 0, p, -1, columns[i], 256 );
4698 p = strtok( NULL, ";" );
4700 if (i < 7) continue;
4702 expect = columns[5];
4703 if (!*expect) expect = columns[3];
4704 if (!*expect) expect = columns[1];
4705 if (!*expect) expect = columns[0];
4706 error = columns[6];
4707 if (!*error) error = columns[4];
4708 if (!*error) error = columns[2];
4709 SetLastError( 0xdeadbeef );
4710 memset( dst, 0xcc, sizeof(dst) );
4711 ret = pIdnToAscii( 0, columns[0], -1, dst, ARRAY_SIZE(dst) );
4712 if (!is_idn_error( error ))
4714 ok( ret, "line %u: toAscii failed for %s expected %s\n", line,
4715 debugstr_w(columns[0]), debugstr_w(expect) );
4716 if (ret) ok( !wcscmp( dst, expect ), "line %u: got %s expected %s\n",
4717 line, debugstr_w(dst), debugstr_w(expect) );
4719 else
4721 ok( !ret, "line %u: toAscii didn't fail for %s got %s expected error %s\n",
4722 line, debugstr_w(columns[0]), debugstr_w(dst), debugstr_w(error) );
4725 expect = columns[1];
4726 if (!*expect) expect = columns[0];
4727 error = columns[2];
4728 SetLastError( 0xdeadbeef );
4729 memset( dst, 0xcc, sizeof(dst) );
4730 ret = pIdnToUnicode( IDN_USE_STD3_ASCII_RULES, columns[0], -1, dst, ARRAY_SIZE(dst) );
4731 for (i = 0; columns[0][i]; i++) if (columns[0][i] > 0x7f) break;
4732 if (columns[0][i])
4734 ok( !ret, "line %u: didn't fail for unicode chars in %s\n", line, debugstr_w(columns[0]) );
4736 else if (!is_idn_error( error ))
4738 ok( ret, "line %u: toUnicode failed for %s expected %s\n", line,
4739 debugstr_w(columns[0]), debugstr_w(expect) );
4740 if (ret) ok( !wcscmp( dst, expect ), "line %u: got %s expected %s\n",
4741 line, debugstr_w(dst), debugstr_w(expect) );
4743 else
4745 ok( !ret, "line %u: toUnicode didn't fail for %s got %s expected error %s\n",
4746 line, debugstr_w(columns[0]), debugstr_w(dst), debugstr_w(error) );
4749 fclose( f );
4754 static void test_GetLocaleInfoEx(void)
4756 static const WCHAR enW[] = {'e','n',0};
4757 WCHAR bufferW[80], buffer2[80];
4758 INT ret;
4760 if (!pGetLocaleInfoEx)
4762 win_skip("GetLocaleInfoEx not supported\n");
4763 return;
4766 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4767 ok(ret || broken(ret == 0) /* Vista */, "got %d\n", ret);
4768 if (ret)
4770 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
4771 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
4772 static const WCHAR enusW[] = {'e','n','-','U','S',0};
4773 static const WCHAR usaW[] = {'U','S','A',0};
4774 static const WCHAR enuW[] = {'E','N','U',0};
4775 const struct neutralsublang_name_t *ptr = neutralsublang_names;
4776 DWORD val;
4778 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4779 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
4781 SetLastError(0xdeadbeef);
4782 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, 2);
4783 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, %d\n", ret, GetLastError());
4785 SetLastError(0xdeadbeef);
4786 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, NULL, 0);
4787 ok(ret == 3 && GetLastError() == 0xdeadbeef, "got %d, %d\n", ret, GetLastError());
4789 ret = pGetLocaleInfoEx(enusW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4790 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4791 ok(!lstrcmpW(bufferW, enusW), "got %s\n", wine_dbgstr_w(bufferW));
4793 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVCTRYNAME, bufferW, ARRAY_SIZE(bufferW));
4794 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4795 ok(!lstrcmpW(bufferW, usaW), "got %s\n", wine_dbgstr_w(bufferW));
4797 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVLANGNAME, bufferW, ARRAY_SIZE(bufferW));
4798 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4799 ok(!lstrcmpW(bufferW, enuW), "got %s\n", wine_dbgstr_w(bufferW));
4801 ret = pGetLocaleInfoEx(enusW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
4802 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4803 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
4805 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
4806 ok(ret == 1, "got %d\n", ret);
4807 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
4809 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT | LOCALE_NOUSEROVERRIDE, bufferW, ARRAY_SIZE(bufferW));
4810 ok(ret == 1, "got %d\n", ret);
4811 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
4813 ret = pGetLocaleInfoEx(enW, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
4814 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4815 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
4816 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
4818 skip("Non-English locale\n");
4820 else
4821 ok(!lstrcmpW(bufferW, statesW), "got %s\n", wine_dbgstr_w(bufferW));
4823 bufferW[0] = 0;
4824 SetLastError(0xdeadbeef);
4825 ret = pGetLocaleInfoEx(dummyW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4826 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
4828 while (*ptr->name)
4830 val = 0;
4831 pGetLocaleInfoEx(ptr->name, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
4832 ok(val == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", wine_dbgstr_w(ptr->name), val, ptr->lcid);
4833 bufferW[0] = 0;
4834 ret = pGetLocaleInfoEx(ptr->name, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4835 ok(ret == lstrlenW(bufferW)+1, "%s: got ret value %d\n", wine_dbgstr_w(ptr->name), ret);
4836 ok(!lstrcmpW(bufferW, ptr->name), "%s: got wrong LOCALE_SNAME %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
4837 ptr++;
4840 ret = pGetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4841 ok(ret && ret == lstrlenW(bufferW)+1, "got ret value %d\n", ret);
4842 ret = GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SNAME, buffer2, ARRAY_SIZE(buffer2));
4843 ok(ret && ret == lstrlenW(buffer2)+1, "got ret value %d\n", ret);
4844 ok(!lstrcmpW(bufferW, buffer2), "LOCALE_SNAMEs don't match %s %s\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(buffer2));
4848 static void test_IsValidLocaleName(void)
4850 static const WCHAR enusW[] = {'e','n','-','U','S',0};
4851 static const WCHAR enW[] = {'e','n',0};
4852 static const WCHAR zzW[] = {'z','z',0};
4853 static const WCHAR zz_zzW[] = {'z','z','-','Z','Z',0};
4854 static const WCHAR zzzzW[] = {'z','z','z','z',0};
4855 BOOL ret;
4857 if (!pIsValidLocaleName)
4859 win_skip("IsValidLocaleName not supported\n");
4860 return;
4863 ret = pIsValidLocaleName(enusW);
4864 ok(ret, "IsValidLocaleName failed\n");
4865 ret = pIsValidLocaleName(enW);
4866 ok(ret || broken(!ret), "IsValidLocaleName failed\n");
4867 ret = pIsValidLocaleName(zzW);
4868 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
4869 ret = pIsValidLocaleName(zz_zzW);
4870 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
4871 ret = pIsValidLocaleName(zzzzW);
4872 ok(!ret, "IsValidLocaleName should have failed\n");
4873 ret = pIsValidLocaleName(LOCALE_NAME_INVARIANT);
4874 ok(ret, "IsValidLocaleName failed\n");
4875 ret = pIsValidLocaleName(NULL);
4876 ok(!ret, "IsValidLocaleName should have failed\n");
4879 static void test_CompareStringOrdinal(void)
4881 INT ret;
4882 WCHAR test1[] = { 't','e','s','t',0 };
4883 WCHAR test2[] = { 'T','e','S','t',0 };
4884 WCHAR test3[] = { 't','e','s','t','3',0 };
4885 WCHAR null1[] = { 'a',0,'a',0 };
4886 WCHAR null2[] = { 'a',0,'b',0 };
4887 WCHAR bills1[] = { 'b','i','l','l','\'','s',0 };
4888 WCHAR bills2[] = { 'b','i','l','l','s',0 };
4889 WCHAR coop1[] = { 'c','o','-','o','p',0 };
4890 WCHAR coop2[] = { 'c','o','o','p',0 };
4891 WCHAR nonascii1[] = { 0x0102,0 };
4892 WCHAR nonascii2[] = { 0x0201,0 };
4893 WCHAR ch1, ch2;
4895 if (!pCompareStringOrdinal)
4897 win_skip("CompareStringOrdinal not supported\n");
4898 return;
4901 /* Check errors */
4902 SetLastError(0xdeadbeef);
4903 ret = pCompareStringOrdinal(NULL, 0, NULL, 0, FALSE);
4904 ok(!ret, "Got %u, expected 0\n", ret);
4905 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4906 SetLastError(0xdeadbeef);
4907 ret = pCompareStringOrdinal(test1, -1, NULL, 0, FALSE);
4908 ok(!ret, "Got %u, expected 0\n", ret);
4909 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4910 SetLastError(0xdeadbeef);
4911 ret = pCompareStringOrdinal(NULL, 0, test1, -1, FALSE);
4912 ok(!ret, "Got %u, expected 0\n", ret);
4913 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4915 /* Check case */
4916 ret = pCompareStringOrdinal(test1, -1, test1, -1, FALSE);
4917 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
4918 ret = pCompareStringOrdinal(test1, -1, test2, -1, FALSE);
4919 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4920 ret = pCompareStringOrdinal(test2, -1, test1, -1, FALSE);
4921 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4922 ret = pCompareStringOrdinal(test1, -1, test2, -1, TRUE);
4923 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
4925 /* Check different sizes */
4926 ret = pCompareStringOrdinal(test1, 3, test2, -1, TRUE);
4927 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4928 ret = pCompareStringOrdinal(test1, -1, test2, 3, TRUE);
4929 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4931 /* Check null character */
4932 ret = pCompareStringOrdinal(null1, 3, null2, 3, FALSE);
4933 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4934 ret = pCompareStringOrdinal(null1, 3, null2, 3, TRUE);
4935 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4936 ret = pCompareStringOrdinal(test1, 5, test3, 5, FALSE);
4937 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4938 ret = pCompareStringOrdinal(test1, 4, test1, 5, FALSE);
4939 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4941 /* Check ordinal behaviour */
4942 ret = pCompareStringOrdinal(bills1, -1, bills2, -1, FALSE);
4943 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4944 ret = pCompareStringOrdinal(coop2, -1, coop1, -1, FALSE);
4945 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4946 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, FALSE);
4947 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4948 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, TRUE);
4949 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4951 for (ch1 = 0; ch1 < 512; ch1++)
4953 for (ch2 = 0; ch2 < 1024; ch2++)
4955 int diff = ch1 - ch2;
4956 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, FALSE );
4957 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
4958 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
4959 diff = pRtlUpcaseUnicodeChar( ch1 ) - pRtlUpcaseUnicodeChar( ch2 );
4960 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, TRUE );
4961 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
4962 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
4967 static void test_GetGeoInfo(void)
4969 char buffA[20];
4970 INT ret;
4972 if (!pGetGeoInfoA)
4974 win_skip("GetGeoInfo is not available.\n");
4975 return;
4978 /* unassigned id */
4979 SetLastError(0xdeadbeef);
4980 ret = pGetGeoInfoA(344, GEO_ISO2, NULL, 0, 0);
4981 ok(ret == 0, "got %d\n", ret);
4982 ok(GetLastError() == ERROR_INVALID_PARAMETER ||
4983 broken(GetLastError() == 0xdeadbeef /* Win10 */), "got %d\n", GetLastError());
4985 ret = pGetGeoInfoA(203, GEO_ISO2, NULL, 0, 0);
4986 ok(ret == 3, "got %d\n", ret);
4988 ret = pGetGeoInfoA(203, GEO_ISO3, NULL, 0, 0);
4989 ok(ret == 4, "got %d\n", ret);
4991 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 3, 0);
4992 ok(ret == 3, "got %d\n", ret);
4993 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
4995 /* buffer pointer not NULL, length is 0 - return required length */
4996 buffA[0] = 'a';
4997 SetLastError(0xdeadbeef);
4998 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 0, 0);
4999 ok(ret == 3, "got %d\n", ret);
5000 ok(buffA[0] == 'a', "got %c\n", buffA[0]);
5002 ret = pGetGeoInfoA(203, GEO_ISO3, buffA, 4, 0);
5003 ok(ret == 4, "got %d\n", ret);
5004 ok(!strcmp(buffA, "RUS"), "got %s\n", buffA);
5006 /* shorter buffer */
5007 SetLastError(0xdeadbeef);
5008 buffA[1] = buffA[2] = 0;
5009 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 2, 0);
5010 ok(ret == 0, "got %d\n", ret);
5011 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
5012 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d\n", GetLastError());
5014 /* GEO_NATION returns GEOID in a string form, but only for GEOCLASS_NATION-type IDs */
5015 ret = pGetGeoInfoA(203, GEO_NATION, buffA, 20, 0); /* GEOCLASS_NATION */
5016 ok(ret == 4, "GEO_NATION of nation: expected 4, got %d\n", ret);
5017 ok(!strcmp(buffA, "203"), "GEO_NATION of nation: expected 203, got %s\n", buffA);
5019 buffA[0] = 0;
5020 ret = pGetGeoInfoA(39070, GEO_NATION, buffA, 20, 0); /* GEOCLASS_REGION */
5021 ok(ret == 0, "GEO_NATION of region: expected 0, got %d\n", ret);
5022 ok(*buffA == 0, "GEO_NATION of region: expected empty string, got %s\n", buffA);
5024 buffA[0] = 0;
5025 ret = pGetGeoInfoA(333, GEO_NATION, buffA, 20, 0); /* LOCATION_BOTH internal Wine type */
5026 ok(ret == 0 ||
5027 broken(ret == 4) /* Win7 and older */,
5028 "GEO_NATION of LOCATION_BOTH: expected 0, got %d\n", ret);
5029 ok(*buffA == 0 ||
5030 broken(!strcmp(buffA, "333")) /* Win7 and older */,
5031 "GEO_NATION of LOCATION_BOTH: expected empty string, got %s\n", buffA);
5033 /* GEO_ID is like GEO_NATION but works for any ID */
5034 buffA[0] = 0;
5035 ret = pGetGeoInfoA(203, GEO_ID, buffA, 20, 0); /* GEOCLASS_NATION */
5036 if (ret == 0)
5037 win_skip("GEO_ID not supported.\n");
5038 else
5040 ok(ret == 4, "GEO_ID: expected 4, got %d\n", ret);
5041 ok(!strcmp(buffA, "203"), "GEO_ID: expected 203, got %s\n", buffA);
5043 ret = pGetGeoInfoA(47610, GEO_ID, buffA, 20, 0); /* GEOCLASS_REGION */
5044 ok(ret == 6, "got %d\n", ret);
5045 ok(!strcmp(buffA, "47610"), "got %s\n", buffA);
5047 ret = pGetGeoInfoA(333, GEO_ID, buffA, 20, 0); /* LOCATION_BOTH internal Wine type */
5048 ok(ret == 4, "got %d\n", ret);
5049 ok(!strcmp(buffA, "333"), "got %s\n", buffA);
5052 /* GEO_PARENT */
5053 buffA[0] = 0;
5054 ret = pGetGeoInfoA(203, GEO_PARENT, buffA, 20, 0);
5055 if (ret == 0)
5056 win_skip("GEO_PARENT not supported.\n");
5057 else
5059 ok(ret == 6, "got %d\n", ret);
5060 ok(!strcmp(buffA, "47609"), "got %s\n", buffA);
5063 buffA[0] = 0;
5064 ret = pGetGeoInfoA(203, GEO_ISO_UN_NUMBER, buffA, 20, 0);
5065 if (ret == 0)
5066 win_skip("GEO_ISO_UN_NUMBER not supported.\n");
5067 else
5069 ok(ret == 4, "got %d\n", ret);
5070 ok(!strcmp(buffA, "643"), "got %s\n", buffA);
5073 /* try invalid type value */
5074 SetLastError(0xdeadbeef);
5075 ret = pGetGeoInfoA(203, GEO_ID + 1, NULL, 0, 0);
5076 ok(ret == 0, "got %d\n", ret);
5077 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError());
5080 static int geoidenum_count;
5081 static BOOL CALLBACK test_geoid_enumproc(GEOID geoid)
5083 INT ret = pGetGeoInfoA(geoid, GEO_ISO2, NULL, 0, 0);
5084 ok(ret == 3, "got %d for %d\n", ret, geoid);
5085 /* valid geoid starts at 2 */
5086 ok(geoid >= 2, "got geoid %d\n", geoid);
5088 return geoidenum_count++ < 5;
5091 static BOOL CALLBACK test_geoid_enumproc2(GEOID geoid)
5093 geoidenum_count++;
5094 return TRUE;
5097 static void test_EnumSystemGeoID(void)
5099 BOOL ret;
5101 if (!pEnumSystemGeoID)
5103 win_skip("EnumSystemGeoID is not available.\n");
5104 return;
5107 SetLastError(0xdeadbeef);
5108 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, NULL);
5109 ok(!ret, "got %d\n", ret);
5110 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %d\n", GetLastError());
5112 SetLastError(0xdeadbeef);
5113 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, test_geoid_enumproc);
5114 ok(!ret, "got %d\n", ret);
5115 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError());
5117 SetLastError(0xdeadbeef);
5118 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, NULL);
5119 ok(!ret, "got %d\n", ret);
5120 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %d\n", GetLastError());
5122 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, test_geoid_enumproc);
5123 ok(ret, "got %d\n", ret);
5125 /* only the first level is enumerated, not the whole hierarchy */
5126 geoidenum_count = 0;
5127 ret = pEnumSystemGeoID(GEOCLASS_NATION, 39070, test_geoid_enumproc2);
5128 if (ret == 0)
5129 win_skip("Parent GEOID is not supported in EnumSystemGeoID.\n");
5130 else
5131 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5133 geoidenum_count = 0;
5134 ret = pEnumSystemGeoID(GEOCLASS_REGION, 39070, test_geoid_enumproc2);
5135 if (ret == 0)
5136 win_skip("GEOCLASS_REGION is not supported in EnumSystemGeoID.\n");
5137 else
5139 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5141 geoidenum_count = 0;
5142 ret = pEnumSystemGeoID(GEOCLASS_REGION, 0, test_geoid_enumproc2);
5143 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5146 geoidenum_count = 0;
5147 ret = pEnumSystemGeoID(GEOCLASS_ALL, 39070, test_geoid_enumproc2);
5148 if (ret == 0)
5149 win_skip("GEOCLASS_ALL is not supported in EnumSystemGeoID.\n");
5150 else
5152 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5154 geoidenum_count = 0;
5155 ret = pEnumSystemGeoID(GEOCLASS_ALL, 0, test_geoid_enumproc2);
5156 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5160 struct invariant_entry {
5161 const char *name;
5162 int id;
5163 const char *expect, *expect2;
5166 #define X(x) #x, x
5167 static const struct invariant_entry invariant_list[] = {
5168 { X(LOCALE_ILANGUAGE), "007f" },
5169 { X(LOCALE_SENGLANGUAGE), "Invariant Language" },
5170 { X(LOCALE_SABBREVLANGNAME), "IVL" },
5171 { X(LOCALE_SNATIVELANGNAME), "Invariant Language" },
5172 { X(LOCALE_ICOUNTRY), "1" },
5173 { X(LOCALE_SENGCOUNTRY), "Invariant Country" },
5174 { X(LOCALE_SABBREVCTRYNAME), "IVC", "" },
5175 { X(LOCALE_SNATIVECTRYNAME), "Invariant Country" },
5176 { X(LOCALE_IDEFAULTLANGUAGE), "0409" },
5177 { X(LOCALE_IDEFAULTCOUNTRY), "1" },
5178 { X(LOCALE_IDEFAULTCODEPAGE), "437" },
5179 { X(LOCALE_IDEFAULTANSICODEPAGE), "1252" },
5180 { X(LOCALE_IDEFAULTMACCODEPAGE), "10000" },
5181 { X(LOCALE_SLIST), "," },
5182 { X(LOCALE_IMEASURE), "0" },
5183 { X(LOCALE_SDECIMAL), "." },
5184 { X(LOCALE_STHOUSAND), "," },
5185 { X(LOCALE_SGROUPING), "3;0" },
5186 { X(LOCALE_IDIGITS), "2" },
5187 { X(LOCALE_ILZERO), "1" },
5188 { X(LOCALE_INEGNUMBER), "1" },
5189 { X(LOCALE_SNATIVEDIGITS), "0123456789" },
5190 { X(LOCALE_SCURRENCY), "\x00a4" },
5191 { X(LOCALE_SINTLSYMBOL), "XDR" },
5192 { X(LOCALE_SMONDECIMALSEP), "." },
5193 { X(LOCALE_SMONTHOUSANDSEP), "," },
5194 { X(LOCALE_SMONGROUPING), "3;0" },
5195 { X(LOCALE_ICURRDIGITS), "2" },
5196 { X(LOCALE_IINTLCURRDIGITS), "2" },
5197 { X(LOCALE_ICURRENCY), "0" },
5198 { X(LOCALE_INEGCURR), "0" },
5199 { X(LOCALE_SDATE), "/" },
5200 { X(LOCALE_STIME), ":" },
5201 { X(LOCALE_SSHORTDATE), "MM/dd/yyyy" },
5202 { X(LOCALE_SLONGDATE), "dddd, dd MMMM yyyy" },
5203 { X(LOCALE_STIMEFORMAT), "HH:mm:ss" },
5204 { X(LOCALE_IDATE), "0" },
5205 { X(LOCALE_ILDATE), "1" },
5206 { X(LOCALE_ITIME), "1" },
5207 { X(LOCALE_ITIMEMARKPOSN), "0" },
5208 { X(LOCALE_ICENTURY), "1" },
5209 { X(LOCALE_ITLZERO), "1" },
5210 { X(LOCALE_IDAYLZERO), "1" },
5211 { X(LOCALE_IMONLZERO), "1" },
5212 { X(LOCALE_S1159), "AM" },
5213 { X(LOCALE_S2359), "PM" },
5214 { X(LOCALE_ICALENDARTYPE), "1" },
5215 { X(LOCALE_IOPTIONALCALENDAR), "0" },
5216 { X(LOCALE_IFIRSTDAYOFWEEK), "6" },
5217 { X(LOCALE_IFIRSTWEEKOFYEAR), "0" },
5218 { X(LOCALE_SDAYNAME1), "Monday" },
5219 { X(LOCALE_SDAYNAME2), "Tuesday" },
5220 { X(LOCALE_SDAYNAME3), "Wednesday" },
5221 { X(LOCALE_SDAYNAME4), "Thursday" },
5222 { X(LOCALE_SDAYNAME5), "Friday" },
5223 { X(LOCALE_SDAYNAME6), "Saturday" },
5224 { X(LOCALE_SDAYNAME7), "Sunday" },
5225 { X(LOCALE_SABBREVDAYNAME1), "Mon" },
5226 { X(LOCALE_SABBREVDAYNAME2), "Tue" },
5227 { X(LOCALE_SABBREVDAYNAME3), "Wed" },
5228 { X(LOCALE_SABBREVDAYNAME4), "Thu" },
5229 { X(LOCALE_SABBREVDAYNAME5), "Fri" },
5230 { X(LOCALE_SABBREVDAYNAME6), "Sat" },
5231 { X(LOCALE_SABBREVDAYNAME7), "Sun" },
5232 { X(LOCALE_SMONTHNAME1), "January" },
5233 { X(LOCALE_SMONTHNAME2), "February" },
5234 { X(LOCALE_SMONTHNAME3), "March" },
5235 { X(LOCALE_SMONTHNAME4), "April" },
5236 { X(LOCALE_SMONTHNAME5), "May" },
5237 { X(LOCALE_SMONTHNAME6), "June" },
5238 { X(LOCALE_SMONTHNAME7), "July" },
5239 { X(LOCALE_SMONTHNAME8), "August" },
5240 { X(LOCALE_SMONTHNAME9), "September" },
5241 { X(LOCALE_SMONTHNAME10), "October" },
5242 { X(LOCALE_SMONTHNAME11), "November" },
5243 { X(LOCALE_SMONTHNAME12), "December" },
5244 { X(LOCALE_SMONTHNAME13), "" },
5245 { X(LOCALE_SABBREVMONTHNAME1), "Jan" },
5246 { X(LOCALE_SABBREVMONTHNAME2), "Feb" },
5247 { X(LOCALE_SABBREVMONTHNAME3), "Mar" },
5248 { X(LOCALE_SABBREVMONTHNAME4), "Apr" },
5249 { X(LOCALE_SABBREVMONTHNAME5), "May" },
5250 { X(LOCALE_SABBREVMONTHNAME6), "Jun" },
5251 { X(LOCALE_SABBREVMONTHNAME7), "Jul" },
5252 { X(LOCALE_SABBREVMONTHNAME8), "Aug" },
5253 { X(LOCALE_SABBREVMONTHNAME9), "Sep" },
5254 { X(LOCALE_SABBREVMONTHNAME10), "Oct" },
5255 { X(LOCALE_SABBREVMONTHNAME11), "Nov" },
5256 { X(LOCALE_SABBREVMONTHNAME12), "Dec" },
5257 { X(LOCALE_SABBREVMONTHNAME13), "" },
5258 { X(LOCALE_SPOSITIVESIGN), "+" },
5259 { X(LOCALE_SNEGATIVESIGN), "-" },
5260 { X(LOCALE_IPOSSIGNPOSN), "3" },
5261 { X(LOCALE_INEGSIGNPOSN), "0" },
5262 { X(LOCALE_IPOSSYMPRECEDES), "1" },
5263 { X(LOCALE_IPOSSEPBYSPACE), "0" },
5264 { X(LOCALE_INEGSYMPRECEDES), "1" },
5265 { X(LOCALE_INEGSEPBYSPACE), "0" },
5266 { X(LOCALE_SISO639LANGNAME), "iv" },
5267 { X(LOCALE_SISO3166CTRYNAME), "IV" },
5268 { X(LOCALE_IDEFAULTEBCDICCODEPAGE), "037" },
5269 { X(LOCALE_IPAPERSIZE), "9" },
5270 { X(LOCALE_SENGCURRNAME), "International Monetary Fund" },
5271 { X(LOCALE_SNATIVECURRNAME), "International Monetary Fund" },
5272 { X(LOCALE_SYEARMONTH), "yyyy MMMM" },
5273 { X(LOCALE_IDIGITSUBSTITUTION), "1" },
5274 { X(LOCALE_SNAME), "" },
5275 { X(LOCALE_SSCRIPTS), "Latn;" },
5276 { 0 }
5278 #undef X
5280 static void test_invariant(void)
5282 int ret;
5283 int len;
5284 char buffer[BUFFER_SIZE];
5285 const struct invariant_entry *ptr = invariant_list;
5287 if (!GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer)))
5289 win_skip("GetLocaleInfoA(LOCALE_INVARIANT) not supported\n"); /* win2k */
5290 return;
5293 while (ptr->name)
5295 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|ptr->id, buffer, sizeof(buffer));
5296 if (!ret && (ptr->id == LOCALE_SNAME || ptr->id == LOCALE_SSCRIPTS))
5297 win_skip("not supported\n"); /* winxp/win2k3 */
5298 else
5300 len = strlen(ptr->expect)+1; /* include \0 */
5301 ok(ret == len || (ptr->expect2 && ret == strlen(ptr->expect2)+1),
5302 "For id %d, expected ret == %d, got %d, error %d\n",
5303 ptr->id, len, ret, GetLastError());
5304 ok(!strcmp(buffer, ptr->expect) || (ptr->expect2 && !strcmp(buffer, ptr->expect2)),
5305 "For id %d, Expected %s, got '%s'\n",
5306 ptr->id, ptr->expect, buffer);
5309 ptr++;
5312 if ((LANGIDFROMLCID(GetSystemDefaultLCID()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)) ||
5313 (LANGIDFROMLCID(GetThreadLocale()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)))
5315 skip("Non US-English locale\n");
5317 else
5319 /* some locales translate these */
5320 static const char lang[] = "Invariant Language (Invariant Country)";
5321 static const char cntry[] = "Invariant Country";
5322 static const char sortm[] = "Math Alphanumerics";
5323 static const char sortms[] = "Maths Alphanumerics";
5324 static const char sortd[] = "Default"; /* win2k3 */
5326 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer));
5327 len = lstrlenA(lang) + 1;
5328 ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
5329 ok(!strcmp(buffer, lang), "Expected %s, got '%s'\n", lang, buffer);
5331 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SCOUNTRY, buffer, sizeof(buffer));
5332 len = lstrlenA(cntry) + 1;
5333 ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
5334 ok(!strcmp(buffer, cntry), "Expected %s, got '%s'\n", cntry, buffer);
5336 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SSORTNAME, buffer, sizeof(buffer));
5337 ok(ret, "Failed err %d\n", GetLastError());
5338 ok(!strcmp(buffer, sortm) || !strcmp(buffer, sortd) || !strcmp(buffer, sortms), "Got '%s'\n", buffer);
5342 static void test_GetSystemPreferredUILanguages(void)
5344 BOOL ret;
5345 NTSTATUS status;
5346 ULONG count, size, size_id, size_name, size_buffer;
5347 WCHAR *buffer;
5349 if (!pGetSystemPreferredUILanguages)
5351 win_skip("GetSystemPreferredUILanguages is not available.\n");
5352 return;
5355 /* (in)valid first parameter */
5356 count = 0;
5357 size = 0;
5358 SetLastError(0xdeadbeef);
5359 ret = pGetSystemPreferredUILanguages(0, &count, NULL, &size);
5360 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5361 ok(count, "Expected count > 0\n");
5362 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5364 size = 0;
5365 SetLastError(0xdeadbeef);
5366 ret = pGetSystemPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
5367 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5368 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5369 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5371 size = 0;
5372 SetLastError(0xdeadbeef);
5373 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
5374 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5375 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5376 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5378 size = 0;
5379 SetLastError(0xdeadbeef);
5380 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME, &count, NULL, &size);
5381 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5382 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5383 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5385 count = 0;
5386 size = 0;
5387 SetLastError(0xdeadbeef);
5388 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5389 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5390 ok(count, "Expected count > 0\n");
5391 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5393 count = 0;
5394 size = 0;
5395 SetLastError(0xdeadbeef);
5396 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5397 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5398 ok(count, "Expected count > 0\n");
5399 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5401 /* second parameter
5402 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, NULL, NULL, &size);
5403 * -> unhandled exception c0000005
5406 /* invalid third parameter */
5407 size = 1;
5408 SetLastError(0xdeadbeef);
5409 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
5410 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5411 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5412 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5414 /* fourth parameter
5415 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, NULL);
5416 * -> unhandled exception c0000005
5419 count = 0;
5420 size_id = 0;
5421 SetLastError(0xdeadbeef);
5422 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5423 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5424 ok(count, "Expected count > 0\n");
5425 ok(size_id % 5 == 1, "Expected size (%d) %% 5 == 1\n", size_id);
5427 count = 0;
5428 size_name = 0;
5429 SetLastError(0xdeadbeef);
5430 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
5431 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5432 ok(count, "Expected count > 0\n");
5433 ok(size_name % 6 == 1, "Expected size (%d) %% 6 == 1\n", size_name);
5435 size_buffer = max(size_id, size_name);
5436 if(!size_buffer)
5438 skip("No valid buffer size\n");
5439 return;
5442 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
5443 if (!buffer)
5445 skip("Failed to allocate memory for %d chars\n", size_buffer);
5446 return;
5449 count = 0;
5450 size = size_buffer;
5451 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5452 SetLastError(0xdeadbeef);
5453 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
5454 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5455 ok(count, "Expected count > 0\n");
5456 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5457 if (ret && size % 6 == 1)
5458 ok(!buffer[size -2] && !buffer[size -1],
5459 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5460 buffer[size -2], buffer[size -1]);
5462 count = 0;
5463 size = size_buffer;
5464 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5465 SetLastError(0xdeadbeef);
5466 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5467 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5468 ok(count, "Expected count > 0\n");
5469 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5470 if (ret && size % 5 == 1)
5471 ok(!buffer[size -2] && !buffer[size -1],
5472 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5473 buffer[size -2], buffer[size -1]);
5475 count = 0;
5476 size = size_buffer;
5477 SetLastError(0xdeadbeef);
5478 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
5479 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5480 ok(count, "Expected count > 0\n");
5481 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5482 if (ret && size % 5 == 1)
5483 ok(!buffer[size -2] && !buffer[size -1],
5484 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5485 buffer[size -2], buffer[size -1]);
5487 count = 0;
5488 size = 0;
5489 SetLastError(0xdeadbeef);
5490 ret = pGetSystemPreferredUILanguages(MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5491 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5492 ok(count, "Expected count > 0\n");
5493 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5494 if (ret && size % 6 == 1)
5495 ok(!buffer[size -2] && !buffer[size -1],
5496 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5497 buffer[size -2], buffer[size -1]);
5499 /* ntdll version is the same, but apparently takes an extra second parameter */
5500 count = 0;
5501 size = size_buffer;
5502 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5503 status = pRtlGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, 0, &count, buffer, &size);
5504 ok(!status, "got %x\n", status);
5505 ok(count, "Expected count > 0\n");
5506 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5507 if (ret && size % 5 == 1)
5508 ok(!buffer[size -2] && !buffer[size -1],
5509 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5510 buffer[size -2], buffer[size -1]);
5512 count = 0;
5513 size = size_buffer;
5514 status = pRtlGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, 0, &count, buffer, &size);
5515 ok(!status, "got %x\n", status);
5516 ok(count, "Expected count > 0\n");
5517 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5518 if (ret && size % 5 == 1)
5519 ok(!buffer[size -2] && !buffer[size -1],
5520 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5521 buffer[size -2], buffer[size -1]);
5523 count = 0;
5524 size = 0;
5525 status = pRtlGetSystemPreferredUILanguages(MUI_MACHINE_LANGUAGE_SETTINGS, 0, &count, NULL, &size);
5526 ok(!status, "got %x\n", status);
5527 ok(count, "Expected count > 0\n");
5528 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5529 if (ret && size % 6 == 1)
5530 ok(!buffer[size -2] && !buffer[size -1],
5531 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5532 buffer[size -2], buffer[size -1]);
5534 size = 0;
5535 SetLastError(0xdeadbeef);
5536 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5537 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5538 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5539 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5540 ok(size == size_id, "expected %u, got %u\n", size_id, size);
5542 size = 1;
5543 SetLastError(0xdeadbeef);
5544 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5545 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5546 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5547 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5548 ok(size == size_id, "expected %u, got %u\n", size_id, size);
5550 size = size_id -1;
5551 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5552 SetLastError(0xdeadbeef);
5553 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5554 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5555 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5556 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5557 ok(size == size_id, "expected %u, got %u\n", size_id, size);
5559 size = size_id -2;
5560 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5561 SetLastError(0xdeadbeef);
5562 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
5563 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5564 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5565 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5566 ok(size == size_id + 2 || size == size_id + 1 /* before win10 1809 */, "expected %u, got %u\n", size_id + 2, size);
5568 HeapFree(GetProcessHeap(), 0, buffer);
5571 static void test_GetThreadPreferredUILanguages(void)
5573 BOOL ret;
5574 NTSTATUS status;
5575 ULONG count, size, size_id;
5576 WCHAR *buf;
5578 if (!pGetThreadPreferredUILanguages)
5580 win_skip("GetThreadPreferredUILanguages is not available.\n");
5581 return;
5584 size = count = 0;
5585 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, NULL, &size);
5586 ok(ret, "got %u\n", GetLastError());
5587 ok(count, "expected count > 0\n");
5588 ok(size, "expected size > 0\n");
5590 count = 0;
5591 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size * sizeof(WCHAR));
5592 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, buf, &size);
5593 ok(ret, "got %u\n", GetLastError());
5594 ok(count, "expected count > 0\n");
5596 size_id = count = 0;
5597 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5598 ok(ret, "got %u\n", GetLastError());
5599 ok(count, "expected count > 0\n");
5600 ok(size_id, "expected size > 0\n");
5601 ok(size_id <= size, "expected size > 0\n");
5603 /* ntdll function is the same */
5604 size_id = count = 0;
5605 status = pRtlGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5606 ok(!status, "got %x\n", status);
5607 ok(count, "expected count > 0\n");
5608 ok(size_id, "expected size > 0\n");
5609 ok(size_id <= size, "expected size > 0\n");
5611 size = 0;
5612 SetLastError(0xdeadbeef);
5613 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, buf, &size);
5614 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
5615 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
5616 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5617 ok(size == size_id, "expected %u, got %u\n", size_id, size);
5619 size = 1;
5620 SetLastError(0xdeadbeef);
5621 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, buf, &size);
5622 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
5623 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
5624 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5625 ok(size == size_id, "expected %u, got %u\n", size_id, size);
5627 size = size_id - 1;
5628 SetLastError(0xdeadbeef);
5629 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, buf, &size);
5630 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
5631 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
5632 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5633 ok(size == size_id, "expected %u, got %u\n", size_id, size);
5635 size = size_id - 2;
5636 SetLastError(0xdeadbeef);
5637 ret = pGetThreadPreferredUILanguages(0, &count, buf, &size);
5638 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
5639 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
5640 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5641 todo_wine
5642 ok(size == size_id || size == size_id - 1 /* before win10 1809 */, "expected %u, got %u\n", size_id, size);
5644 HeapFree(GetProcessHeap(), 0, buf);
5647 static void test_GetUserPreferredUILanguages(void)
5649 BOOL ret;
5650 NTSTATUS status;
5651 ULONG count, size, size_id, size_name, size_buffer;
5652 WCHAR *buffer;
5654 if (!pGetUserPreferredUILanguages)
5656 win_skip("GetUserPreferredUILanguages is not available.\n");
5657 return;
5660 size = 0;
5661 SetLastError(0xdeadbeef);
5662 ret = pGetUserPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
5663 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5664 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5665 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5667 size = 0;
5668 SetLastError(0xdeadbeef);
5669 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
5670 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5671 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5672 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5674 size = 0;
5675 SetLastError(0xdeadbeef);
5676 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5677 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5678 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5679 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5681 size = 1;
5682 SetLastError(0xdeadbeef);
5683 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
5684 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5685 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5686 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5688 count = 0;
5689 size_id = 0;
5690 SetLastError(0xdeadbeef);
5691 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5692 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5693 ok(count, "Expected count > 0\n");
5694 ok(size_id % 5 == 1, "Expected size (%d) %% 5 == 1\n", size_id);
5696 count = 0;
5697 size_name = 0;
5698 SetLastError(0xdeadbeef);
5699 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
5700 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5701 ok(count, "Expected count > 0\n");
5702 ok(size_name % 6 == 1, "Expected size (%d) %% 6 == 1\n", size_name);
5704 size_buffer = max(size_id, size_name);
5705 if(!size_buffer)
5707 skip("No valid buffer size\n");
5708 return;
5711 /* ntdll version is the same, but apparently takes an extra second parameter */
5712 count = 0;
5713 size_id = 0;
5714 SetLastError(0xdeadbeef);
5715 status = pRtlGetUserPreferredUILanguages(MUI_LANGUAGE_ID, 0, &count, NULL, &size_id);
5716 ok(!status, "got %x\n", status);
5717 ok(count, "Expected count > 0\n");
5718 ok(size_id % 5 == 1, "Expected size (%d) %% 5 == 1\n", size_id);
5720 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
5722 count = 0;
5723 size = size_buffer;
5724 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5725 SetLastError(0xdeadbeef);
5726 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
5727 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5728 ok(count, "Expected count > 0\n");
5729 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5730 if (ret && size % 6 == 1)
5731 ok(!buffer[size -2] && !buffer[size -1],
5732 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5733 buffer[size -2], buffer[size -1]);
5735 count = 0;
5736 size = size_buffer;
5737 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5738 SetLastError(0xdeadbeef);
5739 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5740 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5741 ok(count, "Expected count > 0\n");
5742 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5743 if (ret && size % 5 == 1)
5744 ok(!buffer[size -2] && !buffer[size -1],
5745 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5746 buffer[size -2], buffer[size -1]);
5748 count = 0;
5749 size = size_buffer;
5750 SetLastError(0xdeadbeef);
5751 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
5752 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5753 ok(count, "Expected count > 0\n");
5754 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5755 if (ret && size % 5 == 1)
5756 ok(!buffer[size -2] && !buffer[size -1],
5757 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5758 buffer[size -2], buffer[size -1]);
5760 size = 1;
5761 SetLastError(0xdeadbeef);
5762 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5763 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5764 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5765 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5767 size = size_id -1;
5768 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5769 SetLastError(0xdeadbeef);
5770 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5771 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5772 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5773 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5775 count = 0;
5776 size = size_id -2;
5777 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5778 SetLastError(0xdeadbeef);
5779 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
5780 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5781 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5782 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5784 HeapFree(GetProcessHeap(), 0, buffer);
5787 static void test_FindNLSStringEx(void)
5789 INT res;
5790 static WCHAR en_simpsimpW[] = {'S','i','m','p','l','e','S','i','m','p','l','e',0};
5791 static WCHAR en_simpW[] = {'S','i','m','p','l','e',0};
5792 static WCHAR comb_s_accent1W[] = {0x1e69, 'o','u','r','c','e',0};
5793 static WCHAR comb_s_accent2W[] = {0x0073,0x323,0x307,'o','u','r','c','e',0};
5794 static WCHAR comb_q_accent1W[] = {0x0071,0x0307,0x323,'u','o','t','e',0};
5795 static WCHAR comb_q_accent2W[] = {0x0071,0x0323,0x307,'u','o','t','e',0};
5796 struct test_data {
5797 const WCHAR *locale;
5798 DWORD flags;
5799 WCHAR *src;
5800 INT src_size;
5801 WCHAR *value;
5802 INT val_size;
5803 INT found;
5804 INT expected_ret;
5805 INT expected_found;
5806 int todo;
5807 BOOL broken_vista_servers;
5810 static struct test_data test_arr[] =
5812 { localeW, FIND_FROMSTART, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5813 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 0, 6, 0, FALSE},
5814 { localeW, FIND_FROMEND, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5815 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 6, 6, 0, FALSE},
5816 { localeW, FIND_STARTSWITH, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5817 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 0, 6, 0, FALSE},
5818 { localeW, FIND_ENDSWITH, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5819 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 6, 6, 0, FALSE},
5820 { localeW, FIND_FROMSTART, comb_s_accent1W, ARRAY_SIZE(comb_s_accent1W)-1,
5821 comb_s_accent2W, ARRAY_SIZE(comb_s_accent2W)-1, 0, 0, 6, 1, TRUE },
5822 { localeW, FIND_FROMSTART, comb_q_accent1W, ARRAY_SIZE(comb_q_accent1W)-1,
5823 comb_q_accent2W, ARRAY_SIZE(comb_q_accent2W)-1, 0, 0, 7, 1, FALSE },
5824 { 0 }
5826 struct test_data *ptest;
5828 if (!pFindNLSStringEx)
5830 win_skip("FindNLSStringEx is not available.\n");
5831 return;
5834 SetLastError( 0xdeadbeef );
5835 res = pFindNLSStringEx(invalidW, FIND_FROMSTART, fooW, 3, fooW,
5836 3, NULL, NULL, NULL, 0);
5837 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5838 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5839 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5841 SetLastError( 0xdeadbeef );
5842 res = pFindNLSStringEx(localeW, FIND_FROMSTART, NULL, 3, fooW, 3,
5843 NULL, NULL, NULL, 0);
5844 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5845 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5846 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5848 SetLastError( 0xdeadbeef );
5849 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, -5, fooW, 3,
5850 NULL, NULL, NULL, 0);
5851 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5852 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5853 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5855 SetLastError( 0xdeadbeef );
5856 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, NULL, 3,
5857 NULL, NULL, NULL, 0);
5858 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5859 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5860 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5862 SetLastError( 0xdeadbeef );
5863 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, fooW, -5,
5864 NULL, NULL, NULL, 0);
5865 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5866 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5867 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5869 for (ptest = test_arr; ptest->src != NULL; ptest++)
5871 todo_wine_if(ptest->todo)
5873 res = pFindNLSStringEx(ptest->locale, ptest->flags, ptest->src, ptest->src_size,
5874 ptest->value, ptest->val_size, &ptest->found, NULL, NULL, 0);
5875 if (ptest->broken_vista_servers)
5877 ok(res == ptest->expected_ret || /* Win 7 onwards */
5878 broken(res == -1), /* Win Vista, Server 2003 and 2008 */
5879 "Expected FindNLSStringEx to return %d. Returned value was %d\n",
5880 ptest->expected_ret, res);
5881 ok(ptest->found == ptest->expected_found || /* Win 7 onwards */
5882 broken(ptest->found == 0), /* Win Vista, Server 2003 and 2008 */
5883 "Expected FindNLSStringEx to output %d. Value was %d\n",
5884 ptest->expected_found, ptest->found);
5886 else
5888 ok(res == ptest->expected_ret,
5889 "Expected FindNLSStringEx to return %d. Returned value was %d\n",
5890 ptest->expected_ret, res);
5891 ok(ptest->found == ptest->expected_found,
5892 "Expected FindNLSStringEx to output %d. Value was %d\n",
5893 ptest->expected_found, ptest->found);
5899 static void test_FindStringOrdinal(void)
5901 static const WCHAR abc123aBcW[] = {'a', 'b', 'c', '1', '2', '3', 'a', 'B', 'c', 0};
5902 static const WCHAR abcW[] = {'a', 'b', 'c', 0};
5903 static const WCHAR aBcW[] = {'a', 'B', 'c', 0};
5904 static const WCHAR aaaW[] = {'a', 'a', 'a', 0};
5905 static const struct
5907 DWORD flag;
5908 const WCHAR *src;
5909 INT src_size;
5910 const WCHAR *val;
5911 INT val_size;
5912 BOOL ignore_case;
5913 INT ret;
5914 DWORD err;
5916 tests[] =
5918 /* Invalid */
5919 {1, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, ERROR_INVALID_FLAGS},
5920 {FIND_FROMSTART, NULL, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1,
5921 ERROR_INVALID_PARAMETER},
5922 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, NULL, ARRAY_SIZE(abcW) - 1, FALSE, -1,
5923 ERROR_INVALID_PARAMETER},
5924 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, NULL, 0, FALSE, -1, ERROR_INVALID_PARAMETER},
5925 {FIND_FROMSTART, NULL, 0, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, ERROR_INVALID_PARAMETER},
5926 {FIND_FROMSTART, NULL, 0, NULL, 0, FALSE, -1, ERROR_INVALID_PARAMETER},
5927 /* Case-insensitive */
5928 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5929 {FIND_FROMEND, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5930 {FIND_STARTSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5931 {FIND_ENDSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, NO_ERROR},
5932 /* Case-sensitive */
5933 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 0, NO_ERROR},
5934 {FIND_FROMEND, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 6, NO_ERROR},
5935 {FIND_STARTSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 0, NO_ERROR},
5936 {FIND_ENDSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 6, NO_ERROR},
5937 /* Other */
5938 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aaaW, ARRAY_SIZE(aaaW) - 1, FALSE, -1, NO_ERROR},
5939 {FIND_FROMSTART, abc123aBcW, -1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5940 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, -1, FALSE, 0, NO_ERROR},
5941 {FIND_FROMSTART, abc123aBcW, 0, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, NO_ERROR},
5942 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, 0, FALSE, 0, NO_ERROR},
5943 {FIND_FROMSTART, abc123aBcW, 0, abcW, 0, FALSE, 0, NO_ERROR},
5945 INT ret;
5946 DWORD err;
5947 INT i;
5949 if (!pFindStringOrdinal)
5951 win_skip("FindStringOrdinal is not available.\n");
5952 return;
5955 for (i = 0; i < ARRAY_SIZE(tests); i++)
5957 SetLastError(0xdeadbeef);
5958 ret = pFindStringOrdinal(tests[i].flag, tests[i].src, tests[i].src_size, tests[i].val, tests[i].val_size,
5959 tests[i].ignore_case);
5960 err = GetLastError();
5961 ok(ret == tests[i].ret, "Item %d expected %d, got %d\n", i, tests[i].ret, ret);
5962 ok(err == tests[i].err, "Item %d expected %#x, got %#x\n", i, tests[i].err, err);
5966 static void test_SetThreadUILanguage(void)
5968 LANGID res;
5970 if (!pGetThreadUILanguage)
5972 win_skip("GetThreadUILanguage isn't implemented, skipping SetThreadUILanguage tests for version < Vista\n");
5973 return; /* BTW SetThreadUILanguage is present on winxp/2003 but doesn`t set the LANGID anyway when tested */
5976 res = pSetThreadUILanguage(0);
5977 ok(res == pGetThreadUILanguage(), "expected %d got %d\n", pGetThreadUILanguage(), res);
5979 res = pSetThreadUILanguage(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN));
5980 ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
5981 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
5983 res = pSetThreadUILanguage(0);
5984 todo_wine ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
5985 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
5988 static int put_utf16( WCHAR *str, unsigned int c )
5990 if (c < 0x10000)
5992 *str = c;
5993 return 1;
5995 c -= 0x10000;
5996 str[0] = 0xd800 | (c >> 10);
5997 str[1] = 0xdc00 | (c & 0x3ff);
5998 return 2;
6001 /* read a Unicode string from NormalizationTest.txt format; helper for test_NormalizeString */
6002 static int read_str( char *str, WCHAR res[32] )
6004 int pos = 0;
6005 char *end;
6007 while (*str && pos < 31)
6009 unsigned int c = strtoul( str, &end, 16 );
6010 pos += put_utf16( res + pos, c );
6011 while (*end == ' ') end++;
6012 str = end;
6014 res[pos] = 0;
6015 return pos;
6018 static void test_NormalizeString(void)
6020 /* part 0: specific cases */
6021 /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
6022 static const WCHAR part0_str1[] = {0x1e0a,0};
6023 static const WCHAR part0_nfd1[] = {0x0044,0x0307,0};
6025 /* LATIN CAPITAL LETTER D, COMBINING DOT BELOW, COMBINING DOT ABOVE */
6026 static const WCHAR part0_str2[] = {0x0044,0x0323,0x0307,0};
6027 static const WCHAR part0_nfc2[] = {0x1e0c,0x0307,0};
6029 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
6030 static const WCHAR part0_str3[] = {0x0044,0x031b,0x0323,0x0307,0};
6031 static const WCHAR part0_nfc3[] = {0x1e0c,0x031b,0x0307,0};
6033 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
6034 static const WCHAR part0_str4[] = {0x0044,0x031b,0x0323,0x0307,0};
6035 static const WCHAR part0_nfc4[] = {0x1e0c,0x031b,0x0307,0};
6038 * HEBREW ACCENT SEGOL, HEBREW POINT PATAH, HEBREW POINT DAGESH OR MAPIQ,
6039 * HEBREW ACCENT MERKHA, HEBREW POINT SHEVA, HEBREW PUNCTUATION PASEQ,
6040 * HEBREW MARK UPPER DOT, HEBREW ACCENT DEHI
6042 static const WCHAR part0_str5[] = {0x0592,0x05B7,0x05BC,0x05A5,0x05B0,0x05C0,0x05C4,0x05AD,0};
6043 static const WCHAR part0_nfc5[] = {0x05B0,0x05B7,0x05BC,0x05A5,0x0592,0x05C0,0x05AD,0x05C4,0};
6046 * HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT HATAF SEGOL,
6047 * HEBREW ACCENT ETNAHTA, HEBREW PUNCTUATION SOF PASUQ, HEBREW POINT SHEVA,
6048 * HEBREW ACCENT ILUY, HEBREW ACCENT QARNEY PARA
6050 static const WCHAR part0_str6[] = {0x05B8,0x05B9,0x05B1,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
6051 static const WCHAR part0_nfc6[] = {0x05B1,0x05B8,0x05B9,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
6053 /* LATIN CAPITAL LETTER D WITH DOT BELOW */
6054 static const WCHAR part0_str8[] = {0x1E0C,0};
6055 static const WCHAR part0_nfd8[] = {0x0044,0x0323,0};
6057 /* LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING DOT BELOW */
6058 static const WCHAR part0_str9[] = {0x1E0A,0x0323,0};
6059 static const WCHAR part0_nfc9[] = {0x1E0C,0x0307,0};
6060 static const WCHAR part0_nfd9[] = {0x0044,0x0323,0x0307,0};
6062 /* LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING DOT ABOVE */
6063 static const WCHAR part0_str10[] = {0x1E0C,0x0307,0};
6064 static const WCHAR part0_nfd10[] = {0x0044,0x0323,0x0307,0};
6066 /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE, COMBINING MACRON */
6067 static const WCHAR part0_str11[] = {0x1E14,0x0304,0};
6068 static const WCHAR part0_nfd11[] = {0x0045,0x0304,0x0300,0x0304,0};
6070 /* LATIN CAPITAL LETTER E WITH MACRON, COMBINING GRAVE ACCENT */
6071 static const WCHAR part0_str12[] = {0x0112,0x0300,0};
6072 static const WCHAR part0_nfc12[] = {0x1E14,0};
6073 static const WCHAR part0_nfd12[] = {0x0045,0x0304,0x0300,0};
6075 /* part 1: character by character */
6076 /* DIAERESIS */
6077 static const WCHAR part1_str1[] = {0x00a8,0};
6078 static const WCHAR part1_nfkc1[] = {0x0020,0x0308,0};
6080 /* VULGAR FRACTION ONE QUARTER */
6081 static const WCHAR part1_str2[] = {0x00bc,0};
6082 static const WCHAR part1_nfkc2[] = {0x0031,0x2044,0x0034,0};
6084 /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
6085 static const WCHAR part1_str3[] = {0x00ca,0};
6086 static const WCHAR part1_nfd3[] = {0x0045,0x0302,0};
6088 /* MODIFIER LETTER SMALL GAMMA */
6089 static const WCHAR part1_str4[] = {0x02e0,0};
6090 static const WCHAR part1_nfkc4[] = {0x0263,0};
6092 /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */
6093 static const WCHAR part1_str5[] = {0x0400,0};
6094 static const WCHAR part1_nfd5[] = {0x0415,0x0300,0};
6096 /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */
6097 static const WCHAR part1_str6[] = {0x0476,0};
6098 static const WCHAR part1_nfd6[] = {0x0474,0x030F,0};
6100 /* ARABIC LIGATURE HAH WITH JEEM INITIAL FORM */
6101 static const WCHAR part1_str7[] = {0xFCA9,0};
6102 static const WCHAR part1_nfkc7[] = {0x062D,0x062C,0};
6104 /* GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA */
6105 static const WCHAR part1_str8[] = {0x1F42,0};
6106 static const WCHAR part1_nfd8[] = {0x03BF,0x0313,0x0300,0};
6108 /* QUADRUPLE PRIME */
6109 static const WCHAR part1_str9[] = {0x2057,0};
6110 static const WCHAR part1_nfkc9[] = {0x2032,0x2032,0x2032,0x2032,0};
6112 /* KATAKANA-HIRAGANA VOICED SOUND MARK */
6113 static const WCHAR part1_str10[] = {0x309B,0};
6114 static const WCHAR part1_nfkc10[] = {0x20,0x3099,0};
6116 /* ANGSTROM SIGN */
6117 static const WCHAR part1_str11[] = {0x212B,0};
6118 static const WCHAR part1_nfc11[] = {0xC5,0};
6119 static const WCHAR part1_nfd11[] = {'A',0x030A,0};
6121 static const WCHAR composite_src[] =
6123 0x008a, 0x008e, 0x009a, 0x009e, 0x009f, 0x00c0, 0x00c1, 0x00c2,
6124 0x00c3, 0x00c4, 0x00c5, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb,
6125 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d1, 0x00d2, 0x00d3, 0x00d4,
6126 0x00d5, 0x00d6, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd,
6127 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e7, 0x00e8,
6128 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f1,
6129 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f8, 0x00f9, 0x00fa,
6130 0x00fb, 0x00fc, 0x00fd, 0x00ff, 0x212b
6133 struct test_data_normal {
6134 const WCHAR *str;
6135 const WCHAR *expected[4];
6137 static const struct test_data_normal test_arr[] =
6139 { part0_str1, { part0_str1, part0_nfd1, part0_str1, part0_nfd1 } },
6140 { part0_str2, { part0_nfc2, part0_str2, part0_nfc2, part0_str2 } },
6141 { part0_str3, { part0_nfc3, part0_str3, part0_nfc3, part0_str3 } },
6142 { part0_str4, { part0_nfc4, part0_str4, part0_nfc4, part0_str4 } },
6143 { part0_str5, { part0_nfc5, part0_nfc5, part0_nfc5, part0_nfc5 } },
6144 { part0_str6, { part0_nfc6, part0_nfc6, part0_nfc6, part0_nfc6 } },
6145 { part0_str8, { part0_str8, part0_nfd8, part0_str8, part0_nfd8 } },
6146 { part0_str9, { part0_nfc9, part0_nfd9, part0_nfc9, part0_nfd9 } },
6147 { part0_str10, { part0_str10, part0_nfd10, part0_str10, part0_nfd10 } },
6148 { part0_str11, { part0_str11, part0_nfd11, part0_str11, part0_nfd11 } },
6149 { part0_str12, { part0_nfc12, part0_nfd12, part0_nfc12, part0_nfd12 } },
6150 { part1_str1, { part1_str1, part1_str1, part1_nfkc1, part1_nfkc1 } },
6151 { part1_str2, { part1_str2, part1_str2, part1_nfkc2, part1_nfkc2 } },
6152 { part1_str3, { part1_str3, part1_nfd3, part1_str3, part1_nfd3 } },
6153 { part1_str4, { part1_str4, part1_str4, part1_nfkc4, part1_nfkc4 } },
6154 { part1_str5, { part1_str5, part1_nfd5, part1_str5, part1_nfd5 } },
6155 { part1_str6, { part1_str6, part1_nfd6, part1_str6, part1_nfd6 } },
6156 { part1_str7, { part1_str7, part1_str7, part1_nfkc7, part1_nfkc7 } },
6157 { part1_str8, { part1_str8, part1_nfd8, part1_str8, part1_nfd8 } },
6158 { part1_str9, { part1_str9, part1_str9, part1_nfkc9, part1_nfkc9 } },
6159 { part1_str10, { part1_str10, part1_str10, part1_nfkc10, part1_nfkc10 } },
6160 { part1_str11, { part1_nfc11, part1_nfd11, part1_nfc11, part1_nfd11 } },
6161 { 0 }
6163 const struct test_data_normal *ptest = test_arr;
6164 const int norm_forms[] = { NormalizationC, NormalizationD, NormalizationKC, NormalizationKD };
6165 WCHAR dst[256];
6166 BOOLEAN ret;
6167 NTSTATUS status;
6168 int dstlen, str_cmp, i, j;
6169 FILE *f;
6171 if (!pNormalizeString)
6173 win_skip("NormalizeString is not available.\n");
6174 return;
6176 if (!pRtlNormalizeString) win_skip("RtlNormalizeString is not available.\n");
6179 * For each string, first test passing -1 as srclen to NormalizeString,
6180 * thereby assuming a null-terminating string in src, and then test passing
6181 * explicitly the string length.
6182 * Do that for all 4 normalization forms.
6184 while (ptest->str)
6186 for (i = 0; i < 4; i++)
6188 SetLastError(0xdeadbeef);
6189 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, NULL, 0 );
6190 ok( dstlen > lstrlenW(ptest->str), "%s:%d: wrong len %d / %d\n",
6191 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW(ptest->str) );
6192 ok(GetLastError() == ERROR_SUCCESS, "%s:%d: got error %u\n",
6193 wine_dbgstr_w(ptest->str), i, GetLastError());
6194 SetLastError(0xdeadbeef);
6195 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, dst, dstlen );
6196 ok(GetLastError() == ERROR_SUCCESS, "%s:%d: got error %u\n",
6197 wine_dbgstr_w(ptest->str), i, GetLastError());
6198 ok(dstlen == lstrlenW( dst )+1, "%s:%d: Copied length differed: was %d, should be %d\n",
6199 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst )+1);
6200 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen+1 );
6201 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
6202 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
6204 dstlen = pNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), NULL, 0 );
6205 memset(dst, 0xcc, sizeof(dst));
6206 dstlen = pNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), dst, dstlen );
6207 ok(dstlen == lstrlenW( ptest->expected[i] ), "%s:%d: Copied length differed: was %d, should be %d\n",
6208 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst ));
6209 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen );
6210 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
6211 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
6213 if (pRtlNormalizeString)
6215 dstlen = 0;
6216 status = pRtlNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), NULL, &dstlen );
6217 ok( !status, "%s:%d: failed %x\n", wine_dbgstr_w(ptest->str), i, status );
6218 ok( dstlen > lstrlenW(ptest->str), "%s:%d: wrong len %d / %d\n",
6219 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW(ptest->str) );
6220 memset(dst, 0, sizeof(dst));
6221 status = pRtlNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), dst, &dstlen );
6222 ok( !status, "%s:%d: failed %x\n", wine_dbgstr_w(ptest->str), i, status );
6223 ok(dstlen == lstrlenW( dst ), "%s:%d: Copied length differed: was %d, should be %d\n",
6224 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst ));
6225 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen );
6226 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
6227 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
6228 ret = FALSE;
6229 status = pRtlIsNormalizedString( norm_forms[i], ptest->str, -1, &ret );
6230 ok( !status, "%s:%d: failed %x\n", wine_dbgstr_w(ptest->str), i, status );
6231 if (!wcscmp( ptest->str, dst ))
6232 ok( ret, "%s:%d: not normalized\n", wine_dbgstr_w(ptest->str), i );
6233 else
6234 ok( !ret, "%s:%d: normalized (dst %s)\n", wine_dbgstr_w(ptest->str), i, wine_dbgstr_w(dst) );
6235 ret = FALSE;
6236 status = pRtlIsNormalizedString( norm_forms[i], dst, dstlen, &ret );
6237 ok( !status, "%s:%d: failed %x\n", wine_dbgstr_w(ptest->str), i, status );
6238 ok( ret, "%s:%d: not normalized\n", wine_dbgstr_w(ptest->str), i );
6241 ptest++;
6244 /* buffer overflows */
6246 SetLastError(0xdeadbeef);
6247 dstlen = pNormalizeString( NormalizationD, part0_str1, -1, dst, 1 );
6248 ok( dstlen <= 0, "wrong len %d\n", dstlen );
6249 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %u\n", GetLastError());
6251 SetLastError(0xdeadbeef);
6252 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, dst, 1 );
6253 ok( dstlen <= 0, "wrong len %d\n", dstlen );
6254 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %u\n", GetLastError());
6256 SetLastError(0xdeadbeef);
6257 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, NULL, 0 );
6258 ok( dstlen == 12, "wrong len %d\n", dstlen );
6259 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6261 SetLastError(0xdeadbeef);
6262 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, dst, 3 );
6263 ok( dstlen == 3, "wrong len %d\n", dstlen );
6264 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6266 SetLastError(0xdeadbeef);
6267 dstlen = pNormalizeString( NormalizationC, part0_str2, 0, NULL, 0 );
6268 ok( dstlen == 0, "wrong len %d\n", dstlen );
6269 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6271 SetLastError(0xdeadbeef);
6272 dstlen = pNormalizeString( NormalizationC, part0_str2, 0, dst, 3 );
6273 ok( dstlen == 0, "wrong len %d\n", dstlen );
6274 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6276 /* size estimations */
6278 memset( dst, 'A', sizeof(dst) );
6279 for (j = 1; j < ARRAY_SIZE(dst); j++)
6281 for (i = 0; i < 4; i++)
6283 int expect = (i < 2) ? j * 3 : j * 18;
6284 if (expect > 64) expect = max( 64, j + j / 8 );
6285 dstlen = pNormalizeString( norm_forms[i], dst, j, NULL, 0 );
6286 ok( dstlen == expect, "%d: %d -> wrong len %d\n", i, j, dstlen );
6287 if (pRtlNormalizeString)
6289 dstlen = 0;
6290 status = pRtlNormalizeString( norm_forms[i], dst, j, NULL, &dstlen );
6291 ok( !status, "%d: failed %x\n", i, status );
6292 ok( dstlen == expect, "%d: %d -> wrong len %d\n", i, j, dstlen );
6296 for (i = 0; i < 4; i++)
6298 int srclen = ARRAY_SIZE( composite_src );
6299 int expect = max( 64, srclen + srclen / 8 );
6300 dstlen = pNormalizeString( norm_forms[i], composite_src, srclen, NULL, 0 );
6301 ok( dstlen == expect, "%d: wrong len %d\n", i, dstlen );
6302 dstlen = pNormalizeString( norm_forms[i], composite_src, srclen, dst, dstlen );
6303 if (i == 0 || i == 2)
6305 ok( dstlen == srclen, "%d: wrong len %d\n", i, dstlen );
6306 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6308 else
6310 ok( dstlen < -expect, "%d: wrong len %d\n", i, dstlen );
6311 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %u\n", GetLastError());
6313 if (pRtlNormalizeString)
6315 dstlen = 0;
6316 status = pRtlNormalizeString( norm_forms[i], composite_src, srclen, NULL, &dstlen );
6317 ok( !status, "%d: failed %x\n", i, status );
6318 ok( dstlen == expect, "%d: wrong len %d\n", i, dstlen );
6319 status = pRtlNormalizeString( norm_forms[i], composite_src, srclen, dst, &dstlen );
6320 if (i == 0 || i == 2)
6322 ok( !status, "%d: failed %x\n", i, status );
6323 ok( dstlen == srclen, "%d: wrong len %d\n", i, dstlen );
6325 else
6327 ok( status == STATUS_BUFFER_TOO_SMALL, "%d: failed %x\n", i, status );
6328 ok( dstlen > expect, "%d: wrong len %d\n", i, dstlen );
6333 /* invalid parameters */
6335 for (i = 0; i < 32; i++)
6337 SetLastError(0xdeadbeef);
6338 dstlen = pNormalizeString( i, L"ABC", -1, NULL, 0 );
6339 switch (i)
6341 case NormalizationC:
6342 case NormalizationD:
6343 case NormalizationKC:
6344 case NormalizationKD:
6345 case 13: /* Idn */
6346 ok( dstlen > 0, "%d: wrong len %d\n", i, dstlen );
6347 ok( GetLastError() == ERROR_SUCCESS, "%d: got error %u\n", i, GetLastError());
6348 break;
6349 default:
6350 ok( dstlen <= 0, "%d: wrong len %d\n", i, dstlen );
6351 ok( GetLastError() == ERROR_INVALID_PARAMETER, "%d: got error %u\n", i, GetLastError());
6352 break;
6354 if (pRtlNormalizeString)
6356 dstlen = 0;
6357 status = pRtlNormalizeString( i, L"ABC", -1, NULL, &dstlen );
6358 switch (i)
6360 case 0:
6361 ok( status == STATUS_INVALID_PARAMETER, "%d: failed %x\n", i, status );
6362 break;
6363 case NormalizationC:
6364 case NormalizationD:
6365 case NormalizationKC:
6366 case NormalizationKD:
6367 case 13: /* Idn */
6368 ok( status == STATUS_SUCCESS, "%d: failed %x\n", i, status );
6369 break;
6370 default:
6371 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%d: failed %x\n", i, status );
6372 break;
6377 /* invalid sequences */
6379 for (i = 0; i < 4; i++)
6381 dstlen = pNormalizeString( norm_forms[i], L"AB\xd800Z", -1, NULL, 0 );
6382 ok( dstlen == (i < 2 ? 15 : 64), "%d: wrong len %d\n", i, dstlen );
6383 SetLastError( 0xdeadbeef );
6384 dstlen = pNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, ARRAY_SIZE(dst) );
6385 ok( dstlen == -3, "%d: wrong len %d\n", i, dstlen );
6386 ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%d: wrong error %d\n", i, GetLastError() );
6387 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, NULL, 0 );
6388 ok( dstlen == (i < 2 ? 21 : 64), "%d: wrong len %d\n", i, dstlen );
6389 SetLastError( 0xdeadbeef );
6390 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, dst, ARRAY_SIZE(dst) );
6391 ok( dstlen == -4, "%d: wrong len %d\n", i, dstlen );
6392 ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%d: wrong error %d\n", i, GetLastError() );
6393 SetLastError( 0xdeadbeef );
6394 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, dst, 2 );
6395 todo_wine
6396 ok( dstlen == (i < 2 ? -18 : -74), "%d: wrong len %d\n", i, dstlen );
6397 todo_wine_if (i == 0 || i == 2)
6398 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "%d: wrong error %d\n", i, GetLastError() );
6399 if (pRtlNormalizeString)
6401 dstlen = 0;
6402 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, NULL, &dstlen );
6403 ok( !status, "%d: failed %x\n", i, status );
6404 ok( dstlen == (i < 2 ? 15 : 64), "%d: wrong len %d\n", i, dstlen );
6405 dstlen = ARRAY_SIZE(dst);
6406 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
6407 ok( status == STATUS_NO_UNICODE_TRANSLATION, "%d: failed %x\n", i, status );
6408 ok( dstlen == 3, "%d: wrong len %d\n", i, dstlen );
6409 dstlen = 1;
6410 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
6411 todo_wine_if( i == 0 || i == 2)
6412 ok( status == STATUS_BUFFER_TOO_SMALL, "%d: failed %x\n", i, status );
6413 todo_wine_if( i != 3)
6414 ok( dstlen == (i < 2 ? 14 : 73), "%d: wrong len %d\n", i, dstlen );
6415 dstlen = 2;
6416 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
6417 ok( status == STATUS_NO_UNICODE_TRANSLATION, "%d: failed %x\n", i, status );
6418 ok( dstlen == 3, "%d: wrong len %d\n", i, dstlen );
6422 /* optionally run the full test file from Unicode.org
6423 * available at http://www.unicode.org/Public/UCD/latest/ucd/NormalizationTest.txt
6425 if ((f = fopen( "NormalizationTest.txt", "r" )))
6427 char *p, buffer[1024];
6428 WCHAR str[3], srcW[32], dstW[32], resW[4][32];
6429 int line = 0, part = 0, ch;
6430 char tested[0x110000 / 8];
6432 while (fgets( buffer, sizeof(buffer), f ))
6434 line++;
6435 if ((p = strchr( buffer, '#' ))) *p = 0;
6436 if (!strncmp( buffer, "@Part", 5 ))
6438 part = atoi( buffer + 5 );
6439 continue;
6441 if (!(p = strtok( buffer, ";" ))) continue;
6442 read_str( p, srcW );
6443 for (i = 0; i < 4; i++)
6445 p = strtok( NULL, ";" );
6446 read_str( p, &resW[i][0] );
6448 if (part == 1)
6450 ch = srcW[0];
6451 if (ch >= 0xd800 && ch <= 0xdbff)
6452 ch = 0x10000 + ((srcW[0] & 0x3ff) << 10) + (srcW[1] & 0x3ff);
6453 tested[ch / 8] |= 1 << (ch % 8);
6455 for (i = 0; i < 4; i++)
6457 memset( dstW, 0xcc, sizeof(dstW) );
6458 dstlen = pNormalizeString( norm_forms[i], srcW, -1, dstW, ARRAY_SIZE(dstW) );
6459 ok( !wcscmp( dstW, resW[i] ),
6460 "line %u form %u: wrong result %s for %s expected %s\n", line, i,
6461 wine_dbgstr_w( dstW ), wine_dbgstr_w( srcW ), wine_dbgstr_w( resW[i] ));
6463 ret = FALSE;
6464 status = pRtlIsNormalizedString( norm_forms[i], srcW, -1, &ret );
6465 ok( !status, "line %u form %u: RtlIsNormalizedString failed %x\n", line, i, status );
6466 if (!wcscmp( srcW, dstW ))
6467 ok( ret, "line %u form %u: source not normalized %s\n", line, i, wine_dbgstr_w(srcW) );
6468 else
6469 ok( !ret, "line %u form %u: source normalized %s\n", line, i, wine_dbgstr_w(srcW) );
6470 ret = FALSE;
6471 status = pRtlIsNormalizedString( norm_forms[i], dstW, -1, &ret );
6472 ok( !status, "line %u form %u: RtlIsNormalizedString failed %x\n", line, i, status );
6473 ok( ret, "line %u form %u: dest not normalized %s\n", line, i, wine_dbgstr_w(dstW) );
6475 for (j = 0; j < 4; j++)
6477 int expect = i | (j & 2);
6478 memset( dstW, 0xcc, sizeof(dstW) );
6479 dstlen = pNormalizeString( norm_forms[i], resW[j], -1, dstW, ARRAY_SIZE(dstW) );
6480 ok( !wcscmp( dstW, resW[expect] ),
6481 "line %u form %u res %u: wrong result %s for %s expected %s\n", line, i, j,
6482 wine_dbgstr_w( dstW ), wine_dbgstr_w( resW[j] ), wine_dbgstr_w( resW[expect] ));
6486 fclose( f );
6488 /* test chars that are not in the @Part1 list */
6489 for (ch = 0; ch < 0x110000; ch++)
6491 if (tested[ch / 8] & (1 << (ch % 8))) continue;
6492 str[put_utf16( str, ch )] = 0;
6493 for (i = 0; i < 4; i++)
6495 memset( dstW, 0xcc, sizeof(dstW) );
6496 SetLastError( 0xdeadbeef );
6497 dstlen = pNormalizeString( norm_forms[i], str, -1, dstW, ARRAY_SIZE(dstW) );
6498 if ((ch >= 0xd800 && ch <= 0xdfff) ||
6499 (ch >= 0xfdd0 && ch <= 0xfdef) ||
6500 ((ch & 0xffff) >= 0xfffe))
6502 ok( dstlen <= 0, "char %04x form %u: wrong result %d %s expected error\n",
6503 ch, i, dstlen, wine_dbgstr_w( dstW ));
6504 ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION,
6505 "char %04x form %u: error %u\n", str[0], i, GetLastError() );
6506 status = pRtlIsNormalizedString( norm_forms[i], str, -1, &ret );
6507 ok( status == STATUS_NO_UNICODE_TRANSLATION,
6508 "char %04x form %u: failed %x\n", ch, i, status );
6510 else
6512 ok( !wcscmp( dstW, str ),
6513 "char %04x form %u: wrong result %s expected unchanged\n",
6514 ch, i, wine_dbgstr_w( dstW ));
6515 ret = FALSE;
6516 status = pRtlIsNormalizedString( norm_forms[i], str, -1, &ret );
6517 ok( !status, "char %04x form %u: failed %x\n", ch, i, status );
6518 ok( ret, "char %04x form %u: not normalized\n", ch, i );
6525 static void test_SpecialCasing(void)
6527 int ret, i;
6528 WCHAR exp, buffer[8];
6529 static const WCHAR azCyrlazW[] = {'a','z','-','C','y','r','l','-','a','z',0};
6530 static const WCHAR azLatnazW[] = {'a','z','-','L','a','t','n','-','a','z',0};
6531 static const WCHAR deDEW[] = {'d','e','-','D','E',0};
6532 static const WCHAR elGRW[] = {'e','l','-','G','R',0};
6533 static const WCHAR enUSW[] = {'e','n','-','U','S',0};
6534 static const WCHAR hyAMW[] = {'h','y','-','A','M',0};
6535 static const WCHAR ltLTW[] = {'l','t','-','L','T',0};
6536 static const WCHAR trTRW[] = {'t','r','-','T','R',0};
6537 static const WCHAR TRTRW[] = {'T','R','-','T','R',0};
6538 static const struct test {
6539 const WCHAR *lang;
6540 DWORD flags;
6541 WCHAR ch;
6542 WCHAR exp; /* 0 if self */
6543 WCHAR exp_ling; /* 0 if exp */
6544 } tests[] = {
6545 {deDEW, LCMAP_UPPERCASE, 0x00DF}, /* LATIN SMALL LETTER SHARP S */
6547 {enUSW, LCMAP_UPPERCASE, 0xFB00}, /* LATIN SMALL LIGATURE FF */
6548 {enUSW, LCMAP_UPPERCASE, 0xFB01}, /* LATIN SMALL LIGATURE FI */
6549 {enUSW, LCMAP_UPPERCASE, 0xFB02}, /* LATIN SMALL LIGATURE FL */
6550 {enUSW, LCMAP_UPPERCASE, 0xFB03}, /* LATIN SMALL LIGATURE FFI */
6551 {enUSW, LCMAP_UPPERCASE, 0xFB04}, /* LATIN SMALL LIGATURE FFL */
6552 {enUSW, LCMAP_UPPERCASE, 0xFB05}, /* LATIN SMALL LIGATURE LONG S T */
6553 {enUSW, LCMAP_UPPERCASE, 0xFB06}, /* LATIN SMALL LIGATURE ST */
6555 {hyAMW, LCMAP_UPPERCASE, 0x0587}, /* ARMENIAN SMALL LIGATURE ECH YIWN */
6556 {hyAMW, LCMAP_UPPERCASE, 0xFB13}, /* ARMENIAN SMALL LIGATURE MEN NOW */
6557 {hyAMW, LCMAP_UPPERCASE, 0xFB14}, /* ARMENIAN SMALL LIGATURE MEN ECH */
6558 {hyAMW, LCMAP_UPPERCASE, 0xFB15}, /* ARMENIAN SMALL LIGATURE MEN INI */
6559 {hyAMW, LCMAP_UPPERCASE, 0xFB16}, /* ARMENIAN SMALL LIGATURE VEW NOW */
6560 {hyAMW, LCMAP_UPPERCASE, 0xFB17}, /* ARMENIAN SMALL LIGATURE MEN XEH */
6562 {enUSW, LCMAP_UPPERCASE, 0x0149}, /* LATIN SMALL LETTER N PRECEDED BY APOSTROPHE */
6563 {elGRW, LCMAP_UPPERCASE, 0x0390}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
6564 {elGRW, LCMAP_UPPERCASE, 0x03B0}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
6565 {enUSW, LCMAP_UPPERCASE, 0x01F0}, /* LATIN SMALL LETTER J WITH CARON */
6566 {enUSW, LCMAP_UPPERCASE, 0x1E96}, /* LATIN SMALL LETTER H WITH LINE BELOW */
6567 {enUSW, LCMAP_UPPERCASE, 0x1E97}, /* LATIN SMALL LETTER T WITH DIAERESIS */
6568 {enUSW, LCMAP_UPPERCASE, 0x1E98}, /* LATIN SMALL LETTER W WITH RING ABOVE */
6569 {enUSW, LCMAP_UPPERCASE, 0x1E99}, /* LATIN SMALL LETTER Y WITH RING ABOVE */
6570 {enUSW, LCMAP_UPPERCASE, 0x1E9A}, /* LATIN SMALL LETTER A WITH RIGHT HALF RING */
6571 {elGRW, LCMAP_UPPERCASE, 0x1F50}, /* GREEK SMALL LETTER UPSILON WITH PSILI */
6572 {elGRW, LCMAP_UPPERCASE, 0x1F52}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA */
6573 {elGRW, LCMAP_UPPERCASE, 0x1F54}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA */
6574 {elGRW, LCMAP_UPPERCASE, 0x1F56}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI */
6575 {elGRW, LCMAP_UPPERCASE, 0x1FB6}, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI */
6576 {elGRW, LCMAP_UPPERCASE, 0x1FC6}, /* GREEK SMALL LETTER ETA WITH PERISPOMENI */
6577 {elGRW, LCMAP_UPPERCASE, 0x1FD2}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA */
6578 {elGRW, LCMAP_UPPERCASE, 0x1FD3}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA */
6579 {elGRW, LCMAP_UPPERCASE, 0x1FD6}, /* GREEK SMALL LETTER IOTA WITH PERISPOMENI */
6580 {elGRW, LCMAP_UPPERCASE, 0x1FD7}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI */
6581 {elGRW, LCMAP_UPPERCASE, 0x1FE2}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA */
6582 {elGRW, LCMAP_UPPERCASE, 0x1FE3}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA */
6583 {elGRW, LCMAP_UPPERCASE, 0x1FE4}, /* GREEK SMALL LETTER RHO WITH PSILI */
6584 {elGRW, LCMAP_UPPERCASE, 0x1FE6}, /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI */
6585 {elGRW, LCMAP_UPPERCASE, 0x1FE7}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI */
6586 {elGRW, LCMAP_UPPERCASE, 0x1FF6}, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI */
6588 {elGRW, LCMAP_UPPERCASE, 0x1F80,0x1F88}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI */
6589 {elGRW, LCMAP_UPPERCASE, 0x1F81,0x1F89}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI */
6590 {elGRW, LCMAP_UPPERCASE, 0x1F82,0x1F8A}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
6591 {elGRW, LCMAP_UPPERCASE, 0x1F83,0x1F8B}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
6592 {elGRW, LCMAP_UPPERCASE, 0x1F84,0x1F8C}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
6593 {elGRW, LCMAP_UPPERCASE, 0x1F85,0x1F8D}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
6594 {elGRW, LCMAP_UPPERCASE, 0x1F86,0x1F8E}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
6595 {elGRW, LCMAP_UPPERCASE, 0x1F87,0x1F8F}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
6597 {elGRW, LCMAP_LOWERCASE, 0x1F88,0x1F80}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */
6598 {elGRW, LCMAP_LOWERCASE, 0x1F89,0x1F81}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */
6599 {elGRW, LCMAP_LOWERCASE, 0x1F8A,0x1F82}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
6600 {elGRW, LCMAP_LOWERCASE, 0x1F8B,0x1F83}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
6601 {elGRW, LCMAP_LOWERCASE, 0x1F8C,0x1F84}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
6602 {elGRW, LCMAP_LOWERCASE, 0x1F8D,0x1F85}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
6603 {elGRW, LCMAP_LOWERCASE, 0x1F8E,0x1F86}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
6604 {elGRW, LCMAP_LOWERCASE, 0x1F8F,0x1F87}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
6606 {elGRW, LCMAP_UPPERCASE, 0x1F90,0x1F98}, /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI */
6607 {elGRW, LCMAP_UPPERCASE, 0x1F91,0x1F99}, /* GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI */
6608 {elGRW, LCMAP_UPPERCASE, 0x1F92,0x1F9A}, /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
6609 {elGRW, LCMAP_UPPERCASE, 0x1F93,0x1F9B}, /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
6610 {elGRW, LCMAP_UPPERCASE, 0x1F94,0x1F9C}, /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
6611 {elGRW, LCMAP_UPPERCASE, 0x1F95,0x1F9D}, /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
6612 {elGRW, LCMAP_UPPERCASE, 0x1F96,0x1F9E}, /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
6613 {elGRW, LCMAP_UPPERCASE, 0x1F97,0x1F9F}, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
6615 {elGRW, LCMAP_LOWERCASE, 0x1FA8,0x1FA0}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */
6616 {elGRW, LCMAP_LOWERCASE, 0x1FA9,0x1FA1}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */
6617 {elGRW, LCMAP_LOWERCASE, 0x1FAA,0x1FA2}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
6618 {elGRW, LCMAP_LOWERCASE, 0x1FAB,0x1FA3}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
6619 {elGRW, LCMAP_LOWERCASE, 0x1FAC,0x1FA4}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
6620 {elGRW, LCMAP_LOWERCASE, 0x1FAD,0x1FA5}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
6621 {elGRW, LCMAP_LOWERCASE, 0x1FAE,0x1FA6}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
6622 {elGRW, LCMAP_LOWERCASE, 0x1FAF,0x1FA7}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
6624 {elGRW, LCMAP_UPPERCASE, 0x1FB3,0x1FBC}, /* GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI */
6625 {elGRW, LCMAP_LOWERCASE, 0x1FBC,0x1FB3}, /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */
6626 {elGRW, LCMAP_UPPERCASE, 0x1FC3,0x1FCC}, /* GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI */
6627 {elGRW, LCMAP_LOWERCASE, 0x1FCC,0x1FC3}, /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */
6628 {elGRW, LCMAP_UPPERCASE, 0x1FF3,0x1FFC}, /* GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI */
6629 {elGRW, LCMAP_LOWERCASE, 0x1FFC,0x1FF3}, /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */
6631 {elGRW, LCMAP_UPPERCASE, 0x1FB2}, /* GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI */
6632 {elGRW, LCMAP_UPPERCASE, 0x1FB4}, /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI */
6633 {elGRW, LCMAP_UPPERCASE, 0x1FC2}, /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI */
6634 {elGRW, LCMAP_UPPERCASE, 0x1FC4}, /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI */
6635 {elGRW, LCMAP_UPPERCASE, 0x1FF2}, /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI */
6636 {elGRW, LCMAP_UPPERCASE, 0x1FF4}, /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI */
6638 {elGRW, LCMAP_UPPERCASE, 0x1FB7}, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI */
6639 {elGRW, LCMAP_UPPERCASE, 0x1FC7}, /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI */
6640 {elGRW, LCMAP_UPPERCASE, 0x1FF7}, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI */
6642 {elGRW, LCMAP_LOWERCASE, 0x03A3,0x03C3}, /* GREEK CAPITAL LETTER SIGMA */
6644 {ltLTW, LCMAP_LOWERCASE, 'J','j'}, /* LATIN CAPITAL LETTER J */
6645 {ltLTW, LCMAP_LOWERCASE, 0x012E,0x012F}, /* LATIN CAPITAL LETTER I WITH OGONEK */
6646 {ltLTW, LCMAP_LOWERCASE, 0x00CC,0x00EC}, /* LATIN CAPITAL LETTER I WITH GRAVE */
6647 {ltLTW, LCMAP_LOWERCASE, 0x00CD,0x00ED}, /* LATIN CAPITAL LETTER I WITH ACUTE */
6648 {ltLTW, LCMAP_LOWERCASE, 0x0128,0x0129}, /* LATIN CAPITAL LETTER I WITH TILDE */
6650 {enUSW, LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
6651 {ltLTW, LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
6652 {trTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
6653 {TRTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
6654 {azCyrlazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
6655 {azLatnazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
6657 {enUSW, LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
6658 {ltLTW, LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
6659 {trTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
6660 {TRTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
6661 {azCyrlazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
6662 {azLatnazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
6664 {enUSW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6665 {trTRW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6666 {TRTRW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6667 {azCyrlazW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6668 {azLatnazW, LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6670 {enUSW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
6671 {trTRW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
6672 {TRTRW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
6673 {azCyrlazW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
6674 {azLatnazW, LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
6677 if (!pLCMapStringEx)
6679 win_skip("LCMapStringEx not available\n");
6680 return;
6683 for (i = 0; i < ARRAY_SIZE(tests); i++) {
6684 memset(buffer, 0, sizeof(buffer));
6685 ret = pLCMapStringEx(tests[i].lang, tests[i].flags,
6686 &tests[i].ch, 1, buffer, ARRAY_SIZE(buffer), NULL, NULL, 0);
6687 ok(ret == 1, "expected 1, got %d for %04x for %s\n", ret, tests[i].ch,
6688 wine_dbgstr_w(tests[i].lang));
6689 exp = tests[i].exp ? tests[i].exp : tests[i].ch;
6690 ok(buffer[0] == exp || broken(buffer[0] != exp),
6691 "expected %04x, got %04x for %04x for %s\n",
6692 exp, buffer[0], tests[i].ch, wine_dbgstr_w(tests[i].lang));
6694 memset(buffer, 0, sizeof(buffer));
6695 ret = pLCMapStringEx(tests[i].lang, tests[i].flags|LCMAP_LINGUISTIC_CASING,
6696 &tests[i].ch, 1, buffer, ARRAY_SIZE(buffer), NULL, NULL, 0);
6697 ok(ret == 1, "expected 1, got %d for %04x for %s\n", ret, tests[i].ch,
6698 wine_dbgstr_w(tests[i].lang));
6699 exp = tests[i].exp_ling ? tests[i].exp_ling : exp;
6700 ok(buffer[0] == exp || broken(buffer[0] != exp),
6701 "expected %04x, got %04x for %04x for %s\n",
6702 exp, buffer[0], tests[i].ch, wine_dbgstr_w(tests[i].lang));
6706 static void test_NLSVersion(void)
6708 static const GUID guid_null = { 0 };
6709 static const GUID guid_def = { 0x000000001, 0x57ee, 0x1e5c, {0x00,0xb4,0xd0,0x00,0x0b,0xb1,0xe1,0x1e}};
6710 static const GUID guid_fr = { 0x000000003, 0x57ee, 0x1e5c, {0x00,0xb4,0xd0,0x00,0x0b,0xb1,0xe1,0x1e}};
6711 static const GUID guid_ja = { 0x000000046, 0x57ee, 0x1e5c, {0x00,0xb4,0xd0,0x00,0x0b,0xb1,0xe1,0x1e}};
6712 BOOL ret;
6713 NLSVERSIONINFOEX info;
6715 if (!pGetNLSVersion)
6717 win_skip( "GetNLSVersion not available\n" );
6718 return;
6721 SetLastError( 0xdeadbeef );
6722 memset( &info, 0xcc, sizeof(info) );
6723 info.dwNLSVersionInfoSize = offsetof( NLSVERSIONINFO, dwEffectiveId );
6724 ret = pGetNLSVersion( COMPARE_STRING, MAKELANGID( LANG_FRENCH, SUBLANG_FRENCH_CANADIAN ),
6725 (NLSVERSIONINFO *)&info );
6726 ok( ret, "GetNLSVersion failed err %u\n", GetLastError() );
6728 SetLastError( 0xdeadbeef );
6729 memset( &info, 0xcc, sizeof(info) );
6730 info.dwNLSVersionInfoSize = sizeof(info);
6731 ret = pGetNLSVersion( COMPARE_STRING, MAKELANGID( LANG_FRENCH, SUBLANG_FRENCH_CANADIAN ),
6732 (NLSVERSIONINFO *)&info );
6733 ok( ret || GetLastError() == ERROR_INSUFFICIENT_BUFFER /* < Vista */,
6734 "GetNLSVersion failed err %u\n", GetLastError() );
6735 if (ret)
6737 ok( info.dwEffectiveId == MAKELANGID( LANG_FRENCH, SUBLANG_FRENCH_CANADIAN ),
6738 "wrong id %x\n", info.dwEffectiveId );
6739 ok( IsEqualIID( &info.guidCustomVersion, &guid_fr ) ||
6740 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
6741 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
6744 SetLastError( 0xdeadbeef );
6745 info.dwNLSVersionInfoSize = 8;
6746 ret = pGetNLSVersion( COMPARE_STRING, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
6747 ok( !ret, "GetNLSVersion succeeded\n" );
6748 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %u\n", GetLastError() );
6750 SetLastError( 0xdeadbeef );
6751 info.dwNLSVersionInfoSize = sizeof(info);
6752 ret = pGetNLSVersion( 2, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
6753 ok( !ret, "GetNLSVersion succeeded\n" );
6754 ok( GetLastError() == ERROR_INVALID_FLAGS ||
6755 broken( GetLastError() == ERROR_INSUFFICIENT_BUFFER ), /* win2003 */
6756 "wrong error %u\n", GetLastError() );
6758 SetLastError( 0xdeadbeef );
6759 info.dwNLSVersionInfoSize = sizeof(info);
6760 ret = pGetNLSVersion( COMPARE_STRING, 0xdeadbeef, (NLSVERSIONINFO *)&info );
6761 ok( !ret, "GetNLSVersion succeeded\n" );
6762 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
6764 if (pGetNLSVersionEx)
6766 SetLastError( 0xdeadbeef );
6767 memset( &info, 0xcc, sizeof(info) );
6768 info.dwNLSVersionInfoSize = sizeof(info);
6769 ret = pGetNLSVersionEx( COMPARE_STRING, L"ja-JP", &info );
6770 ok( ret, "GetNLSVersionEx failed err %u\n", GetLastError() );
6771 ok( info.dwEffectiveId == MAKELANGID( LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN ),
6772 "wrong id %x\n", info.dwEffectiveId );
6773 ok( IsEqualIID( &info.guidCustomVersion, &guid_ja ) ||
6774 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
6775 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
6776 trace( "version %08x %08x %08x %s\n", info.dwNLSVersion, info.dwDefinedVersion, info.dwEffectiveId,
6777 debugstr_guid(&info.guidCustomVersion) );
6779 SetLastError( 0xdeadbeef );
6780 memset( &info, 0xcc, sizeof(info) );
6781 info.dwNLSVersionInfoSize = sizeof(info);
6782 ret = pGetNLSVersionEx( COMPARE_STRING, L"fr", &info );
6783 ok( !ret == !pIsValidLocaleName(L"fr"), "GetNLSVersionEx doesn't match IsValidLocaleName\n" );
6784 if (ret)
6786 ok( info.dwEffectiveId == MAKELANGID( LANG_FRENCH, SUBLANG_DEFAULT ),
6787 "wrong id %x\n", info.dwEffectiveId );
6788 ok( IsEqualIID( &info.guidCustomVersion, &guid_fr ) ||
6789 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
6790 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
6793 SetLastError( 0xdeadbeef );
6794 info.dwNLSVersionInfoSize = sizeof(info) - 1;
6795 ret = pGetNLSVersionEx( COMPARE_STRING, L"en-US", &info );
6796 ok( !ret, "GetNLSVersionEx succeeded\n" );
6797 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %u\n", GetLastError() );
6799 SetLastError( 0xdeadbeef );
6800 memset( &info, 0xcc, sizeof(info) );
6801 info.dwNLSVersionInfoSize = offsetof( NLSVERSIONINFO, dwEffectiveId );
6802 ret = pGetNLSVersionEx( COMPARE_STRING, L"en-US", &info );
6803 ok( ret, "GetNLSVersionEx failed err %u\n", GetLastError() );
6804 ok( info.dwEffectiveId == 0xcccccccc, "wrong id %x\n", info.dwEffectiveId );
6806 SetLastError( 0xdeadbeef );
6807 info.dwNLSVersionInfoSize = sizeof(info);
6808 ret = pGetNLSVersionEx( 2, L"en-US", &info );
6809 ok( !ret, "GetNLSVersionEx succeeded\n" );
6810 ok( GetLastError() == ERROR_INVALID_FLAGS, "wrong error %u\n", GetLastError() );
6812 SetLastError( 0xdeadbeef );
6813 info.dwNLSVersionInfoSize = sizeof(info);
6814 ret = pGetNLSVersionEx( COMPARE_STRING, L"foobar", &info );
6815 ok( !ret, "GetNLSVersionEx succeeded\n" );
6816 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
6818 SetLastError( 0xdeadbeef );
6819 memset( &info, 0xcc, sizeof(info) );
6820 info.dwNLSVersionInfoSize = sizeof(info);
6821 ret = pGetNLSVersionEx( COMPARE_STRING, L"zz-XX", &info );
6822 if (!ret) ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
6823 ok( !ret == !pIsValidLocaleName(L"zz-XX"), "GetNLSVersionEx doesn't match IsValidLocaleName\n" );
6824 if (ret)
6826 ok( info.dwEffectiveId == LOCALE_CUSTOM_UNSPECIFIED, "wrong id %x\n", info.dwEffectiveId );
6827 ok( IsEqualIID( &info.guidCustomVersion, &guid_def ),
6828 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
6831 SetLastError( 0xdeadbeef );
6832 memset( &info, 0xcc, sizeof(info) );
6833 info.dwNLSVersionInfoSize = sizeof(info);
6834 ret = pGetNLSVersionEx( COMPARE_STRING, LOCALE_NAME_INVARIANT, &info );
6835 ok( ret, "GetNLSVersionEx failed err %u\n", GetLastError() );
6836 if (ret)
6838 ok( info.dwEffectiveId == LOCALE_INVARIANT, "wrong id %x\n", info.dwEffectiveId );
6839 ok( IsEqualIID( &info.guidCustomVersion, &guid_def ) ||
6840 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
6841 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
6843 else ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
6845 else win_skip( "GetNLSVersionEx not available\n" );
6847 if (pIsValidNLSVersion)
6849 info.dwNLSVersionInfoSize = sizeof(info);
6850 pGetNLSVersion( COMPARE_STRING, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
6852 SetLastError( 0xdeadbeef );
6853 info.dwNLSVersionInfoSize = sizeof(info);
6854 ret = pIsValidNLSVersion( COMPARE_STRING, L"ja-JP", &info );
6855 ok( ret, "IsValidNLSVersion failed err %u\n", GetLastError() );
6856 ok( GetLastError() == 0xdeadbeef, "wrong error %u\n", GetLastError() );
6858 SetLastError( 0xdeadbeef );
6859 info.dwNLSVersionInfoSize = offsetof( NLSVERSIONINFO, dwEffectiveId );
6860 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6861 ok( ret, "IsValidNLSVersion failed err %u\n", GetLastError() );
6862 ok( GetLastError() == 0xdeadbeef, "wrong error %u\n", GetLastError() );
6864 SetLastError( 0xdeadbeef );
6865 info.dwNLSVersionInfoSize = sizeof(info);
6866 ret = pIsValidNLSVersion( 2, L"en-US", &info );
6867 ok( !ret, "IsValidNLSVersion succeeded\n" );
6868 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
6870 SetLastError( 0xdeadbeef );
6871 info.dwNLSVersionInfoSize = sizeof(info);
6872 ret = pIsValidNLSVersion( COMPARE_STRING, L"foobar", &info );
6873 ok( !ret, "IsValidNLSVersion succeeded\n" );
6874 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
6876 SetLastError( 0xdeadbeef );
6877 memset( &info, 0xcc, sizeof(info) );
6878 info.dwNLSVersionInfoSize = sizeof(info);
6879 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6880 ok( !ret, "IsValidNLSVersion succeeded\n" );
6881 ok( GetLastError() == ERROR_SUCCESS, "wrong error %u\n", GetLastError() );
6883 info.dwNLSVersionInfoSize = sizeof(info);
6884 pGetNLSVersion( COMPARE_STRING, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
6885 info.dwNLSVersion++;
6886 SetLastError( 0xdeadbeef );
6887 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6888 ok( ret, "IsValidNLSVersion failed err %u\n", GetLastError() );
6889 ok( GetLastError() == 0xdeadbeef, "wrong error %u\n", GetLastError() );
6891 info.dwNLSVersion += 0x100;
6892 SetLastError( 0xdeadbeef );
6893 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6894 ok( !ret, "IsValidNLSVersion succeeded\n" );
6895 ok( GetLastError() == 0, "wrong error %u\n", GetLastError() );
6897 info.dwNLSVersion -= 0x200;
6898 SetLastError( 0xdeadbeef );
6899 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6900 ok( !ret, "IsValidNLSVersion succeeded\n" );
6901 ok( GetLastError() == 0, "wrong error %u\n", GetLastError() );
6903 info.dwNLSVersion += 0x100;
6904 info.dwDefinedVersion += 0x100;
6905 SetLastError( 0xdeadbeef );
6906 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6907 ok( ret, "IsValidNLSVersion failed err %u\n", GetLastError() );
6908 ok( GetLastError() == 0xdeadbeef, "wrong error %u\n", GetLastError() );
6910 info.dwDefinedVersion -= 0x100;
6911 info.guidCustomVersion.Data1 = 0x123;
6912 SetLastError( 0xdeadbeef );
6913 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6914 ok( !ret, "IsValidNLSVersion succeeded\n" );
6915 ok( GetLastError() == 0, "wrong error %u\n", GetLastError() );
6917 info.guidCustomVersion = guid_null;
6918 SetLastError( 0xdeadbeef );
6919 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
6920 ok( ret, "IsValidNLSVersion failed err %u\n", GetLastError() );
6921 ok( GetLastError() == 0xdeadbeef, "wrong error %u\n", GetLastError() );
6923 else win_skip( "IsValidNLSVersion not available\n" );
6926 static void test_geo_name(void)
6928 WCHAR reg_name[32], buf[32], set_name[32], nation[32], region[32];
6929 BOOL have_name = FALSE, have_region = FALSE, have_nation = FALSE;
6930 DWORD size, type, name_size;
6931 LSTATUS status;
6932 GEOID geoid;
6933 BOOL bret;
6934 HKEY key;
6935 int ret;
6937 if (!pSetUserGeoName || !pGetUserDefaultGeoName)
6939 win_skip("GetUserDefaultGeoName / SetUserGeoName is not available, skipping test.\n");
6940 return;
6943 status = RegOpenKeyExA(HKEY_CURRENT_USER, "Control Panel\\International\\Geo", 0, KEY_READ | KEY_WRITE, &key);
6944 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
6946 size = sizeof(reg_name);
6947 if (!RegQueryValueExW(key, L"Name", NULL, &type, (BYTE *)reg_name, &size))
6948 have_name = TRUE;
6950 lstrcpyW(buf, L"QQ");
6951 RegSetValueExW(key, L"Name", 0, REG_SZ, (BYTE *)buf, (lstrlenW(buf) + 1) * sizeof(WCHAR));
6953 size = sizeof(reg_name);
6954 if ((ret = pGetUserDefaultGeoName(NULL, 0)) == 1)
6956 if (have_name)
6958 status = RegSetValueExW(key, L"Name", 0, REG_SZ, (BYTE *)reg_name, (lstrlenW(reg_name) + 1) * sizeof(*reg_name));
6959 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
6961 else
6963 RegDeleteValueW(key, L"Name");
6965 win_skip("Geo names are not available, skipping test.\n");
6966 return;
6969 size = sizeof(nation);
6970 if (!RegQueryValueExW(key, L"Nation", NULL, &type, (BYTE *)nation, &size))
6971 have_nation = TRUE;
6972 size = sizeof(region);
6973 if (!RegQueryValueExW(key, L"Region", NULL, &type, (BYTE *)region, &size))
6974 have_region = TRUE;
6976 SetLastError(0xdeadbeef);
6977 ret = pGetUserDefaultGeoName(NULL, 0);
6978 ok((ret == 3 || ret == 4) && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
6979 name_size = ret;
6981 SetLastError(0xdeadbeef);
6982 ret = pGetUserDefaultGeoName(buf, 0);
6983 ok(ret >= 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
6985 SetLastError(0xdeadbeef);
6986 ret = pGetUserDefaultGeoName(buf, 2);
6987 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
6989 SetLastError(0xdeadbeef);
6990 ret = pGetUserDefaultGeoName(NULL, 1);
6991 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
6993 SetLastError(0xdeadbeef);
6994 ret = pGetUserDefaultGeoName(NULL, name_size);
6995 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
6997 SetLastError(0xdeadbeef);
6998 ret = pGetUserDefaultGeoName(buf, name_size);
6999 ok(ret == name_size && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7000 ok(!lstrcmpW(buf, L"QQ"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7002 SetLastError(0xdeadbeef);
7003 bret = pSetUserGeoName(NULL);
7004 ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7006 lstrcpyW(set_name, L"QQ");
7007 SetLastError(0xdeadbeef);
7008 bret = pSetUserGeoName(set_name);
7009 ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7011 lstrcpyW(set_name, L"Xx");
7012 SetLastError(0xdeadbeef);
7013 bret = pSetUserGeoName(set_name);
7014 ok((bret && GetLastError() == 0xdeadbeef) || broken(bret && GetLastError() == 0),
7015 "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7017 SetLastError(0xdeadbeef);
7018 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7019 ok(ret == 4 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7020 ok(!lstrcmpW(buf, L"001"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7021 geoid = GetUserGeoID(GEOCLASS_REGION);
7022 ok(geoid == 39070, "Got unexpected geoid %u.\n", geoid);
7023 size = sizeof(buf);
7024 status = RegQueryValueExW(key, L"Name", NULL, &type, (BYTE *)buf, &size);
7025 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
7026 ok(type == REG_SZ, "Got unexpected type %#x.\n", type);
7027 ok(!lstrcmpW(buf, L"001"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7029 lstrcpyW(set_name, L"ar");
7030 SetLastError(0xdeadbeef);
7031 bret = pSetUserGeoName(set_name);
7032 ok((bret && GetLastError() == 0xdeadbeef) || broken(bret && GetLastError() == 0),
7033 "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7034 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7035 ok((ret == 3 && GetLastError() == 0xdeadbeef) || broken(ret == 3 && GetLastError() == 0),
7036 "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7037 ok(!lstrcmpW(buf, L"AR"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7038 geoid = GetUserGeoID(GEOCLASS_NATION);
7039 ok(geoid == 11, "Got unexpected geoid %u.\n", geoid);
7041 lstrcpyW(set_name, L"150");
7042 SetLastError(0xdeadbeef);
7043 bret = pSetUserGeoName(set_name);
7044 ok((bret && GetLastError() == 0xdeadbeef) || broken(bret && GetLastError() == 0),
7045 "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7046 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7047 ok((ret == 4 && GetLastError() == 0xdeadbeef) || broken(ret == 4 && GetLastError() == 0),
7048 "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7049 ok(!lstrcmpW(buf, L"150"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7050 geoid = GetUserGeoID(GEOCLASS_NATION);
7051 ok(geoid == 11, "Got unexpected geoid %u.\n", geoid);
7053 lstrcpyW(set_name, L"150a");
7054 SetLastError(0xdeadbeef);
7055 bret = pSetUserGeoName(set_name);
7056 ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7058 bret = SetUserGeoID(21242);
7059 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7060 SetLastError(0xdeadbeef);
7061 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7062 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7063 ok(!lstrcmpW(buf, L"XX"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7065 bret = SetUserGeoID(42483);
7066 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7067 SetLastError(0xdeadbeef);
7068 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7069 ok(ret == 4 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7070 ok(!lstrcmpW(buf, L"011"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7072 bret = SetUserGeoID(333);
7073 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7074 SetLastError(0xdeadbeef);
7075 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7076 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7077 ok(!lstrcmpW(buf, L"AN"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7079 RegDeleteValueW(key, L"Name");
7080 RegDeleteValueW(key, L"Region");
7081 lstrcpyW(buf, L"124");
7082 status = RegSetValueExW(key, L"Nation", 0, REG_SZ, (BYTE *)buf, (lstrlenW(buf) + 1) * sizeof(*buf));
7083 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
7084 SetLastError(0xdeadbeef);
7085 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7086 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7087 ok(!lstrcmpW(buf, L"JM"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7089 lstrcpyW(buf, L"333");
7090 status = RegSetValueExW(key, L"Region", 0, REG_SZ, (BYTE *)buf, (lstrlenW(buf) + 1) * sizeof(*buf));
7091 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
7092 SetLastError(0xdeadbeef);
7093 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7094 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7095 ok(!lstrcmpW(buf, L"JM"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7097 RegDeleteValueW(key, L"Nation");
7098 SetLastError(0xdeadbeef);
7099 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
7100 ok(ret == 4 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %u.\n", ret, GetLastError());
7101 ok(!lstrcmpW(buf, L"001"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
7103 /* Restore user geo data. */
7104 if (have_name)
7106 status = RegSetValueExW(key, L"Name", 0, REG_SZ, (BYTE *)reg_name, (lstrlenW(reg_name) + 1) * sizeof(*reg_name));
7107 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
7109 else
7111 RegDeleteValueW(key, L"Name");
7113 if (have_nation)
7115 status = RegSetValueExW(key, L"Nation", 0, REG_SZ, (BYTE *)nation, (lstrlenW(nation) + 1) * sizeof(*nation));
7116 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
7118 else
7120 RegDeleteValueW(key, L"Nation");
7122 if (have_region)
7124 status = RegSetValueExW(key, L"Region", 0, REG_SZ, (BYTE *)region, (lstrlenW(region) + 1) * sizeof(*region));
7125 ok(status == ERROR_SUCCESS, "Got unexpected status %#x.\n", status);
7127 else
7129 RegDeleteValueW(key, L"Region");
7132 RegCloseKey(key);
7135 START_TEST(locale)
7137 InitFunctionPointers();
7139 test_EnumTimeFormatsA();
7140 test_EnumTimeFormatsW();
7141 test_EnumDateFormatsA();
7142 test_GetLocaleInfoA();
7143 test_GetLocaleInfoW();
7144 test_GetLocaleInfoEx();
7145 test_GetTimeFormatA();
7146 test_GetTimeFormatEx();
7147 test_GetDateFormatA();
7148 test_GetDateFormatEx();
7149 test_GetDateFormatW();
7150 test_GetCurrencyFormatA(); /* Also tests the W version */
7151 test_GetNumberFormatA(); /* Also tests the W version */
7152 test_GetNumberFormatEx();
7153 test_CompareStringA();
7154 test_CompareStringW();
7155 test_CompareStringEx();
7156 test_LCMapStringA();
7157 test_LCMapStringW();
7158 test_LCMapStringEx();
7159 test_LocaleNameToLCID();
7160 test_FoldStringA();
7161 test_FoldStringW();
7162 test_ConvertDefaultLocale();
7163 test_EnumSystemLanguageGroupsA();
7164 test_EnumSystemLocalesEx();
7165 test_EnumLanguageGroupLocalesA();
7166 test_SetLocaleInfoA();
7167 test_EnumUILanguageA();
7168 test_GetCPInfo();
7169 test_GetStringTypeW();
7170 test_Idn();
7171 test_IsValidLocaleName();
7172 test_CompareStringOrdinal();
7173 test_GetGeoInfo();
7174 test_EnumSystemGeoID();
7175 test_invariant();
7176 test_GetSystemPreferredUILanguages();
7177 test_GetThreadPreferredUILanguages();
7178 test_GetUserPreferredUILanguages();
7179 test_FindNLSStringEx();
7180 test_FindStringOrdinal();
7181 test_SetThreadUILanguage();
7182 test_NormalizeString();
7183 test_SpecialCasing();
7184 test_NLSVersion();
7185 test_geo_name();
7186 /* this requires collation table patch to make it MS compatible */
7187 if (0) test_sorting();