wow64: In wow64_NtSetInformationToken forward TokenIntegrityLevel.
[wine.git] / dlls / kernel32 / tests / locale.c
blob5760d7c9eb101c80b1848440c6695f43d6e0dc9c
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 BOOLEAN (WINAPI *pRtlIsValidLocaleName)(const WCHAR *,ULONG);
67 static NTSTATUS (WINAPI *pRtlLcidToLocaleName)(LCID, UNICODE_STRING *,DWORD,BYTE);
68 static INT (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD);
69 static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
70 static INT (WINAPI *pIdnToNameprepUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
71 static INT (WINAPI *pIdnToAscii)(DWORD, LPCWSTR, INT, LPWSTR, INT);
72 static INT (WINAPI *pIdnToUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
73 static INT (WINAPI *pGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, INT);
74 static BOOL (WINAPI *pIsValidLocaleName)(LPCWSTR);
75 static INT (WINAPI *pResolveLocaleName)(LPCWSTR,LPWSTR,INT);
76 static INT (WINAPI *pCompareStringOrdinal)(const WCHAR *, INT, const WCHAR *, INT, BOOL);
77 static INT (WINAPI *pCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT,
78 LPNLSVERSIONINFO, LPVOID, LPARAM);
79 static INT (WINAPI *pGetGeoInfoA)(GEOID, GEOTYPE, LPSTR, INT, LANGID);
80 static INT (WINAPI *pGetGeoInfoW)(GEOID, GEOTYPE, LPWSTR, INT, LANGID);
81 static INT (WINAPI *pGetGeoInfoEx)(const WCHAR *, GEOTYPE, PWSTR, INT);
82 static INT (WINAPI *pGetUserDefaultGeoName)(LPWSTR, int);
83 static BOOL (WINAPI *pSetUserGeoName)(PWSTR);
84 static BOOL (WINAPI *pEnumSystemGeoID)(GEOCLASS, GEOID, GEO_ENUMPROC);
85 static BOOL (WINAPI *pGetSystemPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
86 static BOOL (WINAPI *pGetThreadPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
87 static BOOL (WINAPI *pGetUserPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
88 static WCHAR (WINAPI *pRtlUpcaseUnicodeChar)(WCHAR);
89 static INT (WINAPI *pGetNumberFormatEx)(LPCWSTR, DWORD, LPCWSTR, const NUMBERFMTW *, LPWSTR, int);
90 static INT (WINAPI *pFindNLSStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT, LPINT, LPNLSVERSIONINFO, LPVOID, LPARAM);
91 static void * (WINAPI *pNlsValidateLocale)(LCID*,ULONG);
92 static LANGID (WINAPI *pSetThreadUILanguage)(LANGID);
93 static LANGID (WINAPI *pGetThreadUILanguage)(VOID);
94 static INT (WINAPI *pNormalizeString)(NORM_FORM, LPCWSTR, INT, LPWSTR, INT);
95 static INT (WINAPI *pFindStringOrdinal)(DWORD, LPCWSTR lpStringSource, INT, LPCWSTR, INT, BOOL);
96 static BOOL (WINAPI *pGetNLSVersion)(NLS_FUNCTION,LCID,NLSVERSIONINFO*);
97 static BOOL (WINAPI *pGetNLSVersionEx)(NLS_FUNCTION,LPCWSTR,NLSVERSIONINFOEX*);
98 static DWORD (WINAPI *pIsValidNLSVersion)(NLS_FUNCTION,LPCWSTR,NLSVERSIONINFOEX*);
99 static BOOL (WINAPI *pIsNLSDefinedString)(NLS_FUNCTION,DWORD,NLSVERSIONINFO*,const WCHAR *,int);
100 static NTSTATUS (WINAPI *pRtlNormalizeString)(ULONG, LPCWSTR, INT, LPWSTR, INT*);
101 static NTSTATUS (WINAPI *pRtlIsNormalizedString)(ULONG, LPCWSTR, INT, BOOLEAN*);
102 static NTSTATUS (WINAPI *pNtGetNlsSectionPtr)(ULONG,ULONG,void*,void**,SIZE_T*);
103 static NTSTATUS (WINAPI *pNtInitializeNlsFiles)(void**,LCID*,LARGE_INTEGER*);
104 static NTSTATUS (WINAPI *pRtlGetLocaleFileMappingAddress)(void**,LCID*,LARGE_INTEGER*);
105 static void (WINAPI *pRtlInitCodePageTable)(USHORT*,CPTABLEINFO*);
106 static NTSTATUS (WINAPI *pRtlCustomCPToUnicodeN)(CPTABLEINFO*,WCHAR*,DWORD,DWORD*,const char*,DWORD);
107 static NTSTATUS (WINAPI *pRtlGetSystemPreferredUILanguages)(DWORD,ULONG,ULONG*,WCHAR*,ULONG*);
108 static NTSTATUS (WINAPI *pRtlGetThreadPreferredUILanguages)(DWORD,ULONG*,WCHAR*,ULONG*);
109 static NTSTATUS (WINAPI *pRtlGetUserPreferredUILanguages)(DWORD,ULONG,ULONG*,WCHAR*,ULONG*);
111 static void InitFunctionPointers(void)
113 HMODULE mod = GetModuleHandleA("kernel32");
115 #define X(f) p##f = (void*)GetProcAddress(mod, #f)
116 X(GetTimeFormatEx);
117 X(GetDateFormatEx);
118 X(EnumSystemLanguageGroupsA);
119 X(EnumLanguageGroupLocalesA);
120 X(LocaleNameToLCID);
121 X(LCIDToLocaleName);
122 X(LCMapStringEx);
123 X(IsValidLanguageGroup);
124 X(EnumUILanguagesA);
125 X(EnumSystemLocalesEx);
126 X(IdnToNameprepUnicode);
127 X(IdnToAscii);
128 X(IdnToUnicode);
129 X(GetLocaleInfoEx);
130 X(IsValidLocaleName);
131 X(ResolveLocaleName);
132 X(CompareStringOrdinal);
133 X(CompareStringEx);
134 X(GetGeoInfoA);
135 X(GetGeoInfoW);
136 X(GetGeoInfoEx);
137 X(GetUserDefaultGeoName);
138 X(SetUserGeoName);
139 X(EnumSystemGeoID);
140 X(GetSystemPreferredUILanguages);
141 X(GetThreadPreferredUILanguages);
142 X(GetUserPreferredUILanguages);
143 X(GetNumberFormatEx);
144 X(FindNLSStringEx);
145 X(SetThreadUILanguage);
146 X(GetThreadUILanguage);
147 X(NormalizeString);
148 X(FindStringOrdinal);
149 X(GetNLSVersion);
150 X(GetNLSVersionEx);
151 X(IsValidNLSVersion);
152 X(IsNLSDefinedString);
154 mod = GetModuleHandleA("kernelbase");
155 X(NlsValidateLocale);
157 mod = GetModuleHandleA("ntdll");
158 X(RtlUpcaseUnicodeChar);
159 X(RtlLocaleNameToLcid);
160 X(RtlIsValidLocaleName);
161 X(RtlLcidToLocaleName);
162 X(RtlNormalizeString);
163 X(RtlIsNormalizedString);
164 X(NtGetNlsSectionPtr);
165 X(NtInitializeNlsFiles);
166 X(RtlInitCodePageTable);
167 X(RtlCustomCPToUnicodeN);
168 X(RtlGetLocaleFileMappingAddress);
169 X(RtlGetSystemPreferredUILanguages);
170 X(RtlGetThreadPreferredUILanguages);
171 X(RtlGetUserPreferredUILanguages);
172 #undef X
175 #define eq(received, expected, label, type) \
176 ok((received) == (expected), "%s: got " type " instead of " type "\n", \
177 (label), (received), (expected))
179 #define BUFFER_SIZE 128
181 #define expect_str(r,s,e) expect_str_(__LINE__, r, s, e)
182 static void expect_str_(int line, int ret, const char *str, const char *expected)
184 if (ret)
186 ok_(__FILE__, line)(GetLastError() == 0xdeadbeef, "unexpected gle %lu\n", GetLastError());
187 ok_(__FILE__, line)(ret == strlen(expected) + 1, "Expected ret %Id, got %d\n", strlen(expected) + 1, ret);
188 if (str)
189 ok_(__FILE__, line)(strcmp(str, expected) == 0, "Expected '%s', got '%s'\n", expected, str);
191 else
192 ok_(__FILE__, line)(0, "expected success, got error %ld\n", GetLastError());
195 #define expect_err(r,s,e) expect_err_(__LINE__, r, s, e, #e)
196 static void expect_err_(int line, int ret, const char *str, DWORD err, const char* err_name)
198 ok_(__FILE__, line)(!ret && GetLastError() == err,
199 "Expected %s, got %ld and ret=%d\n", err_name, GetLastError(), ret);
200 if (str)
201 ok_(__FILE__, line)(strcmp(str, "pristine") == 0, "Expected a pristine buffer, got '%s'\n", str);
204 #define expect_wstr(r,s,e) expect_wstr_(__LINE__, r, s, e)
205 static void expect_wstr_(int line, int ret, const WCHAR *str, const WCHAR *expected)
207 if (ret)
209 ok_(__FILE__, line)(GetLastError() == 0xdeadbeef, "unexpected gle %lu\n", GetLastError());
210 ok_(__FILE__, line)(ret == wcslen(expected) + 1, "Expected ret %Id, got %d\n", wcslen(expected) + 1, ret);
211 if (str)
212 ok_(__FILE__, line)(wcscmp(str, expected) == 0, "Expected %s, got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(str));
214 else
215 ok_(__FILE__, line)(0, "expected success, got error %ld\n", GetLastError());
218 #define expect_werr(r,s,e) expect_werr_(__LINE__, r, s, e, #e)
219 static void expect_werr_(int line, int ret, const WCHAR *str, DWORD err, const char* err_name)
221 ok_(__FILE__, line)(!ret && GetLastError() == err,
222 "Expected %s, got %ld and ret=%d\n", err_name, GetLastError(), ret);
223 if (str)
224 ok_(__FILE__, line)(wcscmp(str, L"pristine") == 0, "Expected a pristine buffer, got %s\n", wine_dbgstr_w(str));
227 static int get_utf16( const WCHAR *src, unsigned int srclen, unsigned int *ch )
229 if (IS_HIGH_SURROGATE( src[0] ))
231 if (srclen <= 1) return 0;
232 if (!IS_LOW_SURROGATE( src[1] )) return 0;
233 *ch = 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff);
234 return 2;
236 if (IS_LOW_SURROGATE( src[0] )) return 0;
237 *ch = src[0];
238 return 1;
241 static int put_utf16( WCHAR *str, unsigned int c )
243 if (c < 0x10000)
245 *str = c;
246 return 1;
248 c -= 0x10000;
249 str[0] = 0xd800 | (c >> 10);
250 str[1] = 0xdc00 | (c & 0x3ff);
251 return 2;
254 #define NUO LOCALE_NOUSEROVERRIDE
256 static void test_GetLocaleInfoA(void)
258 int ret;
259 int len;
260 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
261 char buffer[BUFFER_SIZE];
262 char expected[BUFFER_SIZE];
263 DWORD val;
265 ok(lcid == 0x409, "wrong LCID calculated - %ld\n", lcid);
267 ret = GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (char*)&val, sizeof(val));
268 ok(ret, "got %d\n", ret);
269 ok(val == lcid, "got 0x%08lx\n", val);
271 /* en and ar use SUBLANG_NEUTRAL, but GetLocaleInfo assume SUBLANG_DEFAULT
272 Same is true for zh on pre-Vista, but on Vista and higher GetLocaleInfo
273 assumes SUBLANG_NEUTRAL for zh */
274 memset(expected, 0, ARRAY_SIZE(expected));
275 len = GetLocaleInfoA(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
276 SetLastError(0xdeadbeef);
277 memset(buffer, 0, ARRAY_SIZE(buffer));
278 ret = GetLocaleInfoA(LANG_ENGLISH, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
279 ok((ret == len) && !lstrcmpA(buffer, expected),
280 "got %d with '%s' (expected %d with '%s')\n",
281 ret, buffer, len, expected);
283 memset(expected, 0, ARRAY_SIZE(expected));
284 len = GetLocaleInfoA(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
285 if (len) {
286 SetLastError(0xdeadbeef);
287 memset(buffer, 0, ARRAY_SIZE(buffer));
288 ret = GetLocaleInfoA(LANG_ARABIC, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
289 ok((ret == len) && !lstrcmpA(buffer, expected),
290 "got %d with '%s' (expected %d with '%s')\n",
291 ret, buffer, len, expected);
293 else
294 win_skip("LANG_ARABIC not installed\n");
296 /* SUBLANG_DEFAULT is required for mlang.dll, but optional for GetLocaleInfo */
297 memset(expected, 0, ARRAY_SIZE(expected));
298 len = GetLocaleInfoA(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
299 SetLastError(0xdeadbeef);
300 memset(buffer, 0, ARRAY_SIZE(buffer));
301 ret = GetLocaleInfoA(LANG_GERMAN, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
302 ok((ret == len) && !lstrcmpA(buffer, expected),
303 "got %d with '%s' (expected %d with '%s')\n",
304 ret, buffer, len, expected);
306 len = GetLocaleInfoA(GetUserDefaultLCID(), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
307 ret = GetLocaleInfoA(LOCALE_NEUTRAL, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
308 ok( (ret == len) && !lstrcmpA(buffer, expected), "got %d with '%s' (expected %d with '%s')\n",
309 ret, buffer, len, expected);
310 ret = GetLocaleInfoA(LOCALE_CUSTOM_DEFAULT, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
311 ok( (ret == len) && !lstrcmpA(buffer, expected), "got %d with '%s' (expected %d with '%s')\n",
312 ret, buffer, len, expected);
313 ret = GetLocaleInfoA(LOCALE_CUSTOM_UNSPECIFIED, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
314 ok( (ret == len && !lstrcmpA(buffer, expected)) || broken(!ret), /* <= win8 */
315 "got %d with '%s' (expected %d with '%s')\n", ret, buffer, len, expected);
316 len = GetLocaleInfoA(GetUserDefaultUILanguage(), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
317 ret = GetLocaleInfoA(LOCALE_CUSTOM_UI_DEFAULT, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
318 if (ret) ok( (ret == len && !lstrcmpA(buffer, expected)),
319 "got %d with '%s' (expected %d with '%s')\n", ret, buffer, len, expected);
321 /* HTMLKit and "Font xplorer lite" expect GetLocaleInfoA to
322 * partially fill the buffer even if it is too short. See bug 637.
324 SetLastError(0xdeadbeef);
325 memset(buffer, 0, ARRAY_SIZE(buffer));
326 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 0);
327 ok(ret == 7 && !buffer[0], "Expected len=7, got %d\n", ret);
329 SetLastError(0xdeadbeef);
330 memset(buffer, 0, ARRAY_SIZE(buffer));
331 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 3);
332 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
333 "Expected ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
334 ok(!strcmp(buffer, "Mon"), "Expected 'Mon', got '%s'\n", buffer);
336 SetLastError(0xdeadbeef);
337 memset(buffer, 0, ARRAY_SIZE(buffer));
338 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 10);
339 ok(ret == 7, "Expected ret == 7, got %d, error %ld\n", ret, GetLastError());
340 ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer);
343 struct neutralsublang_name2_t {
344 WCHAR name[3];
345 WCHAR sname[15];
346 LCID lcid;
347 LCID lcid_broken;
348 WCHAR sname_broken[15];
351 static const struct neutralsublang_name2_t neutralsublang_names2[] = {
352 { {'a','r',0}, {'a','r','-','S','A',0},
353 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
354 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0},
355 MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
356 { {'d','e',0}, {'d','e','-','D','E',0},
357 MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
358 { {'e','n',0}, {'e','n','-','U','S',0},
359 MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
360 { {'e','s',0}, {'e','s','-','E','S',0},
361 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT),
362 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), SORT_DEFAULT) /* vista */,
363 {'e','s','-','E','S','_','t','r','a','d','n','l',0} },
364 { {'g','a',0}, {'g','a','-','I','E',0},
365 MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 0, {0} },
366 { {'i','t',0}, {'i','t','-','I','T',0},
367 MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
368 { {'m','s',0}, {'m','s','-','M','Y',0},
369 MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
370 { {'n','l',0}, {'n','l','-','N','L',0},
371 MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
372 { {'p','t',0}, {'p','t','-','B','R',0},
373 MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
374 { {'s','r',0}, {'h','r','-','H','R',0},
375 MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CROATIA), SORT_DEFAULT) },
376 { {'s','v',0}, {'s','v','-','S','E',0},
377 MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
378 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0},
379 MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
380 { {'z','h',0}, {'z','h','-','C','N',0},
381 MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
382 { {0} }
385 static void test_GetLocaleInfoW(void)
387 LCID lcid_en = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
388 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
389 LCID lcid_en_neut = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT);
390 WCHAR bufferW[80], buffer2W[80];
391 CHAR bufferA[80];
392 DWORD val;
393 DWORD ret;
394 INT i;
396 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
397 if (!ret) {
398 win_skip("GetLocaleInfoW() isn't implemented\n");
399 return;
402 ret = GetLocaleInfoW(lcid_en, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
403 ok(ret, "got %ld\n", ret);
404 ok(val == lcid_en, "got 0x%08lx\n", val);
406 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
407 if (ret)
409 static const WCHAR slangW[] = {'E','n','g','l','i','s','h',' ','(','U','n','i','t','e','d',' ',
410 'S','t','a','t','e','s',')',0};
411 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
412 static const WCHAR enW[] = {'e','n','-','U','S',0};
413 const struct neutralsublang_name2_t *ptr = neutralsublang_names2;
415 ok(!lstrcmpW(bufferW, enW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
417 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
418 ok(ret, "got %ld\n", ret);
419 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
420 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
422 skip("Non-English locale\n");
424 else
425 ok(!lstrcmpW(statesW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
427 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SLANGUAGE, bufferW, ARRAY_SIZE(bufferW));
428 ok(ret, "got %ld\n", ret);
429 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
430 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
432 skip("Non-English locale\n");
434 else
435 ok(!lstrcmpW(slangW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
437 while (*ptr->name)
439 LANGID langid;
440 LCID lcid;
442 /* make neutral lcid */
443 langid = MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(ptr->lcid)), SUBLANG_NEUTRAL);
444 lcid = MAKELCID(langid, SORT_DEFAULT);
446 val = 0;
447 GetLocaleInfoW(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
448 ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04lx, expected 0x%04lx\n",
449 wine_dbgstr_w(ptr->name), val, ptr->lcid);
451 /* now check LOCALE_SNAME */
452 GetLocaleInfoW(lcid, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
453 ok(!lstrcmpW(bufferW, ptr->sname) ||
454 (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))),
455 "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
456 ptr++;
459 else
460 win_skip("English neutral locale not supported\n");
462 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
463 if (!ret) {
464 win_skip("LANG_RUSSIAN locale data unavailable\n");
465 return;
467 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
468 bufferW, ARRAY_SIZE(bufferW));
469 if (!ret) {
470 win_skip("LOCALE_RETURN_GENITIVE_NAMES isn't supported\n");
471 return;
474 /* LOCALE_RETURN_GENITIVE_NAMES isn't supported for GetLocaleInfoA */
475 bufferA[0] = 'a';
476 SetLastError(0xdeadbeef);
477 ret = GetLocaleInfoA(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
478 bufferA, ARRAY_SIZE(bufferA));
479 ok(ret == 0, "LOCALE_RETURN_GENITIVE_NAMES should fail with GetLocaleInfoA\n");
480 ok(bufferA[0] == 'a', "Expected buffer to be untouched\n");
481 ok(GetLastError() == ERROR_INVALID_FLAGS,
482 "Expected ERROR_INVALID_FLAGS, got %lx\n", GetLastError());
484 bufferW[0] = 'a';
485 SetLastError(0xdeadbeef);
486 ret = GetLocaleInfoW(lcid_ru, LOCALE_RETURN_GENITIVE_NAMES, bufferW, ARRAY_SIZE(bufferW));
487 ok(ret == 0,
488 "LOCALE_RETURN_GENITIVE_NAMES itself doesn't return anything, got %ld\n", ret);
489 ok(bufferW[0] == 'a', "Expected buffer to be untouched\n");
490 ok(GetLastError() == ERROR_INVALID_FLAGS,
491 "Expected ERROR_INVALID_FLAGS, got %lx\n", GetLastError());
493 /* yes, test empty 13 month entry too */
494 for (i = 0; i < 12; i++) {
495 bufferW[0] = 0;
496 ret = GetLocaleInfoW(lcid_ru, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
497 bufferW, ARRAY_SIZE(bufferW));
498 ok(ret, "Expected non zero result\n");
499 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %ld, length %d\n",
500 ret, lstrlenW(bufferW));
501 buffer2W[0] = 0;
502 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
503 ok(ret, "Expected non zero result\n");
504 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %ld, length %d\n",
505 ret, lstrlenW(buffer2W));
507 ok(lstrcmpW(bufferW, buffer2W) != 0,
508 "Expected genitive name to differ, got the same for month %d\n", i+1);
510 /* for locale without genitive names nominative returned in both cases */
511 bufferW[0] = 0;
512 ret = GetLocaleInfoW(lcid_en, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
513 bufferW, ARRAY_SIZE(bufferW));
514 ok(ret, "Expected non zero result\n");
515 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %ld, length %d\n",
516 ret, lstrlenW(bufferW));
517 buffer2W[0] = 0;
518 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
519 ok(ret, "Expected non zero result\n");
520 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %ld, length %d\n",
521 ret, lstrlenW(buffer2W));
523 ok(lstrcmpW(bufferW, buffer2W) == 0,
524 "Expected same names, got different for month %d\n", i+1);
528 static void test_GetTimeFormatA(void)
530 int ret;
531 SYSTEMTIME curtime;
532 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
533 char buffer[BUFFER_SIZE];
535 SetLastError(0xdeadbeef);
537 /* Invalid time */
538 memset(&curtime, 2, sizeof(SYSTEMTIME));
539 strcpy(buffer, "pristine");
540 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, "tt HH':'mm'@'ss", buffer, ARRAY_SIZE(buffer));
541 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
542 SetLastError(0xdeadbeef);
544 /* Valid time */
545 curtime.wHour = 8;
546 curtime.wMinute = 56;
547 curtime.wSecond = 13;
548 curtime.wMilliseconds = 22;
549 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, "tt HH':'mm'@'ss", buffer, ARRAY_SIZE(buffer));
550 expect_str(ret, buffer, "AM 08:56@13");
552 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
553 strcpy(buffer, "pristine");
554 ret = GetTimeFormatA(lcid, NUO|TIME_FORCE24HOURFORMAT, &curtime, "HH", buffer, ARRAY_SIZE(buffer));
555 expect_err(ret, buffer, ERROR_INVALID_FLAGS);
556 SetLastError(0xdeadbeef);
558 /* Insufficient buffer */
559 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, " HH", buffer, 2);
560 /* The content of the buffer depends on implementation details:
561 * it may be " ristine" or " 0ristine" or unchanged and cannot be relied on.
563 expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
564 SetLastError(0xdeadbeef);
566 /* Calculate length only */
567 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, "tt HH':'mm'@'ss", NULL, 0);
568 expect_str(ret, NULL, "AM 08:56@13");
570 /* TIME_NOMINUTESORSECONDS, default format */
571 ret = GetTimeFormatA(lcid, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
572 expect_str(ret, buffer, "8 AM");
574 /* TIME_NOMINUTESORSECONDS/complex format */
575 ret = GetTimeFormatA(lcid, TIME_NOMINUTESORSECONDS, &curtime, "m1s2m3s4", buffer, ARRAY_SIZE(buffer));
576 expect_str(ret, buffer, "");
578 /* TIME_NOSECONDS/Default format */
579 ret = GetTimeFormatA(lcid, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
580 expect_str(ret, buffer, "8:56 AM");
582 /* TIME_NOSECONDS */
583 strcpy(buffer, "pristine"); /* clear previous identical result */
584 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, "h:m:s tt", buffer, ARRAY_SIZE(buffer));
585 expect_str(ret, buffer, "8:56 AM");
587 /* Multiple delimiters */
588 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, "h.@:m.@:s.@:tt", buffer, ARRAY_SIZE(buffer));
589 expect_str(ret, buffer, "8.@:56AM");
591 /* Duplicate tokens */
592 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, "s1s2s3", buffer, ARRAY_SIZE(buffer));
593 expect_str(ret, buffer, "");
595 /* AM time marker */
596 ret = GetTimeFormatA(lcid, 0, &curtime, "t/tt", buffer, ARRAY_SIZE(buffer));
597 expect_str(ret, buffer, "A/AM");
599 /* PM time marker */
600 curtime.wHour = 13;
601 ret = GetTimeFormatA(lcid, 0, &curtime, "t/tt", buffer, ARRAY_SIZE(buffer));
602 expect_str(ret, buffer, "P/PM");
604 /* TIME_NOTIMEMARKER: removes text around time marker token */
605 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, "h1t2tt3m", buffer, ARRAY_SIZE(buffer));
606 expect_str(ret, buffer, "156");
608 /* TIME_FORCE24HOURFORMAT */
609 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, "h:m:s tt", buffer, ARRAY_SIZE(buffer));
610 expect_str(ret, buffer, "13:56:13 PM");
612 /* TIME_FORCE24HOURFORMAT doesn't add time marker */
613 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, "h:m:s", buffer, ARRAY_SIZE(buffer));
614 expect_str(ret, buffer, "13:56:13");
616 /* 24 hrs, leading 0 */
617 curtime.wHour = 14; /* change this to 14 or 2pm */
618 curtime.wMinute = 5;
619 curtime.wSecond = 3;
620 ret = GetTimeFormatA(lcid, 0, &curtime, "h hh H HH m mm s ss t tt", buffer, ARRAY_SIZE(buffer));
621 expect_str(ret, buffer, "2 02 14 14 5 05 3 03 P PM");
623 /* "hh" and "HH" */
624 curtime.wHour = 0;
625 ret = GetTimeFormatA(lcid, 0, &curtime, "h/H/hh/HH", buffer, ARRAY_SIZE(buffer));
626 expect_str(ret, buffer, "12/0/12/00");
628 /* non-zero flags should fail with format, doesn't */
629 ret = GetTimeFormatA(lcid, 0, &curtime, "h:m:s tt", buffer, ARRAY_SIZE(buffer));
630 expect_str(ret, buffer, "12:5:3 AM");
632 /* try to convert formatting strings with more than two letters
633 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
634 * NOTE: We expect any letter for which there is an upper case value
635 * we should see a replacement. For letters that DO NOT have
636 * upper case values we should see NO REPLACEMENT.
638 curtime.wHour = 8;
639 curtime.wMinute = 56;
640 curtime.wSecond = 13;
641 curtime.wMilliseconds = 22;
642 ret = GetTimeFormatA(lcid, 0, &curtime, "h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS", buffer, ARRAY_SIZE(buffer));
643 expect_str(ret, buffer, "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
645 /* Don't write to buffer if len is 0 */
646 strcpy(buffer, "pristine");
647 ret = GetTimeFormatA(lcid, 0, &curtime, "h:mm", buffer, 0);
648 expect_str(ret, NULL, "8:56");
649 ok(strcmp(buffer, "pristine") == 0, "Expected a pristine buffer, got '%s'\n", buffer);
651 /* "'" preserves tokens */
652 ret = GetTimeFormatA(lcid, 0, &curtime, "h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'", buffer, ARRAY_SIZE(buffer));
653 expect_str(ret, buffer, "8 h 8 H 08 HH 56 m 13 s A t AM tt");
655 /* invalid quoted string */
656 ret = GetTimeFormatA(lcid, 0, &curtime, "'''", buffer, ARRAY_SIZE(buffer));
657 expect_str(ret, buffer, "'");
659 /* check that MSDN's suggested single quotation usage works as expected */
660 strcpy(buffer, "pristine"); /* clear previous identical result */
661 ret = GetTimeFormatA(lcid, 0, &curtime, "''''", buffer, ARRAY_SIZE(buffer));
662 expect_str(ret, buffer, "'");
664 /* Normal use */
665 ret = GetTimeFormatA(lcid, 0, &curtime, "''HHHHHH", buffer, ARRAY_SIZE(buffer));
666 expect_str(ret, buffer, "08");
668 /* and test for normal use of the single quotation mark */
669 ret = GetTimeFormatA(lcid, 0, &curtime, "'''HHHHHH'", buffer, ARRAY_SIZE(buffer));
670 expect_str(ret, buffer, "'HHHHHH");
672 /* Odd use */
673 strcpy(buffer, "pristine"); /* clear previous identical result */
674 ret = GetTimeFormatA(lcid, 0, &curtime, "'''HHHHHH", buffer, ARRAY_SIZE(buffer));
675 expect_str(ret, buffer, "'HHHHHH");
677 /* TIME_NOTIMEMARKER drops literals too */
678 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, "'123'tt", buffer, ARRAY_SIZE(buffer));
679 expect_str(ret, buffer, "");
681 /* Invalid time */
682 curtime.wHour = 25;
683 strcpy(buffer, "pristine");
684 ret = GetTimeFormatA(lcid, 0, &curtime, "'123'tt", buffer, ARRAY_SIZE(buffer));
685 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
686 SetLastError(0xdeadbeef);
688 /* Invalid date */
689 curtime.wHour = 12;
690 curtime.wMonth = 60;
691 ret = GetTimeFormatA(lcid, 0, &curtime, "h:m:s", buffer, ARRAY_SIZE(buffer));
692 expect_str(ret, buffer, "12:56:13");
694 /* The ANSI string may be longer than the Unicode one.
695 * In particular, in the Japanese code page, "\x93\xfa" = L"\x65e5".
698 lcid = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT);
700 ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 5);
701 expect_str(ret, buffer, "12\x93\xfa"); /* only 3+1 WCHARs */
703 ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 4);
704 expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
705 SetLastError(0xdeadbeef);
707 ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", NULL, 0);
708 expect_str(ret, NULL, "12\x93\xfa");
710 strcpy(buffer, "pristine"); /* clear previous identical result */
711 ret = GetTimeFormatA(lcid, 0, &curtime, "h\x93\xfa", buffer, 0);
712 expect_str(ret, NULL, "12\x93\xfa");
713 ok(strcmp(buffer, "pristine") == 0, "Expected a pristine buffer, got '%s'\n", buffer);
716 static void test_GetTimeFormatEx(void)
718 int ret;
719 SYSTEMTIME curtime;
720 WCHAR buffer[BUFFER_SIZE];
722 if (!pGetTimeFormatEx)
724 win_skip("GetTimeFormatEx not supported\n");
725 return;
728 SetLastError(0xdeadbeef);
730 /* Invalid time */
731 memset(&curtime, 2, sizeof(SYSTEMTIME));
732 wcscpy(buffer, L"pristine");
733 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, L"tt HH':'mm'@'ss", buffer, ARRAY_SIZE(buffer));
734 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
735 SetLastError(0xdeadbeef);
737 /* Valid time */
738 curtime.wHour = 8;
739 curtime.wMinute = 56;
740 curtime.wSecond = 13;
741 curtime.wMilliseconds = 22;
742 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, L"tt HH':'mm'@'ss", buffer, ARRAY_SIZE(buffer));
743 expect_wstr(ret, buffer, L"AM 08:56@13");
745 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
746 wcscpy(buffer, L"pristine");
747 ret = pGetTimeFormatEx(localeW, NUO|TIME_FORCE24HOURFORMAT, &curtime, L"HH", buffer, ARRAY_SIZE(buffer));
748 expect_werr(ret, buffer, ERROR_INVALID_FLAGS);
749 SetLastError(0xdeadbeef);
751 /* Insufficient buffer */
752 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, L" tt", buffer, 2);
753 /* there is no guarantee on the buffer content, see GetTimeFormatA() */
754 expect_werr(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
755 SetLastError(0xdeadbeef);
757 /* Calculate length only */
758 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, L"tt HH':'mm'@'ss", NULL, 0);
759 expect_wstr(ret, NULL, L"AM 08:56@13");
761 /* TIME_NOMINUTESORSECONDS, default format */
762 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
763 expect_wstr(ret, buffer, L"8 AM");
765 /* TIME_NOMINUTESORSECONDS/complex format */
766 wcscpy(buffer, L"pristine");
767 ret = pGetTimeFormatEx(localeW, TIME_NOMINUTESORSECONDS, &curtime, L"m1s2m3s4", buffer, ARRAY_SIZE(buffer));
768 expect_wstr(ret, buffer, L"");
770 /* TIME_NOSECONDS/Default format */
771 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
772 expect_wstr(ret, buffer, L"8:56 AM");
774 /* TIME_NOSECONDS */
775 wcscpy(buffer, L"pristine"); /* clear previous identical result */
776 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, L"h:m:s tt", buffer, ARRAY_SIZE(buffer));
777 expect_wstr(ret, buffer, L"8:56 AM");
779 /* Multiple delimiters */
780 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, L"h.@:m.@:s.@:tt", buffer, ARRAY_SIZE(buffer));
781 expect_wstr(ret, buffer, L"8.@:56AM");
783 /* Duplicate tokens */
784 wcscpy(buffer, L"pristine");
785 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, L"s1s2s3", buffer, ARRAY_SIZE(buffer));
786 expect_wstr(ret, buffer, L"");
788 /* AM time marker */
789 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"t/tt", buffer, ARRAY_SIZE(buffer));
790 expect_wstr(ret, buffer, L"A/AM");
792 /* PM time marker */
793 curtime.wHour = 13;
794 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"t/tt", buffer, ARRAY_SIZE(buffer));
795 expect_wstr(ret, buffer, L"P/PM");
797 /* TIME_NOTIMEMARKER: removes text around time marker token */
798 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, L"h1t2tt3m", buffer, ARRAY_SIZE(buffer));
799 expect_wstr(ret, buffer, L"156");
801 /* TIME_FORCE24HOURFORMAT */
802 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, L"h:m:s tt", buffer, ARRAY_SIZE(buffer));
803 expect_wstr(ret, buffer, L"13:56:13 PM");
805 /* TIME_FORCE24HOURFORMAT doesn't add time marker */
806 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, L"h:m:s", buffer, ARRAY_SIZE(buffer));
807 expect_wstr(ret, buffer, L"13:56:13");
809 /* 24 hrs, leading 0 */
810 curtime.wHour = 14; /* change this to 14 or 2pm */
811 curtime.wMinute = 5;
812 curtime.wSecond = 3;
813 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"h hh H HH m mm s ss t tt", buffer, ARRAY_SIZE(buffer));
814 expect_wstr(ret, buffer, L"2 02 14 14 5 05 3 03 P PM");
816 /* "hh" and "HH" */
817 curtime.wHour = 0;
818 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"h/H/hh/HH", buffer, ARRAY_SIZE(buffer));
819 expect_wstr(ret, buffer, L"12/0/12/00");
821 /* non-zero flags should fail with format, doesn't */
822 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"h:m:s tt", buffer, ARRAY_SIZE(buffer));
823 expect_wstr(ret, buffer, L"12:5:3 AM");
825 /* try to convert formatting strings with more than two letters
826 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
827 * NOTE: We expect any letter for which there is an upper case value
828 * we should see a replacement. For letters that DO NOT have
829 * upper case values we should see NO REPLACEMENT.
831 curtime.wHour = 8;
832 curtime.wMinute = 56;
833 curtime.wSecond = 13;
834 curtime.wMilliseconds = 22;
835 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS", buffer, ARRAY_SIZE(buffer));
836 expect_wstr(ret, buffer, L"8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
838 /* Don't write to buffer if len is 0 */
839 wcscpy(buffer, L"pristine");
840 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"h:mm", buffer, 0);
841 expect_wstr(ret, NULL, L"8:56");
842 ok(wcscmp(buffer, L"pristine") == 0, "Expected a pristine buffer, got %s\n", wine_dbgstr_w(buffer));
844 /* "'" preserves tokens */
845 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'", buffer, ARRAY_SIZE(buffer));
846 expect_wstr(ret, buffer, L"8 h 8 H 08 HH 56 m 13 s A t AM tt");
848 /* invalid quoted string */
849 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"'''", buffer, ARRAY_SIZE(buffer));
850 expect_wstr(ret, buffer, L"'");
852 /* check that MSDN's suggested single quotation usage works as expected */
853 wcscpy(buffer, L"pristine"); /* clear previous identical result */
854 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"''''", buffer, ARRAY_SIZE(buffer));
855 expect_wstr(ret, buffer, L"'");
857 /* Normal use */
858 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"''HHHHHH", buffer, ARRAY_SIZE(buffer));
859 expect_wstr(ret, buffer, L"08");
861 /* and test for normal use of the single quotation mark */
862 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"'''HHHHHH'", buffer, ARRAY_SIZE(buffer));
863 expect_wstr(ret, buffer, L"'HHHHHH");
865 /* Odd use */
866 wcscpy(buffer, L"pristine"); /* clear previous identical result */
867 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"'''HHHHHH", buffer, ARRAY_SIZE(buffer));
868 expect_wstr(ret, buffer, L"'HHHHHH");
870 /* TIME_NOTIMEMARKER drops literals too */
871 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, L"'123'tt", buffer, ARRAY_SIZE(buffer));
872 expect_wstr(ret, buffer, L"");
874 /* Invalid time */
875 curtime.wHour = 25;
876 wcscpy(buffer, L"pristine");
877 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"'123'tt", buffer, ARRAY_SIZE(buffer));
878 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
879 SetLastError(0xdeadbeef);
881 /* Invalid date */
882 curtime.wHour = 12;
883 curtime.wMonth = 60;
884 ret = pGetTimeFormatEx(localeW, 0, &curtime, L"h:m:s", buffer, ARRAY_SIZE(buffer));
885 expect_wstr(ret, buffer, L"12:56:13");
888 static void test_GetDateFormatA(void)
890 int ret;
891 SYSTEMTIME curtime;
892 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
893 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
894 LCID lcid_ja = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT);
895 char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE];
896 char short_day[10], month[10], genitive_month[10];
898 SetLastError(0xdeadbeef);
900 /* Invalid time */
901 memset(&curtime, 2, sizeof(SYSTEMTIME));
902 strcpy(buffer, "pristine");
903 ret = GetDateFormatA(lcid, 0, &curtime, "ddd',' MMM dd yy", buffer, ARRAY_SIZE(buffer));
904 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
905 SetLastError(0xdeadbeef);
907 /* Simple case */
908 curtime.wYear = 2002;
909 curtime.wMonth = 5;
910 curtime.wDay = 4;
911 curtime.wDayOfWeek = 3;
912 ret = GetDateFormatA(lcid, 0, &curtime, "ddd',' MMM dd yy", buffer, ARRAY_SIZE(buffer));
913 expect_str(ret, buffer, "Sat, May 04 02");
915 /* Same as above but with LOCALE_NOUSEROVERRIDE */
916 strcpy(buffer, "pristine");
917 ret = GetDateFormatA(lcid, NUO, &curtime, "ddd',' MMM dd yy", buffer, ARRAY_SIZE(buffer));
918 expect_err(ret, buffer, ERROR_INVALID_FLAGS);
919 SetLastError(0xdeadbeef);
921 /* Format containing "'" */
922 ret = GetDateFormatA(lcid, 0, &curtime, "ddd',' MMM dd yy", buffer, ARRAY_SIZE(buffer));
923 expect_str(ret, buffer, "Sat, May 04 02");
925 /* Invalid time */
926 curtime.wHour = 36;
927 ret = GetDateFormatA(lcid, 0, &curtime, "ddd',' MMM dd ''''yy", buffer, ARRAY_SIZE(buffer));
928 expect_str(ret, buffer, "Sat, May 04 '02");
930 /* Get size only */
931 ret = GetDateFormatA(lcid, 0, &curtime, "ddd',' MMM dd ''''yy", NULL, 0);
932 expect_str(ret, NULL, "Sat, May 04 '02");
934 /* Buffer too small */
935 strcpy(buffer, "pristine");
936 ret = GetDateFormatA(lcid, 0, &curtime, "ddd", buffer, 2);
937 /* there is no guarantee on the buffer content, see GetTimeFormatA() */
938 expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
939 SetLastError(0xdeadbeef);
941 /* Default to DATE_SHORTDATE */
942 ret = GetDateFormatA(lcid, NUO, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
943 expect_str(ret, buffer, "5/4/2002");
945 /* DATE_LONGDATE */
946 ret = GetDateFormatA(lcid, NUO|DATE_LONGDATE, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
947 ok(ret, "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
948 ok(strcmp(buffer, "Saturday, May 04, 2002") == 0 ||
949 strcmp(buffer, "Saturday, May 4, 2002") == 0 /* Win 8 */,
950 "got an unexpected date string '%s'\n", buffer);
952 /* test for expected DATE_YEARMONTH behavior with null format */
953 /* NT4 returns ERROR_INVALID_FLAGS for DATE_YEARMONTH */
954 strcpy(buffer, "pristine");
955 ret = GetDateFormatA(lcid, NUO|DATE_YEARMONTH, &curtime, "ddd',' MMM dd ''''yy", buffer, ARRAY_SIZE(buffer));
956 expect_err(ret, buffer, ERROR_INVALID_FLAGS);
957 SetLastError(0xdeadbeef);
959 /* Test that using invalid DATE_* flags results in the correct error */
960 /* and return values */
961 ret = GetDateFormatA(lcid, DATE_YEARMONTH|DATE_SHORTDATE|DATE_LONGDATE, &curtime, "m/d/y", buffer, ARRAY_SIZE(buffer));
962 expect_err(ret, NULL, ERROR_INVALID_FLAGS);
963 SetLastError(0xdeadbeef);
965 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddMMMM", buffer, ARRAY_SIZE(buffer));
966 if (!ret)
968 win_skip("LANG_RUSSIAN locale data unavailable\n");
969 return;
972 /* month part should be in genitive form */
973 strcpy(genitive_month, buffer + 2);
974 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMM", buffer, ARRAY_SIZE(buffer));
975 ok(ret, "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
976 strcpy(month, buffer);
977 ok(strcmp(genitive_month, month) != 0, "Expected different month forms\n");
979 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddd", buffer, ARRAY_SIZE(buffer));
980 ok(ret, "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
981 strcpy(short_day, buffer);
983 ret = GetDateFormatA(lcid_ru, 0, &curtime, "dd MMMMddd dd", buffer, ARRAY_SIZE(buffer));
984 sprintf(Expected, "04 %s%s 04", genitive_month, short_day);
985 expect_str(ret, buffer, Expected);
987 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMMddd dd", buffer, ARRAY_SIZE(buffer));
988 sprintf(Expected, "%s%s 04", month, short_day);
989 expect_str(ret, buffer, Expected);
991 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMMddd", buffer, ARRAY_SIZE(buffer));
992 sprintf(Expected, "%s%s", month, short_day);
993 expect_str(ret, buffer, Expected);
995 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMMdd", buffer, ARRAY_SIZE(buffer));
996 sprintf(Expected, "%s04", genitive_month);
997 expect_str(ret, buffer, Expected);
999 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMMdd ddd", buffer, ARRAY_SIZE(buffer));
1000 sprintf(Expected, "%s04 %s", genitive_month, short_day);
1001 expect_str(ret, buffer, Expected);
1003 ret = GetDateFormatA(lcid_ru, 0, &curtime, "dd dddMMMM", buffer, ARRAY_SIZE(buffer));
1004 sprintf(Expected, "04 %s%s", short_day, month);
1005 expect_str(ret, buffer, Expected);
1007 ret = GetDateFormatA(lcid_ru, 0, &curtime, "dd dddMMMM ddd MMMMdd", buffer, ARRAY_SIZE(buffer));
1008 sprintf(Expected, "04 %s%s %s %s04", short_day, month, short_day, genitive_month);
1009 expect_str(ret, buffer, Expected);
1011 /* with literal part */
1012 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddd',' MMMM dd", buffer, ARRAY_SIZE(buffer));
1013 sprintf(Expected, "%s, %s 04", short_day, genitive_month);
1014 expect_str(ret, buffer, Expected);
1016 /* The ANSI string may be longer than the Unicode one.
1017 * In particular, in the Japanese code page, "\x93\xfa" = L"\x65e5".
1018 * See the corresponding GetDateFormatW() test.
1021 ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", buffer, 4);
1022 expect_str(ret, buffer, "4\x93\xfa"); /* only 2+1 WCHARs */
1024 ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", buffer, 3);
1025 expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
1026 SetLastError(0xdeadbeef);
1028 strcpy(buffer, "pristine"); /* clear previous identical result */
1029 ret = GetDateFormatA(lcid_ja, 0, &curtime, "d\x93\xfa", NULL, 0);
1030 expect_str(ret, NULL, "4\x93\xfa");
1033 static void test_GetDateFormatEx(void)
1035 int ret;
1036 SYSTEMTIME curtime;
1037 WCHAR buffer[BUFFER_SIZE];
1039 if (!pGetDateFormatEx)
1041 win_skip("GetDateFormatEx not supported\n");
1042 return;
1045 SetLastError(0xdeadbeef);
1047 /* If flags are set, then format must be NULL */
1048 wcscpy(buffer, L"pristine");
1049 ret = pGetDateFormatEx(localeW, DATE_LONGDATE, NULL, L"", buffer, ARRAY_SIZE(buffer), NULL);
1050 expect_werr(ret, buffer, ERROR_INVALID_FLAGS);
1051 SetLastError(0xdeadbeef);
1053 /* NULL buffer, len > 0 */
1054 wcscpy(buffer, L"pristine");
1055 ret = pGetDateFormatEx(localeW, 0, NULL, L"", NULL, ARRAY_SIZE(buffer), NULL);
1056 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1057 SetLastError(0xdeadbeef);
1059 /* NULL buffer, len == 0 */
1060 ret = pGetDateFormatEx(localeW, 0, NULL, L"", NULL, 0, NULL);
1061 expect_wstr(ret, NULL, L"");
1063 /* Invalid flag combination */
1064 wcscpy(buffer, L"pristine");
1065 ret = pGetDateFormatEx(localeW, DATE_LONGDATE|DATE_SHORTDATE, NULL,
1066 L"", NULL, 0, NULL);
1067 expect_werr(ret, buffer, ERROR_INVALID_FLAGS);
1068 SetLastError(0xdeadbeef);
1070 /* Incorrect DOW and time */
1071 curtime.wYear = 2002;
1072 curtime.wMonth = 10;
1073 curtime.wDay = 23;
1074 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1075 curtime.wHour = 65432; /* Invalid */
1076 curtime.wMinute = 34512; /* Invalid */
1077 curtime.wSecond = 65535; /* Invalid */
1078 curtime.wMilliseconds = 12345;
1079 ret = pGetDateFormatEx(localeW, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer), NULL);
1080 expect_wstr(ret, buffer, L"Wednesday 23 October 2002");
1082 curtime.wYear = 2002;
1083 curtime.wMonth = 10;
1084 curtime.wDay = 23;
1085 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1086 curtime.wHour = 65432; /* Invalid */
1087 curtime.wMinute = 34512; /* Invalid */
1088 curtime.wSecond = 65535; /* Invalid */
1089 curtime.wMilliseconds = 12345;
1090 wcscpy(buffer, L"pristine");
1091 ret = pGetDateFormatEx(localeW, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer), emptyW); /* Use reserved arg */
1092 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1093 SetLastError(0xdeadbeef);
1095 /* Limit tests */
1097 curtime.wYear = 1601;
1098 curtime.wMonth = 1;
1099 curtime.wDay = 1;
1100 curtime.wDayOfWeek = 0; /* Irrelevant */
1101 curtime.wHour = 0;
1102 curtime.wMinute = 0;
1103 curtime.wSecond = 0;
1104 curtime.wMilliseconds = 0;
1105 ret = pGetDateFormatEx(localeW, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer), NULL);
1106 expect_wstr(ret, buffer, L"Monday 1 January 1601");
1108 curtime.wYear = 1600;
1109 curtime.wMonth = 12;
1110 curtime.wDay = 31;
1111 curtime.wDayOfWeek = 0; /* Irrelevant */
1112 curtime.wHour = 23;
1113 curtime.wMinute = 59;
1114 curtime.wSecond = 59;
1115 curtime.wMilliseconds = 999;
1116 wcscpy(buffer, L"pristine");
1117 ret = pGetDateFormatEx(localeW, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer), NULL);
1118 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1121 static void test_GetDateFormatW(void)
1123 int ret;
1124 SYSTEMTIME curtime;
1125 WCHAR buffer[BUFFER_SIZE];
1126 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1128 SetLastError(0xdeadbeef);
1130 /* If flags is not zero then format must be NULL */
1131 wcscpy(buffer, L"pristine");
1132 ret = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, NULL, L"", buffer, ARRAY_SIZE(buffer));
1133 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1135 win_skip("GetDateFormatW is not implemented\n");
1136 return;
1138 expect_werr(ret, buffer, ERROR_INVALID_FLAGS);
1139 SetLastError(0xdeadbeef);
1141 /* NULL buffer, len > 0 */
1142 wcscpy(buffer, L"pristine");
1143 ret = GetDateFormatW (lcid, 0, NULL, L"", NULL, ARRAY_SIZE(buffer));
1144 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1145 SetLastError(0xdeadbeef);
1147 /* NULL buffer, len == 0 */
1148 ret = GetDateFormatW (lcid, 0, NULL, L"", NULL, 0);
1149 expect_wstr(ret, NULL, L"");
1151 /* Incorrect DOW and time */
1152 curtime.wYear = 2002;
1153 curtime.wMonth = 10;
1154 curtime.wDay = 23;
1155 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1156 curtime.wHour = 65432; /* Invalid */
1157 curtime.wMinute = 34512; /* Invalid */
1158 curtime.wSecond = 65535; /* Invalid */
1159 curtime.wMilliseconds = 12345;
1160 ret = GetDateFormatW (lcid, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer));
1161 expect_wstr(ret, buffer, L"Wednesday 23 October 2002");
1163 /* Limit tests */
1165 curtime.wYear = 1601;
1166 curtime.wMonth = 1;
1167 curtime.wDay = 1;
1168 curtime.wDayOfWeek = 0; /* Irrelevant */
1169 curtime.wHour = 0;
1170 curtime.wMinute = 0;
1171 curtime.wSecond = 0;
1172 curtime.wMilliseconds = 0;
1173 ret = GetDateFormatW (lcid, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer));
1174 expect_wstr(ret, buffer, L"Monday 1 January 1601");
1176 curtime.wYear = 1600;
1177 curtime.wMonth = 12;
1178 curtime.wDay = 31;
1179 curtime.wDayOfWeek = 0; /* Irrelevant */
1180 curtime.wHour = 23;
1181 curtime.wMinute = 59;
1182 curtime.wSecond = 59;
1183 curtime.wMilliseconds = 999;
1184 wcscpy(buffer, L"pristine");
1185 ret = GetDateFormatW (lcid, 0, &curtime, L"dddd d MMMM yyyy", buffer, ARRAY_SIZE(buffer));
1186 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1187 SetLastError(0xdeadbeef);
1189 /* See the corresponding GetDateFormatA() test */
1191 lcid = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT);
1193 curtime.wYear = 2002;
1194 curtime.wMonth = 5;
1195 curtime.wDay = 4;
1196 ret = GetDateFormatW(lcid, 0, &curtime, L"d\x65e5", buffer, 3);
1197 expect_wstr(ret, buffer, L"4\x65e5");
1199 ret = GetDateFormatW(lcid, 0, &curtime, L"d\x65e5", NULL, 0);
1200 expect_wstr(ret, NULL, L"4\x65e5");
1204 #define CY_POS_LEFT 0
1205 #define CY_POS_RIGHT 1
1206 #define CY_POS_LEFT_SPACE 2
1207 #define CY_POS_RIGHT_SPACE 3
1209 static void test_GetCurrencyFormatA(void)
1211 static char szDot[] = { '.', '\0' };
1212 static char szComma[] = { ',', '\0' };
1213 static char szDollar[] = { '$', '\0' };
1214 static const char* const negative_order[] =
1215 {"($1.0)", /* 0 */
1216 "-$1.0",
1217 "$-1.0",
1218 "$1.0-",
1219 "(1.0$)",
1220 "-1.0$", /* 5 */
1221 "1.0-$",
1222 "1.0$-",
1223 "-1.0 $",
1224 "-$ 1.0",
1225 "1.0 $-", /* 10 */
1226 "$ 1.0-",
1227 "$ -1.0",
1228 "1.0- $",
1229 "($ 1.0)",
1230 "(1.0 $)", /* 15 */
1232 int ret, o;
1233 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1234 char buffer[BUFFER_SIZE];
1235 CURRENCYFMTA format;
1237 SetLastError(0xdeadbeef);
1239 /* NULL output, length > 0 --> Error */
1240 ret = GetCurrencyFormatA(lcid, 0, "23", NULL, NULL, ARRAY_SIZE(buffer));
1241 expect_err(ret, NULL, ERROR_INVALID_PARAMETER);
1242 SetLastError(0xdeadbeef);
1244 /* Invalid character --> Error */
1245 strcpy(buffer, "pristine");
1246 ret = GetCurrencyFormatA(lcid, 0, "23,53", NULL, buffer, ARRAY_SIZE(buffer));
1247 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1248 SetLastError(0xdeadbeef);
1250 /* Double '-' --> Error */
1251 strcpy(buffer, "pristine");
1252 ret = GetCurrencyFormatA(lcid, 0, "--", NULL, buffer, ARRAY_SIZE(buffer));
1253 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1254 SetLastError(0xdeadbeef);
1256 /* Trailing '-' --> Error */
1257 strcpy(buffer, "pristine");
1258 ret = GetCurrencyFormatA(lcid, 0, "0-", NULL, buffer, ARRAY_SIZE(buffer));
1259 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1260 SetLastError(0xdeadbeef);
1262 /* Double '.' --> Error */
1263 strcpy(buffer, "pristine");
1264 ret = GetCurrencyFormatA(lcid, 0, "0..", NULL, buffer, ARRAY_SIZE(buffer));
1265 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1266 SetLastError(0xdeadbeef);
1268 /* Leading space --> Error */
1269 ret = GetCurrencyFormatA(lcid, 0, " 0.1", NULL, buffer, ARRAY_SIZE(buffer));
1270 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1271 SetLastError(0xdeadbeef);
1273 /* Length too small --> Write up to length chars */
1274 strcpy(buffer, "pristine");
1275 ret = GetCurrencyFormatA(lcid, NUO, "1234", NULL, buffer, 2);
1276 /* there is no guarantee on the buffer content, see GetTimeFormatA() */
1277 expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
1278 SetLastError(0xdeadbeef);
1280 /* Valid number */
1281 ret = GetCurrencyFormatA(lcid, NUO, "2353", NULL, buffer, ARRAY_SIZE(buffer));
1282 expect_str(ret, buffer, "$2,353.00");
1284 /* Valid negative number */
1285 ret = GetCurrencyFormatA(lcid, NUO, "-2353", NULL, buffer, ARRAY_SIZE(buffer));
1286 expect_str(ret, buffer, "($2,353.00)");
1288 /* Valid real number */
1289 ret = GetCurrencyFormatA(lcid, NUO, "2353.1", NULL, buffer, ARRAY_SIZE(buffer));
1290 expect_str(ret, buffer, "$2,353.10");
1292 /* Too many DP --> Truncated */
1293 ret = GetCurrencyFormatA(lcid, NUO, "2353.111", NULL, buffer, ARRAY_SIZE(buffer));
1294 expect_str(ret, buffer, "$2,353.11");
1296 /* Too many DP --> Rounded */
1297 ret = GetCurrencyFormatA(lcid, NUO, "2353.119", NULL, buffer, ARRAY_SIZE(buffer));
1298 expect_str(ret, buffer, "$2,353.12");
1300 /* Format and flags given --> Error */
1301 memset(&format, 0, sizeof(format));
1302 strcpy(buffer, "pristine");
1303 ret = GetCurrencyFormatA(lcid, NUO, "2353", &format, buffer, ARRAY_SIZE(buffer));
1304 expect_err(ret, buffer, ERROR_INVALID_FLAGS);
1305 SetLastError(0xdeadbeef);
1307 /* Invalid format --> Error */
1308 strcpy(buffer, "pristine");
1309 ret = GetCurrencyFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1310 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1311 SetLastError(0xdeadbeef);
1313 format.NumDigits = 0; /* No decimal separator */
1314 format.LeadingZero = 0;
1315 format.Grouping = 0; /* No grouping char */
1316 format.NegativeOrder = 0;
1317 format.PositiveOrder = CY_POS_LEFT;
1318 format.lpDecimalSep = szDot;
1319 format.lpThousandSep = szComma;
1320 format.lpCurrencySymbol = szDollar;
1322 /* No decimal or grouping chars expected */
1323 ret = GetCurrencyFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1324 expect_str(ret, buffer, "$2353");
1326 /* 1 DP --> Expect decimal separator */
1327 format.NumDigits = 1;
1328 ret = GetCurrencyFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1329 expect_str(ret, buffer, "$2353.0");
1331 /* Group by 100's */
1332 format.Grouping = 2;
1333 ret = GetCurrencyFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1334 expect_str(ret, buffer, "$23,53.0");
1336 /* Grouping of a positive number */
1337 format.Grouping = 3;
1338 ret = GetCurrencyFormatA(lcid, 0, "235", &format, buffer, ARRAY_SIZE(buffer));
1339 expect_str(ret, buffer, "$235.0");
1341 format.Grouping = 31;
1342 ret = GetCurrencyFormatA(lcid, 0, "1234567890", &format, buffer, ARRAY_SIZE(buffer));
1343 expect_str(ret, buffer, "$1,2,3,4,5,6,7,890.0");
1345 format.Grouping = 312;
1346 ret = GetCurrencyFormatA(lcid, 0, "1234567890", &format, buffer, ARRAY_SIZE(buffer));
1347 expect_str(ret, buffer, "$12,34,56,7,890.0");
1349 format.Grouping = 310;
1350 ret = GetCurrencyFormatA(lcid, 0, "1234567890", &format, buffer, ARRAY_SIZE(buffer));
1351 expect_str(ret, buffer, "$123456,7,890.0");
1353 /* Grouping of a negative number */
1354 format.NegativeOrder = 2;
1355 ret = GetCurrencyFormatA(lcid, 0, "-235", &format, buffer, ARRAY_SIZE(buffer));
1356 expect_str(ret, buffer, "$-235.0");
1358 /* Always provide leading zero */
1359 format.LeadingZero = 1;
1360 ret = GetCurrencyFormatA(lcid, 0, ".5", &format, buffer, ARRAY_SIZE(buffer));
1361 expect_str(ret, buffer, "$0.5");
1362 ret = GetCurrencyFormatA(lcid, 0, "0.5", &format, buffer, ARRAY_SIZE(buffer));
1363 expect_str(ret, buffer, "$0.5");
1365 format.LeadingZero = 0;
1366 ret = GetCurrencyFormatA(lcid, 0, "0.5", &format, buffer, ARRAY_SIZE(buffer));
1367 expect_str(ret, buffer, "$.5");
1368 ret = GetCurrencyFormatA(lcid, 0, "0.5", &format, buffer, ARRAY_SIZE(buffer));
1369 expect_str(ret, buffer, "$.5");
1371 format.PositiveOrder = CY_POS_RIGHT;
1372 ret = GetCurrencyFormatA(lcid, 0, "1", &format, buffer, ARRAY_SIZE(buffer));
1373 expect_str(ret, buffer, "1.0$");
1375 format.PositiveOrder = CY_POS_LEFT_SPACE;
1376 ret = GetCurrencyFormatA(lcid, 0, "1", &format, buffer, ARRAY_SIZE(buffer));
1377 expect_str(ret, buffer, "$ 1.0");
1379 format.PositiveOrder = CY_POS_RIGHT_SPACE;
1380 ret = GetCurrencyFormatA(lcid, 0, "1", &format, buffer, ARRAY_SIZE(buffer));
1381 expect_str(ret, buffer, "1.0 $");
1384 for (o = 0; o <= 15; o++)
1386 winetest_push_context("%d", o);
1387 format.NegativeOrder = o;
1388 strcpy(buffer, "pristine");
1389 ret = GetCurrencyFormatA(lcid, 0, "-1", &format, buffer, ARRAY_SIZE(buffer));
1390 expect_str(ret, buffer, negative_order[o]);
1391 winetest_pop_context();
1395 #define NEG_PARENS 0 /* "(1.1)" */
1396 #define NEG_LEFT 1 /* "-1.1" */
1397 #define NEG_LEFT_SPACE 2 /* "- 1.1" */
1398 #define NEG_RIGHT 3 /* "1.1-" */
1399 #define NEG_RIGHT_SPACE 4 /* "1.1 -" */
1401 static void test_GetNumberFormatA(void)
1403 static char szDot[] = { '.', '\0' };
1404 static char szComma[] = { ',', '\0' };
1405 int ret;
1406 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1407 WCHAR grouping[32], t1000[8], dec[8], frac[8], lzero[8];
1408 char buffer[BUFFER_SIZE];
1409 NUMBERFMTA format;
1411 SetLastError(0xdeadbeef);
1413 /* NULL output, length > 0 --> Error */
1414 strcpy(buffer, "pristine");
1415 ret = GetNumberFormatA(lcid, 0, "23", NULL, NULL, ARRAY_SIZE(buffer));
1416 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1417 SetLastError(0xdeadbeef);
1419 /* Invalid character --> Error */
1420 strcpy(buffer, "pristine");
1421 ret = GetNumberFormatA(lcid, 0, "23,53", NULL, buffer, ARRAY_SIZE(buffer));
1422 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1423 SetLastError(0xdeadbeef);
1425 /* Double '-' --> Error */
1426 strcpy(buffer, "pristine");
1427 ret = GetNumberFormatA(lcid, 0, "--", NULL, buffer, ARRAY_SIZE(buffer));
1428 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1429 SetLastError(0xdeadbeef);
1431 /* Trailing '-' --> Error */
1432 strcpy(buffer, "pristine");
1433 ret = GetNumberFormatA(lcid, 0, "0-", NULL, buffer, ARRAY_SIZE(buffer));
1434 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1435 SetLastError(0xdeadbeef);
1437 /* Double '.' --> Error */
1438 strcpy(buffer, "pristine");
1439 ret = GetNumberFormatA(lcid, 0, "0..", NULL, buffer, ARRAY_SIZE(buffer));
1440 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1441 SetLastError(0xdeadbeef);
1443 /* Leading space --> Error */
1444 strcpy(buffer, "pristine");
1445 ret = GetNumberFormatA(lcid, 0, " 0.1", NULL, buffer, ARRAY_SIZE(buffer));
1446 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1447 SetLastError(0xdeadbeef);
1449 /* Length too small --> Write up to length chars */
1450 ret = GetNumberFormatA(lcid, NUO, "1234", NULL, buffer, 2);
1451 /* there is no guarantee on the buffer content, see GetTimeFormatA() */
1452 expect_err(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
1453 SetLastError(0xdeadbeef);
1455 /* Valid number */
1456 ret = GetNumberFormatA(lcid, NUO, "2353", NULL, buffer, ARRAY_SIZE(buffer));
1457 expect_str(ret, buffer, "2,353.00");
1459 /* Valid negative number */
1460 ret = GetNumberFormatA(lcid, NUO, "-2353", NULL, buffer, ARRAY_SIZE(buffer));
1461 expect_str(ret, buffer, "-2,353.00");
1463 /* test for off by one error in grouping */
1464 ret = GetNumberFormatA(lcid, NUO, "-353", NULL, buffer, ARRAY_SIZE(buffer));
1465 expect_str(ret, buffer, "-353.00");
1467 /* Valid real number */
1468 ret = GetNumberFormatA(lcid, NUO, "2353.1", NULL, buffer, ARRAY_SIZE(buffer));
1469 expect_str(ret, buffer, "2,353.10");
1471 /* Too many DP --> Truncated */
1472 ret = GetNumberFormatA(lcid, NUO, "2353.111", NULL, buffer, ARRAY_SIZE(buffer));
1473 expect_str(ret, buffer, "2,353.11");
1475 /* Too many DP --> Rounded */
1476 ret = GetNumberFormatA(lcid, NUO, "2353.119", NULL, buffer, ARRAY_SIZE(buffer));
1477 expect_str(ret, buffer, "2,353.12");
1479 /* Format and flags given --> Error */
1480 memset(&format, 0, sizeof(format));
1481 strcpy(buffer, "pristine");
1482 ret = GetNumberFormatA(lcid, NUO, "2353", &format, buffer, ARRAY_SIZE(buffer));
1483 expect_err(ret, buffer, ERROR_INVALID_FLAGS);
1484 SetLastError(0xdeadbeef);
1486 /* Invalid format --> Error */
1487 strcpy(buffer, "pristine");
1488 ret = GetNumberFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1489 expect_err(ret, buffer, ERROR_INVALID_PARAMETER);
1490 SetLastError(0xdeadbeef);
1492 format.NumDigits = 0; /* No decimal separator */
1493 format.LeadingZero = 0;
1494 format.Grouping = 0; /* No grouping char */
1495 format.NegativeOrder = 0;
1496 format.lpDecimalSep = szDot;
1497 format.lpThousandSep = szComma;
1499 /* No decimal or grouping chars expected */
1500 ret = GetNumberFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1501 expect_str(ret, buffer, "2353");
1503 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1504 ret = GetNumberFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1505 expect_str(ret, buffer, "2353.0");
1507 format.Grouping = 2; /* Group by 100's */
1508 ret = GetNumberFormatA(lcid, 0, "2353", &format, buffer, ARRAY_SIZE(buffer));
1509 expect_str(ret, buffer, "23,53.0");
1511 /* Grouping of a positive number */
1512 format.Grouping = 3;
1513 ret = GetNumberFormatA(lcid, 0, "235", &format, buffer, ARRAY_SIZE(buffer));
1514 expect_str(ret, buffer, "235.0");
1515 ret = GetNumberFormatA(lcid, 0, "000235", &format, buffer, ARRAY_SIZE(buffer));
1516 expect_str(ret, buffer, "235.0");
1518 format.Grouping = 23;
1519 ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
1520 expect_str(ret, buffer, "1,234,567,89.0");
1522 format.Grouping = 230;
1523 ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
1524 expect_str(ret, buffer, "1234,567,89.0");
1526 format.Grouping = 203;
1527 ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
1528 expect_str(ret, buffer, "1,234,567,,89.0");
1530 format.Grouping = 2030;
1531 ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
1532 expect_str(ret, buffer, "1234,567,,89.0");
1534 format.Grouping = 2003;
1535 ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
1536 expect_str(ret, buffer, "1,234,567,,,89.0");
1538 format.Grouping = 1200;
1539 ret = GetNumberFormatA(lcid, 0, "123456789", &format, buffer, ARRAY_SIZE(buffer));
1540 expect_str(ret, buffer, "123456,,78,9.0");
1542 /* Grouping of a negative number */
1543 format.NegativeOrder = NEG_LEFT;
1544 format.Grouping = 3;
1545 ret = GetNumberFormatA(lcid, 0, "-235", &format, buffer, ARRAY_SIZE(buffer));
1546 expect_str(ret, buffer, "-235.0");
1547 ret = GetNumberFormatA(lcid, 0, "-000235", &format, buffer, ARRAY_SIZE(buffer));
1548 expect_str(ret, buffer, "-235.0");
1550 format.LeadingZero = 1; /* Always provide leading zero */
1551 ret = GetNumberFormatA(lcid, 0, ".5", &format, buffer, ARRAY_SIZE(buffer));
1552 expect_str(ret, buffer, "0.5");
1553 ret = GetNumberFormatA(lcid, 0, "0000.5", &format, buffer, ARRAY_SIZE(buffer));
1554 expect_str(ret, buffer, "0.5");
1556 format.NegativeOrder = NEG_PARENS;
1557 ret = GetNumberFormatA(lcid, 0, "-1", &format, buffer, ARRAY_SIZE(buffer));
1558 expect_str(ret, buffer, "(1.0)");
1560 format.NegativeOrder = NEG_LEFT;
1561 ret = GetNumberFormatA(lcid, 0, "-1", &format, buffer, ARRAY_SIZE(buffer));
1562 expect_str(ret, buffer, "-1.0");
1564 format.NegativeOrder = NEG_LEFT_SPACE;
1565 ret = GetNumberFormatA(lcid, 0, "-1", &format, buffer, ARRAY_SIZE(buffer));
1566 expect_str(ret, buffer, "- 1.0");
1568 format.NegativeOrder = NEG_RIGHT;
1569 ret = GetNumberFormatA(lcid, 0, "-1", &format, buffer, ARRAY_SIZE(buffer));
1570 expect_str(ret, buffer, "1.0-");
1572 format.NegativeOrder = NEG_RIGHT_SPACE;
1573 ret = GetNumberFormatA(lcid, 0, "-1", &format, buffer, ARRAY_SIZE(buffer));
1574 expect_str(ret, buffer, "1.0 -");
1576 /* Test French formatting */
1577 lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1578 if (IsValidLocale(lcid, 0))
1580 ret = GetNumberFormatA(lcid, NUO, "-12345", NULL, buffer, ARRAY_SIZE(buffer));
1581 expect_str(ret, buffer, "-12\xa0\x33\x34\x35,00"); /* Non breaking space */
1584 /* Test the actual LOCALE_SGROUPING string, the rules for repeats are opposite */
1585 if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, ARRAY_SIZE(grouping)) &&
1586 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, t1000, ARRAY_SIZE(t1000)) &&
1587 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, dec, ARRAY_SIZE(dec)) &&
1588 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, frac, ARRAY_SIZE(frac)) &&
1589 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, lzero, ARRAY_SIZE(lzero)))
1591 static const struct
1593 const char *grouping;
1594 const char *expected;
1595 } tests[] = {
1596 { "3;0", "1,234,567,890.54321" },
1597 { "2;3", "12345,678,90.54321" },
1598 { "1", "123456789,0.54321" },
1599 { "1;0", "1,2,3,4,5,6,7,8,9,0.54321" },
1600 { "1;0;3", "123456,789,,0.54321" },
1601 { "0", "1234567890.54321" },
1602 { "0;0", "1234567890.54321" },
1603 { "0;1", "123456789,0.54321" },
1604 { "0;0;0", "1234567890.54321" },
1605 { "0;1;0", "1,2,3,4,5,6,7,8,9,0.54321" },
1606 { "2;0;0", "12345678,90.54321" },
1607 { "2;0;0;0", "12345678,,90.54321" },
1608 { "2;0;0;0;0", "12345678,,,90.54321" },
1609 { "2;0;0;1;0", "1,2,3,4,5,6,7,8,,,90.54321" },
1610 { "1;3;2", "1234,56,789,0.54321" },
1611 { "1;3;2;0", "12,34,56,789,0.54321" },
1612 { "3;1;1;2;0", "1,23,45,6,7,890.54321" },
1613 { "6;1", "123,4,567890.54321" },
1615 unsigned i;
1617 SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, ",");
1618 SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, ".");
1619 SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, "5");
1620 SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILZERO, "0");
1622 for (i = 0; i < ARRAY_SIZE(tests); i++)
1624 SetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, tests[i].grouping);
1625 SetLastError(0xdeadbeef);
1626 ret = GetNumberFormatA(LOCALE_USER_DEFAULT, 0, "1234567890.54321", NULL, buffer, ARRAY_SIZE(buffer));
1627 if (ret)
1629 ok(GetLastError() == 0xdeadbeef, "[%u] unexpected error %lu\n", i, GetLastError());
1630 ok(ret == strlen(tests[i].expected) + 1, "[%u] unexpected ret %d\n", i, ret);
1631 ok(!strcmp(buffer, tests[i].expected), "[%u] unexpected string %s\n", i, buffer);
1633 else
1634 ok(0, "[%u] expected success, got error %ld\n", i, GetLastError());
1637 /* Restore */
1638 ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping), "Restoring SGROUPING failed\n");
1639 ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, t1000), "Restoring STHOUSAND failed\n");
1640 ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, dec), "Restoring SDECIMAL failed\n");
1641 ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, frac), "Restoring IDIGITS failed\n");
1642 ok(SetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, lzero), "Restoring ILZERO failed\n");
1646 static void test_GetNumberFormatEx(void)
1648 int ret;
1649 NUMBERFMTW format;
1650 static WCHAR dotW[] = {'.',0};
1651 static WCHAR commaW[] = {',',0};
1652 static const WCHAR enW[] = {'e','n','-','U','S',0};
1653 static const WCHAR frW[] = {'f','r','-','F','R',0};
1654 static const WCHAR bogusW[] = {'b','o','g','u','s',0};
1655 WCHAR buffer[BUFFER_SIZE];
1657 if (!pGetNumberFormatEx)
1659 win_skip("GetNumberFormatEx is not available.\n");
1660 return;
1663 SetLastError(0xdeadbeef);
1665 /* NULL output, length > 0 --> Error */
1666 wcscpy(buffer, L"pristine");
1667 ret = pGetNumberFormatEx(enW, 0, L"23", NULL, NULL, ARRAY_SIZE(buffer));
1668 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1669 SetLastError(0xdeadbeef);
1671 /* Invalid character --> Error */
1672 wcscpy(buffer, L"pristine");
1673 ret = pGetNumberFormatEx(enW, 0, L"23,53", NULL, buffer, ARRAY_SIZE(buffer));
1674 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1675 SetLastError(0xdeadbeef);
1677 /* Double '-' --> Error */
1678 wcscpy(buffer, L"pristine");
1679 ret = pGetNumberFormatEx(enW, 0, L"--", NULL, buffer, ARRAY_SIZE(buffer));
1680 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1681 SetLastError(0xdeadbeef);
1683 /* Trailing '-' --> Error */
1684 wcscpy(buffer, L"pristine");
1685 ret = pGetNumberFormatEx(enW, 0, L"0-", NULL, buffer, ARRAY_SIZE(buffer));
1686 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1687 SetLastError(0xdeadbeef);
1689 /* Double '.' --> Error */
1690 wcscpy(buffer, L"pristine");
1691 ret = pGetNumberFormatEx(enW, 0, L"0..", NULL, buffer, ARRAY_SIZE(buffer));
1692 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1693 SetLastError(0xdeadbeef);
1695 /* Leading space --> Error */
1696 wcscpy(buffer, L"pristine");
1697 ret = pGetNumberFormatEx(enW, 0, L" 0.1", NULL, buffer, ARRAY_SIZE(buffer));
1698 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1699 SetLastError(0xdeadbeef);
1701 /* Length too small --> Write up to length chars */
1702 wcscpy(buffer, L"pristine");
1703 ret = pGetNumberFormatEx(enW, NUO, L"1234", NULL, buffer, 2);
1704 /* there is no guarantee on the buffer content, see GetTimeFormatA() */
1705 expect_werr(ret, NULL, ERROR_INSUFFICIENT_BUFFER);
1706 SetLastError(0xdeadbeef);
1708 /* Bogus locale --> Error */
1709 wcscpy(buffer, L"pristine");
1710 ret = pGetNumberFormatEx(bogusW, NUO, L"23", NULL, buffer, ARRAY_SIZE(buffer));
1711 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1712 SetLastError(0xdeadbeef);
1714 memset(&format, 0, sizeof(format));
1716 /* Format and flags given --> Error */
1717 wcscpy(buffer, L"pristine");
1718 ret = pGetNumberFormatEx(enW, NUO, L"2353", &format, buffer, ARRAY_SIZE(buffer));
1719 expect_werr(ret, buffer, ERROR_INVALID_FLAGS);
1720 SetLastError(0xdeadbeef);
1722 /* Invalid format --> Error */
1723 wcscpy(buffer, L"pristine");
1724 ret = pGetNumberFormatEx(enW, 0, L"2353", &format, buffer, ARRAY_SIZE(buffer));
1725 expect_werr(ret, buffer, ERROR_INVALID_PARAMETER);
1726 SetLastError(0xdeadbeef);
1728 /* Valid number */
1729 ret = pGetNumberFormatEx(enW, NUO, L"2353", NULL, buffer, ARRAY_SIZE(buffer));
1730 expect_wstr(ret, buffer, L"2,353.00");
1732 /* Valid negative number */
1733 ret = pGetNumberFormatEx(enW, NUO, L"-2353", NULL, buffer, ARRAY_SIZE(buffer));
1734 expect_wstr(ret, buffer, L"-2,353.00");
1736 /* test for off by one error in grouping */
1737 ret = pGetNumberFormatEx(enW, NUO, L"-353", NULL, buffer, ARRAY_SIZE(buffer));
1738 expect_wstr(ret, buffer, L"-353.00");
1740 /* Valid real number */
1741 ret = pGetNumberFormatEx(enW, NUO, L"2353.1", NULL, buffer, ARRAY_SIZE(buffer));
1742 expect_wstr(ret, buffer, L"2,353.10");
1744 /* Too many DP --> Truncated */
1745 ret = pGetNumberFormatEx(enW, NUO, L"2353.111", NULL, buffer, ARRAY_SIZE(buffer));
1746 expect_wstr(ret, buffer, L"2,353.11");
1748 /* Too many DP --> Rounded */
1749 ret = pGetNumberFormatEx(enW, NUO, L"2353.119", NULL, buffer, ARRAY_SIZE(buffer));
1750 expect_wstr(ret, buffer, L"2,353.12");
1752 format.NumDigits = 0; /* No decimal separator */
1753 format.LeadingZero = 0;
1754 format.Grouping = 0; /* No grouping char */
1755 format.NegativeOrder = 0;
1756 format.lpDecimalSep = dotW;
1757 format.lpThousandSep = commaW;
1759 /* No decimal or grouping chars expected */
1760 ret = pGetNumberFormatEx(enW, 0, L"2353", &format, buffer, ARRAY_SIZE(buffer));
1761 expect_wstr(ret, buffer, L"2353");
1763 /* 1 DP --> Expect decimal separator */
1764 format.NumDigits = 1;
1765 ret = pGetNumberFormatEx(enW, 0, L"2353", &format, buffer, ARRAY_SIZE(buffer));
1766 expect_wstr(ret, buffer, L"2353.0");
1768 /* Group by 100's */
1769 format.Grouping = 2;
1770 ret = pGetNumberFormatEx(enW, 0, L"2353", &format, buffer, ARRAY_SIZE(buffer));
1771 expect_wstr(ret, buffer, L"23,53.0");
1773 /* Grouping of a positive number */
1774 format.Grouping = 3;
1775 ret = pGetNumberFormatEx(enW, 0, L"235", &format, buffer, ARRAY_SIZE(buffer));
1776 expect_wstr(ret, buffer, L"235.0");
1778 /* Grouping of a negative number */
1779 format.NegativeOrder = NEG_LEFT;
1780 ret = pGetNumberFormatEx(enW, 0, L"-235", &format, buffer, ARRAY_SIZE(buffer));
1781 expect_wstr(ret, buffer, L"-235.0");
1783 /* Always provide leading zero */
1784 format.LeadingZero = 1;
1785 ret = pGetNumberFormatEx(enW, 0, L".5", &format, buffer, ARRAY_SIZE(buffer));
1786 expect_wstr(ret, buffer, L"0.5");
1787 ret = pGetNumberFormatEx(enW, 0, L"0.5", &format, buffer, ARRAY_SIZE(buffer));
1788 expect_wstr(ret, buffer, L"0.5");
1790 format.LeadingZero = 0;
1791 ret = pGetNumberFormatEx(enW, 0, L".5", &format, buffer, ARRAY_SIZE(buffer));
1792 expect_wstr(ret, buffer, L".5");
1793 ret = pGetNumberFormatEx(enW, 0, L"0.5", &format, buffer, ARRAY_SIZE(buffer));
1794 expect_wstr(ret, buffer, L".5");
1796 format.NegativeOrder = NEG_PARENS;
1797 ret = pGetNumberFormatEx(enW, 0, L"-1", &format, buffer, ARRAY_SIZE(buffer));
1798 expect_wstr(ret, buffer, L"(1.0)");
1800 format.NegativeOrder = NEG_LEFT;
1801 ret = pGetNumberFormatEx(enW, 0, L"-1", &format, buffer, ARRAY_SIZE(buffer));
1802 expect_wstr(ret, buffer, L"-1.0");
1804 format.NegativeOrder = NEG_LEFT_SPACE;
1805 ret = pGetNumberFormatEx(enW, 0, L"-1", &format, buffer, ARRAY_SIZE(buffer));
1806 expect_wstr(ret, buffer, L"- 1.0");
1808 format.NegativeOrder = NEG_RIGHT;
1809 ret = pGetNumberFormatEx(enW, 0, L"-1", &format, buffer, ARRAY_SIZE(buffer));
1810 expect_wstr(ret, buffer, L"1.0-");
1812 format.NegativeOrder = NEG_RIGHT_SPACE;
1813 ret = pGetNumberFormatEx(enW, 0, L"-1", &format, buffer, ARRAY_SIZE(buffer));
1814 expect_wstr(ret, buffer, L"1.0 -");
1816 /* Test French formatting */
1817 if (pIsValidLocaleName(frW))
1819 const WCHAR *expected;
1820 ret = pGetNumberFormatEx(frW, NUO, L"-12345", NULL, buffer, ARRAY_SIZE(buffer));
1821 expected = (ret && wcschr(buffer, 0x202f)) ?
1822 L"-12\x202f\x33\x34\x35,00" : /* Same but narrow (win11) */
1823 L"-12\xa0\x33\x34\x35,00"; /* Non breaking space */
1824 expect_wstr(ret, buffer, expected);
1828 struct comparestringa_entry {
1829 LCID lcid;
1830 DWORD flags;
1831 const char *first;
1832 int first_len;
1833 const char *second;
1834 int second_len;
1835 int ret;
1836 DWORD le;
1839 static const struct comparestringa_entry comparestringa_data[] = {
1840 { LOCALE_SYSTEM_DEFAULT, 0, "EndDialog", -1, "_Property", -1, CSTR_GREATER_THAN },
1841 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0070", -1, "_IEWWBrowserComp", -1, CSTR_GREATER_THAN },
1842 { LOCALE_SYSTEM_DEFAULT, 0, "r", -1, "\\", -1, CSTR_GREATER_THAN },
1843 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0031", -1, "OriginalDatabase", -1, CSTR_GREATER_THAN },
1844 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1, CSTR_GREATER_THAN },
1845 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1, CSTR_LESS_THAN },
1846 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1847 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1848 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1, CSTR_LESS_THAN },
1849 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1, CSTR_LESS_THAN },
1850 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1, CSTR_LESS_THAN },
1851 { LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1, CSTR_GREATER_THAN },
1852 { LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1, CSTR_LESS_THAN },
1853 { LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1, CSTR_LESS_THAN },
1854 { LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1, CSTR_GREATER_THAN },
1855 { LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1, CSTR_GREATER_THAN },
1856 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1, CSTR_LESS_THAN },
1857 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1, CSTR_LESS_THAN },
1858 { LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1, CSTR_LESS_THAN },
1859 /* hyphen and apostrophe are treated differently depending on whether SORT_STRINGSORT specified or not */
1860 { LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1, CSTR_GREATER_THAN },
1861 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1, CSTR_LESS_THAN },
1862 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1, CSTR_LESS_THAN },
1863 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1, CSTR_GREATER_THAN },
1864 { LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1, CSTR_GREATER_THAN },
1865 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1, CSTR_LESS_THAN },
1866 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1, CSTR_LESS_THAN },
1867 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1, CSTR_GREATER_THAN },
1868 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9, CSTR_EQUAL },
1869 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10, CSTR_LESS_THAN },
1870 { LOCALE_SYSTEM_DEFAULT, 0, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1871 { LOCALE_SYSTEM_DEFAULT, 0, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1872 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1873 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1874 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a.", 3, "a\0", 3, CSTR_EQUAL },
1875 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a ", 3, "a\0", 3, CSTR_EQUAL },
1876 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0\0", 4, CSTR_EQUAL },
1877 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0\0", 4, CSTR_EQUAL },
1878 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 1, CSTR_EQUAL },
1879 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 2, CSTR_EQUAL },
1880 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0x", 4, CSTR_LESS_THAN },
1881 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0x", 4, CSTR_LESS_THAN },
1882 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 1, CSTR_GREATER_THAN },
1883 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 2, CSTR_GREATER_THAN },
1884 /* flag tests */
1885 { LOCALE_SYSTEM_DEFAULT, LOCALE_USE_CP_ACP, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1886 { LOCALE_SYSTEM_DEFAULT, LINGUISTIC_IGNORECASE, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1887 { LOCALE_SYSTEM_DEFAULT, LINGUISTIC_IGNOREDIACRITIC, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1888 { LOCALE_SYSTEM_DEFAULT, NORM_IGNOREKANATYPE, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1889 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORENONSPACE, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1890 { LOCALE_SYSTEM_DEFAULT, NORM_IGNOREWIDTH, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1891 { LOCALE_SYSTEM_DEFAULT, NORM_LINGUISTIC_CASING, "NULL", -1, "NULL", -1, CSTR_EQUAL },
1892 { LOCALE_SYSTEM_DEFAULT, SORT_DIGITSASNUMBERS, "NULL", -1, "NULL", -1, 0, ERROR_INVALID_FLAGS }
1895 static void test_CompareStringA(void)
1897 static const char ABC_EE[] = {'A','B','C',0,0xEE};
1898 static const char ABC_FF[] = {'A','B','C',0,0xFF};
1899 int ret, i;
1900 char a[256];
1901 BOOL is_utf8;
1902 CPINFOEXA cpinfo;
1903 LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1905 GetCPInfoExA( CP_ACP, 0, &cpinfo );
1906 is_utf8 = cpinfo.CodePage == CP_UTF8;
1908 for (i = 0; i < ARRAY_SIZE(comparestringa_data); i++)
1910 const struct comparestringa_entry *entry = &comparestringa_data[i];
1912 SetLastError(0xdeadbeef);
1913 ret = CompareStringA(entry->lcid, entry->flags, entry->first, entry->first_len,
1914 entry->second, entry->second_len);
1915 ok(ret == entry->ret, "%d: got %d, expected %d\n", i, ret, entry->ret);
1916 ok(GetLastError() == (ret ? 0xdeadbeef : entry->le), "%d: got last error %ld, expected %ld\n",
1917 i, GetLastError(), (ret ? 0xdeadbeef : entry->le));
1920 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
1921 ok (ret == CSTR_LESS_THAN, "(Salut/Salute) Expected CSTR_LESS_THAN, got %d\n", ret);
1923 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
1924 ok (ret == CSTR_EQUAL, "(Salut/SaLuT) Expected CSTR_EQUAL, got %d\n", ret);
1926 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
1927 ok (ret == CSTR_GREATER_THAN, "(Salut/hola) Expected CSTR_GREATER_THAN, got %d\n", ret);
1929 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1930 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1932 lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1934 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1935 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1937 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
1938 ok (ret == CSTR_GREATER_THAN, "(haha/hoho) Expected CSTR_GREATER_THAN, got %d\n", ret);
1940 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1);
1941 ok (ret == CSTR_EQUAL, "(Salut/saLuT) Expected CSTR_EQUAL, got %d\n", ret);
1943 ret = lstrcmpA("", "");
1944 ok (ret == 0, "lstrcmpA(\"\", \"\") should return 0, got %d\n", ret);
1946 ret = lstrcmpA(NULL, NULL);
1947 ok (ret == 0 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, NULL) should return 0, got %d\n", ret);
1949 ret = lstrcmpA("", NULL);
1950 ok (ret == 1 || broken(ret == -2) /* win9x */, "lstrcmpA(\"\", NULL) should return 1, got %d\n", ret);
1952 ret = lstrcmpA(NULL, "");
1953 ok (ret == -1 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, \"\") should return -1, got %d\n", ret);
1955 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
1956 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1958 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 );
1959 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1961 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 );
1962 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1964 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 );
1965 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1967 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 );
1968 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1970 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 );
1971 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1973 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 );
1974 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1976 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 );
1977 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1979 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 );
1980 ok(ret == CSTR_LESS_THAN, "`o vs -m expected CSTR_LESS_THAN, got %d\n", ret);
1982 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 );
1983 ok(ret == CSTR_GREATER_THAN, "-m vs `o CSTR_GREATER_THAN got %d\n", ret);
1985 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 );
1986 ok(ret == CSTR_GREATER_THAN, "`o vs -m CSTR_GREATER_THAN got %d\n", ret);
1988 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 );
1989 ok(ret == CSTR_LESS_THAN, "-m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1991 /* WinXP handles embedded NULLs differently than earlier versions */
1992 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
1993 ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLuZkUtZ vs aLuZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret);
1995 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10);
1996 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);
1998 ret = CompareStringA(lcid, 0, "a\0b", -1, "a", -1);
1999 ok(ret == CSTR_EQUAL, "a vs a expected CSTR_EQUAL, got %d\n", ret);
2001 ret = CompareStringA(lcid, 0, "a\0b", 4, "a", 2);
2002 ok(ret == CSTR_EQUAL || /* win2k */
2003 ret == CSTR_GREATER_THAN,
2004 "a\\0b vs a expected CSTR_EQUAL or CSTR_GREATER_THAN, got %d\n", ret);
2006 ret = CompareStringA(lcid, 0, "\2", 2, "\1", 2);
2007 ok(ret != CSTR_EQUAL, "\\2 vs \\1 expected unequal\n");
2009 ret = CompareStringA(lcid, NORM_IGNORECASE | LOCALE_USE_CP_ACP, "#", -1, ".", -1);
2010 ok(ret == CSTR_LESS_THAN, "\"#\" vs \".\" expected CSTR_LESS_THAN, got %d\n", ret);
2012 ret = CompareStringA(lcid, NORM_IGNORECASE, "_", -1, ".", -1);
2013 ok(ret == CSTR_GREATER_THAN, "\"_\" vs \".\" expected CSTR_GREATER_THAN, got %d\n", ret);
2015 ret = lstrcmpiA("#", ".");
2016 ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret);
2018 lcid = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT);
2020 /* \xB9 character lies between a and b */
2021 ret = CompareStringA(lcid, 0, "a", 1, "\xB9", 1);
2022 ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be greater than \'a\'\n");
2023 ret = CompareStringA(lcid, 0, "\xB9", 1, "b", 1);
2024 ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be smaller than \'b\'\n");
2026 memset(a, 'a', sizeof(a));
2027 SetLastError(0xdeadbeef);
2028 ret = CompareStringA(lcid, 0, a, sizeof(a), a, sizeof(a));
2029 ok (GetLastError() == 0xdeadbeef && ret == CSTR_EQUAL,
2030 "ret %d, error %ld, expected value %d\n", ret, GetLastError(), CSTR_EQUAL);
2032 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, ABC_EE, 3, ABC_FF, 3);
2033 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2034 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, ABC_EE, 5, ABC_FF, 3);
2035 ok(ret == CSTR_GREATER_THAN || (is_utf8 && ret == CSTR_EQUAL), "expected CSTR_GREATER_THAN, got %d\n", ret);
2036 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, ABC_EE, 3, ABC_FF, 5);
2037 ok(ret == CSTR_LESS_THAN || (is_utf8 && ret == CSTR_EQUAL), "expected CSTR_LESS_THAN, got %d\n", ret);
2038 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, ABC_EE, 5, ABC_FF, 5);
2039 ok(ret == CSTR_LESS_THAN || (is_utf8 && ret == CSTR_EQUAL), "expected CSTR_LESS_THAN, got %d\n", ret);
2040 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, ABC_FF, 5, ABC_EE, 5);
2041 ok(ret == CSTR_GREATER_THAN || (is_utf8 && ret == CSTR_EQUAL), "expected CSTR_GREATER_THAN, got %d\n", ret);
2044 static void test_CompareStringW(void)
2046 static const WCHAR ABC_EE[] = {'A','B','C',0,0xEE};
2047 static const WCHAR ABC_FF[] = {'A','B','C',0,0xFF};
2048 static const WCHAR A_ACUTE_BC[] = {0xc1,'B','C',0};
2049 static const WCHAR A_ACUTE_BC_DECOMP[] = {'A',0x301,'B','C',0};
2050 static const WCHAR A_NULL_BC[] = {'A',0,'B','C',0};
2051 WCHAR *str1, *str2;
2052 SYSTEM_INFO si;
2053 DWORD old_prot;
2054 BOOL success;
2055 char *buf;
2056 int ret;
2058 GetSystemInfo(&si);
2059 buf = VirtualAlloc(NULL, si.dwPageSize * 4, MEM_COMMIT, PAGE_READWRITE);
2060 ok(buf != NULL, "VirtualAlloc failed with %lu\n", GetLastError());
2061 success = VirtualProtect(buf + si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
2062 ok(success, "VirtualProtect failed with %lu\n", GetLastError());
2063 success = VirtualProtect(buf + 3 * si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
2064 ok(success, "VirtualProtect failed with %lu\n", GetLastError());
2066 str1 = (WCHAR *)(buf + si.dwPageSize - sizeof(WCHAR));
2067 str2 = (WCHAR *)(buf + 3 * si.dwPageSize - sizeof(WCHAR));
2068 *str1 = 'A';
2069 *str2 = 'B';
2071 /* CompareStringW should abort on the first non-matching character */
2072 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, str1, 100, str2, 100);
2073 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2075 success = VirtualFree(buf, 0, MEM_RELEASE);
2076 ok(success, "VirtualFree failed with %lu\n", GetLastError());
2078 SetLastError(0xdeadbeef);
2079 ret = CompareStringW(LOCALE_USER_DEFAULT, SORT_DIGITSASNUMBERS, L"NULL", -1, L"NULL", -1);
2080 ok(ret == CSTR_EQUAL || broken(!ret && GetLastError() == ERROR_INVALID_FLAGS) /* <Win7 */,
2081 "expected CSTR_EQUAL, got %d, last error %ld\n", ret, GetLastError());
2083 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_EE, 3, ABC_FF, 3);
2084 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2085 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_EE, 5, ABC_FF, 3);
2086 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
2087 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_EE, 3, ABC_FF, 5);
2088 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2089 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_EE, 5, ABC_FF, 5);
2090 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2091 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_FF, 5, ABC_EE, 5);
2092 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
2094 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_EE, 4, A_ACUTE_BC, 4);
2095 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2096 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_EE, 4, A_ACUTE_BC_DECOMP, 5);
2097 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2098 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, A_ACUTE_BC, 4, A_ACUTE_BC_DECOMP, 5);
2099 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2101 ret = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE, ABC_EE, 3, A_ACUTE_BC, 4);
2102 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2103 ret = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE, ABC_EE, 4, A_ACUTE_BC_DECOMP, 5);
2104 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2105 ret = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE, A_ACUTE_BC, 4, A_ACUTE_BC_DECOMP, 5);
2106 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2108 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, ABC_EE, 4, A_NULL_BC, 4);
2109 ok(ret == CSTR_EQUAL, "expected CSTR_LESS_THAN, got %d\n", ret);
2110 ret = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE, ABC_EE, 4, A_NULL_BC, 4);
2111 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2113 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, A_NULL_BC, 4, A_ACUTE_BC, 4);
2114 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2115 ret = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE, A_NULL_BC, 4, A_ACUTE_BC, 4);
2116 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2118 ret = CompareStringW(LOCALE_USER_DEFAULT, 0, A_NULL_BC, 4, A_ACUTE_BC_DECOMP, 5);
2119 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2120 ret = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE, A_NULL_BC, 4, A_ACUTE_BC_DECOMP, 5);
2121 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2124 struct comparestringex_test {
2125 const char *locale;
2126 DWORD flags;
2127 const WCHAR first[2];
2128 const WCHAR second[2];
2129 INT ret;
2130 INT broken;
2133 static const struct comparestringex_test comparestringex_tests[] = {
2134 /* default behavior */
2135 { /* 0 */
2136 "tr-TR", 0,
2137 {'i',0}, {'I',0}, CSTR_LESS_THAN, -1
2139 { /* 1 */
2140 "tr-TR", 0,
2141 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1
2143 { /* 2 */
2144 "tr-TR", 0,
2145 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1
2147 { /* 3 */
2148 "tr-TR", 0,
2149 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1
2151 { /* 4 */
2152 "tr-TR", 0,
2153 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1
2155 { /* 5 */
2156 "tr-TR", 0,
2157 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1
2159 /* with NORM_IGNORECASE */
2160 { /* 6 */
2161 "tr-TR", NORM_IGNORECASE,
2162 {'i',0}, {'I',0}, CSTR_EQUAL, -1
2164 { /* 7 */
2165 "tr-TR", NORM_IGNORECASE,
2166 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1
2168 { /* 8 */
2169 "tr-TR", NORM_IGNORECASE,
2170 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1
2172 { /* 9 */
2173 "tr-TR", NORM_IGNORECASE,
2174 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1
2176 { /* 10 */
2177 "tr-TR", NORM_IGNORECASE,
2178 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1
2180 { /* 11 */
2181 "tr-TR", NORM_IGNORECASE,
2182 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1
2184 /* with NORM_LINGUISTIC_CASING */
2185 { /* 12 */
2186 "tr-TR", NORM_LINGUISTIC_CASING,
2187 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_LESS_THAN
2189 { /* 13 */
2190 "tr-TR", NORM_LINGUISTIC_CASING,
2191 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1
2193 { /* 14 */
2194 "tr-TR", NORM_LINGUISTIC_CASING,
2195 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN
2197 { /* 15 */
2198 "tr-TR", NORM_LINGUISTIC_CASING,
2199 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1
2201 { /* 16 */
2202 "tr-TR", NORM_LINGUISTIC_CASING,
2203 {'I',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN
2205 { /* 17 */
2206 "tr-TR", NORM_LINGUISTIC_CASING,
2207 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1
2209 /* with LINGUISTIC_IGNORECASE */
2210 { /* 18 */
2211 "tr-TR", LINGUISTIC_IGNORECASE,
2212 {'i',0}, {'I',0}, CSTR_EQUAL, -1
2214 { /* 19 */
2215 "tr-TR", LINGUISTIC_IGNORECASE,
2216 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1
2218 { /* 20 */
2219 "tr-TR", LINGUISTIC_IGNORECASE,
2220 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1
2222 { /* 21 */
2223 "tr-TR", LINGUISTIC_IGNORECASE,
2224 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1
2226 { /* 22 */
2227 "tr-TR", LINGUISTIC_IGNORECASE,
2228 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1
2230 { /* 23 */
2231 "tr-TR", LINGUISTIC_IGNORECASE,
2232 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1
2234 /* with NORM_LINGUISTIC_CASING | NORM_IGNORECASE */
2235 { /* 24 */
2236 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2237 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL
2239 { /* 25 */
2240 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2241 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN
2243 { /* 26 */
2244 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2245 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN
2247 { /* 27 */
2248 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2249 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1
2251 { /* 28 */
2252 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2253 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN
2255 { /* 29 */
2256 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2257 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1
2259 /* with NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE */
2260 { /* 30 */
2261 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2262 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL
2264 { /* 31 */
2265 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2266 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN
2268 { /* 32 */
2269 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2270 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN
2272 { /* 33 */
2273 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2274 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1
2276 { /* 34 */
2277 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2278 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN
2280 { /* 35 */
2281 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2282 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN
2286 static void test_CompareStringEx(void)
2288 const char *op[] = {"ERROR", "CSTR_LESS_THAN", "CSTR_EQUAL", "CSTR_GREATER_THAN"};
2289 WCHAR locale[6];
2290 INT ret, i;
2292 /* CompareStringEx is only available on Vista+ */
2293 if (!pCompareStringEx)
2295 win_skip("CompareStringEx not supported\n");
2296 return;
2299 for (i = 0; i < ARRAY_SIZE(comparestringex_tests); i++)
2301 const struct comparestringex_test *e = &comparestringex_tests[i];
2303 MultiByteToWideChar(CP_ACP, 0, e->locale, -1, locale, ARRAY_SIZE(locale));
2304 ret = pCompareStringEx(locale, e->flags, e->first, -1, e->second, -1, NULL, NULL, 0);
2305 ok(ret == e->ret || broken(ret == e->broken),
2306 "%d: got %s, expected %s\n", i, op[ret], op[e->ret]);
2311 static const DWORD lcmap_invalid_flags[] = {
2313 LCMAP_HIRAGANA | LCMAP_KATAKANA,
2314 LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
2315 LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
2316 LCMAP_LOWERCASE | SORT_STRINGSORT,
2317 LCMAP_UPPERCASE | NORM_IGNORESYMBOLS,
2318 LCMAP_LOWERCASE | NORM_IGNORESYMBOLS,
2319 LCMAP_UPPERCASE | NORM_IGNORENONSPACE,
2320 LCMAP_LOWERCASE | NORM_IGNORENONSPACE,
2321 LCMAP_HIRAGANA | NORM_IGNORENONSPACE,
2322 LCMAP_HIRAGANA | NORM_IGNORESYMBOLS,
2323 LCMAP_HIRAGANA | LCMAP_SIMPLIFIED_CHINESE,
2324 LCMAP_HIRAGANA | LCMAP_TRADITIONAL_CHINESE,
2325 LCMAP_KATAKANA | NORM_IGNORENONSPACE,
2326 LCMAP_KATAKANA | NORM_IGNORESYMBOLS,
2327 LCMAP_KATAKANA | LCMAP_SIMPLIFIED_CHINESE,
2328 LCMAP_KATAKANA | LCMAP_TRADITIONAL_CHINESE,
2329 LCMAP_FULLWIDTH | NORM_IGNORENONSPACE,
2330 LCMAP_FULLWIDTH | NORM_IGNORESYMBOLS,
2331 LCMAP_FULLWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2332 LCMAP_FULLWIDTH | LCMAP_TRADITIONAL_CHINESE,
2333 LCMAP_HALFWIDTH | NORM_IGNORENONSPACE,
2334 LCMAP_HALFWIDTH | NORM_IGNORESYMBOLS,
2335 LCMAP_HALFWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2336 LCMAP_HALFWIDTH | LCMAP_TRADITIONAL_CHINESE,
2339 static void test_LCMapStringA(void)
2341 int ret, ret2, i;
2342 char buf[256], buf2[256];
2343 static const char upper_case[] = "\tJUST! A, TEST; STRING 1/*+-.\r\n";
2344 static const char lower_case[] = "\tjust! a, test; string 1/*+-.\r\n";
2345 static const char symbols_stripped[] = "justateststring1";
2347 SetLastError(0xdeadbeef);
2348 ret = LCMapStringA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP | LCMAP_LOWERCASE,
2349 lower_case, -1, buf, sizeof(buf));
2350 ok(ret == lstrlenA(lower_case) + 1,
2351 "ret %d, error %ld, expected value %d\n",
2352 ret, GetLastError(), lstrlenA(lower_case) + 1);
2353 ok(!memcmp(buf, lower_case, ret), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2355 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
2356 upper_case, -1, buf, sizeof(buf));
2357 ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
2358 ok(GetLastError() == ERROR_INVALID_FLAGS,
2359 "unexpected error code %ld\n", GetLastError());
2361 /* test invalid flag combinations */
2362 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2363 lstrcpyA(buf, "foo");
2364 SetLastError(0xdeadbeef);
2365 ret = LCMapStringA(LOCALE_USER_DEFAULT, lcmap_invalid_flags[i],
2366 lower_case, -1, buf, sizeof(buf));
2367 ok(GetLastError() == ERROR_INVALID_FLAGS,
2368 "LCMapStringA (flag %08lx) unexpected error code %ld\n",
2369 lcmap_invalid_flags[i], GetLastError());
2370 ok(!ret, "LCMapStringA (flag %08lx) should return 0, got %d\n",
2371 lcmap_invalid_flags[i], ret);
2374 /* test LCMAP_LOWERCASE */
2375 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2376 upper_case, -1, buf, sizeof(buf));
2377 ok(ret == lstrlenA(upper_case) + 1,
2378 "ret %d, error %ld, expected value %d\n",
2379 ret, GetLastError(), lstrlenA(upper_case) + 1);
2380 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2382 /* test LCMAP_UPPERCASE */
2383 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2384 lower_case, -1, buf, sizeof(buf));
2385 ok(ret == lstrlenA(lower_case) + 1,
2386 "ret %d, error %ld, expected value %d\n",
2387 ret, GetLastError(), lstrlenA(lower_case) + 1);
2388 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2390 /* test buffer overflow */
2391 SetLastError(0xdeadbeef);
2392 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2393 lower_case, -1, buf, 4);
2394 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2395 "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
2397 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2398 lstrcpyA(buf, lower_case);
2399 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2400 buf, -1, buf, sizeof(buf));
2401 if (!ret) /* Win9x */
2402 trace("Ignoring LCMapStringA(LCMAP_UPPERCASE, buf, buf) error on Win9x\n");
2403 else
2405 ok(ret == lstrlenA(lower_case) + 1,
2406 "ret %d, error %ld, expected value %d\n",
2407 ret, GetLastError(), lstrlenA(lower_case) + 1);
2408 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2410 lstrcpyA(buf, upper_case);
2411 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2412 buf, -1, buf, sizeof(buf));
2413 if (!ret) /* Win9x */
2414 trace("Ignoring LCMapStringA(LCMAP_LOWERCASE, buf, buf) error on Win9x\n");
2415 else
2417 ok(ret == lstrlenA(upper_case) + 1,
2418 "ret %d, error %ld, expected value %d\n",
2419 ret, GetLastError(), lstrlenA(lower_case) + 1);
2420 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2423 /* otherwise src == dst should fail */
2424 SetLastError(0xdeadbeef);
2425 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
2426 buf, 10, buf, sizeof(buf));
2427 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2428 GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
2429 "unexpected error code %ld\n", GetLastError());
2430 ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
2432 /* test whether '\0' is always appended */
2433 memset(buf, 0xff, sizeof(buf));
2434 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2435 upper_case, -1, buf, sizeof(buf));
2436 ok(ret, "LCMapStringA must succeed\n");
2437 ok(buf[ret-1] == 0, "LCMapStringA not null-terminated\n");
2438 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2439 upper_case, lstrlenA(upper_case), buf2, sizeof(buf2));
2440 ok(ret2, "LCMapStringA must succeed\n");
2441 ok(buf2[ret2-1] == 0, "LCMapStringA not null-terminated\n" );
2442 ok(ret == ret2, "lengths of sort keys must be equal\n");
2443 ok(!memcmp(buf, buf2, ret), "sort keys must be equal\n");
2445 /* test we get the same length when no dest buffer is provided */
2446 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2447 upper_case, lstrlenA(upper_case), NULL, 0);
2448 ok(ret2, "LCMapStringA must succeed\n");
2449 ok(ret == ret2, "lengths of sort keys must be equal (%d vs %d)\n", ret, ret2);
2451 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2452 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
2453 upper_case, -1, buf, sizeof(buf));
2454 ok(ret, "LCMapStringA must succeed\n");
2455 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2456 lower_case, -1, buf2, sizeof(buf2));
2457 ok(ret2, "LCMapStringA must succeed\n");
2458 ok(ret == ret2, "lengths of sort keys must be equal\n");
2459 ok(!memcmp(buf, buf2, ret), "sort keys must be equal\n");
2461 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2462 results from plain LCMAP_SORTKEY on Vista */
2464 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2465 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2466 lower_case, -1, buf, sizeof(buf));
2467 ok(ret, "LCMapStringA must succeed\n");
2468 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2469 symbols_stripped, -1, buf2, sizeof(buf2));
2470 ok(ret2, "LCMapStringA must succeed\n");
2471 ok(ret == ret2, "lengths of sort keys must be equal\n");
2472 ok(!memcmp(buf, buf2, ret), "sort keys must be equal\n");
2474 /* test NORM_IGNORENONSPACE */
2475 lstrcpyA(buf, "foo");
2476 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
2477 lower_case, -1, buf, sizeof(buf));
2478 ok(ret == lstrlenA(lower_case) + 1, "LCMapStringA should return %d, ret = %d\n",
2479 lstrlenA(lower_case) + 1, ret);
2480 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2482 /* test NORM_IGNORESYMBOLS */
2483 lstrcpyA(buf, "foo");
2484 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
2485 lower_case, -1, buf, sizeof(buf));
2486 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2487 lstrlenA(symbols_stripped) + 1, ret);
2488 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2490 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2491 lstrcpyA(buf, "foo");
2492 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE,
2493 lower_case, -1, buf, sizeof(buf));
2494 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2495 lstrlenA(symbols_stripped) + 1, ret);
2496 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2498 /* test srclen = 0 */
2499 SetLastError(0xdeadbeef);
2500 ret = LCMapStringA(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
2501 ok(!ret, "LCMapStringA should fail with srclen = 0\n");
2502 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2503 "unexpected error code %ld\n", GetLastError());
2506 typedef INT (*lcmapstring_wrapper)(DWORD, LPCWSTR, INT, LPWSTR, INT);
2508 static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *func_name)
2510 const static WCHAR japanese_text[] = {
2511 0x3044, 0x309d, 0x3084, 0x3001, 0x30a4, 0x30fc, 0x30cf, 0x30c8,
2512 0x30fc, 0x30f4, 0x30a9, 0x306e, 0x91ce, 0x539f, 0x306f, 0x5e83,
2513 0x3044, 0x3093, 0x3060, 0x3088, 0x3002, 0
2515 const static WCHAR hiragana_text[] = {
2516 0x3044, 0x309d, 0x3084, 0x3001, 0x3044, 0x30fc, 0x306f, 0x3068,
2517 0x30fc, 0x3094, 0x3049, 0x306e, 0x91ce, 0x539f, 0x306f, 0x5e83,
2518 0x3044, 0x3093, 0x3060, 0x3088, 0x3002, 0
2520 const static WCHAR katakana_text[] = {
2521 0x30a4, 0x30fd, 0x30e4, 0x3001, 0x30a4, 0x30fc, 0x30cf, 0x30c8,
2522 0x30fc, 0x30f4, 0x30a9, 0x30ce, 0x91ce, 0x539f, 0x30cf, 0x5e83,
2523 0x30a4, 0x30f3, 0x30c0, 0x30e8, 0x3002, 0
2525 const static WCHAR halfwidth_text[] = {
2526 0x3044, 0x309d, 0x3084, 0xff64, 0xff72, 0xff70, 0xff8a, 0xff84,
2527 0xff70, 0xff73, 0xff9e, 0xff6b, 0x306e, 0x91ce, 0x539f, 0x306f,
2528 0x5e83, 0x3044, 0x3093, 0x3060, 0x3088, 0xff61, 0
2530 const static WCHAR halfwidth_text2[] = {
2531 0xff72, 0x30fd, 0xff94, 0xff64, 0xff72, 0xff70, 0xff8a, 0xff84,
2532 0xff70, 0xff73, 0xff9e, 0xff6b, 0xff89, 0x91ce, 0x539f, 0xff8a,
2533 0x5e83, 0xff72, 0xff9d, 0xff80, 0xff9e, 0xff96, 0xff61, 0
2535 const static WCHAR math_text[] = {
2536 0xd835, 0xdc00, 0xd835, 0xdc01, 0xd835, 0xdc02, 0xd835, 0xdc03,
2537 0xd835, 0xdd52, 0xd835, 0xdd53, 0xd835, 0xdd54, 0xd835, 0xdd55, 0
2539 const static WCHAR math_result[] = L"ABCDabcd";
2540 const static WCHAR math_arabic_text[] = {
2541 0xd83b, 0xde00, 0xd83b, 0xde01, 0xd83b, 0xde02, 0xd83b, 0xde03,
2542 0xd83b, 0xde10, 0xd83b, 0xde11, 0xd83b, 0xde12, 0xd83b, 0xde13, 0
2544 const static WCHAR math_arabic_result[] = {
2545 0x0627, 0x0628, 0x062c, 0x062f, 0x0641, 0x0635, 0x0642, 0x0631, 0
2547 const static WCHAR cjk_compat_text[] = {
2548 0xd87e, 0xdc20, 0xd87e, 0xdc21, 0xd87e, 0xdc22, 0xd87e, 0xdc23,
2549 0xd87e, 0xdc24, 0xd87e, 0xdc25, 0xd87e, 0xdc26, 0xd87e, 0xdc27, 0
2551 const static WCHAR cjk_compat_result[] = {
2552 0x523b, 0x5246, 0x5272, 0x5277, 0x3515, 0x52c7, 0x52c9, 0x52e4, 0
2554 const static WCHAR accents_text[] = {
2555 0x00e0, 0x00e7, ' ', 0x00e9, ',', 0x00ee, 0x00f1, '/', 0x00f6, 0x00fb, 0x00fd, '!', 0
2557 const static WCHAR accents_result[] = L"ac e,in/ouy!";
2558 const static WCHAR accents_result2[] = L"aceinouy";
2559 int ret, ret2, i;
2560 WCHAR buf[256], buf2[256];
2561 char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
2563 /* LCMAP_LOWERCASE | LCMAP_UPPERCASE makes LCMAP_TITLECASE, so it's valid now. */
2564 ret = func_ptr(LCMAP_LOWERCASE | LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2565 todo_wine ok(ret == lstrlenW(title_case) + 1 || broken(!ret),
2566 "%s ret %d, error %ld, expected value %d\n", func_name,
2567 ret, GetLastError(), lstrlenW(title_case) + 1);
2568 todo_wine ok(lstrcmpW(buf, title_case) == 0 || broken(!ret),
2569 "Expected title case string\n");
2571 /* test invalid flag combinations */
2572 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2573 lstrcpyW(buf, fooW);
2574 SetLastError(0xdeadbeef);
2575 ret = func_ptr(lcmap_invalid_flags[i],
2576 lower_case, -1, buf, sizeof(buf));
2577 ok(GetLastError() == ERROR_INVALID_FLAGS,
2578 "%s (flag %08lx) unexpected error code %ld\n",
2579 func_name, lcmap_invalid_flags[i], GetLastError());
2580 ok(!ret, "%s (flag %08lx) should return 0, got %d\n",
2581 func_name, lcmap_invalid_flags[i], ret);
2584 /* test LCMAP_LOWERCASE */
2585 ret = func_ptr(LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2586 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2587 ret, GetLastError(), lstrlenW(upper_case) + 1);
2588 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2590 /* test LCMAP_UPPERCASE */
2591 ret = func_ptr(LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2592 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2593 ret, GetLastError(), lstrlenW(lower_case) + 1);
2594 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2596 /* test LCMAP_HIRAGANA */
2597 ret = func_ptr(LCMAP_HIRAGANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2598 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2599 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2600 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2602 buf[0] = 0x30f5; /* KATAKANA LETTER SMALL KA */
2603 ret = func_ptr(LCMAP_HIRAGANA, buf, 1, buf2, 1);
2604 ok(ret == 1, "%s ret %d, error %ld, expected value 1\n", func_name,
2605 ret, GetLastError());
2606 /* U+3095: HIRAGANA LETTER SMALL KA was added in Unicode 3.2 */
2607 ok(buf2[0] == 0x3095 || broken(buf2[0] == 0x30f5 /* Vista and earlier versions */),
2608 "%s expected %04x, got %04x\n", func_name, 0x3095, buf2[0]);
2610 /* test LCMAP_KATAKANA | LCMAP_LOWERCASE */
2611 ret = func_ptr(LCMAP_KATAKANA | LCMAP_LOWERCASE, japanese_text, -1, buf, ARRAY_SIZE(buf));
2612 ok(ret == lstrlenW(katakana_text) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2613 ret, GetLastError(), lstrlenW(katakana_text) + 1);
2614 ok(!lstrcmpW(buf, katakana_text), "%s string compare mismatch\n", func_name);
2616 /* test LCMAP_FULLWIDTH */
2617 ret = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2618 ok(ret == lstrlenW(japanese_text) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2619 ret, GetLastError(), lstrlenW(japanese_text) + 1);
2620 ok(!lstrcmpW(buf, japanese_text), "%s string compare mismatch\n", func_name);
2622 ret2 = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, NULL, 0);
2623 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret2, ret);
2625 ret = func_ptr(LCMAP_FULLWIDTH, math_text, -1, buf, ARRAY_SIZE(buf));
2626 ok(ret == lstrlenW(buf) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2627 ret, GetLastError(), lstrlenW(buf) + 1);
2628 ok(!lstrcmpW(buf, math_result), "%s string compare mismatch %s\n", func_name, debugstr_w(buf));
2630 ret = func_ptr(LCMAP_FULLWIDTH, math_arabic_text, -1, buf, ARRAY_SIZE(buf));
2631 ok(ret == lstrlenW(buf) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2632 ret, GetLastError(), lstrlenW(buf) + 1);
2633 ok(!lstrcmpW(buf, math_arabic_result) ||
2634 broken(!lstrcmpW( buf, math_arabic_text )), /* win7 */
2635 "%s string compare mismatch %s\n", func_name, debugstr_w(buf));
2637 ret = func_ptr(LCMAP_FULLWIDTH, cjk_compat_text, -1, buf, ARRAY_SIZE(buf));
2638 ok(ret == lstrlenW(buf) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2639 ret, GetLastError(), lstrlenW(buf) + 1);
2640 ok(!lstrcmpW(buf, cjk_compat_result), "%s string compare mismatch %s\n", func_name, debugstr_w(buf));
2642 /* test LCMAP_FULLWIDTH | LCMAP_HIRAGANA
2643 (half-width katakana is converted into full-width hiragana) */
2644 ret = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2645 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2646 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2647 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2649 ret2 = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, NULL, 0);
2650 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2652 /* LCMAP_FULLWIDTH | LCMAP_KATAKANA maps to full-width first */
2653 ret = func_ptr(LCMAP_FULLWIDTH | LCMAP_KATAKANA, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2654 ok(ret == lstrlenW(katakana_text) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2655 ret, GetLastError(), lstrlenW(katakana_text) + 1);
2656 ok(!lstrcmpW(buf, katakana_text), "%s string compare mismatch\n", func_name);
2658 /* test LCMAP_HALFWIDTH */
2659 ret = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, buf, ARRAY_SIZE(buf));
2660 ok(ret == lstrlenW(halfwidth_text) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2661 ret, GetLastError(), lstrlenW(halfwidth_text) + 1);
2662 ok(!lstrcmpW(buf, halfwidth_text), "%s string compare mismatch\n", func_name);
2664 ret2 = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, NULL, 0);
2665 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2667 /* test LCMAP_HALFWIDTH | LCMAP_KATAKANA
2668 (hiragana character is converted into half-width katakana) */
2669 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_KATAKANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2670 ok(ret == lstrlenW(halfwidth_text2) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2671 ret, GetLastError(), lstrlenW(halfwidth_text2) + 1);
2672 ok(!lstrcmpW(buf, halfwidth_text2), "%s string compare mismatch\n", func_name);
2674 ret2 = func_ptr(LCMAP_HALFWIDTH | LCMAP_KATAKANA, japanese_text, -1, NULL, 0);
2675 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2677 /* LCMAP_HALFWIDTH | LCMAP_HIRAGANA maps to Hiragana first */
2678 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_HIRAGANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2679 ret2 = func_ptr(LCMAP_HALFWIDTH, hiragana_text, -1, buf2, ARRAY_SIZE(buf2));
2680 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2681 ok(!lstrcmpW(buf, buf2), "%s string compare mismatch %s vs %s\n", func_name, debugstr_w(buf), debugstr_w(buf2));
2683 /* test buffer overflow */
2684 SetLastError(0xdeadbeef);
2685 ret = func_ptr(LCMAP_UPPERCASE,
2686 lower_case, -1, buf, 4);
2687 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2688 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2690 /* KATAKANA LETTER GA (U+30AC) is converted into two half-width characters.
2691 Thus, it requires two WCHARs. */
2692 buf[0] = 0x30ac;
2693 SetLastError(0xdeadbeef);
2694 ret = func_ptr(LCMAP_HALFWIDTH, buf, 1, buf2, 1);
2695 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2696 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2698 buf[0] = 'a';
2699 buf[1] = 0x30ac;
2700 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 0);
2701 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2703 SetLastError(0xdeadbeef);
2704 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 1);
2705 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2706 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2708 SetLastError(0xdeadbeef);
2709 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 2);
2710 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2711 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2713 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 3);
2714 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2716 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 4);
2717 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2719 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2720 lstrcpyW(buf, lower_case);
2721 ret = func_ptr(LCMAP_UPPERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2722 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2723 ret, GetLastError(), lstrlenW(lower_case) + 1);
2724 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2726 lstrcpyW(buf, upper_case);
2727 ret = func_ptr(LCMAP_LOWERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2728 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %ld, expected value %d\n", func_name,
2729 ret, GetLastError(), lstrlenW(lower_case) + 1);
2730 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2732 /* otherwise src == dst should fail */
2733 SetLastError(0xdeadbeef);
2734 ret = func_ptr(LCMAP_SORTKEY | LCMAP_UPPERCASE,
2735 buf, 10, buf, sizeof(buf));
2736 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2737 GetLastError() == ERROR_INVALID_PARAMETER /* Win7+ */,
2738 "%s unexpected error code %ld\n", func_name, GetLastError());
2739 ok(!ret, "%s src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n", func_name);
2741 /* test whether '\0' is always appended */
2742 ret = func_ptr(LCMAP_SORTKEY,
2743 upper_case, -1, buf, sizeof(buf));
2744 ok(ret, "%s func_ptr must succeed\n", func_name);
2745 ret2 = func_ptr(LCMAP_SORTKEY,
2746 upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
2747 ok(ret, "%s func_ptr must succeed\n", func_name);
2748 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2749 ok(!memcmp(p_buf, p_buf2, ret), "%s sort keys must be equal\n", func_name);
2751 /* test contents with short buffer */
2752 memset( buf, 0xcc, sizeof(buf) );
2753 ret = func_ptr(LCMAP_SORTKEY, upper_case, -1, buf, ret - 1);
2754 ok( !ret, "%s succeeded with %u\n", func_name, ret );
2755 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "%s wrong error %lu\n", func_name, GetLastError() );
2756 ret = (char *)memchr( p_buf, 0xcc, sizeof(buf) ) - p_buf;
2757 ok( ret, "%s buffer not filled\n", func_name );
2758 ok( p_buf[ret - 1] == 0x01, "%s buffer filled up to %02x\n", func_name, (BYTE)p_buf[ret - 1] );
2759 ok( !memcmp( p_buf, p_buf2, ret - 1 ), "%s buffers differ\n", func_name );
2761 memset( buf, 0xcc, sizeof(buf) );
2762 ret = func_ptr(LCMAP_SORTKEY, upper_case, -1, buf, ret2 - 20);
2763 ok( !ret, "%s succeeded with %u\n", func_name, ret );
2764 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "%s wrong error %lu\n", func_name, GetLastError() );
2765 ret = (char *)memchr( p_buf, 0xcc, sizeof(buf) ) - p_buf;
2766 ok( ret, "%s buffer not filled\n", func_name );
2767 ok( p_buf[ret - 1] == 0x01, "%s buffer filled up to %02x\n", func_name, (BYTE)p_buf[ret - 1] );
2768 ok( !memcmp( p_buf, p_buf2, ret - 1 ), "%s buffers differ\n", func_name );
2770 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2771 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORECASE,
2772 upper_case, -1, buf, sizeof(buf));
2773 ok(ret, "%s func_ptr must succeed\n", func_name);
2774 ret2 = func_ptr(LCMAP_SORTKEY,
2775 lower_case, -1, buf2, sizeof(buf2));
2776 ok(ret2, "%s func_ptr must succeed\n", func_name);
2777 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2778 ok(!memcmp(p_buf, p_buf2, ret), "%s sort keys must be equal\n", func_name);
2780 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2781 results from plain LCMAP_SORTKEY on Vista */
2783 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2784 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2785 lower_case, -1, buf, sizeof(buf));
2786 ok(ret, "%s func_ptr must succeed\n", func_name);
2787 ret2 = func_ptr(LCMAP_SORTKEY,
2788 symbols_stripped, -1, buf2, sizeof(buf2));
2789 ok(ret2, "%s func_ptr must succeed\n", func_name);
2790 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2791 ok(!memcmp(p_buf, p_buf2, ret), "%s sort keys must be equal\n", func_name);
2793 /* test NORM_IGNORENONSPACE */
2794 lstrcpyW(buf, fooW);
2795 ret = func_ptr(NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2796 ok(ret == lstrlenW(lower_case) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2797 lstrlenW(lower_case) + 1, ret);
2798 ok(!lstrcmpW(buf, lower_case), "%s string comparison mismatch\n", func_name);
2800 lstrcpyW(buf, fooW);
2801 ret = func_ptr(NORM_IGNORENONSPACE, accents_text, -1, buf, ARRAY_SIZE(buf));
2802 ok(ret == lstrlenW(buf) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2803 lstrlenW(buf) + 1, ret);
2804 ok(!lstrcmpW(buf, accents_result), "%s string comparison mismatch %s\n", func_name, debugstr_w(buf));
2806 /* test NORM_IGNORESYMBOLS */
2807 lstrcpyW(buf, fooW);
2808 ret = func_ptr(NORM_IGNORESYMBOLS, lower_case, -1, buf, ARRAY_SIZE(buf));
2809 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2810 lstrlenW(symbols_stripped) + 1, ret);
2811 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2813 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2814 lstrcpyW(buf, fooW);
2815 ret = func_ptr(NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2816 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2817 lstrlenW(symbols_stripped) + 1, ret);
2818 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2820 lstrcpyW(buf, fooW);
2821 ret = func_ptr(NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE, accents_text, -1, buf, ARRAY_SIZE(buf));
2822 ok(ret == lstrlenW(buf) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2823 lstrlenW(buf) + 1, ret);
2824 ok(!lstrcmpW(buf, accents_result2), "%s string comparison mismatch %s\n", func_name, debugstr_w(buf));
2826 /* test small buffer */
2827 lstrcpyW(buf, fooW);
2828 ret = func_ptr(LCMAP_SORTKEY, lower_case, -1, buf, 2);
2829 ok(ret == 0, "Expected a failure\n");
2830 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2831 "%s unexpected error code %ld\n", func_name, GetLastError());
2833 /* test srclen = 0 */
2834 SetLastError(0xdeadbeef);
2835 ret = func_ptr(0, upper_case, 0, buf, ARRAY_SIZE(buf));
2836 ok(!ret, "%s func_ptr should fail with srclen = 0\n", func_name);
2837 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2838 "%s unexpected error code %ld\n", func_name, GetLastError());
2841 static INT LCMapStringW_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2843 return LCMapStringW(LOCALE_USER_DEFAULT, flags, src, srclen, dst, dstlen);
2846 static void test_LCMapStringW(void)
2848 int ret;
2849 WCHAR buf[256];
2851 trace("testing LCMapStringW\n");
2853 SetLastError(0xdeadbeef);
2854 ret = LCMapStringW((LCID)-1, LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2855 ok(!ret, "LCMapStringW should fail with bad lcid\n");
2856 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %ld\n", GetLastError());
2858 SetLastError(0xdeadbeef);
2859 ret = LCMapStringW((LCID)0xdead, LCMAP_HIRAGANA, upper_case, -1, buf, ARRAY_SIZE(buf));
2860 ok(!ret, "LCMapStringW should fail with bad lcid\n");
2861 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %ld\n", GetLastError());
2863 test_lcmapstring_unicode(LCMapStringW_wrapper, "LCMapStringW:");
2866 static INT LCMapStringEx_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2868 return pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2871 static void test_LCMapStringEx(void)
2873 int ret;
2874 WCHAR buf[256];
2876 if (!pLCMapStringEx)
2878 win_skip( "LCMapStringEx not available\n" );
2879 return;
2882 trace("testing LCMapStringEx\n");
2884 SetLastError(0xdeadbeef);
2885 ret = pLCMapStringEx(invalidW, LCMAP_LOWERCASE,
2886 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 0);
2887 ok(!ret, "LCMapStringEx should fail with bad locale name\n");
2888 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %ld\n", GetLastError());
2890 SetLastError(0xdeadbeef);
2891 ret = pLCMapStringEx(invalidW, LCMAP_HIRAGANA,
2892 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 0);
2893 ok(ret, "LCMapStringEx should not fail with bad locale name\n");
2895 /* test reserved parameters */
2896 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2897 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 1);
2898 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %ld, expected value %d\n",
2899 ret, GetLastError(), lstrlenW(upper_case) + 1);
2900 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2902 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2903 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, (void*)1, 0);
2904 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %ld, expected value %d\n",
2905 ret, GetLastError(), lstrlenW(upper_case) + 1);
2906 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2908 /* crashes on native */
2909 if(0)
2910 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2911 upper_case, -1, buf, ARRAY_SIZE(buf), (void*)1, NULL, 0);
2913 test_lcmapstring_unicode(LCMapStringEx_wrapper, "LCMapStringEx:");
2916 struct neutralsublang_name_t {
2917 WCHAR name[3];
2918 WCHAR sname[16];
2919 LCID lcid;
2922 static const struct neutralsublang_name_t neutralsublang_names[] = {
2923 { {'a','r',0}, {'a','r','-','S','A',0}, MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
2924 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0}, MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
2925 { {'d','e',0}, {'d','e','-','D','E',0}, MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
2926 { {'e','n',0}, {'e','n','-','U','S',0}, MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
2927 { {'e','s',0}, {'e','s','-','E','S',0}, MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT) },
2928 { {'g','a',0}, {'g','a','-','I','E',0}, MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT) },
2929 { {'i','t',0}, {'i','t','-','I','T',0}, MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
2930 { {'m','s',0}, {'m','s','-','M','Y',0}, MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
2931 { {'n','l',0}, {'n','l','-','N','L',0}, MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
2932 { {'p','t',0}, {'p','t','-','B','R',0}, MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
2933 { {'s','r',0}, {'s','r','-','L','a','t','n','-','R','S',0}, MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN), SORT_DEFAULT) },
2934 { {'s','v',0}, {'s','v','-','S','E',0}, MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
2935 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0}, MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
2936 { {'z','h',0}, {'z','h','-','C','N',0}, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
2937 { {0} }
2940 static void test_LocaleNameToLCID(void)
2942 LCID lcid, expect;
2943 NTSTATUS status;
2944 INT ret;
2945 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
2946 WCHAR expbuff[LOCALE_NAME_MAX_LENGTH];
2947 const struct neutralsublang_name_t *ptr;
2949 if (!pLocaleNameToLCID)
2951 win_skip( "LocaleNameToLCID not available\n" );
2952 return;
2955 /* special cases */
2956 buffer[0] = 0;
2957 SetLastError(0xdeadbeef);
2958 lcid = pLocaleNameToLCID(LOCALE_NAME_USER_DEFAULT, 0);
2959 ok(lcid == GetUserDefaultLCID() || broken(GetLastError() == ERROR_INVALID_PARAMETER /* Vista */),
2960 "Expected lcid == %08lx, got %08lx, error %ld\n", GetUserDefaultLCID(), lcid, GetLastError());
2961 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2962 ok(ret > 0, "Expected ret > 0, got %d, error %ld\n", ret, GetLastError());
2963 trace("%08lx, %s\n", lcid, wine_dbgstr_w(buffer));
2965 buffer[0] = 0;
2966 SetLastError(0xdeadbeef);
2967 lcid = LocaleNameToLCID(LOCALE_NAME_SYSTEM_DEFAULT, 0);
2968 ok(lcid == GetSystemDefaultLCID(),
2969 "Expected lcid == %08lx, got %08lx, error %ld\n", GetSystemDefaultLCID(), lcid, GetLastError());
2970 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2971 ok(ret > 0, "Expected ret > 0, got %d, error %ld\n", ret, GetLastError());
2972 trace("%08lx, %s\n", lcid, wine_dbgstr_w(buffer));
2974 buffer[0] = 0;
2975 SetLastError(0xdeadbeef);
2976 lcid = pLocaleNameToLCID(LOCALE_NAME_INVARIANT, 0);
2977 ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08lx, error %ld\n", lcid, GetLastError());
2978 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2979 ok(ret > 0, "Expected ret > 0, got %d, error %ld\n", ret, GetLastError());
2980 trace("%08lx, %s\n", lcid, wine_dbgstr_w(buffer));
2982 pLCIDToLocaleName(GetUserDefaultLCID(), expbuff, LOCALE_NAME_MAX_LENGTH, 0);
2983 ret = pLCIDToLocaleName(LOCALE_NEUTRAL, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2984 ok(ret > 0, "Expected ret > 0, got %d, error %ld\n", ret, GetLastError());
2985 ok( !wcscmp( buffer, expbuff ), "got %s / %s\n", debugstr_w(buffer), debugstr_w(expbuff));
2987 ret = pLCIDToLocaleName(LOCALE_CUSTOM_DEFAULT, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2988 ok(ret > 0, "Expected ret > 0, got %d, error %ld\n", ret, GetLastError());
2989 ok( !wcscmp( buffer, expbuff ), "got %s / %s\n", debugstr_w(buffer), debugstr_w(expbuff));
2991 SetLastError( 0xdeadbeef );
2992 ret = pLCIDToLocaleName(LOCALE_CUSTOM_UNSPECIFIED, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2993 ok(ret > 0 || broken(!ret), /* <= win8 */ "Expected ret > 0, got %d, error %ld\n", ret, GetLastError());
2994 if (ret) ok( !wcscmp( buffer, expbuff ), "got %s / %s\n", debugstr_w(buffer), debugstr_w(expbuff));
2996 SetLastError( 0xdeadbeef );
2997 ret = pLCIDToLocaleName(LOCALE_CUSTOM_UI_DEFAULT, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2998 if (ret) trace("%08x, %s\n", GetUserDefaultUILanguage(), wine_dbgstr_w(buffer));
2999 else ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError());
3001 /* bad name */
3002 SetLastError(0xdeadbeef);
3003 lcid = pLocaleNameToLCID(invalidW, 0);
3004 ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
3005 "Expected lcid == 0, got %08lx, error %ld\n", lcid, GetLastError());
3007 /* lower-case */
3008 lcid = pLocaleNameToLCID(L"es-es", 0);
3009 ok(lcid == MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT), "Got wrong lcid for es-es: 0x%lx\n", lcid);
3011 /* english neutral name */
3012 lcid = pLocaleNameToLCID(L"en", LOCALE_ALLOW_NEUTRAL_NAMES);
3013 ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT) ||
3014 broken(lcid == 0) /* Vista */, "got 0x%04lx\n", lcid);
3015 lcid = pLocaleNameToLCID(L"en", 0);
3016 ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) ||
3017 broken(lcid == 0) /* Vista */, "got 0x%04lx\n", lcid);
3018 if (lcid)
3020 for (ptr = neutralsublang_names; *ptr->name; ptr++)
3022 lcid = pLocaleNameToLCID(ptr->name, 0);
3023 ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04lx, expected 0x%04lx\n",
3024 wine_dbgstr_w(ptr->name), lcid, ptr->lcid);
3026 *buffer = 0;
3027 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
3028 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(ptr->name), ret);
3029 ok(!lstrcmpW(ptr->sname, buffer), "%s: got wrong locale name %s\n",
3030 wine_dbgstr_w(ptr->name), wine_dbgstr_w(buffer));
3034 /* zh-Hant has LCID 0x7c04, but LocaleNameToLCID actually returns 0x0c04, which is the LCID of zh-HK */
3035 lcid = pLocaleNameToLCID(L"zh-Hant", 0);
3036 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
3037 "%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-Hant"), lcid);
3038 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
3039 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hant"), ret);
3040 ok(!lstrcmpW(L"zh-HK", buffer), "%s: got wrong locale name %s\n",
3041 wine_dbgstr_w(L"zh-Hant"), wine_dbgstr_w(buffer));
3042 /* check that 0x7c04 also works and is mapped to zh-HK */
3043 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE_TRADITIONAL, SUBLANG_CHINESE_TRADITIONAL),
3044 buffer, ARRAY_SIZE(buffer), 0);
3045 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hant"), ret);
3046 ok(!lstrcmpW(L"zh-HK", buffer), "%s: got wrong locale name %s\n",
3047 wine_dbgstr_w(L"zh-Hant"), wine_dbgstr_w(buffer));
3049 /* zh-hant */
3050 lcid = pLocaleNameToLCID(L"zh-hant", 0);
3051 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
3052 "%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-hant"), lcid);
3053 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
3054 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-hant"), ret);
3055 ok(!lstrcmpW(L"zh-HK", buffer), "%s: got wrong locale name %s\n",
3056 wine_dbgstr_w(L"zh-hant"), wine_dbgstr_w(buffer));
3058 /* zh-Hans has LCID 0x0004, but LocaleNameToLCID actually returns 0x0804, which is the LCID of zh-CN */
3059 lcid = pLocaleNameToLCID(L"zh-Hans", 0);
3060 /* check that LocaleNameToLCID actually returns 0x0804 */
3061 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
3062 "%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-Hans"), lcid);
3063 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
3064 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hans"), ret);
3065 ok(!lstrcmpW(L"zh-CN", buffer), "%s: got wrong locale name %s\n",
3066 wine_dbgstr_w(L"zh-Hans"), wine_dbgstr_w(buffer));
3067 /* check that 0x0004 also works and is mapped to zh-CN */
3068 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE, SUBLANG_NEUTRAL), buffer, ARRAY_SIZE(buffer), 0);
3069 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-Hans"), ret);
3070 ok(!lstrcmpW(L"zh-CN", buffer), "%s: got wrong locale name %s\n",
3071 wine_dbgstr_w(L"zh-Hans"), wine_dbgstr_w(buffer));
3073 /* zh-hans */
3074 lcid = pLocaleNameToLCID(L"zh-hans", 0);
3075 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
3076 "%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-hans"), lcid);
3077 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
3078 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(L"zh-hans"), ret);
3079 ok(!lstrcmpW(L"zh-CN", buffer), "%s: got wrong locale name %s\n",
3080 wine_dbgstr_w(L"zh-hans"), wine_dbgstr_w(buffer));
3082 /* de-DE_phoneb */
3083 lcid = pLocaleNameToLCID(L"de-DE_phoneb", 0);
3084 ok(lcid == MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT), SORT_GERMAN_PHONE_BOOK),
3085 "%s: got wrong lcid 0x%04lx\n", wine_dbgstr_w(L"zh-hans"), lcid);
3086 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
3087 ok(!lstrcmpW(L"de-DE_phoneb", buffer), "got wrong locale name %s\n",
3088 wine_dbgstr_w(buffer));
3091 if (pRtlLocaleNameToLcid)
3093 status = pRtlLocaleNameToLcid( LOCALE_NAME_USER_DEFAULT, &lcid, 0 );
3094 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3095 status = pRtlLocaleNameToLcid( LOCALE_NAME_SYSTEM_DEFAULT, &lcid, 0 );
3096 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3097 status = pRtlLocaleNameToLcid( invalidW, &lcid, 0 );
3098 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3100 lcid = 0;
3101 status = pRtlLocaleNameToLcid( LOCALE_NAME_INVARIANT, &lcid, 0 );
3102 ok( !status, "failed error %lx\n", status );
3103 ok( lcid == LANG_INVARIANT, "got %08lx\n", lcid );
3105 lcid = 0;
3106 status = pRtlLocaleNameToLcid( localeW, &lcid, 0 );
3107 ok( !status, "failed error %lx\n", status );
3108 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), "got %08lx\n", lcid );
3110 lcid = 0;
3111 status = pRtlLocaleNameToLcid( L"es-es", &lcid, 0 );
3112 ok( !status, "failed error %lx\n", status );
3113 ok( lcid == MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), "got %08lx\n", lcid );
3115 lcid = 0;
3116 status = pRtlLocaleNameToLcid( L"de-DE_phoneb", &lcid, 0 );
3117 ok( !status, "failed error %lx\n", status );
3118 ok( lcid == 0x00010407, "got %08lx\n", lcid );
3120 lcid = 0;
3121 status = pRtlLocaleNameToLcid( L"DE_de-PHONEB", &lcid, 0 );
3122 ok( !status || broken( status == STATUS_INVALID_PARAMETER_1 ), "failed error %lx\n", status );
3123 if (!status) ok( lcid == 0x00010407, "got %08lx\n", lcid );
3125 lcid = 0xdeadbeef;
3126 status = pRtlLocaleNameToLcid( L"de+de+phoneb", &lcid, 0 );
3127 ok( status == STATUS_INVALID_PARAMETER_1, "failed error %lx\n", status );
3128 ok( lcid == 0xdeadbeef, "got %08lx\n", lcid );
3130 lcid = 0;
3131 status = pRtlLocaleNameToLcid( L"en", &lcid, 0 );
3132 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3133 status = pRtlLocaleNameToLcid( L"en", &lcid, 1 );
3134 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3135 status = pRtlLocaleNameToLcid( L"en", &lcid, 2 );
3136 ok( !status, "failed error %lx\n", status );
3137 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), "got %08lx\n", lcid );
3138 status = pRtlLocaleNameToLcid( L"en-RR", &lcid, 2 );
3139 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3140 status = pRtlLocaleNameToLcid( L"en-Latn-RR", &lcid, 2 );
3141 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3143 for (ptr = neutralsublang_names; *ptr->name; ptr++)
3145 switch (LANGIDFROMLCID(ptr->lcid))
3147 case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN): expect = LANG_SERBIAN_NEUTRAL; break;
3148 case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_CYRILLIC): expect = 0x6c1a; break;
3149 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ): expect = 0x7804; break;
3150 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ): expect = LANG_CHINESE_TRADITIONAL; break;
3151 default: expect = MAKELANGID( PRIMARYLANGID(ptr->lcid), SUBLANG_NEUTRAL ); break;
3154 status = pRtlLocaleNameToLcid( ptr->name, &lcid, 2 );
3155 ok( !status || broken(ptr->lcid == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)), /* vista */
3156 "%s failed error %lx\n", wine_dbgstr_w(ptr->name), status );
3157 if (!status) ok( lcid == expect, "%s: got wrong lcid 0x%04lx, expected 0x%04lx\n",
3158 wine_dbgstr_w(ptr->name), lcid, expect );
3159 status = pRtlLocaleNameToLcid( ptr->sname, &lcid, 0 );
3160 ok( !status || broken(ptr->lcid == MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN)), /* vista */
3161 "%s failed error %lx\n", wine_dbgstr_w(ptr->name), status );
3162 if (!status) ok( lcid == ptr->lcid, "%s: got wrong lcid 0x%04lx, expected 0x%04lx\n",
3163 wine_dbgstr_w(ptr->name), lcid, ptr->lcid );
3166 else win_skip( "RtlLocaleNameToLcid not available\n" );
3168 if (pRtlLcidToLocaleName)
3170 WCHAR buffer[128], expect[128];
3171 UNICODE_STRING str;
3173 str.Buffer = buffer;
3174 str.MaximumLength = sizeof( buffer );
3175 memset( buffer, 0xcc, sizeof(buffer) );
3177 ok( !IsValidLocale( LOCALE_NEUTRAL, 0 ), "expected invalid\n" );
3178 status = pRtlLcidToLocaleName( LOCALE_NEUTRAL, &str, 0, 0 );
3179 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3180 status = pRtlLcidToLocaleName( LOCALE_NEUTRAL, &str, 2, 0 );
3181 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3182 status = pRtlLcidToLocaleName( LOCALE_INVARIANT, NULL, 0, 0 );
3183 ok( status == STATUS_INVALID_PARAMETER_2, "wrong error %lx\n", status );
3185 memset( buffer, 0xcc, sizeof(buffer) );
3186 status = pRtlLcidToLocaleName( MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), &str, 0, 0 );
3187 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3188 ok( !wcscmp( buffer, L"en-US" ), "wrong name %s\n", debugstr_w(buffer) );
3189 ok( str.Length == wcslen(buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3190 ok( str.MaximumLength == sizeof(buffer), "wrong max len %u\n", str.MaximumLength );
3192 status = pRtlLcidToLocaleName( MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), &str, 0, 0 );
3193 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3195 memset( buffer, 0xcc, sizeof(buffer) );
3196 status = pRtlLcidToLocaleName( MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), &str, 2, 0 );
3197 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3198 ok( str.Length == wcslen(buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3199 ok( !wcscmp( buffer, L"en" ), "wrong name %s\n", debugstr_w(buffer) );
3201 ok( IsValidLocale( 0x00010407, 0 ), "expected valid\n" );
3202 memset( buffer, 0xcc, sizeof(buffer) );
3203 status = pRtlLcidToLocaleName( 0x00010407, &str, 0, 0 );
3204 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3205 ok( str.Length == wcslen(buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3206 ok( !wcscmp( buffer, L"de-DE_phoneb" ), "wrong name %s\n", debugstr_w(buffer) );
3208 ok( !IsValidLocale( LOCALE_SYSTEM_DEFAULT, 0 ), "expected invalid\n" );
3209 memset( buffer, 0xcc, sizeof(buffer) );
3210 status = pRtlLcidToLocaleName( LOCALE_SYSTEM_DEFAULT, &str, 0, 0 );
3211 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3212 ok( str.Length == wcslen(buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3213 LCIDToLocaleName( GetSystemDefaultLCID(), expect, ARRAY_SIZE(expect), 0 );
3214 ok( !wcscmp( buffer, expect ), "wrong name %s / %s\n", debugstr_w(buffer), debugstr_w(expect) );
3216 ok( !IsValidLocale( LOCALE_USER_DEFAULT, 0 ), "expected invalid\n" );
3217 memset( buffer, 0xcc, sizeof(buffer) );
3218 status = pRtlLcidToLocaleName( LOCALE_USER_DEFAULT, &str, 0, 0 );
3219 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3220 ok( str.Length == wcslen(buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3221 LCIDToLocaleName( GetUserDefaultLCID(), expect, ARRAY_SIZE(expect), 0 );
3222 ok( !wcscmp( buffer, expect ), "wrong name %s / %s\n", debugstr_w(buffer), debugstr_w(expect) );
3224 ok( IsValidLocale( LOCALE_INVARIANT, 0 ), "expected valid\n" );
3225 memset( buffer, 0xcc, sizeof(buffer) );
3226 status = pRtlLcidToLocaleName( LOCALE_INVARIANT, &str, 0, 0 );
3227 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3228 ok( str.Length == wcslen(buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3229 ok( !wcscmp( buffer, L"" ), "wrong name %s\n", debugstr_w(buffer) );
3231 memset( buffer, 0xcc, sizeof(buffer) );
3232 status = pRtlLcidToLocaleName( LOCALE_CUSTOM_DEFAULT, &str, 0, 0 );
3233 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3234 ok( str.Length == wcslen(buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3235 LCIDToLocaleName( GetUserDefaultLCID(), expect, ARRAY_SIZE(expect), 0 );
3236 ok( !wcscmp( buffer, expect ), "wrong name %s / %s\n", debugstr_w(buffer), debugstr_w(expect) );
3238 status = pRtlLcidToLocaleName( LOCALE_CUSTOM_UI_DEFAULT, &str, 0, 0 );
3239 ok( status == STATUS_SUCCESS || status == STATUS_UNSUCCESSFUL, "wrong error %lx\n", status );
3241 status = pRtlLcidToLocaleName( LOCALE_CUSTOM_UNSPECIFIED, &str, 0, 0 );
3242 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %lx\n", status );
3244 memset( buffer, 0xcc, sizeof(buffer) );
3245 str.Length = 0xbeef;
3246 str.MaximumLength = 5 * sizeof(WCHAR);
3247 status = pRtlLcidToLocaleName( 0x00010407, &str, 0, 0 );
3248 ok( status == STATUS_BUFFER_TOO_SMALL, "wrong error %lx\n", status );
3249 ok( str.Length == 0xbeef, "wrong len %u\n", str.Length );
3250 ok( str.MaximumLength == 5 * sizeof(WCHAR), "wrong len %u\n", str.MaximumLength );
3251 ok( buffer[0] == 0xcccc, "wrong name %s\n", debugstr_w(buffer) );
3253 memset( &str, 0xcc, sizeof(str) );
3254 status = pRtlLcidToLocaleName( MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), &str, 0, 1 );
3255 ok( status == STATUS_SUCCESS, "wrong error %lx\n", status );
3256 ok( str.Length == wcslen(str.Buffer) * sizeof(WCHAR), "wrong len %u\n", str.Length );
3257 ok( str.MaximumLength == str.Length + sizeof(WCHAR), "wrong max len %u\n", str.MaximumLength );
3258 ok( !wcscmp( str.Buffer, L"en-US" ), "wrong name %s\n", debugstr_w(str.Buffer) );
3259 RtlFreeUnicodeString( &str );
3261 else win_skip( "RtlLcidToLocaleName not available\n" );
3263 if (pNlsValidateLocale)
3265 void *ret, *ret2;
3266 LCID lcid;
3268 lcid = LOCALE_NEUTRAL;
3269 ret = pNlsValidateLocale( &lcid, 0 );
3270 ok( !!ret, "failed for %04lx\n", lcid );
3271 ok( lcid == GetUserDefaultLCID(), "wrong lcid %04lx\n", lcid );
3273 lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
3274 ret = pNlsValidateLocale( &lcid, 0 );
3275 ok( !!ret, "failed for %04lx\n", lcid );
3276 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), "wrong lcid %04lx\n", lcid );
3278 lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL);
3279 ret2 = pNlsValidateLocale( &lcid, 0 );
3280 ok( !!ret2, "failed for %04lx\n", lcid );
3281 ok( ret == ret2, "got different pointer for neutral\n" );
3282 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), "wrong lcid %04lx\n", lcid );
3284 lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL);
3285 ret2 = pNlsValidateLocale( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES );
3286 ok( !!ret2, "failed for %04lx\n", lcid );
3287 ok( ret != ret2, "got same pointer for neutral\n" );
3288 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), "wrong lcid %04lx\n", lcid );
3290 lcid = 0x00010407;
3291 ret = pNlsValidateLocale( &lcid, 0 );
3292 ok( !!ret, "failed for %04lx\n", lcid );
3293 ok( lcid == 0x00010407, "wrong lcid %04lx\n", lcid );
3295 lcid = LOCALE_SYSTEM_DEFAULT;
3296 ret = pNlsValidateLocale( &lcid, 0 );
3297 ok( !!ret, "failed for %04lx\n", lcid );
3298 ok( lcid == GetSystemDefaultLCID(), "wrong lcid %04lx\n", lcid );
3299 ret2 = pNlsValidateLocale( &lcid, 0 );
3300 ok( ret == ret2, "got different pointer for system\n" );
3302 lcid = LOCALE_USER_DEFAULT;
3303 ret = pNlsValidateLocale( &lcid, 0 );
3304 ok( !!ret, "failed for %04lx\n", lcid );
3305 ok( lcid == GetUserDefaultLCID(), "wrong lcid %04lx\n", lcid );
3306 ret2 = pNlsValidateLocale( &lcid, 0 );
3307 ok( ret == ret2, "got different pointer for user\n" );
3309 lcid = LOCALE_INVARIANT;
3310 ret = pNlsValidateLocale( &lcid, 0 );
3311 ok( !!ret, "failed for %04lx\n", lcid );
3312 ok( lcid == LOCALE_INVARIANT, "wrong lcid %04lx\n", lcid );
3313 ret2 = pNlsValidateLocale( &lcid, 0 );
3314 ok( ret == ret2, "got different pointer for invariant\n" );
3316 lcid = LOCALE_CUSTOM_DEFAULT;
3317 ret = pNlsValidateLocale( &lcid, 0 );
3318 ok( !!ret, "failed for %04lx\n", lcid );
3319 ok( lcid == GetUserDefaultLCID(), "wrong lcid %04lx\n", lcid );
3320 ret2 = pNlsValidateLocale( &lcid, 0 );
3321 ok( ret == ret2, "got different pointer for custom default\n" );
3323 lcid = LOCALE_CUSTOM_UNSPECIFIED;
3324 ret = pNlsValidateLocale( &lcid, 0 );
3325 ok( ret || broken(!ret), /* <= win8 */ "failed for %04lx\n", lcid );
3326 if (ret) ok( lcid == GetUserDefaultLCID(), "wrong lcid %04lx\n", lcid );
3328 SetLastError( 0xdeadbeef );
3329 lcid = LOCALE_CUSTOM_UI_DEFAULT;
3330 ret = pNlsValidateLocale( &lcid, 0 );
3331 if (!ret) ok( GetLastError() == 0xdeadbeef, "error %lu\n", GetLastError());
3333 lcid = 0xbeef;
3334 ret = pNlsValidateLocale( &lcid, 0 );
3335 ok( !ret, "succeeded\n" );
3336 ok( lcid == 0xbeef, "wrong lcid %04lx\n", lcid );
3337 ok( GetLastError() == 0xdeadbeef, "error %lu\n", GetLastError());
3339 else win_skip( "NlsValidateLocale not available\n" );
3342 static const char * const strings_sorted[] =
3344 "'",
3345 "-",
3346 "!",
3347 "\"",
3348 ".",
3349 ":",
3350 "\\",
3351 "_",
3352 "`",
3353 "{",
3354 "}",
3355 "+",
3356 "0",
3357 "1",
3358 "2",
3359 "3",
3360 "4",
3361 "5",
3362 "6",
3363 "7",
3364 "8",
3365 "9",
3366 "a",
3367 "A",
3368 "b",
3369 "B",
3370 "c",
3374 static const char * const strings[] =
3376 "C",
3377 "\"",
3378 "9",
3379 "'",
3380 "}",
3381 "-",
3382 "7",
3383 "+",
3384 "`",
3385 "1",
3386 "a",
3387 "5",
3388 "\\",
3389 "8",
3390 "B",
3391 "3",
3392 "_",
3393 "6",
3394 "{",
3395 "2",
3396 "c",
3397 "4",
3398 "!",
3399 "0",
3400 "A",
3401 ":",
3402 "b",
3406 static int compare_string1(const void *e1, const void *e2)
3408 const char *s1 = *(const char *const *)e1;
3409 const char *s2 = *(const char *const *)e2;
3411 return lstrcmpA(s1, s2);
3414 static int compare_string2(const void *e1, const void *e2)
3416 const char *s1 = *(const char *const *)e1;
3417 const char *s2 = *(const char *const *)e2;
3419 return CompareStringA(0, 0, s1, -1, s2, -1) - 2;
3422 static int compare_string3(const void *e1, const void *e2)
3424 const char *s1 = *(const char *const *)e1;
3425 const char *s2 = *(const char *const *)e2;
3426 char key1[256], key2[256];
3428 int len1 = LCMapStringA(0, LCMAP_SORTKEY, s1, -1, key1, sizeof(key1));
3429 int len2 = LCMapStringA(0, LCMAP_SORTKEY, s2, -1, key2, sizeof(key2));
3430 int ret = memcmp(key1, key2, min(len1, len2));
3431 if (!ret) ret = len1 - len2;
3432 return ret;
3435 static void test_sorting(void)
3437 char buf[256];
3438 char **str_buf = (char **)buf;
3439 int i;
3441 assert(sizeof(buf) >= sizeof(strings));
3443 /* 1. sort using lstrcmpA */
3444 memcpy(buf, strings, sizeof(strings));
3445 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string1);
3446 for (i = 0; i < ARRAY_SIZE(strings); i++)
3448 ok(!strcmp(strings_sorted[i], str_buf[i]),
3449 "qsort using lstrcmpA failed for element %d\n", i);
3451 /* 2. sort using CompareStringA */
3452 memcpy(buf, strings, sizeof(strings));
3453 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string2);
3454 for (i = 0; i < ARRAY_SIZE(strings); i++)
3456 ok(!strcmp(strings_sorted[i], str_buf[i]),
3457 "qsort using CompareStringA failed for element %d\n", i);
3459 /* 3. sort using sort keys */
3460 memcpy(buf, strings, sizeof(strings));
3461 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string3);
3462 for (i = 0; i < ARRAY_SIZE(strings); i++)
3464 ok(!strcmp(strings_sorted[i], str_buf[i]),
3465 "qsort using sort keys failed for element %d\n", i);
3469 struct sorting_test_entry {
3470 const WCHAR *locale;
3471 int result_sortkey;
3472 int result_compare;
3473 DWORD flags;
3474 const WCHAR *first;
3475 const WCHAR *second;
3478 static const struct sorting_test_entry unicode_sorting_tests[] =
3480 /* Normal character */
3481 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x0037", L"\x277c" },
3482 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x1eca", L"\x1ecb" },
3483 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x1d05", L"\x1d48" },
3484 /* Normal character diacritics */
3485 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x19d7", L"\x096d" },
3486 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x00f5", L"\x1ecf" },
3487 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x2793", L"\x0d70" },
3488 /* Normal character case weights */
3489 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"A", L"a" },
3490 { L"en-US", -1, CSTR_LESS_THAN, 0, L"z", L"Z" },
3491 /* PUA character */
3492 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xe5a6", L"\xe5a5\x0333" },
3493 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xe5d7", L"\xe5d6\x0330" },
3494 /* Symbols add diacritic weight */
3495 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\u276a", L"\u2768" },
3496 /* Symbols add case weight */
3497 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\u204d", L"\uff02" },
3498 /* Default character, when there is main weight extra there must be no diacritic weight */
3499 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\ue6e3\u0a02", L"\ue6e3\u20dc" },
3500 /* Unsortable characters */
3501 { L"en-US", 0, CSTR_EQUAL, 0, L"a \u2060 b", L"a b" },
3502 /* Invalid/undefined characters */
3503 { L"en-US", 0, CSTR_EQUAL, 0, L"a \xfff0 b", L"a b" },
3504 { L"en-US", 0, CSTR_EQUAL, 0, L"a\x139F a", L"a a" },
3505 { L"en-US", -1, CSTR_LESS_THAN, 0, L"a\x139F a", L"a b" },
3506 /* Default characters */
3507 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x00fc", L"\x016d" },
3508 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x3fcb\x7fd5", L"\x0006\x3032" },
3509 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x00fc\x30fd", L"\x00fa\x1833" },
3510 /* Diacritic is added */
3511 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x1B56\x0330", L"\x1096" },
3512 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x1817\x0333", L"\x19d7" },
3513 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x04de\x05ac", L"\x0499" },
3514 /* Diacritic can overflow */
3515 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x01ba\x0654", L"\x01b8" },
3516 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x06b7\x06eb", L"\x06b6" },
3517 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x1420\x0333", L"\x141f" },
3518 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x1b56\x0654", L"\x1b56\x0655" },
3519 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x1b56\x0654\x0654", L"\x1b56\x0655" },
3520 /* Jamo case weight */
3521 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x11bc", L"\x110b" },
3522 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x11c1", L"\x1111" },
3523 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x11af", L"\x1105" },
3524 /* Jamo main weight */
3525 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x11c2", L"\x11f5" },
3526 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x1108", L"\x1121" },
3527 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x1116", L"\x11c7" },
3528 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x11b1", L"\x11d1" },
3529 /* CJK main weight 1 */
3530 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x4550\x73d2", L"\x3211\x23ad" },
3531 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x3265", L"\x4079" },
3532 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x4c19\x68d0\x52d0", L"\x316d" },
3533 /* CJK main weight 2 */
3534 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x72dd", L"\x6b8a" },
3535 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x6785\x3bff\x6f83", L"\x7550\x34c9\x71a7" },
3536 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x5d61", L"\x3aef" },
3537 /* Symbols case weights */
3538 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x207a", L"\xfe62" },
3539 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xfe65", L"\xff1e" },
3540 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x2502", L"\xffe8" },
3541 /* Symbols diacritic weights */
3542 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x21da", L"\x21dc" },
3543 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x29fb", L"\x2295" },
3544 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x0092", L"\x009c" },
3545 /* NORM_IGNORESYMBOLS */
3546 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS, L"\x21da", L"\x21dc" },
3547 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS, L"\x29fb", L"\x2295" },
3548 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS, L"\x0092", L"\x009c" },
3549 { L"en-US", 0, CSTR_EQUAL, 0, L"\x3099", L"\x309b" }, /* Small diacritic weights at the end get ignored */
3550 /* Main weights have priority over diacritic weights */
3551 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"a b", L"\x0103 a" },
3552 { L"en-US", -1, CSTR_LESS_THAN, 0, L"a", L"\x0103" },
3553 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"e x", L"\x0113 v" },
3554 { L"en-US", -1, CSTR_LESS_THAN, 0, L"e", L"\x0113" },
3555 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"c s", L"\x0109 r" },
3556 { L"en-US", -1, CSTR_LESS_THAN, 0, L"c", L"\x0109" },
3557 /* Diacritic weights have priority over case weights */
3558 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"a \x0103", L"A a" },
3559 { L"en-US", -1, CSTR_LESS_THAN, 0, L"a", L"A" },
3560 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"e \x0113", L"E e" },
3561 { L"en-US", -1, CSTR_LESS_THAN, 0, L"e", L"E" },
3562 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"c \x0109", L"C c" },
3563 { L"en-US", -1, CSTR_LESS_THAN, 0, L"c", L"C" },
3564 /* Diacritic values for Jamo are not ignored */
3565 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORENONSPACE, L"\x1152", L"\x1153" },
3566 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORENONSPACE, L"\x1143", L"\x1145" },
3567 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORENONSPACE, L"\x1196", L"\x1174" },
3568 /* Jungseong < PUA */
3569 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x318e", L"\x382a" },
3570 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xffcb", L"\x3d13" },
3571 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xffcc", L"\x8632" },
3572 /* Surrogate > PUA */
3573 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xd847", L"\x382a" },
3574 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xd879", L"\x3d13" },
3575 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xd850", L"\x8632" },
3576 /* Unsortable combined with diacritics */
3577 { L"en-US", 0, CSTR_EQUAL, 0, L"A\x0301\x0301", L"A\x0301\x00ad\x0301" },
3578 { L"en-US", 0, CSTR_EQUAL, 0, L"b\x07f2\x07f2", L"b\x07f2\x2064\x07f2" },
3579 { L"en-US", 0, CSTR_EQUAL, 0, L"X\x0337\x0337", L"X\x0337\xfffd\x0337" },
3580 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORECASE, L"c", L"C" },
3581 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORECASE, L"e", L"E" },
3582 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORECASE, L"A", L"a" },
3583 /* Punctuation primary weight */
3584 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x001b", L"\x001c" },
3585 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x0005", L"\x0006" },
3586 /* Punctuation diacritic/case weight */
3587 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x0027", L"\xff07" },
3588 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x07f4", L"\x07f5" },
3589 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x207b", L"\x0008" },
3590 /* Punctuation primary weight has priority */
3591 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xff07", L"\x07f4" },
3592 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xfe32", L"\x2014" },
3593 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x058a", L"\x2027" },
3594 /* Punctuation */
3595 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x207b", L"\x0008" },
3596 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x0004", L"\x0011" },
3597 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS, L"\x207b", L"\x0008" },
3598 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS, L"\x0004", L"\x0011" },
3599 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x207b", L"\x0008" },
3600 { L"en-US", -1, CSTR_LESS_THAN, SORT_STRINGSORT, L"\x0004", L"\x0011" },
3601 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS | SORT_STRINGSORT, L"\x207b", L"\x0008" },
3602 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS | SORT_STRINGSORT, L"\x0004", L"\x0011" },
3603 /* Punctuation main weight */
3604 { L"en-US", -1, CSTR_LESS_THAN, SORT_STRINGSORT, L"\x001a", L"\x001b" },
3605 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x2027", L"\x2011" },
3606 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x3030", L"\x301c" },
3607 /* Punctuation diacritic weight */
3608 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x058a", L"\x2010" },
3609 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x07F5", L"\x07F4" },
3610 /* Punctuation case weight */
3611 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\xfe32", L"\x2013" },
3612 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\xfe31", L"\xfe58" },
3613 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\xff07", L"\x0027" },
3614 /* Punctuation NORM_IGNORESYMBOLS */
3615 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS, L"\x207b", L"\x0008" },
3616 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS, L"\x0004", L"\x0011" },
3617 /* Punctuation NORM_IGNORESYMBOLS SORT_STRINGSORT */
3618 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS | SORT_STRINGSORT, L"\x207b", L"\x0008" },
3619 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORESYMBOLS | SORT_STRINGSORT, L"\x0004", L"\x0011" },
3620 /* Punctuation SORT_STRINGSORT main weight */
3621 { L"en-US", -1, CSTR_LESS_THAN, SORT_STRINGSORT, L"\x001a", L"\x001b" },
3622 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x2027", L"\x2011", },
3623 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x3030", L"\x301c", },
3624 /* Punctuation SORT_STRINGSORT diacritic weight */
3625 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x058a", L"\x2010" },
3626 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\x07F5", L"\x07F4" },
3627 /* Punctuation SORT_STRINGSORT case weight */
3628 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\xfe32", L"\x2013" },
3629 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\xfe31", L"\xfe58" },
3630 { L"en-US", 1, CSTR_GREATER_THAN, SORT_STRINGSORT, L"\xff07", L"\x0027" },
3631 /* Japanese main weight */
3632 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x04b0", L"\x32db" },
3633 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x3093", L"\x1e62\x013f" },
3634 /* Japanese diacritic weight */
3635 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30d3", L"\x30d4" },
3636 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x307b", L"\x307c" },
3637 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30ea", L"\x32f7" },
3638 /* Japanese case weight small */
3639 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x31fb", L"\x30e9" },
3640 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30db", L"\x31f9" },
3641 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xff6d", L"\xff95" },
3642 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORENONSPACE, L"\x31fb", L"\x30e9" },
3643 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORENONSPACE, L"\x30db", L"\x31f9" },
3644 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORENONSPACE, L"\xff6d", L"\xff95" },
3645 /* Japanese case weight kana */
3646 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30d5", L"\x3075" },
3647 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x306a", L"\x30ca" },
3648 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x305a", L"\x30ba" },
3649 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREKANATYPE, L"\x30d5", L"\x3075" },
3650 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREKANATYPE, L"\x306a", L"\x30ca" },
3651 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREKANATYPE, L"\x305a", L"\x30ba" },
3652 /* Japanese case weight width */
3653 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30bf", L"\xff80" },
3654 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30ab", L"\xff76" },
3655 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30a2", L"\xff71" },
3656 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\x30bf", L"\xff80" },
3657 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\x30ab", L"\xff76" },
3658 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\x30a2", L"\xff71" },
3659 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORENONSPACE, L"\x31a2", L"\x3110" },
3660 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORENONSPACE, L"\x1342", L"\x133a" },
3661 { L"en-US", 0, CSTR_EQUAL, NORM_IGNORENONSPACE, L"\x16a4", L"\x16a5" },
3662 /* Kana small data must have priority over width data */
3663 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30b1\x30f6", L"\xff79\x30b1" },
3664 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30a6\x30a5", L"\xff73\x30a6" },
3665 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30a8\x30a7", L"\xff74\x30a8" },
3666 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30b1", L"\xff79" },
3667 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30a6", L"\xff73" },
3668 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30a8", L"\xff74" },
3669 /* Kana small data must have priority over kana type data */
3670 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x3046\x30a9", L"\x30a6\x30aa" },
3671 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x304a\x3041", L"\x30aa\x3042" },
3672 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x3059\x30a7", L"\x30b9\x30a8" },
3673 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x3046", L"\x30a6" },
3674 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x304a", L"\x30aa" },
3675 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x3059", L"\x30b9" },
3676 /* Kana type data must have priority over width data */
3677 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30a6\x30a8", L"\xff73\x3048" },
3678 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30ab\x30a3", L"\xff76\x3043" },
3679 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30b5\x30ac", L"\xff7b\x304c" },
3680 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30a6", L"\xff73" },
3681 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30ab", L"\xff76" },
3682 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30b5", L"\xff7b" },
3683 /* Case weights have priority over extra weights */
3684 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x305a a", L"\x30ba A" },
3685 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30c1 b", L"\xff81 B" },
3686 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xff8b x", L"\x31f6 X" },
3687 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x305a", L"\x30ba" },
3688 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30c1", L"\xff81" },
3689 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xff8b", L"\x31f6" },
3690 /* Extra weights have priority over special weights */
3691 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x0027\x31ff", L"\x007f\xff9b" },
3692 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x07f5\x30f3", L"\x07f4\x3093" },
3693 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xfe63\x30e0", L"\xff0d\x3080" },
3694 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x0027", L"\x007f" },
3695 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x07f5", L"\x07f4" },
3696 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xfe63", L"\xff0d" },
3697 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\xff68", L"\x30a3" },
3698 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\xff75", L"\x30aa" },
3699 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\x30e2", L"\xff93" },
3700 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xff68", L"\x30a3" },
3701 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\xff75", L"\x30aa" },
3702 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x30e2", L"\xff93" },
3703 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREKANATYPE, L"\x30a8", L"\x3048" },
3704 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREKANATYPE, L"\x30af", L"\x304f" },
3705 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREKANATYPE, L"\x3067", L"\x30c7" },
3706 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30a8", L"\x3048" },
3707 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30af", L"\x304f" },
3708 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x3067", L"\x30c7" },
3709 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\xffb7", L"\x3147" },
3710 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\xffb6", L"\x3146" },
3711 { L"en-US", 0, CSTR_EQUAL, NORM_IGNOREWIDTH, L"\x3145", L"\xffb5" },
3712 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORECASE, L"\xffb7", L"\x3147" },
3713 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORECASE, L"\xffb6", L"\x3146" },
3714 { L"en-US", 1, CSTR_GREATER_THAN, NORM_IGNORECASE, L"\x3145", L"\xffb5" },
3715 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x3075\x30fc", L"\x30d5\x30fc" },
3716 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x30a1\x30fc", L"\x30a2\x30fc" },
3717 /* Coptic < Japanese */
3718 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORECASE, L"\x2cff", L"\x30ba" },
3719 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORECASE, L"\x2cdb", L"\x32de" },
3720 { L"en-US", -1, CSTR_LESS_THAN, NORM_IGNORECASE, L"\x2ce0", L"\x30c6" },
3721 /* Hebrew > Japanese */
3722 { L"en-US", 1, CSTR_GREATER_THAN, NORM_IGNORECASE, L"\x05d3", L"\x30ba" },
3723 { L"en-US", 1, CSTR_GREATER_THAN, NORM_IGNORECASE, L"\x05e3", L"\x32de" },
3724 { L"en-US", 1, CSTR_GREATER_THAN, NORM_IGNORECASE, L"\x05d7", L"\x30c6" },
3725 /* Expansion */
3726 { L"en-US", 0, CSTR_EQUAL, 0, L"\x00c6", L"\x0041\x0045" },
3727 { L"en-US", 0, CSTR_EQUAL, 0, L"\x0f5c", L"\x0f5b\x0fb7" },
3728 { L"en-US", 0, CSTR_EQUAL, 0, L"\x05f0", L"\x05d5\x05d5" },
3729 { L"en-US", -1, CSTR_EQUAL, 0, L"\x0f75", L"\x0f71\x0f74" },
3730 { L"en-US", -1, CSTR_EQUAL, 0, L"\xfc5e", L"\x064c\x0651" },
3731 { L"en-US", -1, CSTR_EQUAL, 0, L"\xfb2b", L"\x05e9\x05c2" },
3732 { L"en-US", -1, CSTR_EQUAL, 0, L"\xfe71", L"\x0640\x064b" },
3733 /* Japanese locale */
3734 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x6df8", L"\x654b\x29e9" },
3735 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x685d\x1239\x1b61", L"\x59b6\x6542\x2a62\x04a7" },
3736 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x62f3\x43e9", L"\x5760" },
3737 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x634c", L"\x2f0d\x5f1c\x7124" },
3738 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x69e7\x0502", L"\x57cc" },
3739 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x7589", L"\x67c5" },
3740 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x5ede\x765c", L"\x7324" },
3741 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x5c7f\x5961", L"\x7cbe" },
3742 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x3162", L"\x6a84\x1549\x0b60" },
3743 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x769e\x448e", L"\x4e6e" },
3744 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x59a4", L"\x5faa\x607c" },
3745 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x529b", L"\x733f" },
3746 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x6ff8\x2a0a", L"\x7953\x6712" },
3747 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x6dfb", L"\x6793" },
3748 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x67ed", L"\x6aa2" },
3749 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x4e61", L"\x6350\x6b08" },
3750 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x5118", L"\x53b3\x75b4" },
3751 { L"ja-JP", -1, CSTR_LESS_THAN, 0, L"\x6bbf", L"\x65a3" },
3752 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x5690", L"\x5fa8" },
3753 { L"ja-JP", 1, CSTR_GREATER_THAN, 0, L"\x61e2", L"\x76e5" },
3754 /* Misc locales */
3755 { L"ko-KR", -1, CSTR_LESS_THAN, 0, L"\x8db6", L"\xd198" },
3756 { L"ko-KR", -1, CSTR_LESS_THAN, 0, L"\x8f72", L"\xd2b9" },
3757 { L"ko-KR", -1, CSTR_LESS_THAN, 0, L"\x91d8", L"\xd318" },
3758 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x8db6", L"\xd198" },
3759 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x8f72", L"\xd2b9" },
3760 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x91d8", L"\xd318" },
3761 { L"cs-CZ", 1, CSTR_GREATER_THAN, 0, L"\x0160", L"\x0219" },
3762 { L"cs-CZ", 1, CSTR_GREATER_THAN, 0, L"\x059a", L"\x0308" },
3763 { L"cs-CZ", 1, CSTR_GREATER_THAN, 0, L"\x013a", L"\x013f" },
3764 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x0160", L"\x0219" },
3765 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x059a", L"\x0308" },
3766 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x013a", L"\x013f" },
3767 { L"vi-VN", -1, CSTR_LESS_THAN, 0, L"\x1d8f", L"\x1ea8" },
3768 { L"vi-VN", -1, CSTR_LESS_THAN, 0, L"\x0323", L"\xfe26" },
3769 { L"vi-VN", 1, CSTR_GREATER_THAN, 0, L"R", L"\xff32" },
3770 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x1d8f", L"\x1ea8" },
3771 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x0323", L"\xfe26" },
3772 { L"en-US", -1, CSTR_LESS_THAN, 0, L"R", L"\xff32" },
3773 { L"zh-HK", -1, CSTR_LESS_THAN, 0, L"\x83ae", L"\x71b9" },
3774 { L"zh-HK", -1, CSTR_LESS_THAN, 0, L"\x7e50", L"\xc683" },
3775 { L"zh-HK", 1, CSTR_GREATER_THAN, 0, L"\x6c69", L"\x7f8a" },
3776 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x83ae", L"\x71b9" },
3777 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x7e50", L"\xc683" },
3778 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x6c69", L"\x7f8a" },
3779 { L"tr-TR", 1, CSTR_GREATER_THAN, 0, L"\x00dc", L"\x1ee9" },
3780 { L"tr-TR", 1, CSTR_GREATER_THAN, 0, L"\x00fc", L"\x1ee6" },
3781 { L"tr-TR", -1, CSTR_LESS_THAN, 0, L"\x0152", L"\x00d6" },
3782 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x00dc", L"\x1ee9" },
3783 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x00fc", L"\x1ee6" },
3784 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\x0152", L"\x00d6" },
3785 /* Diacritic is added */
3786 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xa042\x09bc", L"\xa042" },
3787 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xa063\x302b", L"\xa063" },
3788 { L"en-US", 1, CSTR_GREATER_THAN, 0, L"\xa07e\x0c56", L"\xa07e" },
3789 /* Reversed diacritics */
3790 { L"en-US", -1, CSTR_LESS_THAN, 0, L"\x00e9\x00e8", L"\x00e8\x00e9" },
3791 { L"fr-FR", 1, CSTR_GREATER_THAN, 0, L"\x00e9\x00e8", L"\x00e8\x00e9" },
3792 /* Digit sort */
3793 { L"en-US", -1, CSTR_LESS_THAN, 0, L"1230", L"321" },
3794 { L"en-US", 1, CSTR_GREATER_THAN, SORT_DIGITSASNUMBERS, L"1230", L"321" },
3795 { L"en-US", 1, CSTR_GREATER_THAN, SORT_DIGITSASNUMBERS, L"\xc6f\xc6c\xc6a", L"\xc6f\xc6e" },
3796 /* Compressions */
3797 { L"en-US", -1, CSTR_LESS_THAN, 0, L"E\x0300", L"F" },
3798 { L"rm-CH", 1, CSTR_GREATER_THAN, 0, L"E\x0300", L"F" },
3801 static void test_unicode_sorting(void)
3803 int i;
3804 int ret1;
3805 int ret2;
3806 BYTE buffer[1000];
3807 if (!pLCMapStringEx)
3809 win_skip("LCMapStringEx not available\n");
3810 return;
3812 for (i = 0; i < ARRAY_SIZE(unicode_sorting_tests); i++)
3814 BYTE buff1[1000];
3815 BYTE buff2[1000];
3816 int len1, len2;
3817 int result;
3818 const struct sorting_test_entry *entry = &unicode_sorting_tests[i];
3820 len1 = pLCMapStringEx(entry->locale, LCMAP_SORTKEY | entry->flags, entry->first, -1, (WCHAR*)buff1, ARRAY_SIZE(buff1), NULL, NULL, 0);
3821 len2 = pLCMapStringEx(entry->locale, LCMAP_SORTKEY | entry->flags, entry->second, -1, (WCHAR*)buff2, ARRAY_SIZE(buff2), NULL, NULL, 0);
3823 result = memcmp(buff1, buff2, min(len1, len2));
3824 if (result < 0) result = -1;
3825 else if (result > 0) result = 1;
3826 else if (len1 < len2) result = -1;
3827 else if (len1 > len2) result = 1;
3829 ok (result == entry->result_sortkey, "Test %d (%s, %s) - Expected %d, got %d\n",
3830 i, wine_dbgstr_w(entry->first), wine_dbgstr_w(entry->second), entry->result_sortkey, result);
3832 result = CompareStringEx(entry->locale, entry->flags, entry->first, -1, entry->second, -1, NULL, NULL, 0);
3833 ok (result == entry->result_compare, "Test %d (%s, %s) - Expected %d, got %d\n",
3834 i, wine_dbgstr_w(entry->first), wine_dbgstr_w(entry->second), entry->result_compare, result);
3836 /* Test diacritics when buffer is short */
3837 ret1 = pLCMapStringEx(L"en-US", LCMAP_SORTKEY, L"\x0e49\x0e49\x0e49\x0e49\x0e49", -1, (WCHAR*)buffer, 20, NULL, NULL, 0);
3838 ret2 = pLCMapStringEx(L"en-US", LCMAP_SORTKEY, L"\x0e49\x0e49\x0e49\x0e49\x0e49", -1, (WCHAR*)buffer, 0, NULL, NULL, 0);
3839 ok(ret1 == ret2, "Got ret1=%d, ret2=%d\n", ret1, ret2);
3842 static void test_FoldStringA(void)
3844 int ret, i, j;
3845 BOOL is_special;
3846 char src[256], dst[256];
3847 static const char digits_src[] = { 0xB9,0xB2,0xB3,'\0' };
3848 static const char digits_dst[] = { '1','2','3','\0' };
3849 static const char composite_src[] =
3851 0x8a,0x8e,0x9a,0x9e,0x9f,0xc0,0xc1,0xc2,
3852 0xc3,0xc4,0xc5,0xc7,0xc8,0xc9,0xca,0xcb,
3853 0xcc,0xcd,0xce,0xcf,0xd1,0xd2,0xd3,0xd4,
3854 0xd5,0xd6,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,
3855 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe7,0xe8,
3856 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf1,
3857 0xf2,0xf3,0xf4,0xf5,0xf6,0xf8,0xf9,0xfa,
3858 0xfb,0xfc,0xfd,0xff,'\0'
3860 static const char composite_dst[] =
3862 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
3863 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
3864 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
3865 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
3866 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
3867 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
3868 0x4f,0x7e,0x4f,0xa8,0x4f,0x3f,0x55,0x60,
3869 0x55,0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,
3870 0x61,0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,
3871 0x61,0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,
3872 0x65,0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,
3873 0x69,0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,
3874 0x6f,0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,
3875 0x6f,0xa8,0x6f,0x3f,0x75,0x60,0x75,0xb4,
3876 0x75,0x5e,0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
3878 static const char composite_dst_alt[] =
3880 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
3881 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
3882 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
3883 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
3884 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
3885 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
3886 0x4f,0x7e,0x4f,0xa8,0xd8,0x55,0x60,0x55,
3887 0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,0x61,
3888 0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,0x61,
3889 0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,0x65,
3890 0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,0x69,
3891 0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,0x6f,
3892 0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,0x6f,
3893 0xa8,0xf8,0x75,0x60,0x75,0xb4,0x75,0x5e,
3894 0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
3896 static const char ligatures_src[] =
3898 0x8C,0x9C,0xC6,0xDE,0xDF,0xE6,0xFE,'\0'
3900 static const char ligatures_dst[] =
3902 'O','E','o','e','A','E','T','H','s','s','a','e','t','h','\0'
3904 static const struct special
3906 char src;
3907 char dst[4];
3908 } foldczone_special[] =
3910 /* src dst */
3911 { 0x85, { 0x2e, 0x2e, 0x2e, 0x00 } },
3912 { 0x98, { 0x20, 0x7e, 0x00 } },
3913 { 0x99, { 0x54, 0x4d, 0x00 } },
3914 { 0xa0, { 0x20, 0x00 } },
3915 { 0xa8, { 0x20, 0xa8, 0x00 } },
3916 { 0xaa, { 0x61, 0x00 } },
3917 { 0xaf, { 0x20, 0xaf, 0x00 } },
3918 { 0xb2, { 0x32, 0x00 } },
3919 { 0xb3, { 0x33, 0x00 } },
3920 { 0xb4, { 0x20, 0xb4, 0x00 } },
3921 { 0xb8, { 0x20, 0xb8, 0x00 } },
3922 { 0xb9, { 0x31, 0x00 } },
3923 { 0xba, { 0x6f, 0x00 } },
3924 { 0xbc, { 0x31, 0x2f, 0x34, 0x00 } },
3925 { 0xbd, { 0x31, 0x2f, 0x32, 0x00 } },
3926 { 0xbe, { 0x33, 0x2f, 0x34, 0x00 } },
3927 { 0x00 }
3930 /* these tests are locale specific */
3931 if (GetACP() != 1252)
3933 trace("Skipping FoldStringA tests for a not Latin 1 locale\n");
3934 return;
3937 /* MAP_FOLDDIGITS */
3938 SetLastError(0xdeadbeef);
3939 ret = FoldStringA(MAP_FOLDDIGITS, digits_src, -1, dst, 256);
3940 ok(ret == 4, "Expected ret == 4, got %d, error %ld\n", ret, GetLastError());
3941 ok(strcmp(dst, digits_dst) == 0,
3942 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", digits_dst, dst);
3943 for (i = 1; i < 256; i++)
3945 if (!strchr(digits_src, i))
3947 src[0] = i;
3948 src[1] = '\0';
3949 ret = FoldStringA(MAP_FOLDDIGITS, src, -1, dst, 256);
3950 ok(ret == 2, "Expected ret == 2, got %d, error %ld\n", ret, GetLastError());
3951 ok(dst[0] == src[0],
3952 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", src, dst);
3956 /* MAP_EXPAND_LIGATURES */
3957 SetLastError(0xdeadbeef);
3958 ret = FoldStringA(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
3959 ok(ret == sizeof(ligatures_dst), "Got %d, error %ld\n", ret, GetLastError());
3960 ok(strcmp(dst, ligatures_dst) == 0,
3961 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", ligatures_dst, dst);
3962 for (i = 1; i < 256; i++)
3964 if (!strchr(ligatures_src, i))
3966 src[0] = i;
3967 src[1] = '\0';
3968 ret = FoldStringA(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
3969 if (ret == 3)
3971 /* Vista */
3972 ok((i == 0xDC && lstrcmpA(dst, "UE") == 0) ||
3973 (i == 0xFC && lstrcmpA(dst, "ue") == 0),
3974 "Got %s for %d\n", dst, i);
3976 else
3978 ok(ret == 2, "Expected ret == 2, got %d, error %ld\n", ret, GetLastError());
3979 ok(dst[0] == src[0],
3980 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", src, dst);
3985 /* MAP_COMPOSITE */
3986 SetLastError(0xdeadbeef);
3987 ret = FoldStringA(MAP_COMPOSITE, composite_src, -1, dst, 256);
3988 ok(ret, "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
3989 ok( GetLastError() == 0xdeadbeef || broken(!GetLastError()), /* vista */
3990 "wrong error %lu\n", GetLastError());
3991 ok(ret == 121 || ret == 119, "Expected 121 or 119, got %d\n", ret);
3992 ok(strcmp(dst, composite_dst) == 0 || strcmp(dst, composite_dst_alt) == 0,
3993 "MAP_COMPOSITE: Mismatch, got '%s'\n", dst);
3995 for (i = 1; i < 256; i++)
3997 if (!strchr(composite_src, i))
3999 src[0] = i;
4000 src[1] = '\0';
4001 ret = FoldStringA(MAP_COMPOSITE, src, -1, dst, 256);
4002 ok(ret == 2, "Expected ret == 2, got %d, error %ld\n", ret, GetLastError());
4003 ok(dst[0] == src[0],
4004 "0x%02x, 0x%02x,0x%02x,0x%02x,\n", (unsigned char)src[0],
4005 (unsigned char)dst[0],(unsigned char)dst[1],(unsigned char)dst[2]);
4009 /* MAP_FOLDCZONE */
4010 for (i = 1; i < 256; i++)
4012 src[0] = i;
4013 src[1] = '\0';
4014 SetLastError(0xdeadbeef);
4015 ret = FoldStringA(MAP_FOLDCZONE, src, -1, dst, 256);
4016 is_special = FALSE;
4017 for (j = 0; foldczone_special[j].src != 0 && ! is_special; j++)
4019 if (foldczone_special[j].src == src[0])
4021 ok(ret == 2 || ret == lstrlenA(foldczone_special[j].dst) + 1,
4022 "Expected ret == 2 or %d, got %d, error %ld\n",
4023 lstrlenA(foldczone_special[j].dst) + 1, ret, GetLastError());
4024 ok(src[0] == dst[0] || lstrcmpA(foldczone_special[j].dst, dst) == 0,
4025 "MAP_FOLDCZONE: string mismatch for 0x%02x\n",
4026 (unsigned char)src[0]);
4027 is_special = TRUE;
4030 if (! is_special)
4032 ok(ret == 2, "Expected ret == 2, got %d, error %ld\n", ret, GetLastError());
4033 ok(src[0] == dst[0],
4034 "MAP_FOLDCZONE: Expected 0x%02x, got 0x%02x\n",
4035 (unsigned char)src[0], (unsigned char)dst[0]);
4039 /* MAP_PRECOMPOSED */
4040 for (i = 1; i < 256; i++)
4042 src[0] = i;
4043 src[1] = '\0';
4044 ret = FoldStringA(MAP_PRECOMPOSED, src, -1, dst, 256);
4045 ok(ret == 2, "Expected ret == 2, got %d, error %ld\n", ret, GetLastError());
4046 ok(src[0] == dst[0],
4047 "MAP_PRECOMPOSED: Expected 0x%02x, got 0x%02x\n",
4048 (unsigned char)src[0], (unsigned char)dst[0]);
4052 static void test_FoldStringW(void)
4054 int ret;
4055 WORD type;
4056 unsigned int i, j, len;
4057 WCHAR src[256], dst[256];
4058 UINT ch, prev_ch = 1;
4059 static const DWORD badFlags[] =
4062 MAP_PRECOMPOSED|MAP_COMPOSITE,
4063 MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES,
4064 MAP_COMPOSITE|MAP_EXPAND_LIGATURES
4066 /* Ranges of digits 0-9 : Must be sorted! */
4067 static const struct { UINT ch, first, last; int broken; } digitRanges[] =
4069 { 0x0030, 0, 9 }, /* '0'-'9' */
4070 { 0x00b2, 2, 3 }, /* Superscript 2, 3 */
4071 { 0x00b9, 1, 1 }, /* Superscript 1 */
4072 { 0x0660, 0, 9 }, /* Eastern Arabic */
4073 { 0x06f0, 0, 9 }, /* Arabic - Hindu */
4074 { 0x07c0, 0, 9 }, /* Nko */
4075 { 0x0966, 0, 9 }, /* Devengari */
4076 { 0x09e6, 0, 9 }, /* Bengalii */
4077 { 0x0a66, 0, 9 }, /* Gurmukhi */
4078 { 0x0ae6, 0, 9 }, /* Gujarati */
4079 { 0x0b66, 0, 9 }, /* Oriya */
4080 { 0x0be6, 0, 9 }, /* Tamil */
4081 { 0x0c66, 0, 9 }, /* Telugu */
4082 { 0x0c78, 0, 3, TRUE /*win7*/ }, /* Telugu Fraction */
4083 { 0x0c7c, 1, 3, TRUE /*win7*/ }, /* Telugu Fraction */
4084 { 0x0ce6, 0, 9 }, /* Kannada */
4085 { 0x0d66, 0, 9 }, /* Maylayalam */
4086 { 0x0de6, 0, 9, TRUE /*win10*/ }, /* Sinhala Lith */
4087 { 0x0e50, 0, 9 }, /* Thai */
4088 { 0x0ed0, 0, 9 }, /* Laos */
4089 { 0x0f20, 0, 9 }, /* Tibet */
4090 { 0x1040, 0, 9 }, /* Myanmar */
4091 { 0x1090, 0, 9 }, /* Myanmar Shan */
4092 { 0x1369, 1, 9 }, /* Ethiopic */
4093 { 0x17e0, 0, 9 }, /* Khmer */
4094 { 0x1810, 0, 9 }, /* Mongolian */
4095 { 0x1946, 0, 9 }, /* Limbu */
4096 { 0x19d0, 0, 9 }, /* New Tai Lue */
4097 { 0x19da, 1, 1, TRUE /*win7*/ }, /* New Tai Lue Tham 1 */
4098 { 0x1a80, 0, 9, TRUE /*win7*/ }, /* Tai Tham Hora */
4099 { 0x1a90, 0, 9, TRUE /*win7*/ }, /* Tai Tham Tham */
4100 { 0x1b50, 0, 9 }, /* Balinese */
4101 { 0x1bb0, 0, 9 }, /* Sundanese */
4102 { 0x1c40, 0, 9 }, /* Lepcha */
4103 { 0x1c50, 0, 9 }, /* Ol Chiki */
4104 { 0x2070, 0, 0 }, /* Superscript 0 */
4105 { 0x2074, 4, 9 }, /* Superscript 4-9 */
4106 { 0x2080, 0, 9 }, /* Subscript */
4107 { 0x2460, 1, 9 }, /* Circled */
4108 { 0x2474, 1, 9 }, /* Bracketed */
4109 { 0x2488, 1, 9 }, /* Full stop */
4110 { 0x24ea, 0, 0 }, /* Circled 0 */
4111 { 0x24f5, 1, 9 }, /* Double Circled */
4112 { 0x24ff, 0, 0 }, /* Negative Circled 0 */
4113 { 0x2776, 1, 9 }, /* Inverted Circled */
4114 { 0x2780, 1, 9 }, /* Patterned Circled */
4115 { 0x278a, 1, 9 }, /* Inverted Patterned Circled */
4116 { 0x3007, 0, 0 }, /* Ideographic Number 0 */
4117 { 0x3021, 1, 9 }, /* Hangzhou */
4118 { 0xa620, 0, 9 }, /* Vai */
4119 { 0xa8d0, 0, 9 }, /* Saurashtra */
4120 { 0xa8e0, 0, 9, TRUE /*win7*/ }, /* Combining Devanagari */
4121 { 0xa900, 0, 9 }, /* Kayah Li */
4122 { 0xa9d0, 0, 9, TRUE /*win7*/ }, /* Javanese */
4123 { 0xa9f0, 0, 9, TRUE /*win10*/ }, /* Myanmar Tai Laing */
4124 { 0xaa50, 0, 9 }, /* Cham */
4125 { 0xabf0, 0, 9, TRUE /*win7*/ }, /* Meetei Mayek */
4126 { 0xff10, 0, 9 }, /* Full Width */
4127 { 0x10107, 1, 9 }, /* Aegean */
4128 { 0x10320, 1, 1 }, /* Old Italic Numeral 1 */
4129 { 0x10321, 5, 5 }, /* Old Italic Numeral 5 */
4130 { 0x104a0, 0, 9 }, /* Osmanya */
4131 { 0x10a40, 1, 4, TRUE /*win10*/ }, /* Kharoshthi */
4132 { 0x10d30, 0, 9, TRUE /*win10*/ }, /* Hanifi Rohingya */
4133 { 0x10e60, 1, 9, TRUE /*win10*/ }, /* Rumi */
4134 { 0x11052, 1, 9, TRUE /*win10*/ }, /* Brahmi Number */
4135 { 0x11066, 0, 9, TRUE /*win10*/ }, /* Brahmi Digit */
4136 { 0x110f0, 0, 9, TRUE /*win10*/ }, /* Sora Sompeng */
4137 { 0x11136, 0, 9, TRUE /*win10*/ }, /* Chakma */
4138 { 0x111d0, 0, 9, TRUE /*win10*/ }, /* Sharada */
4139 { 0x112f0, 0, 9, TRUE /*win10*/ }, /* Khudawadi */
4140 { 0x11450, 0, 9, TRUE /*win10*/ }, /* Newa */
4141 { 0x114d0, 0, 9, TRUE /*win10*/ }, /* Tirhuta */
4142 { 0x11650, 0, 9, TRUE /*win10*/ }, /* Modi */
4143 { 0x116c0, 0, 9, TRUE /*win10*/ }, /* Takri */
4144 { 0x11730, 0, 9, TRUE /*win10*/ }, /* Ahom */
4145 { 0x118e0, 0, 9, TRUE /*win10*/ }, /* Warang */
4146 { 0x11950, 0, 9, TRUE /*win10*/ }, /* Dives Akuru */
4147 { 0x11c50, 0, 9, TRUE /*win10*/ }, /* Bhaiksuki */
4148 { 0x11d50, 0, 9, TRUE /*win10*/ }, /* Masaram Gondi */
4149 { 0x11da0, 0, 9, TRUE /*win10*/ }, /* Gunjala Gondi */
4150 { 0x11f50, 0, 9, TRUE /*win10*/ }, /* Kawi */
4151 { 0x16a60, 0, 9, TRUE /*win10*/ }, /* Mro */
4152 { 0x16ac0, 0, 9, TRUE /*win10*/ }, /* Tangsa */
4153 { 0x16b50, 0, 9, TRUE /*win10*/ }, /* Pahawh Hmong */
4154 { 0x1d7ce, 0, 9 }, /* Mathematical Bold */
4155 { 0x1d7d8, 0, 9 }, /* Mathematical Double Struck */
4156 { 0x1d7e2, 0, 9 }, /* Mathematical Sans Serif */
4157 { 0x1d7ec, 0, 9 }, /* Mathematical Sans Serif Bold */
4158 { 0x1d7f6, 0, 9 }, /* Mathematical Monospace */
4159 { 0x1e140, 0, 9, TRUE /*win10*/ }, /* Nyiakeng Puachue Hmong */
4160 { 0x1e2f0, 0, 9, TRUE /*win10*/ }, /* Wancho */
4161 { 0x1e4f0, 0, 9, TRUE /*win10*/ }, /* Nag Mundari */
4162 { 0x1e950, 0, 9, TRUE /*win10*/ }, /* Adlam */
4163 { 0x1f100, 0, 0, TRUE /*win10*/ }, /* Full Stop */
4164 { 0x1f101, 0, 9, TRUE /*win10*/ }, /* Comma */
4165 { 0x1fbf0, 0, 9, TRUE /*win10*/ }, /* Segmented */
4166 { 0x10ffff } /* Terminator */
4168 static const WCHAR foldczone_src[] =
4170 'W', 'i', 'n', 'e', 0x0348, 0x0551, 0x1323, 0x280d,
4171 0xff37, 0xff49, 0xff4e, 0xff45, 0x3c5, 0x308, 0x6a, 0x30c, 0xa0, 0xaa, 0
4173 static const WCHAR foldczone_dst[] =
4175 'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x3cb,0x1f0,' ','a',0
4177 static const WCHAR foldczone_broken_dst[] =
4179 'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x03c5,0x0308,'j',0x030c,0x00a0,0x00aa,0
4181 static const WCHAR ligatures_src[] =
4183 'W', 'i', 'n', 'e', 0x03a6, 0x03b9, 0x03bd, 0x03b5,
4184 0x00c6, 0x00de, 0x00df, 0x00e6, 0x00fe, 0x0132, 0x0133, 0x0152,
4185 0x0153, 0x01c4, 0x01c5, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01ca,
4186 0x01cb, 0x01cc, 0x01e2, 0x01e3, 0x01f1, 0x01f2, 0x01f3, 0x01fc,
4187 0x01fd, 0x05f0, 0x05f1, 0x05f2, 0xfb00, 0xfb01, 0xfb02, 0xfb03,
4188 0xfb04, 0xfb05, 0xfb06, '\0'
4190 static const WCHAR ligatures_dst[] =
4192 'W','i','n','e',0x03a6,0x03b9,0x03bd,0x03b5,
4193 'A','E','T','H','s','s','a','e','t','h','I','J','i','j','O','E','o','e',
4194 'D',0x017d,'D',0x017e,'d',0x017e,'L','J','L','j','l','j','N','J','N','j',
4195 'n','j',0x0100,0x0112,0x0101,0x0113,'D','Z','D','z','d','z',0x00c1,0x00c9,
4196 0x00e1,0x00e9,0x05d5,0x05d5,0x05d5,0x05d9,0x05d9,0x05d9,'f','f','f','i',
4197 'f','l','f','f','i','f','f','l',0x017f,'t','s','t','\0'
4200 /* Invalid flag combinations */
4201 for (i = 0; i < ARRAY_SIZE(badFlags); i++)
4203 src[0] = dst[0] = '\0';
4204 SetLastError(0xdeadbeef);
4205 ret = FoldStringW(badFlags[i], src, 256, dst, 256);
4206 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
4207 "Expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
4210 /* src & dst cannot be the same */
4211 SetLastError(0xdeadbeef);
4212 ret = FoldStringW(MAP_FOLDCZONE, src, -1, src, 256);
4213 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4214 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4216 /* src can't be NULL */
4217 SetLastError(0xdeadbeef);
4218 ret = FoldStringW(MAP_FOLDCZONE, NULL, -1, dst, 256);
4219 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4220 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4222 /* srclen can't be 0 */
4223 SetLastError(0xdeadbeef);
4224 ret = FoldStringW(MAP_FOLDCZONE, src, 0, dst, 256);
4225 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4226 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4228 /* dstlen can't be < 0 */
4229 SetLastError(0xdeadbeef);
4230 ret = FoldStringW(MAP_FOLDCZONE, src, -1, dst, -1);
4231 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4232 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4234 /* Ret includes terminating NUL which is appended if srclen = -1 */
4235 SetLastError(0xdeadbeef);
4236 src[0] = 'A';
4237 src[1] = '\0';
4238 dst[0] = '\0';
4239 ret = FoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
4240 ok(ret == 2, "Expected ret == 2, got %d, error %ld\n", ret, GetLastError());
4241 ok(dst[0] == 'A' && dst[1] == '\0',
4242 "srclen=-1: Expected ret=2 [%d,%d], got ret=%d [%d,%d], err=%ld\n",
4243 'A', '\0', ret, dst[0], dst[1], GetLastError());
4245 /* If size is given, result is not NUL terminated */
4246 SetLastError(0xdeadbeef);
4247 src[0] = 'A';
4248 src[1] = 'A';
4249 dst[0] = 'X';
4250 dst[1] = 'X';
4251 ret = FoldStringW(MAP_FOLDCZONE, src, 1, dst, 256);
4252 ok(ret == 1, "Expected ret == 1, got %d, error %ld\n", ret, GetLastError());
4253 ok(dst[0] == 'A' && dst[1] == 'X',
4254 "srclen=1: Expected ret=1, [%d,%d], got ret=%d,[%d,%d], err=%ld\n",
4255 'A','X', ret, dst[0], dst[1], GetLastError());
4257 /* MAP_FOLDDIGITS */
4258 for (j = 0; j < ARRAY_SIZE(digitRanges); j++)
4260 /* Check everything before this range */
4261 for (ch = prev_ch; ch < digitRanges[j].ch; ch++)
4263 len = put_utf16( src, ch );
4264 src[len] = 0;
4265 SetLastError(0xdeadbeef);
4266 ret = FoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
4267 if (ret == 3)
4269 ok( !wcscmp( src, dst ), "%s changed to %s\n", debugstr_w(src), debugstr_w(dst) );
4270 continue;
4272 ok(ret == 2, "Expected ret == 2, got %d, error %ld\n", ret, GetLastError());
4273 ok(dst[0] == ch, "MAP_FOLDDIGITS: ch 0x%04x Expected unchanged got %04x\n", ch, dst[0]);
4274 if (ch < 0x10000)
4276 WCHAR wch = ch;
4277 GetStringTypeW( CT_CTYPE1, &wch, 1, &type );
4278 ok(!(type & C1_DIGIT), "char %04x should not be a digit\n", wch );
4281 if (digitRanges[j].ch == 0x10ffff)
4282 break; /* Finished the whole code point space */
4284 for (ch = digitRanges[j].ch; ch <= digitRanges[j].ch + digitRanges[j].last - digitRanges[j].first; ch++)
4286 UINT exp = '0' + digitRanges[j].first + ch - digitRanges[j].ch;
4288 SetLastError(0xdeadbeef);
4289 len = put_utf16( src, ch );
4290 src[len] = 0;
4291 ret = FoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
4292 ok(ret == 2 || broken( digitRanges[j].broken && ch >= 0x10000 ),
4293 "%04x: Expected ret == 2, got %d, error %ld\n", ch, ret, GetLastError());
4294 ok((dst[0] == exp && dst[1] == '\0') || broken( digitRanges[j].broken ),
4295 "MAP_FOLDDIGITS: ch %04x Expected %04x got %04x\n", ch, exp, dst[0]);
4297 prev_ch = ch;
4300 /* MAP_FOLDCZONE */
4301 SetLastError(0xdeadbeef);
4302 ret = FoldStringW(MAP_FOLDCZONE, foldczone_src, -1, dst, 256);
4303 ok(ret == ARRAY_SIZE(foldczone_dst)
4304 || broken(ret == ARRAY_SIZE(foldczone_broken_dst)), /* winxp, win2003 */
4305 "Got %d, error %ld.\n", ret, GetLastError());
4306 ok(!memcmp(dst, foldczone_dst, sizeof(foldczone_dst))
4307 || broken(!memcmp(dst, foldczone_broken_dst, sizeof(foldczone_broken_dst))), /* winxp, win2003 */
4308 "Got unexpected string %s.\n", wine_dbgstr_w(dst));
4310 /* MAP_EXPAND_LIGATURES */
4311 SetLastError(0xdeadbeef);
4312 ret = FoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
4313 ok(ret == ARRAY_SIZE(ligatures_dst), "Got %d, error %ld\n", ret, GetLastError());
4314 ok(!memcmp(dst, ligatures_dst, sizeof(ligatures_dst)),
4315 "Got unexpected string %s.\n", wine_dbgstr_w(dst));
4317 /* FIXME: MAP_PRECOMPOSED : MAP_COMPOSITE */
4322 #define LCID_OK(l) \
4323 ok(lcid == l, "Expected lcid = %08lx, got %08lx\n", l, lcid)
4324 #define MKLCID(x,y,z) MAKELCID(MAKELANGID(x, y), z)
4325 #define LCID_RES(src, res) do { lcid = ConvertDefaultLocale(src); LCID_OK(res); } while (0)
4326 #define TEST_LCIDLANG(a,b) LCID_RES(MAKELCID(a,b), MAKELCID(a,b))
4327 #define TEST_LCID(a,b,c) LCID_RES(MKLCID(a,b,c), MKLCID(a,b,c))
4329 static void test_ConvertDefaultLocale(void)
4331 /* some languages use a different default than SUBLANG_DEFAULT */
4332 static const struct { WORD lang, sublang; } nondefault_langs[] =
4334 { LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED },
4335 { LANG_SPANISH, SUBLANG_SPANISH_MODERN },
4336 { LANG_IRISH, SUBLANG_IRISH_IRELAND },
4337 { LANG_BENGALI, SUBLANG_BENGALI_BANGLADESH },
4338 { LANG_SINDHI, SUBLANG_SINDHI_AFGHANISTAN },
4339 { LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA_LATIN },
4340 { LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN },
4341 { LANG_FULAH, SUBLANG_FULAH_SENEGAL },
4342 { LANG_TIGRINYA, SUBLANG_TIGRINYA_ERITREA }
4344 LCID lcid;
4345 unsigned int i;
4347 /* Doesn't change lcid, even if non default sublang/sort used */
4348 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_US, SORT_DEFAULT);
4349 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_UK, SORT_DEFAULT);
4350 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT);
4351 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE);
4352 lcid = ConvertDefaultLocale( MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ));
4353 ok( lcid == MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ) ||
4354 broken( lcid == MKLCID( LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE )), /* <= vista */
4355 "Expected lcid = %08lx got %08lx\n",
4356 MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ), lcid );
4357 lcid = ConvertDefaultLocale( MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ));
4358 ok( lcid == MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ) ||
4359 broken( lcid == MKLCID( LANG_IRISH, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE )), /* <= vista */
4360 "Expected lcid = %08lx got %08lx\n",
4361 MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ), lcid );
4363 /* SUBLANG_NEUTRAL -> SUBLANG_DEFAULT */
4364 LCID_RES(MKLCID(LANG_ENGLISH, SUBLANG_NEUTRAL, SORT_DEFAULT),
4365 MKLCID(LANG_ENGLISH, SUBLANG_DEFAULT, SORT_DEFAULT));
4366 LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_DEFAULT),
4367 MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT));
4368 for (i = 0; i < ARRAY_SIZE(nondefault_langs); i++)
4370 lcid = ConvertDefaultLocale( MAKELANGID( nondefault_langs[i].lang, SUBLANG_NEUTRAL ));
4371 ok( lcid == MAKELANGID( nondefault_langs[i].lang, nondefault_langs[i].sublang ) ||
4372 broken( lcid == MAKELANGID( nondefault_langs[i].lang, SUBLANG_DEFAULT )) || /* <= vista */
4373 broken( lcid == MAKELANGID( nondefault_langs[i].lang, SUBLANG_NEUTRAL )), /* w7 */
4374 "Expected lcid = %08x got %08lx\n",
4375 MAKELANGID( nondefault_langs[i].lang, nondefault_langs[i].sublang ), lcid );
4377 lcid = ConvertDefaultLocale( 0x7804 );
4378 ok( lcid == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ) ||
4379 broken( lcid == 0x7804 ), /* <= vista */
4380 "Expected lcid = %08x got %08lx\n", MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ), lcid );
4381 lcid = ConvertDefaultLocale( 0x7c04 );
4382 ok( lcid == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ) ||
4383 broken( lcid == 0x7c04 ) || /* winxp */
4384 broken( lcid == 0x0404 ), /* vista */
4385 "Expected lcid = %08x got %08lx\n", MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ), lcid );
4386 lcid = ConvertDefaultLocale( LANG_SERBIAN_NEUTRAL );
4387 ok( lcid == MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN ) ||
4388 broken( lcid == MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_LATIN ) ), /* <= vista */
4389 "Expected lcid = %08x got %08lx\n", MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN ), lcid );
4391 /* Invariant language is not treated specially */
4392 TEST_LCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT);
4394 /* User/system default languages alone are not mapped */
4395 TEST_LCIDLANG(LANG_SYSTEM_DEFAULT, SORT_JAPANESE_UNICODE);
4396 TEST_LCIDLANG(LANG_USER_DEFAULT, SORT_JAPANESE_UNICODE);
4398 /* Default lcids */
4399 LCID_RES(LOCALE_SYSTEM_DEFAULT, GetSystemDefaultLCID());
4400 LCID_RES(LOCALE_USER_DEFAULT, GetUserDefaultLCID());
4401 LCID_RES(LOCALE_NEUTRAL, GetUserDefaultLCID());
4402 LCID_RES(LOCALE_CUSTOM_DEFAULT, GetUserDefaultLCID());
4403 lcid = ConvertDefaultLocale( LOCALE_CUSTOM_UNSPECIFIED );
4404 ok( lcid == GetUserDefaultLCID() || broken(lcid == LOCALE_CUSTOM_UNSPECIFIED), /* <= win8 */
4405 "wrong lcid %04lx\n", lcid );
4406 lcid = ConvertDefaultLocale( LOCALE_CUSTOM_UI_DEFAULT );
4407 ok( lcid == GetUserDefaultUILanguage() || lcid == LOCALE_CUSTOM_UI_DEFAULT, "wrong lcid %04lx\n", lcid );
4408 lcid = ConvertDefaultLocale(LOCALE_INVARIANT);
4409 ok(lcid == LOCALE_INVARIANT || broken(lcid == 0x47f) /* win2k[3]/winxp */,
4410 "Expected lcid = %08lx, got %08lx\n", LOCALE_INVARIANT, lcid);
4413 static BOOL CALLBACK langgrp_procA(LGRPID lgrpid, LPSTR lpszNum, LPSTR lpszName,
4414 DWORD dwFlags, LONG_PTR lParam)
4416 if (winetest_debug > 1)
4417 trace("%08lx, %s, %s, %08lx, %08Ix\n",
4418 lgrpid, lpszNum, lpszName, dwFlags, lParam);
4420 ok(pIsValidLanguageGroup(lgrpid, dwFlags) == TRUE,
4421 "Enumerated grp %ld not valid (flags %ld)\n", lgrpid, dwFlags);
4423 /* If lParam is one, we are calling with flags defaulted from 0 */
4424 ok(!lParam || (dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED),
4425 "Expected dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED, got %ld\n", dwFlags);
4427 return TRUE;
4430 static void test_EnumSystemLanguageGroupsA(void)
4432 BOOL ret;
4434 if (!pEnumSystemLanguageGroupsA || !pIsValidLanguageGroup)
4436 win_skip("EnumSystemLanguageGroupsA and/or IsValidLanguageGroup are not available\n");
4437 return;
4440 /* No enumeration proc */
4441 SetLastError(0);
4442 ret = pEnumSystemLanguageGroupsA(0, LGRPID_INSTALLED, 0);
4443 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4445 win_skip("EnumSystemLanguageGroupsA is not implemented\n");
4446 return;
4448 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4449 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4451 /* Invalid flags */
4452 SetLastError(0);
4453 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED|LGRPID_SUPPORTED, 0);
4454 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
4456 /* No flags - defaults to LGRPID_INSTALLED */
4457 SetLastError(0xdeadbeef);
4458 pEnumSystemLanguageGroupsA(langgrp_procA, 0, 1);
4459 ok(GetLastError() == 0xdeadbeef, "got error %ld\n", GetLastError());
4461 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED, 0);
4462 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
4465 static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam )
4467 if (winetest_debug > 1)
4468 trace( "%s %lx\n", wine_dbgstr_w(name), flags );
4469 return TRUE;
4472 static void test_EnumSystemLocalesEx(void)
4474 BOOL ret;
4476 if (!pEnumSystemLocalesEx)
4478 win_skip( "EnumSystemLocalesEx not available\n" );
4479 return;
4481 SetLastError( 0xdeadbeef );
4482 ret = pEnumSystemLocalesEx( enum_func, LOCALE_ALL, 0, (void *)1 );
4483 ok( !ret, "should have failed\n" );
4484 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
4485 SetLastError( 0xdeadbeef );
4486 ret = pEnumSystemLocalesEx( enum_func, 0, 0, NULL );
4487 ok( ret, "failed err %lu\n", GetLastError() );
4490 static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
4491 LONG_PTR lParam)
4493 if (winetest_debug > 1)
4494 trace("%08lx, %08lx, %s, %08Ix\n", lgrpid, lcid, lpszNum, lParam);
4496 /* invalid locale enumerated on some platforms */
4497 if (lcid == 0)
4498 return TRUE;
4500 ok(pIsValidLanguageGroup(lgrpid, LGRPID_SUPPORTED) == TRUE,
4501 "Enumerated grp %ld not valid\n", lgrpid);
4502 ok(IsValidLocale(lcid, LCID_SUPPORTED) == TRUE,
4503 "Enumerated grp locale %04lx not valid\n", lcid);
4504 return TRUE;
4507 static void test_EnumLanguageGroupLocalesA(void)
4509 BOOL ret;
4511 if (!pEnumLanguageGroupLocalesA || !pIsValidLanguageGroup)
4513 win_skip("EnumLanguageGroupLocalesA and/or IsValidLanguageGroup are not available\n");
4514 return;
4517 /* No enumeration proc */
4518 SetLastError(0);
4519 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0, 0);
4520 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4522 win_skip("EnumLanguageGroupLocalesA is not implemented\n");
4523 return;
4525 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4526 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4528 /* lgrpid too small */
4529 SetLastError(0);
4530 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, 0, 0, 0);
4531 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4532 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4534 /* lgrpid too big */
4535 SetLastError(0);
4536 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_ARMENIAN + 1, 0, 0);
4537 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4538 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4540 /* dwFlags is reserved */
4541 SetLastError(0);
4542 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0x1, 0);
4543 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
4544 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4546 pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_WESTERN_EUROPE, 0, 0);
4549 static void test_SetLocaleInfo(void)
4551 BOOL bRet;
4552 LCID lcid = GetUserDefaultLCID();
4553 UINT i;
4555 /* Null data */
4556 SetLastError(0xdeadbeef);
4557 bRet = SetLocaleInfoA(lcid, LOCALE_SSHORTDATE, NULL);
4558 ok( !bRet && GetLastError() == ERROR_INVALID_PARAMETER,
4559 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4561 SetLastError(0xdeadbeef);
4562 bRet = SetLocaleInfoW(lcid, LOCALE_SSHORTDATE, NULL);
4563 ok( !bRet && GetLastError() == ERROR_INVALID_PARAMETER,
4564 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4566 SetLastError(0xdeadbeef);
4567 bRet = SetLocaleInfoW(lcid, LOCALE_SDAYNAME1, NULL);
4568 ok( !bRet && GetLastError() == ERROR_INVALID_PARAMETER,
4569 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4571 for (i = 0; i <= 0x1014; i++)
4573 WCHAR buffer[80];
4574 if (!GetLocaleInfoW( LOCALE_USER_DEFAULT, i, buffer, ARRAY_SIZE(buffer) )) continue;
4575 SetLastError(0xdeadbeef);
4576 bRet = SetLocaleInfoW(lcid, i, buffer);
4577 switch (i)
4579 case LOCALE_ICALENDARTYPE:
4580 case LOCALE_ICURRDIGITS:
4581 case LOCALE_ICURRENCY:
4582 case LOCALE_IDIGITS:
4583 case LOCALE_IDIGITSUBSTITUTION:
4584 case LOCALE_IFIRSTDAYOFWEEK:
4585 case LOCALE_IFIRSTWEEKOFYEAR:
4586 case LOCALE_ILZERO:
4587 case LOCALE_IMEASURE:
4588 case LOCALE_INEGCURR:
4589 case LOCALE_INEGNUMBER:
4590 case LOCALE_IPAPERSIZE:
4591 case LOCALE_ITIME:
4592 case LOCALE_S1159:
4593 case LOCALE_S2359:
4594 case LOCALE_SCURRENCY:
4595 case LOCALE_SDATE:
4596 case LOCALE_SDECIMAL:
4597 case LOCALE_SGROUPING:
4598 case LOCALE_SLIST:
4599 case LOCALE_SLONGDATE:
4600 case LOCALE_SMONDECIMALSEP:
4601 case LOCALE_SMONGROUPING:
4602 case LOCALE_SMONTHOUSANDSEP:
4603 case LOCALE_SNATIVEDIGITS:
4604 case LOCALE_SNEGATIVESIGN:
4605 case LOCALE_SPOSITIVESIGN:
4606 case LOCALE_SSHORTDATE:
4607 case LOCALE_SSHORTTIME:
4608 case LOCALE_STHOUSAND:
4609 case LOCALE_STIME:
4610 case LOCALE_STIMEFORMAT:
4611 case LOCALE_SYEARMONTH:
4612 ok( bRet, "%04x: failed err %lu\n", i, GetLastError() );
4613 break;
4614 case LOCALE_SINTLSYMBOL:
4615 ok( bRet || broken(!bRet), /* win10 <= 1507 */
4616 "%04x: failed err %lu\n", i, GetLastError() );
4617 break;
4618 default:
4619 ok( !bRet, "%04x: succeeded\n", i );
4620 ok( GetLastError() == ERROR_INVALID_FLAGS, "%04x: wrong error %lu\n", i, GetLastError() );
4621 break;
4626 static BOOL CALLBACK luilocale_proc1A(LPSTR value, LONG_PTR lParam)
4628 if (winetest_debug > 1)
4629 trace("%s %08Ix\n", value, lParam);
4630 return(TRUE);
4633 static BOOL CALLBACK luilocale_proc2A(LPSTR value, LONG_PTR lParam)
4635 ok(!enumCount, "callback called again unexpected\n");
4636 enumCount++;
4637 return(FALSE);
4640 static BOOL CALLBACK luilocale_proc3A(LPSTR value, LONG_PTR lParam)
4642 ok(0,"callback called unexpected\n");
4643 return(FALSE);
4646 static void test_EnumUILanguageA(void)
4648 BOOL ret;
4649 if (!pEnumUILanguagesA) {
4650 win_skip("EnumUILanguagesA is not available on Win9x or NT4\n");
4651 return;
4654 SetLastError(ERROR_SUCCESS);
4655 ret = pEnumUILanguagesA(luilocale_proc1A, 0, 0);
4656 if (ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4658 win_skip("EnumUILanguagesA is not implemented\n");
4659 return;
4661 ok(ret, "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
4663 SetLastError(0xdeadbeef);
4664 ret = pEnumUILanguagesA(luilocale_proc1A, MUI_LANGUAGE_NAME, 0);
4665 ok(ret, "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
4667 enumCount = 0;
4668 SetLastError(ERROR_SUCCESS);
4669 ret = pEnumUILanguagesA(luilocale_proc2A, 0, 0);
4670 ok(ret, "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
4671 ok(enumCount == 1, "enumCount = %u\n", enumCount);
4673 enumCount = 0;
4674 SetLastError(ERROR_SUCCESS);
4675 ret = pEnumUILanguagesA(luilocale_proc2A, MUI_LANGUAGE_ID, 0);
4676 ok(ret || broken(!ret && GetLastError() == ERROR_INVALID_FLAGS), /* winxp */
4677 "Expected ret != 0, got %d, error %ld\n", ret, GetLastError());
4678 if (ret) ok(enumCount == 1, "enumCount = %u\n", enumCount);
4680 SetLastError(ERROR_SUCCESS);
4681 ret = pEnumUILanguagesA(NULL, 0, 0);
4682 ok(!ret, "Expected return value FALSE, got %u\n", ret);
4683 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4684 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4686 SetLastError(ERROR_SUCCESS);
4687 ret = pEnumUILanguagesA(luilocale_proc3A, 0x5a5a5a5a, 0);
4688 ok(!ret, "Expected return value FALSE, got %u\n", ret);
4689 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
4691 SetLastError(ERROR_SUCCESS);
4692 ret = pEnumUILanguagesA(NULL, 0x5a5a5a5a, 0);
4693 ok(!ret, "Expected return value FALSE, got %u\n", ret);
4694 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4695 "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
4698 static char date_fmt_buf[1024];
4699 static WCHAR date_fmt_bufW[1024];
4701 static BOOL CALLBACK enum_datetime_procA(LPSTR fmt)
4703 lstrcatA(date_fmt_buf, fmt);
4704 lstrcatA(date_fmt_buf, "\n");
4705 return TRUE;
4708 static BOOL CALLBACK enum_datetime_procW(WCHAR *fmt)
4710 lstrcatW(date_fmt_bufW, fmt);
4711 return FALSE;
4714 static void test_EnumDateFormatsA(void)
4716 char *p, buf[256];
4717 BOOL ret;
4718 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
4720 date_fmt_buf[0] = 0;
4721 SetLastError(0xdeadbeef);
4722 ret = EnumDateFormatsA(enum_datetime_procA, lcid, 0);
4723 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
4725 win_skip("0 for dwFlags is not supported\n");
4727 else
4729 ok(ret, "EnumDateFormatsA(0) error %ld\n", GetLastError());
4730 trace("EnumDateFormatsA(0): %s\n", date_fmt_buf);
4731 /* test the 1st enumerated format */
4732 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4733 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
4734 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %ld\n", GetLastError());
4735 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4738 date_fmt_buf[0] = 0;
4739 SetLastError(0xdeadbeef);
4740 ret = EnumDateFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
4741 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
4743 win_skip("LOCALE_USE_CP_ACP is not supported\n");
4745 else
4747 ok(ret, "EnumDateFormatsA(LOCALE_USE_CP_ACP) error %ld\n", GetLastError());
4748 trace("EnumDateFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
4749 /* test the 1st enumerated format */
4750 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4751 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
4752 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %ld\n", GetLastError());
4753 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4756 date_fmt_buf[0] = 0;
4757 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_SHORTDATE);
4758 ok(ret, "EnumDateFormatsA(DATE_SHORTDATE) error %ld\n", GetLastError());
4759 trace("EnumDateFormatsA(DATE_SHORTDATE): %s\n", date_fmt_buf);
4760 /* test the 1st enumerated format */
4761 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4762 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
4763 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %ld\n", GetLastError());
4764 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4766 date_fmt_buf[0] = 0;
4767 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_LONGDATE);
4768 ok(ret, "EnumDateFormatsA(DATE_LONGDATE) error %ld\n", GetLastError());
4769 trace("EnumDateFormatsA(DATE_LONGDATE): %s\n", date_fmt_buf);
4770 /* test the 1st enumerated format */
4771 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4772 ret = GetLocaleInfoA(lcid, LOCALE_SLONGDATE, buf, sizeof(buf));
4773 ok(ret, "GetLocaleInfoA(LOCALE_SLONGDATE) error %ld\n", GetLastError());
4774 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4776 date_fmt_buf[0] = 0;
4777 SetLastError(0xdeadbeef);
4778 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_YEARMONTH);
4779 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
4781 win_skip("DATE_YEARMONTH is only present on W2K and later\n");
4782 return;
4784 ok(ret, "EnumDateFormatsA(DATE_YEARMONTH) error %ld\n", GetLastError());
4785 trace("EnumDateFormatsA(DATE_YEARMONTH): %s\n", date_fmt_buf);
4786 /* test the 1st enumerated format */
4787 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4788 ret = GetLocaleInfoA(lcid, LOCALE_SYEARMONTH, buf, sizeof(buf));
4789 ok(ret, "GetLocaleInfoA(LOCALE_SYEARMONTH) error %ld\n", GetLastError());
4790 ok(!lstrcmpA(date_fmt_buf, buf) || broken(!buf[0]) /* win9x */,
4791 "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4794 static void test_EnumTimeFormatsA(void)
4796 char *p, buf[256];
4797 BOOL ret;
4798 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
4800 date_fmt_buf[0] = 0;
4801 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, 0);
4802 ok(ret, "EnumTimeFormatsA(0) error %ld\n", GetLastError());
4803 trace("EnumTimeFormatsA(0): %s\n", date_fmt_buf);
4804 /* test the 1st enumerated format */
4805 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4806 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
4807 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %ld\n", GetLastError());
4808 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4810 date_fmt_buf[0] = 0;
4811 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
4812 ok(ret, "EnumTimeFormatsA(LOCALE_USE_CP_ACP) error %ld\n", GetLastError());
4813 trace("EnumTimeFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
4814 /* test the 1st enumerated format */
4815 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4816 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
4817 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %ld\n", GetLastError());
4818 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4821 static void test_EnumTimeFormatsW(void)
4823 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
4824 WCHAR bufW[256];
4825 BOOL ret;
4827 date_fmt_bufW[0] = 0;
4828 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, 0);
4829 ok(ret, "EnumTimeFormatsW(0) error %ld\n", GetLastError());
4830 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
4831 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %ld\n", GetLastError());
4832 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4833 wine_dbgstr_w(bufW));
4835 date_fmt_bufW[0] = 0;
4836 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, LOCALE_USE_CP_ACP);
4837 ok(ret, "EnumTimeFormatsW(LOCALE_USE_CP_ACP) error %ld\n", GetLastError());
4838 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
4839 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %ld\n", GetLastError());
4840 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4841 wine_dbgstr_w(bufW));
4843 /* TIME_NOSECONDS is Win7+ feature */
4844 date_fmt_bufW[0] = 0;
4845 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, TIME_NOSECONDS);
4846 if (!ret && GetLastError() == ERROR_INVALID_FLAGS)
4847 win_skip("EnumTimeFormatsW doesn't support TIME_NOSECONDS\n");
4848 else {
4849 char buf[256];
4851 ok(ret, "EnumTimeFormatsW(TIME_NOSECONDS) error %ld\n", GetLastError());
4852 ret = GetLocaleInfoW(lcid, LOCALE_SSHORTTIME, bufW, ARRAY_SIZE(bufW));
4853 ok(ret, "GetLocaleInfoW(LOCALE_SSHORTTIME) error %ld\n", GetLastError());
4854 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4855 wine_dbgstr_w(bufW));
4857 /* EnumTimeFormatsA doesn't support this flag */
4858 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, TIME_NOSECONDS);
4859 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %ld\n", ret,
4860 GetLastError());
4862 ret = EnumTimeFormatsA(NULL, lcid, TIME_NOSECONDS);
4863 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %ld\n", ret,
4864 GetLastError());
4866 /* And it's not supported by GetLocaleInfoA either */
4867 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTTIME, buf, ARRAY_SIZE(buf));
4868 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "GetLocaleInfoA(LOCALE_SSHORTTIME) ret %d, error %ld\n", ret,
4869 GetLastError());
4873 static void test_GetCPInfo(void)
4875 BOOL ret;
4876 CPINFO cpinfo;
4877 CPINFOEXW cpiw;
4879 SetLastError(0xdeadbeef);
4880 ret = GetCPInfo(CP_SYMBOL, &cpinfo);
4881 ok(!ret, "GetCPInfo(CP_SYMBOL) should fail\n");
4882 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4883 "expected ERROR_INVALID_PARAMETER, got %lu\n", GetLastError());
4885 memset(cpinfo.LeadByte, '-', ARRAY_SIZE(cpinfo.LeadByte));
4886 SetLastError(0xdeadbeef);
4887 ret = GetCPInfo(CP_UTF7, &cpinfo);
4888 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
4890 win_skip("Codepage CP_UTF7 is not installed/available\n");
4892 else
4894 unsigned int i;
4896 ok(ret, "GetCPInfo(CP_UTF7) error %lu\n", GetLastError());
4897 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
4898 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
4899 for (i = 0; i < sizeof(cpinfo.LeadByte); i++)
4900 ok(!cpinfo.LeadByte[i], "expected NUL byte in index %u\n", i);
4901 ok(cpinfo.MaxCharSize == 5, "expected 5, got 0x%x\n", cpinfo.MaxCharSize);
4903 memset( &cpiw, 0xcc, sizeof(cpiw) );
4904 ret = GetCPInfoExW( CP_UTF7, 0, &cpiw );
4905 ok( ret, "GetCPInfoExW failed err %lu\n", GetLastError() );
4906 ok( cpiw.DefaultChar[0] == 0x3f, "wrong DefaultChar[0] %02x\n", cpiw.DefaultChar[0] );
4907 ok( cpiw.DefaultChar[1] == 0, "wrong DefaultChar[1] %02x\n", cpiw.DefaultChar[1] );
4908 for (i = 0; i < 12; i++) ok( cpiw.LeadByte[i] == 0, "wrong LeadByte[%u] %02x\n", i, cpiw.LeadByte[i] );
4909 ok( cpiw.MaxCharSize == 5, "wrong MaxCharSize %02x\n", cpiw.MaxCharSize );
4910 ok( cpiw.CodePage == CP_UTF7, "wrong CodePage %02x\n", cpiw.CodePage );
4911 ok( cpiw.UnicodeDefaultChar == 0xfffd, "wrong UnicodeDefaultChar %02x\n", cpiw.UnicodeDefaultChar );
4912 ok( !wcscmp( cpiw.CodePageName, L"65000 (UTF-7)" ),
4913 "wrong CodePageName %s\n", debugstr_w(cpiw.CodePageName) );
4916 memset(cpinfo.LeadByte, '-', ARRAY_SIZE(cpinfo.LeadByte));
4917 SetLastError(0xdeadbeef);
4918 ret = GetCPInfo(CP_UTF8, &cpinfo);
4919 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
4921 win_skip("Codepage CP_UTF8 is not installed/available\n");
4923 else
4925 unsigned int i;
4927 ok(ret, "GetCPInfo(CP_UTF8) error %lu\n", GetLastError());
4928 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
4929 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
4930 for (i = 0; i < sizeof(cpinfo.LeadByte); i++)
4931 ok(!cpinfo.LeadByte[i], "expected NUL byte in index %u\n", i);
4932 ok(cpinfo.MaxCharSize == 4, "expected 4, got %u\n", cpinfo.MaxCharSize);
4934 memset( &cpiw, 0xcc, sizeof(cpiw) );
4935 ret = GetCPInfoExW( CP_UTF8, 0, &cpiw );
4936 ok( ret, "GetCPInfoExW failed err %lu\n", GetLastError() );
4937 ok( cpiw.DefaultChar[0] == 0x3f, "wrong DefaultChar[0] %02x\n", cpiw.DefaultChar[0] );
4938 ok( cpiw.DefaultChar[1] == 0, "wrong DefaultChar[1] %02x\n", cpiw.DefaultChar[1] );
4939 for (i = 0; i < 12; i++) ok( cpiw.LeadByte[i] == 0, "wrong LeadByte[%u] %02x\n", i, cpiw.LeadByte[i] );
4940 ok( cpiw.MaxCharSize == 4, "wrong MaxCharSize %02x\n", cpiw.MaxCharSize );
4941 ok( cpiw.CodePage == CP_UTF8, "wrong CodePage %02x\n", cpiw.CodePage );
4942 ok( cpiw.UnicodeDefaultChar == 0xfffd, "wrong UnicodeDefaultChar %02x\n", cpiw.UnicodeDefaultChar );
4943 ok( !wcscmp( cpiw.CodePageName, L"65001 (UTF-8)" ),
4944 "wrong CodePageName %s\n", debugstr_w(cpiw.CodePageName) );
4948 SetLastError( 0xdeadbeef );
4949 ret = GetCPInfoExW( 0xbeef, 0, &cpiw );
4950 ok( !ret, "GetCPInfoExW succeeeded\n" );
4951 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
4953 if (pNtGetNlsSectionPtr)
4955 CPTABLEINFO table;
4956 NTSTATUS status;
4957 void *ptr, *ptr2;
4958 SIZE_T size;
4959 int i;
4961 for (i = 0; i < 100; i++)
4963 ptr = NULL;
4964 size = 0;
4965 status = pNtGetNlsSectionPtr( i, 9999, NULL, &ptr, &size );
4966 switch (i)
4968 case 9: /* sortkeys */
4969 case 13: /* unknown */
4970 ok( status == STATUS_INVALID_PARAMETER_1 || status == STATUS_INVALID_PARAMETER_3, /* vista */
4971 "%u: failed %lx\n", i, status );
4972 break;
4973 case 10: /* casemap */
4974 ok( status == STATUS_INVALID_PARAMETER_1 || status == STATUS_UNSUCCESSFUL,
4975 "%u: failed %lx\n", i, status );
4976 break;
4977 case 11: /* codepage */
4978 case 12: /* normalization */
4979 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%u: failed %lx\n", i, status );
4980 break;
4981 case 14: /* unknown */
4982 ok( status == STATUS_INVALID_PARAMETER_1 ||
4983 status == STATUS_SUCCESS, /* win11 */
4984 "%u: failed %lx\n", i, status );
4985 break;
4986 default:
4987 ok( status == STATUS_INVALID_PARAMETER_1, "%u: failed %lx\n", i, status );
4988 break;
4992 /* casemap table */
4994 status = pNtGetNlsSectionPtr( 10, 0, NULL, &ptr, &size );
4995 if (status != STATUS_INVALID_PARAMETER_1)
4997 ok( !status, "failed %lx\n", status );
4998 ok( size > 0x1000 && size <= 0x8000 , "wrong size %Ix\n", size );
4999 status = pNtGetNlsSectionPtr( 10, 0, NULL, &ptr2, &size );
5000 ok( !status, "failed %lx\n", status );
5001 ok( ptr != ptr2, "got same pointer\n" );
5002 ret = UnmapViewOfFile( ptr );
5003 ok( ret, "UnmapViewOfFile failed err %lu\n", GetLastError() );
5004 ret = UnmapViewOfFile( ptr2 );
5005 ok( ret, "UnmapViewOfFile failed err %lu\n", GetLastError() );
5008 /* codepage tables */
5010 ptr = (void *)0xdeadbeef;
5011 size = 0xdeadbeef;
5012 status = pNtGetNlsSectionPtr( 11, 437, NULL, &ptr, &size );
5013 ok( !status, "failed %lx\n", status );
5014 ok( size > 0x10000 && size <= 0x20000, "wrong size %Ix\n", size );
5015 memset( &table, 0xcc, sizeof(table) );
5016 if (pRtlInitCodePageTable)
5018 pRtlInitCodePageTable( ptr, &table );
5019 ok( table.CodePage == 437, "wrong codepage %u\n", table.CodePage );
5020 ok( table.MaximumCharacterSize == 1, "wrong char size %u\n", table.MaximumCharacterSize );
5021 ok( table.DefaultChar == '?', "wrong default char %x\n", table.DefaultChar );
5022 ok( !table.DBCSCodePage, "wrong dbcs %u\n", table.DBCSCodePage );
5024 ret = UnmapViewOfFile( ptr );
5025 ok( ret, "UnmapViewOfFile failed err %lu\n", GetLastError() );
5027 status = pNtGetNlsSectionPtr( 11, 936, NULL, &ptr, &size );
5028 ok( !status, "failed %lx\n", status );
5029 ok( size > 0x30000 && size <= 0x40000, "wrong size %Ix\n", size );
5030 memset( &table, 0xcc, sizeof(table) );
5031 if (pRtlInitCodePageTable)
5033 pRtlInitCodePageTable( ptr, &table );
5034 ok( table.CodePage == 936, "wrong codepage %u\n", table.CodePage );
5035 ok( table.MaximumCharacterSize == 2, "wrong char size %u\n", table.MaximumCharacterSize );
5036 ok( table.DefaultChar == '?', "wrong default char %x\n", table.DefaultChar );
5037 ok( table.DBCSCodePage == TRUE, "wrong dbcs %u\n", table.DBCSCodePage );
5039 if (pRtlCustomCPToUnicodeN)
5041 static const unsigned char buf[] = { 0xbf, 0xb4, 0xc7, 0, 0x78 };
5042 static const WCHAR expect[][4] = { { 0xcccc, 0xcccc, 0xcccc, 0xcccc },
5043 { 0x0000, 0xcccc, 0xcccc, 0xcccc },
5044 { 0x770b, 0xcccc, 0xcccc, 0xcccc },
5045 { 0x770b, 0x0000, 0xcccc, 0xcccc },
5046 { 0x770b, 0x003f, 0xcccc, 0xcccc },
5047 { 0x770b, 0x003f, 0x0078, 0xcccc } };
5048 WCHAR wbuf[5];
5049 DWORD i, j, reslen;
5051 for (i = 0; i <= sizeof(buf); i++)
5053 memset( wbuf, 0xcc, sizeof(wbuf) );
5054 pRtlCustomCPToUnicodeN( &table, wbuf, sizeof(wbuf), &reslen, (char *)buf, i );
5055 for (j = 0; j < 4; j++) if (expect[i][j] == 0xcccc) break;
5056 ok( reslen == j * sizeof(WCHAR), "%lu: wrong len %lu\n", i, reslen );
5057 for (j = 0; j < 4; j++)
5058 ok( wbuf[j] == expect[i][j], "%lu: char %lu got %04x\n", i, j, wbuf[j] );
5062 ret = UnmapViewOfFile( ptr );
5063 ok( ret, "UnmapViewOfFile failed err %lu\n", GetLastError() );
5065 status = pNtGetNlsSectionPtr( 11, 65001, NULL, &ptr, &size );
5066 ok( status == STATUS_OBJECT_NAME_NOT_FOUND || broken(!status), /* win10 1709 */
5067 "failed %lx\n", status );
5068 if (!status) UnmapViewOfFile( ptr );
5069 if (pRtlInitCodePageTable)
5071 static USHORT utf8[20] = { 0, CP_UTF8 };
5073 memset( &table, 0xcc, sizeof(table) );
5074 pRtlInitCodePageTable( utf8, &table );
5075 ok( table.CodePage == CP_UTF8, "wrong codepage %u\n", table.CodePage );
5076 if (table.MaximumCharacterSize)
5078 ok( table.MaximumCharacterSize == 4, "wrong char size %u\n", table.MaximumCharacterSize );
5079 ok( table.DefaultChar == '?', "wrong default char %x\n", table.DefaultChar );
5080 ok( table.UniDefaultChar == 0xfffd, "wrong default char %x\n", table.UniDefaultChar );
5081 ok( table.TransDefaultChar == '?', "wrong default char %x\n", table.TransDefaultChar );
5082 ok( table.TransUniDefaultChar == '?', "wrong default char %x\n", table.TransUniDefaultChar );
5083 ok( !table.DBCSCodePage, "wrong dbcs %u\n", table.DBCSCodePage );
5084 ok( !table.MultiByteTable, "wrong mbtable %p\n", table.MultiByteTable );
5085 ok( !table.WideCharTable, "wrong wctable %p\n", table.WideCharTable );
5086 ok( !table.DBCSRanges, "wrong ranges %p\n", table.DBCSRanges );
5087 ok( !table.DBCSOffsets, "wrong offsets %p\n", table.DBCSOffsets );
5089 else win_skip( "utf-8 codepage not supported\n" );
5092 /* normalization tables */
5094 for (i = 0; i < 100; i++)
5096 status = pNtGetNlsSectionPtr( 12, i, NULL, &ptr, &size );
5097 switch (i)
5099 case NormalizationC:
5100 case NormalizationD:
5101 case NormalizationKC:
5102 case NormalizationKD:
5103 case 13: /* IDN */
5104 ok( !status, "%u: failed %lx\n", i, status );
5105 if (status) break;
5106 ok( size > 0x8000 && size <= 0x30000 , "wrong size %Ix\n", size );
5107 ret = UnmapViewOfFile( ptr );
5108 ok( ret, "UnmapViewOfFile failed err %lu\n", GetLastError() );
5109 break;
5110 default:
5111 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%u: failed %lx\n", i, status );
5112 break;
5116 else win_skip( "NtGetNlsSectionPtr not supported\n" );
5120 * The CT_TYPE1 has varied over windows version.
5121 * The current target for correct behavior is windows 7.
5122 * There was a big shift between windows 2000 (first introduced) and windows Xp
5123 * Most of the old values below are from windows 2000.
5124 * A smaller subset of changes happened between windows Xp and Window vista/7
5126 static void test_GetStringTypeW(void)
5128 static const WCHAR blanks[] = {0x9, 0x20, 0xa0, 0x3000, 0xfeff};
5129 static const WORD blanks_new[] = {C1_SPACE | C1_CNTRL | C1_BLANK | C1_DEFINED,
5130 C1_SPACE | C1_BLANK | C1_DEFINED,
5131 C1_SPACE | C1_BLANK | C1_DEFINED,
5132 C1_SPACE | C1_BLANK | C1_DEFINED,
5133 C1_CNTRL | C1_BLANK | C1_DEFINED};
5134 static const WORD blanks_old[] ={C1_SPACE | C1_CNTRL | C1_BLANK,
5135 C1_SPACE | C1_BLANK,
5136 C1_SPACE | C1_BLANK,
5137 C1_SPACE | C1_BLANK,
5138 C1_SPACE | C1_BLANK};
5140 static const WCHAR undefined[] = {0x378, 0x379, 0x5ff, 0xfff8, 0xfffe};
5142 /* Lu, Ll, Lt */
5143 static const WCHAR alpha[] = {0x47, 0x67, 0x1c5};
5144 static const WORD alpha_old[] = {C1_UPPER | C1_ALPHA,
5145 C1_LOWER | C1_ALPHA,
5146 C1_UPPER | C1_LOWER | C1_ALPHA,
5147 C1_ALPHA};
5149 /* Sk, Sk, Mn, So, Me */
5150 static const WCHAR oldpunc[] = { 0x2c2, 0x2e5, 0x322, 0x482, 0x6de,
5151 /* Sc, Sm, No,*/
5152 0xffe0, 0xffe9, 0x2153};
5154 /* Lm, Nl, Cf, 0xad(Cf), 0x1f88 (Lt), Lo, Mc */
5155 static const WCHAR changed[] = {0x2b0, 0x2160, 0x600, 0xad, 0x1f88, 0x294, 0x903};
5156 static const WORD changed_old[] = { C1_PUNCT, C1_PUNCT, 0, C1_PUNCT, C1_UPPER | C1_ALPHA, C1_ALPHA, C1_PUNCT };
5157 static const WORD changed_xp[] = {C1_ALPHA | C1_DEFINED,
5158 C1_ALPHA | C1_DEFINED,
5159 C1_CNTRL | C1_DEFINED,
5160 C1_PUNCT | C1_DEFINED,
5161 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
5162 C1_ALPHA | C1_LOWER | C1_DEFINED,
5163 C1_ALPHA | C1_DEFINED };
5164 static const WORD changed_new[] = { C1_ALPHA | C1_DEFINED,
5165 C1_ALPHA | C1_DEFINED,
5166 C1_CNTRL | C1_DEFINED,
5167 C1_PUNCT | C1_CNTRL | C1_DEFINED,
5168 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
5169 C1_ALPHA | C1_DEFINED,
5170 C1_DEFINED
5172 /* Pc, Pd, Ps, Pe, Pi, Pf, Po*/
5173 static const WCHAR punct[] = { 0x5f, 0x2d, 0x28, 0x29, 0xab, 0xbb, 0x21 };
5175 static const WCHAR punct_special[] = {0x24, 0x2b, 0x3c, 0x3e, 0x5e, 0x60,
5176 0x7c, 0x7e, 0xa2, 0xbe, 0xd7, 0xf7};
5177 static const WCHAR digit_special[] = {0xb2, 0xb3, 0xb9};
5178 static const WCHAR lower_special[] = {0x2071, 0x207f};
5179 static const WCHAR cntrl_special[] = {0x070f, 0x200c, 0x200d,
5180 0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
5181 0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
5182 0xfff9, 0xfffa, 0xfffb};
5183 static const WCHAR space_special[] = {0x09, 0x0d, 0x85};
5185 WORD types[20];
5186 WCHAR ch[2];
5187 BOOL ret;
5188 int i;
5190 /* NULL src */
5191 SetLastError(0xdeadbeef);
5192 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, NULL);
5193 ok(!ret, "got %d\n", ret);
5194 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %ld\n", GetLastError());
5196 SetLastError(0xdeadbeef);
5197 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, types);
5198 ok(!ret, "got %d\n", ret);
5199 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %ld\n", GetLastError());
5201 SetLastError(0xdeadbeef);
5202 ret = GetStringTypeW(CT_CTYPE1, NULL, 5, types);
5203 ok(!ret, "got %d\n", ret);
5204 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %ld\n", GetLastError());
5206 memset(types,0,sizeof(types));
5207 GetStringTypeW(CT_CTYPE1, blanks, 5, types);
5208 for (i = 0; i < 5; i++)
5209 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]);
5211 memset(types,0,sizeof(types));
5212 GetStringTypeW(CT_CTYPE1, alpha, 3, types);
5213 for (i = 0; i < 3; i++)
5214 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]));
5215 memset(types,0,sizeof(types));
5216 GetStringTypeW(CT_CTYPE1, undefined, 5, types);
5217 for (i = 0; i < 5; i++)
5218 ok(types[i] == 0, "incorrect types returned for %x -> (%x != 0)\n",undefined[i], types[i]);
5220 memset(types,0,sizeof(types));
5221 GetStringTypeW(CT_CTYPE1, oldpunc, 8, types);
5222 for (i = 0; i < 8; i++)
5223 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);
5225 memset(types,0,sizeof(types));
5226 GetStringTypeW(CT_CTYPE1, changed, 7, types);
5227 for (i = 0; i < 7; i++)
5228 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]);
5230 memset(types,0,sizeof(types));
5231 GetStringTypeW(CT_CTYPE1, punct, 7, types);
5232 for (i = 0; i < 7; i++)
5233 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));
5236 memset(types,0,sizeof(types));
5237 GetStringTypeW(CT_CTYPE1, punct_special, 12, types);
5238 for (i = 0; i < 12; i++)
5239 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);
5241 memset(types,0,sizeof(types));
5242 GetStringTypeW(CT_CTYPE1, digit_special, 3, types);
5243 for (i = 0; i < 3; i++)
5244 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);
5246 memset(types,0,sizeof(types));
5247 GetStringTypeW(CT_CTYPE1, lower_special, 2, types);
5248 for (i = 0; i < 2; i++)
5249 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);
5251 memset(types,0,sizeof(types));
5252 GetStringTypeW(CT_CTYPE1, cntrl_special, 20, types);
5253 for (i = 0; i < 20; i++)
5254 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);
5256 memset(types,0,sizeof(types));
5257 GetStringTypeW(CT_CTYPE1, space_special, 3, types);
5258 for (i = 0; i < 3; i++)
5259 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 );
5261 /* surrogate pairs */
5262 ch[0] = 0xd800;
5263 memset(types, 0, sizeof(types));
5264 GetStringTypeW(CT_CTYPE3, ch, 1, types);
5265 if (types[0] == C3_NOTAPPLICABLE)
5266 win_skip("C3_HIGHSURROGATE/C3_LOWSURROGATE are not supported.\n");
5267 else {
5268 ok(types[0] == C3_HIGHSURROGATE, "got %x\n", types[0]);
5270 ch[0] = 0xdc00;
5271 memset(types, 0, sizeof(types));
5272 GetStringTypeW(CT_CTYPE3, ch, 1, types);
5273 ok(types[0] == C3_LOWSURROGATE, "got %x\n", types[0]);
5276 /* Zl, Zp categories */
5277 ch[0] = 0x2028;
5278 ch[1] = 0x2029;
5279 memset(types, 0, sizeof(types));
5280 GetStringTypeW(CT_CTYPE1, ch, 2, types);
5281 ok(types[0] == (C1_DEFINED|C1_SPACE), "got %x\n", types[0]);
5282 ok(types[1] == (C1_DEFINED|C1_SPACE), "got %x\n", types[1]);
5284 /* check Arabic range for kashida flag */
5285 for (ch[0] = 0x600; ch[0] <= 0x6ff; ch[0] += 1)
5287 types[0] = 0;
5288 ret = GetStringTypeW(CT_CTYPE3, ch, 1, types);
5289 ok(ret, "%#x: failed %d\n", ch[0], ret);
5290 if (ch[0] == 0x640) /* ARABIC TATWEEL (Kashida) */
5291 ok(types[0] & C3_KASHIDA, "%#x: type %#x\n", ch[0], types[0]);
5292 else
5293 ok(!(types[0] & C3_KASHIDA), "%#x: type %#x\n", ch[0], types[0]);
5297 static void test_IdnToNameprepUnicode(void)
5299 struct {
5300 int in_len;
5301 const WCHAR in[80];
5302 DWORD flags;
5303 DWORD ret;
5304 DWORD broken_ret;
5305 const WCHAR out[80];
5306 NTSTATUS status;
5307 NTSTATUS broken_status;
5308 } test_data[] = {
5309 /* 0 */
5310 { 5, L"test", 0, 5, 5, L"test" },
5311 { 3, L"a\xe111z", 0, 0, 0, L"a\xe111z", 0, STATUS_NO_UNICODE_TRANSLATION },
5312 { 4, L"t\0e", 0, 0, 0, {0}, STATUS_NO_UNICODE_TRANSLATION, STATUS_NO_UNICODE_TRANSLATION },
5313 { 1, L"T", 0, 1, 1, L"T" },
5314 { 1, {0}, 0, 0 },
5315 /* 5 */
5316 { 6, L" -/[]", 0, 6, 6, L" -/[]" },
5317 { 3, L"a-a", IDN_USE_STD3_ASCII_RULES, 3, 3, L"a-a" },
5318 { 3, L"aa-", IDN_USE_STD3_ASCII_RULES, 0, 0, L"aa-" },
5319 { -1, L"T\xdf\x130\x143\x37a\x6a\x30c \xaa", 0, 12, 12, L"tssi\x307\x144 \x3b9\x1f0 a" },
5320 { 11, L"t\xad\x34f\x1806\x180b\x180c\x180d\x200b\x200c\x200d", 0, 0, 2, L"t",
5321 STATUS_NO_UNICODE_TRANSLATION },
5322 /* 10 */
5323 { 2, {0x3b0}, 0, 2, 2, {0x3b0} },
5324 { 2, {0x380}, 0, 0, 2, {0x380} },
5325 { 2, {0x380}, IDN_ALLOW_UNASSIGNED, 2, 2, {0x380} },
5326 { 5, L"a..a", 0, 0, 0, L"a..a" },
5327 { 3, L"a.", 0, 3, 3, L"a." },
5328 /* 15 */
5329 { 5, L"T.\x105.A", 0, 5, 5, L"t.\x105.a" },
5330 { 5, L"T.*.A", 0, 5, 5, L"T.*.A" },
5331 { 5, L"X\xff0e.Z", 0, 0, 0, L"x..z" },
5332 { 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
5333 63, 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
5334 { 64, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
5335 0, 0, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
5338 WCHAR buf[1024];
5339 DWORD i, ret, err;
5341 ret = pIdnToNameprepUnicode(0, test_data[0].in,
5342 test_data[0].in_len, NULL, 0);
5343 ok(ret == test_data[0].ret, "ret = %ld\n", ret);
5345 SetLastError(0xdeadbeef);
5346 ret = pIdnToNameprepUnicode(0, test_data[1].in,
5347 test_data[1].in_len, NULL, 0);
5348 err = GetLastError();
5349 ok(ret == test_data[1].ret, "ret = %ld\n", ret);
5350 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "err = %ld\n", err);
5352 SetLastError(0xdeadbeef);
5353 ret = pIdnToNameprepUnicode(0, test_data[0].in, -1, buf, ARRAY_SIZE(buf));
5354 err = GetLastError();
5355 ok(ret == test_data[0].ret, "ret = %ld\n", ret);
5356 ok(err == 0xdeadbeef, "err = %ld\n", err);
5358 SetLastError(0xdeadbeef);
5359 ret = pIdnToNameprepUnicode(0, test_data[0].in, -2, buf, ARRAY_SIZE(buf));
5360 err = GetLastError();
5361 ok(ret == 0, "ret = %ld\n", ret);
5362 ok(err == ERROR_INVALID_PARAMETER, "err = %ld\n", err);
5364 SetLastError(0xdeadbeef);
5365 ret = pIdnToNameprepUnicode(0, test_data[0].in, 0, buf, ARRAY_SIZE(buf));
5366 err = GetLastError();
5367 ok(ret == 0, "ret = %ld\n", ret);
5368 ok(err == ERROR_INVALID_NAME, "err = %ld\n", err);
5370 ret = pIdnToNameprepUnicode(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES,
5371 test_data[0].in, -1, buf, ARRAY_SIZE(buf));
5372 ok(ret == test_data[0].ret, "ret = %ld\n", ret);
5374 SetLastError(0xdeadbeef);
5375 ret = pIdnToNameprepUnicode(0, NULL, 0, NULL, 0);
5376 err = GetLastError();
5377 ok(ret == 0, "ret = %ld\n", ret);
5378 ok(err == ERROR_INVALID_PARAMETER, "err = %ld\n", err);
5380 SetLastError(0xdeadbeef);
5381 ret = pIdnToNameprepUnicode(4, NULL, 0, NULL, 0);
5382 err = GetLastError();
5383 ok(ret == 0, "ret = %ld\n", ret);
5384 ok(err == ERROR_INVALID_FLAGS || err == ERROR_INVALID_PARAMETER /* Win8 */,
5385 "err = %ld\n", err);
5387 for (i=0; i<ARRAY_SIZE(test_data); i++)
5389 SetLastError(0xdeadbeef);
5390 memset( buf, 0xcc, sizeof(buf) );
5391 ret = pIdnToNameprepUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len,
5392 buf, ARRAY_SIZE(buf));
5393 err = GetLastError();
5395 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%ld: ret = %ld\n", i, ret);
5397 if (ret == test_data[i].ret)
5399 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "%ld: err = %ld\n", i, err);
5400 ok(!wcsncmp(test_data[i].out, buf, ret), "%ld: buf = %s\n", i, wine_dbgstr_wn(buf, ret));
5402 if (pRtlNormalizeString)
5404 NTSTATUS status;
5405 int len = ARRAY_SIZE(buf);
5406 memset( buf, 0xcc, sizeof(buf) );
5407 status = pRtlNormalizeString( 13, test_data[i].in, test_data[i].in_len, buf, &len );
5408 ok( status == test_data[i].status || broken(status == test_data[i].broken_status),
5409 "%ld: failed %lx\n", i, status );
5410 if (!status) ok( !wcsnicmp(test_data[i].out, buf, len), "%ld: buf = %s\n", i, wine_dbgstr_wn(buf, len));
5415 static void test_IdnToAscii(void)
5417 struct {
5418 int in_len;
5419 const WCHAR in[80];
5420 DWORD flags;
5421 DWORD ret;
5422 DWORD broken_ret;
5423 const WCHAR out[80];
5424 } test_data[] = {
5425 /* 0 */
5426 { 5, L"Test", 0, 5, 5, L"Test" },
5427 { 5, L"Te\x017cst", 0, 12, 12, L"xn--test-cbb" },
5428 { 12, L"te\x0105st.te\x017cst", 0, 26, 26, L"xn--test-cta.xn--test-cbb" },
5429 { 3, {0x0105,'.',0}, 0, 9, 9, L"xn--2da." },
5430 { 10, L"http://t\x106", 0, 17, 17, L"xn--http://t-78a" },
5431 /* 5 */
5432 { -1, L"\x4e3a\x8bf4\x4e0d\x4ed6\x5011\x10d\x11b\x305c\x306a", 0,
5433 35, 35, L"xn--bea2a1631avbav44tyha32b91egs2t" },
5434 { 2, L"\x380", IDN_ALLOW_UNASSIGNED, 8, 8, L"xn--7va" },
5435 { 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
5436 63, 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
5437 { 64, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 0 },
5438 { -1, L"\xe4z123456789012345678901234567890123456789012345678901234", 0,
5439 64, 64, L"xn--z123456789012345678901234567890123456789012345678901234-9te" },
5440 /* 10 */
5441 { -1, L"\xd803\xde78\x46b5-\xa861.\x2e87", 0, 28, 0, L"xn----bm3an932a1l5d.xn--xvj" },
5442 { -1, L"\x06ef\x06ef", 0, 9, 0, L"xn--cmba" },
5443 { -1, L"-\x07e1\xff61\x2184", 0, 18, 0, L"xn----8cd.xn--r5g" },
5446 WCHAR buf[1024];
5447 DWORD i, ret, err;
5449 for (i=0; i<ARRAY_SIZE(test_data); i++)
5451 SetLastError(0xdeadbeef);
5452 ret = pIdnToAscii(test_data[i].flags, test_data[i].in, test_data[i].in_len, buf, ARRAY_SIZE(buf));
5453 err = GetLastError();
5454 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%ld: ret = %ld\n", i, ret);
5455 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "%ld: err = %ld\n", i, err);
5456 ok(!wcsnicmp(test_data[i].out, buf, ret), "%ld: buf = %s\n", i, wine_dbgstr_wn(buf, ret));
5460 static void test_IdnToUnicode(void)
5462 struct {
5463 int in_len;
5464 const WCHAR in[80];
5465 DWORD flags;
5466 DWORD ret;
5467 DWORD broken_ret;
5468 const WCHAR out[80];
5469 } test_data[] = {
5470 /* 0 */
5471 { 5, L"Tes.", 0, 5, 5, L"Tes." },
5472 { 2, L"\x105", 0, 0 },
5473 { 33, L"xn--4dbcagdahymbxekheh6e0a7fei0b", 0,
5474 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" },
5475 { 34, L"test.xn--kda9ag5e9jnfsj.xn--pz-fna", 0,
5476 16, 16, L"test.\x0105\x0119\x015b\x0107\x0142\x00f3\x017c.p\x0119z" },
5477 { 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0,
5478 63, 63, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
5479 /* 5 */
5480 { 64, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 0 },
5481 { 8, L"xn--7va", IDN_ALLOW_UNASSIGNED, 2, 2, L"\x380" },
5482 { 8, L"xn--7va", 0, 0, 0, L"\x380" },
5483 { -1, L"xn----bm3an932a1l5d.xn--xvj", 0, 8, 0, L"\xd803\xde78\x46b5-\xa861.\x2e87" },
5484 { -1, L"xn--z123456789012345678901234567890123456789012345678901234-9te", 0,
5485 57, 57, L"\xe4z123456789012345678901234567890123456789012345678901234" },
5486 /* 10 */
5487 { -1, L"foo.bar", 0, 8, 8, L"foo.bar" },
5488 { -1, L"d.xn----dha", 0, 5, 5, L"d.\x00fc-" },
5491 WCHAR buf[1024];
5492 DWORD i, ret, err;
5494 for (i=0; i<ARRAY_SIZE(test_data); i++)
5496 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len, NULL, 0);
5497 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%ld: ret = %ld\n", i, ret);
5499 SetLastError(0xdeadbeef);
5500 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len, buf, ARRAY_SIZE(buf));
5501 err = GetLastError();
5502 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%ld: ret = %ld\n", i, ret);
5503 ok(err == ret ? 0xdeadbeef : ERROR_INVALID_NAME, "%ld: err = %ld\n", i, err);
5504 ok(!wcsncmp(test_data[i].out, buf, ret), "%ld: buf = %s\n", i, wine_dbgstr_wn(buf, ret));
5508 static BOOL is_idn_error( const WCHAR *str )
5510 WCHAR *p, err[256];
5511 lstrcpyW( err, str );
5512 for (p = wcstok( err, L" []" ); p; p = wcstok( NULL, L" []" ) )
5514 if (*p == 'B' || !wcscmp( p, L"V8" )) continue; /* BiDi */
5515 if (!wcscmp( p, L"V2" )) continue; /* CheckHyphens */
5516 if (!wcscmp( p, L"V5" )) continue; /* Combining marks */
5517 return TRUE;
5519 return FALSE;
5522 static void test_Idn(void)
5524 FILE *f;
5526 if (!pIdnToAscii || !pIdnToUnicode || !pIdnToNameprepUnicode)
5528 win_skip("Idn support is not available\n");
5529 return;
5532 test_IdnToNameprepUnicode();
5533 test_IdnToAscii();
5534 test_IdnToUnicode();
5536 /* optionally run the full test file from Unicode.org
5537 * available at https://www.unicode.org/Public/idna/latest/IdnaTestV2.txt
5539 if ((f = fopen( "IdnaTestV2.txt", "r" )))
5541 char *p, *end, buffer[2048];
5542 WCHAR columns[7][256], dst[256], *expect, *error;
5543 int i, ret, line = 0;
5545 while (fgets( buffer, sizeof(buffer), f ))
5547 line++;
5548 if ((p = strchr( buffer, '#' ))) *p = 0;
5549 if (!(p = strtok( buffer, ";" ))) continue;
5550 for (i = 0; i < 7 && p; i++)
5552 while (*p == ' ') p++;
5553 for (end = p + strlen(p); end > p; end--) if (end[-1] != ' ') break;
5554 *end = 0;
5555 MultiByteToWideChar( CP_UTF8, 0, p, -1, columns[i], 256 );
5556 p = strtok( NULL, ";" );
5558 if (i < 7) continue;
5560 expect = columns[5];
5561 if (!*expect) expect = columns[3];
5562 if (!*expect) expect = columns[1];
5563 if (!*expect) expect = columns[0];
5564 error = columns[6];
5565 if (!*error) error = columns[4];
5566 if (!*error) error = columns[2];
5567 SetLastError( 0xdeadbeef );
5568 memset( dst, 0xcc, sizeof(dst) );
5569 ret = pIdnToAscii( 0, columns[0], -1, dst, ARRAY_SIZE(dst) );
5570 if (!is_idn_error( error ))
5572 ok( ret, "line %u: toAscii failed for %s expected %s\n", line,
5573 debugstr_w(columns[0]), debugstr_w(expect) );
5574 if (ret) ok( !wcscmp( dst, expect ), "line %u: got %s expected %s\n",
5575 line, debugstr_w(dst), debugstr_w(expect) );
5577 else
5579 ok( !ret, "line %u: toAscii didn't fail for %s got %s expected error %s\n",
5580 line, debugstr_w(columns[0]), debugstr_w(dst), debugstr_w(error) );
5583 expect = columns[1];
5584 if (!*expect) expect = columns[0];
5585 error = columns[2];
5586 SetLastError( 0xdeadbeef );
5587 memset( dst, 0xcc, sizeof(dst) );
5588 ret = pIdnToUnicode( IDN_USE_STD3_ASCII_RULES, columns[0], -1, dst, ARRAY_SIZE(dst) );
5589 for (i = 0; columns[0][i]; i++) if (columns[0][i] > 0x7f) break;
5590 if (columns[0][i])
5592 ok( !ret, "line %u: didn't fail for unicode chars in %s\n", line, debugstr_w(columns[0]) );
5594 else if (!is_idn_error( error ))
5596 ok( ret, "line %u: toUnicode failed for %s expected %s\n", line,
5597 debugstr_w(columns[0]), debugstr_w(expect) );
5598 if (ret) ok( !wcscmp( dst, expect ), "line %u: got %s expected %s\n",
5599 line, debugstr_w(dst), debugstr_w(expect) );
5601 else
5603 ok( !ret, "line %u: toUnicode didn't fail for %s got %s expected error %s\n",
5604 line, debugstr_w(columns[0]), debugstr_w(dst), debugstr_w(error) );
5607 fclose( f );
5612 static void test_GetLocaleInfoEx(void)
5614 static const WCHAR enW[] = {'e','n',0};
5615 WCHAR bufferW[80], buffer2[80];
5616 INT ret;
5618 if (!pGetLocaleInfoEx)
5620 win_skip("GetLocaleInfoEx not supported\n");
5621 return;
5624 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
5625 ok(ret || broken(ret == 0) /* Vista */, "got %d\n", ret);
5626 if (ret)
5628 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
5629 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
5630 static const WCHAR enusW[] = {'e','n','-','U','S',0};
5631 static const WCHAR usaW[] = {'U','S','A',0};
5632 static const WCHAR enuW[] = {'E','N','U',0};
5633 const struct neutralsublang_name_t *ptr = neutralsublang_names;
5634 DWORD val;
5636 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
5637 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
5639 SetLastError(0xdeadbeef);
5640 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, 2);
5641 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, %ld\n", ret, GetLastError());
5643 SetLastError(0xdeadbeef);
5644 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, NULL, 0);
5645 ok(ret == 3 && GetLastError() == 0xdeadbeef, "got %d, %ld\n", ret, GetLastError());
5647 ret = pGetLocaleInfoEx(enusW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
5648 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
5649 ok(!lstrcmpW(bufferW, enusW), "got %s\n", wine_dbgstr_w(bufferW));
5651 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVCTRYNAME, bufferW, ARRAY_SIZE(bufferW));
5652 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
5653 ok(!lstrcmpW(bufferW, usaW), "got %s\n", wine_dbgstr_w(bufferW));
5655 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVLANGNAME, bufferW, ARRAY_SIZE(bufferW));
5656 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
5657 ok(!lstrcmpW(bufferW, enuW), "got %s\n", wine_dbgstr_w(bufferW));
5659 ret = pGetLocaleInfoEx(enusW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
5660 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
5661 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
5663 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
5664 ok(ret == 1, "got %d\n", ret);
5665 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
5667 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT | LOCALE_NOUSEROVERRIDE, bufferW, ARRAY_SIZE(bufferW));
5668 ok(ret == 1, "got %d\n", ret);
5669 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
5671 ret = pGetLocaleInfoEx(enW, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
5672 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
5673 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
5674 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
5676 skip("Non-English locale\n");
5678 else
5679 ok(!lstrcmpW(bufferW, statesW), "got %s\n", wine_dbgstr_w(bufferW));
5681 bufferW[0] = 0;
5682 SetLastError(0xdeadbeef);
5683 ret = pGetLocaleInfoEx(dummyW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
5684 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %ld\n", ret, GetLastError());
5686 while (*ptr->name)
5688 val = 0;
5689 pGetLocaleInfoEx(ptr->name, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
5690 ok(val == ptr->lcid, "%s: got wrong lcid 0x%04lx, expected 0x%04lx\n", wine_dbgstr_w(ptr->name), val, ptr->lcid);
5691 bufferW[0] = 0;
5692 ret = pGetLocaleInfoEx(ptr->name, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
5693 ok(ret == lstrlenW(bufferW)+1, "%s: got ret value %d\n", wine_dbgstr_w(ptr->name), ret);
5694 ok(!lstrcmpW(bufferW, ptr->name), "%s: got wrong LOCALE_SNAME %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
5695 ptr++;
5698 ret = pGetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
5699 ok(ret && ret == lstrlenW(bufferW)+1, "got ret value %d\n", ret);
5700 ret = GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SNAME, buffer2, ARRAY_SIZE(buffer2));
5701 ok(ret && ret == lstrlenW(buffer2)+1, "got ret value %d\n", ret);
5702 ok(!lstrcmpW(bufferW, buffer2), "LOCALE_SNAMEs don't match %s %s\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(buffer2));
5706 static void test_IsValidLocaleName(void)
5708 BOOL ret;
5710 if (!pIsValidLocaleName)
5712 win_skip("IsValidLocaleName not supported\n");
5713 return;
5716 ret = pIsValidLocaleName(L"en-US");
5717 ok(ret, "IsValidLocaleName failed\n");
5718 ret = pIsValidLocaleName(L"en");
5719 ok(ret, "IsValidLocaleName failed\n");
5720 ret = pIsValidLocaleName(L"es-es");
5721 ok(ret, "IsValidLocaleName failed\n");
5722 ret = pIsValidLocaleName(L"de-DE_phoneb");
5723 ok(ret, "IsValidLocaleName failed\n");
5724 ret = pIsValidLocaleName(L"DE_de-phoneb");
5725 ok(ret || broken(!ret), "IsValidLocaleName failed\n");
5726 ret = pIsValidLocaleName(L"DE_de_PHONEB");
5727 ok(ret || broken(!ret), "IsValidLocaleName failed\n");
5728 ret = pIsValidLocaleName(L"DE_de+phoneb");
5729 ok(!ret, "IsValidLocaleName should have failed\n");
5730 ret = pIsValidLocaleName(L"zz");
5731 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
5732 ret = pIsValidLocaleName(L"zz-ZZ");
5733 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
5734 ret = pIsValidLocaleName(L"zzz");
5735 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
5736 ret = pIsValidLocaleName(L"zzz-ZZZ");
5737 ok(!ret, "IsValidLocaleName should have failed\n");
5738 ret = pIsValidLocaleName(L"zzzz");
5739 ok(!ret, "IsValidLocaleName should have failed\n");
5740 ret = pIsValidLocaleName(LOCALE_NAME_INVARIANT);
5741 ok(ret, "IsValidLocaleName failed\n");
5742 ret = pIsValidLocaleName(LOCALE_NAME_USER_DEFAULT);
5743 ok(!ret, "IsValidLocaleName should have failed\n");
5744 ret = pIsValidLocaleName(LOCALE_NAME_SYSTEM_DEFAULT);
5745 ok(!ret, "IsValidLocaleName should have failed\n");
5747 if (!pRtlIsValidLocaleName)
5749 win_skip( "RtlIsValidLocaleName not available\n" );
5750 return;
5753 ret = pRtlIsValidLocaleName( L"en-US", 0 );
5754 ok(ret, "RtlIsValidLocaleName failed\n");
5755 ret = pRtlIsValidLocaleName( L"en", 0 );
5756 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5757 ret = pRtlIsValidLocaleName( L"en", 1 );
5758 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5759 ret = pRtlIsValidLocaleName( L"en", 2 );
5760 ok(ret, "RtlIsValidLocaleName failed\n");
5761 ret = pRtlIsValidLocaleName( L"en-RR", 2 );
5762 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5763 ret = pRtlIsValidLocaleName( L"es-es", 0 );
5764 ok(ret, "RtlIsValidLocaleName failed\n");
5765 ret = pRtlIsValidLocaleName( L"de-DE_phoneb", 0 );
5766 ok(ret, "RtlIsValidLocaleName failed\n");
5767 ret = pRtlIsValidLocaleName( L"DE_de_PHONEB", 0 );
5768 ok(ret || broken(!ret), "RtlIsValidLocaleName failed\n");
5769 ret = pRtlIsValidLocaleName( L"DE_de+phoneb", 0 );
5770 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5771 ret = pRtlIsValidLocaleName( L"zz", 0 );
5772 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5773 ret = pRtlIsValidLocaleName( L"zz", 2 );
5774 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5775 ret = pRtlIsValidLocaleName( L"zz-ZZ", 0 );
5776 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5777 ret = pRtlIsValidLocaleName( L"zzz", 0 );
5778 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5779 ret = pRtlIsValidLocaleName( L"zzz-ZZZ", 0 );
5780 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5781 ret = pRtlIsValidLocaleName( L"zzzz", 0 );
5782 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5783 ret = pRtlIsValidLocaleName( LOCALE_NAME_INVARIANT, 0 );
5784 ok(ret, "RtlIsValidLocaleName failed\n");
5785 ret = pRtlIsValidLocaleName( LOCALE_NAME_USER_DEFAULT, 0 );
5786 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5787 ret = pRtlIsValidLocaleName( LOCALE_NAME_SYSTEM_DEFAULT, 0 );
5788 ok(!ret, "RtlIsValidLocaleName should have failed\n");
5791 static void test_ResolveLocaleName(void)
5793 static const struct { const WCHAR *name, *exp; BOOL broken; } tests[] =
5795 { L"en-US", L"en-US" },
5796 { L"en", L"en-US" },
5797 { L"en-RR", L"en-US" },
5798 { L"en-na", L"en-NA", TRUE /* <= win8 */ },
5799 { L"EN-zz", L"en-US" },
5800 { L"en-US", L"en-US" },
5801 { L"de-DE_phoneb", L"de-DE" },
5802 { L"DE-de-phoneb", L"de-DE" },
5803 { L"fr-029", L"fr-029", TRUE /* <= win8 */ },
5804 { L"fr-CH_XX", L"fr-CH", TRUE /* <= win10 1809 */ },
5805 { L"fr-CHXX", L"fr-FR" },
5806 { L"zh", L"zh-CN" },
5807 { L"zh-Hant", L"zh-HK" },
5808 { L"zh-hans", L"zh-CN" },
5809 { L"ja-jp_radstr", L"ja-JP" },
5810 { L"az", L"az-Latn-AZ" },
5811 { L"uz", L"uz-Latn-UZ" },
5812 { L"uz-cyrl", L"uz-Cyrl-UZ" },
5813 { L"ia", L"ia-001", TRUE /* <= win10 1809 */ },
5814 { L"zz", L"" },
5815 { L"zzz-ZZZ", L"" },
5816 { L"zzzz", L"" },
5817 { L"zz+XX", NULL },
5818 { L"zz.XX", NULL },
5819 { LOCALE_NAME_INVARIANT, L"" },
5821 INT i, ret;
5822 WCHAR buffer[LOCALE_NAME_MAX_LENGTH], system[LOCALE_NAME_MAX_LENGTH];
5824 if (!pResolveLocaleName)
5826 win_skip( "ResolveLocaleName not available\n" );
5827 return;
5829 for (i = 0; i < ARRAY_SIZE(tests); i++)
5831 SetLastError( 0xdeadbeef );
5832 memset( buffer, 0xcc, sizeof(buffer) );
5833 ret = pResolveLocaleName( tests[i].name, buffer, ARRAY_SIZE(buffer) );
5834 if (tests[i].exp)
5836 ok( !wcscmp( buffer, tests[i].exp ) || broken( tests[i].broken ),
5837 "%s: got %s\n", debugstr_w(tests[i].name), debugstr_w(buffer) );
5838 ok( ret == wcslen(buffer) + 1, "%s: got %u\n", debugstr_w(tests[i].name), ret );
5840 else
5842 ok( !ret || broken( ret == 1 ) /* win7 */,
5843 "%s: got %s\n", debugstr_w(tests[i].name), debugstr_w(buffer) );
5844 if (!ret)
5845 ok( GetLastError() == ERROR_INVALID_PARAMETER,
5846 "%s: wrong error %lu\n", debugstr_w(tests[i].name), GetLastError() );
5849 SetLastError( 0xdeadbeef );
5850 memset( buffer, 0xcc, sizeof(buffer) );
5851 ret = pResolveLocaleName( LOCALE_NAME_SYSTEM_DEFAULT, buffer, ARRAY_SIZE(buffer) );
5852 ok( ret, "failed err %lu\n", GetLastError() );
5853 GetSystemDefaultLocaleName( system, ARRAY_SIZE(system) );
5854 ok( !wcscmp( buffer, system ), "got wrong syslocale %s / %s\n", debugstr_w(buffer), debugstr_w(system));
5855 ok( ret == wcslen(system) + 1, "wrong len %u / %Iu\n", ret, wcslen(system) + 1 );
5857 SetLastError( 0xdeadbeef );
5858 ret = pResolveLocaleName( L"en-US", buffer, 4 );
5859 ok( !ret, "got %u\n", ret );
5860 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %lu\n", GetLastError() );
5861 ok( !wcscmp( buffer, L"en-" ), "got %s\n", debugstr_w(buffer) );
5863 SetLastError( 0xdeadbeef );
5864 ret = pResolveLocaleName( L"en-US", NULL, 0 );
5865 ok( ret == 6, "got %u\n", ret );
5866 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
5869 static void test_CompareStringOrdinal(void)
5871 INT ret;
5872 WCHAR test1[] = { 't','e','s','t',0 };
5873 WCHAR test2[] = { 'T','e','S','t',0 };
5874 WCHAR test3[] = { 't','e','s','t','3',0 };
5875 WCHAR null1[] = { 'a',0,'a',0 };
5876 WCHAR null2[] = { 'a',0,'b',0 };
5877 WCHAR bills1[] = { 'b','i','l','l','\'','s',0 };
5878 WCHAR bills2[] = { 'b','i','l','l','s',0 };
5879 WCHAR coop1[] = { 'c','o','-','o','p',0 };
5880 WCHAR coop2[] = { 'c','o','o','p',0 };
5881 WCHAR nonascii1[] = { 0x0102,0 };
5882 WCHAR nonascii2[] = { 0x0201,0 };
5883 WCHAR ch1, ch2;
5885 if (!pCompareStringOrdinal)
5887 win_skip("CompareStringOrdinal not supported\n");
5888 return;
5891 /* Check errors */
5892 SetLastError(0xdeadbeef);
5893 ret = pCompareStringOrdinal(NULL, 0, NULL, 0, FALSE);
5894 ok(!ret, "Got %u, expected 0\n", ret);
5895 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %lx, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
5896 SetLastError(0xdeadbeef);
5897 ret = pCompareStringOrdinal(test1, -1, NULL, 0, FALSE);
5898 ok(!ret, "Got %u, expected 0\n", ret);
5899 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %lx, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
5900 SetLastError(0xdeadbeef);
5901 ret = pCompareStringOrdinal(NULL, 0, test1, -1, FALSE);
5902 ok(!ret, "Got %u, expected 0\n", ret);
5903 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %lx, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
5905 /* Check case */
5906 ret = pCompareStringOrdinal(test1, -1, test1, -1, FALSE);
5907 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
5908 ret = pCompareStringOrdinal(test1, -1, test2, -1, FALSE);
5909 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
5910 ret = pCompareStringOrdinal(test2, -1, test1, -1, FALSE);
5911 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5912 ret = pCompareStringOrdinal(test1, -1, test2, -1, TRUE);
5913 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
5915 /* Check different sizes */
5916 ret = pCompareStringOrdinal(test1, 3, test2, -1, TRUE);
5917 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5918 ret = pCompareStringOrdinal(test1, -1, test2, 3, TRUE);
5919 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
5921 /* Check null character */
5922 ret = pCompareStringOrdinal(null1, 3, null2, 3, FALSE);
5923 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5924 ret = pCompareStringOrdinal(null1, 3, null2, 3, TRUE);
5925 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5926 ret = pCompareStringOrdinal(test1, 5, test3, 5, FALSE);
5927 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5928 ret = pCompareStringOrdinal(test1, 4, test1, 5, FALSE);
5929 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5931 /* Check ordinal behaviour */
5932 ret = pCompareStringOrdinal(bills1, -1, bills2, -1, FALSE);
5933 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5934 ret = pCompareStringOrdinal(coop2, -1, coop1, -1, FALSE);
5935 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
5936 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, FALSE);
5937 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5938 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, TRUE);
5939 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
5941 for (ch1 = 0; ch1 < 512; ch1++)
5943 for (ch2 = 0; ch2 < 1024; ch2++)
5945 int diff = ch1 - ch2;
5946 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, FALSE );
5947 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
5948 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
5949 diff = pRtlUpcaseUnicodeChar( ch1 ) - pRtlUpcaseUnicodeChar( ch2 );
5950 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, TRUE );
5951 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
5952 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
5957 static void test_GetGeoInfo(void)
5959 char buffA[20];
5960 WCHAR buffW[20];
5961 INT ret;
5963 if (!pGetGeoInfoA)
5965 win_skip("GetGeoInfo is not available.\n");
5966 return;
5969 /* unassigned id */
5970 SetLastError(0xdeadbeef);
5971 ret = pGetGeoInfoA(344, GEO_ISO2, NULL, 0, 0);
5972 ok(ret == 0, "got %d\n", ret);
5973 ok(GetLastError() == ERROR_INVALID_PARAMETER ||
5974 broken(GetLastError() == 0xdeadbeef /* Win10 */), "got %ld\n", GetLastError());
5976 ret = pGetGeoInfoA(203, GEO_ISO2, NULL, 0, 0);
5977 ok(ret == 3, "got %d\n", ret);
5979 ret = pGetGeoInfoA(203, GEO_ISO3, NULL, 0, 0);
5980 ok(ret == 4, "got %d\n", ret);
5982 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 3, 0);
5983 ok(ret == 3, "got %d\n", ret);
5984 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
5986 /* buffer pointer not NULL, length is 0 - return required length */
5987 buffA[0] = 'a';
5988 SetLastError(0xdeadbeef);
5989 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 0, 0);
5990 ok(ret == 3, "got %d\n", ret);
5991 ok(buffA[0] == 'a', "got %c\n", buffA[0]);
5993 ret = pGetGeoInfoA(203, GEO_ISO3, buffA, 4, 0);
5994 ok(ret == 4, "got %d\n", ret);
5995 ok(!strcmp(buffA, "RUS"), "got %s\n", buffA);
5997 /* shorter buffer */
5998 SetLastError(0xdeadbeef);
5999 buffA[1] = buffA[2] = 0;
6000 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 2, 0);
6001 ok(ret == 0, "got %d\n", ret);
6002 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
6003 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %ld\n", GetLastError());
6005 /* GEO_NATION returns GEOID in a string form, but only for GEOCLASS_NATION-type IDs */
6006 ret = pGetGeoInfoA(203, GEO_NATION, buffA, 20, 0); /* GEOCLASS_NATION */
6007 ok(ret == 4, "GEO_NATION of nation: expected 4, got %d\n", ret);
6008 ok(!strcmp(buffA, "203"), "GEO_NATION of nation: expected 203, got %s\n", buffA);
6010 buffA[0] = 0;
6011 ret = pGetGeoInfoA(39070, GEO_NATION, buffA, 20, 0); /* GEOCLASS_REGION */
6012 ok(ret == 0, "GEO_NATION of region: expected 0, got %d\n", ret);
6013 ok(*buffA == 0, "GEO_NATION of region: expected empty string, got %s\n", buffA);
6015 buffA[0] = 0;
6016 ret = pGetGeoInfoA(333, GEO_NATION, buffA, 20, 0); /* LOCATION_BOTH internal Wine type */
6017 ok(ret == 0 ||
6018 broken(ret == 4) /* Win7 and older */,
6019 "GEO_NATION of LOCATION_BOTH: expected 0, got %d\n", ret);
6020 ok(*buffA == 0 ||
6021 broken(!strcmp(buffA, "333")) /* Win7 and older */,
6022 "GEO_NATION of LOCATION_BOTH: expected empty string, got %s\n", buffA);
6024 /* GEO_ID is like GEO_NATION but works for any ID */
6025 buffA[0] = 0;
6026 ret = pGetGeoInfoA(203, GEO_ID, buffA, 20, 0); /* GEOCLASS_NATION */
6027 if (ret == 0)
6028 win_skip("GEO_ID not supported.\n");
6029 else
6031 ok(ret == 4, "GEO_ID: expected 4, got %d\n", ret);
6032 ok(!strcmp(buffA, "203"), "GEO_ID: expected 203, got %s\n", buffA);
6034 ret = pGetGeoInfoA(47610, GEO_ID, buffA, 20, 0); /* GEOCLASS_REGION */
6035 ok(ret == 6, "got %d\n", ret);
6036 ok(!strcmp(buffA, "47610"), "got %s\n", buffA);
6038 ret = pGetGeoInfoA(333, GEO_ID, buffA, 20, 0); /* LOCATION_BOTH internal Wine type */
6039 ok(ret == 4, "got %d\n", ret);
6040 ok(!strcmp(buffA, "333"), "got %s\n", buffA);
6043 /* GEO_PARENT */
6044 buffA[0] = 0;
6045 ret = pGetGeoInfoA(203, GEO_PARENT, buffA, 20, 0);
6046 if (ret == 0)
6047 win_skip("GEO_PARENT not supported.\n");
6048 else
6050 ok(ret == 6, "got %d\n", ret);
6051 ok(!strcmp(buffA, "47609"), "got %s\n", buffA);
6054 buffA[0] = 0;
6055 ret = pGetGeoInfoA(203, GEO_ISO_UN_NUMBER, buffA, 20, 0);
6056 if (ret == 0)
6057 win_skip("GEO_ISO_UN_NUMBER not supported.\n");
6058 else
6060 ok(ret == 4, "got %d\n", ret);
6061 ok(!strcmp(buffA, "643"), "got %s\n", buffA);
6064 /* try invalid type value */
6065 SetLastError(0xdeadbeef);
6066 ret = pGetGeoInfoA(203, GEO_ID + 1, NULL, 0, 0);
6067 ok(ret == 0, "got %d\n", ret);
6068 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %ld\n", GetLastError());
6070 /* Test for GetGeoInfoEx */
6071 if (!pGetGeoInfoEx)
6073 win_skip("GetGeoInfoEx is not available\n");
6074 return;
6077 /* Test with ISO 3166-1 */
6078 ret = pGetGeoInfoEx(L"AR", GEO_ISO3, buffW, ARRAYSIZE(buffW));
6079 ok(ret != 0, "GetGeoInfoEx failed %ld.\n", GetLastError());
6080 ok(!wcscmp(buffW, L"ARG"), "expected string to be ARG, got %ls\n", buffW);
6082 /* Test with UN M.49 */
6083 SetLastError(0xdeadbeef);
6084 ret = pGetGeoInfoEx(L"032", GEO_ISO3, buffW, ARRAYSIZE(buffW));
6085 ok(ret == 0, "expected GetGeoInfoEx to fail.\n");
6086 ok(GetLastError() == ERROR_INVALID_PARAMETER,
6087 "expected ERROR_INVALID_PARAMETER got %ld.\n", GetLastError());
6089 /* Test GEO_ID */
6090 ret = pGetGeoInfoEx(L"AR", GEO_ID, buffW, ARRAYSIZE(buffW));
6091 ok(ret != 0, "GetGeoInfoEx failed %ld.\n", GetLastError());
6092 ok(!wcscmp(buffW, L"11"), "expected string to be 11, got %ls\n", buffW);
6094 /* Test with invalid geo type */
6095 SetLastError(0xdeadbeef);
6096 ret = pGetGeoInfoEx(L"AR", GEO_LCID, buffW, ARRAYSIZE(buffW));
6097 ok(ret == 0, "expected GetGeoInfoEx to fail.\n");
6098 ok(GetLastError() == ERROR_INVALID_FLAGS,
6099 "expected ERROR_INVALID_PARAMETER got %ld.\n", GetLastError());
6102 static int geoidenum_count;
6103 static BOOL CALLBACK test_geoid_enumproc(GEOID geoid)
6105 INT ret = pGetGeoInfoA(geoid, GEO_ISO2, NULL, 0, 0);
6106 ok(ret == 3, "got %d for %ld\n", ret, geoid);
6107 /* valid geoid starts at 2 */
6108 ok(geoid >= 2, "got geoid %ld\n", geoid);
6110 return geoidenum_count++ < 5;
6113 static BOOL CALLBACK test_geoid_enumproc2(GEOID geoid)
6115 geoidenum_count++;
6116 return TRUE;
6119 static void test_EnumSystemGeoID(void)
6121 BOOL ret;
6123 if (!pEnumSystemGeoID)
6125 win_skip("EnumSystemGeoID is not available.\n");
6126 return;
6129 SetLastError(0xdeadbeef);
6130 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, NULL);
6131 ok(!ret, "got %d\n", ret);
6132 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %ld\n", GetLastError());
6134 SetLastError(0xdeadbeef);
6135 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, test_geoid_enumproc);
6136 ok(!ret, "got %d\n", ret);
6137 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %ld\n", GetLastError());
6139 SetLastError(0xdeadbeef);
6140 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, NULL);
6141 ok(!ret, "got %d\n", ret);
6142 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %ld\n", GetLastError());
6144 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, test_geoid_enumproc);
6145 ok(ret, "got %d\n", ret);
6147 /* only the first level is enumerated, not the whole hierarchy */
6148 geoidenum_count = 0;
6149 ret = pEnumSystemGeoID(GEOCLASS_NATION, 39070, test_geoid_enumproc2);
6150 if (ret == 0)
6151 win_skip("Parent GEOID is not supported in EnumSystemGeoID.\n");
6152 else
6153 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
6155 geoidenum_count = 0;
6156 ret = pEnumSystemGeoID(GEOCLASS_REGION, 39070, test_geoid_enumproc2);
6157 if (ret == 0)
6158 win_skip("GEOCLASS_REGION is not supported in EnumSystemGeoID.\n");
6159 else
6161 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
6163 geoidenum_count = 0;
6164 ret = pEnumSystemGeoID(GEOCLASS_REGION, 0, test_geoid_enumproc2);
6165 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
6168 geoidenum_count = 0;
6169 ret = pEnumSystemGeoID(GEOCLASS_ALL, 39070, test_geoid_enumproc2);
6170 if (ret == 0)
6171 win_skip("GEOCLASS_ALL is not supported in EnumSystemGeoID.\n");
6172 else
6174 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
6176 geoidenum_count = 0;
6177 ret = pEnumSystemGeoID(GEOCLASS_ALL, 0, test_geoid_enumproc2);
6178 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
6182 struct invariant_entry {
6183 const char *name;
6184 int id;
6185 const char *expect, *expect2;
6188 #define X(x) #x, x
6189 static const struct invariant_entry invariant_list[] = {
6190 { X(LOCALE_ILANGUAGE), "007f" },
6191 { X(LOCALE_SENGLANGUAGE), "Invariant Language" },
6192 { X(LOCALE_SABBREVLANGNAME), "IVL" },
6193 { X(LOCALE_SNATIVELANGNAME), "Invariant Language" },
6194 { X(LOCALE_ICOUNTRY), "1" },
6195 { X(LOCALE_SENGCOUNTRY), "Invariant Country" },
6196 { X(LOCALE_SABBREVCTRYNAME), "IVC", "" },
6197 { X(LOCALE_SNATIVECTRYNAME), "Invariant Country" },
6198 { X(LOCALE_IDEFAULTLANGUAGE), "0409" },
6199 { X(LOCALE_IDEFAULTCOUNTRY), "1" },
6200 { X(LOCALE_IDEFAULTCODEPAGE), "437" },
6201 { X(LOCALE_IDEFAULTANSICODEPAGE), "1252" },
6202 { X(LOCALE_IDEFAULTMACCODEPAGE), "10000" },
6203 { X(LOCALE_SLIST), "," },
6204 { X(LOCALE_IMEASURE), "0" },
6205 { X(LOCALE_SDECIMAL), "." },
6206 { X(LOCALE_STHOUSAND), "," },
6207 { X(LOCALE_SGROUPING), "3;0" },
6208 { X(LOCALE_IDIGITS), "2" },
6209 { X(LOCALE_ILZERO), "1" },
6210 { X(LOCALE_INEGNUMBER), "1" },
6211 { X(LOCALE_SNATIVEDIGITS), "0123456789" },
6212 { X(LOCALE_SCURRENCY), "\x00a4" },
6213 { X(LOCALE_SINTLSYMBOL), "XDR" },
6214 { X(LOCALE_SMONDECIMALSEP), "." },
6215 { X(LOCALE_SMONTHOUSANDSEP), "," },
6216 { X(LOCALE_SMONGROUPING), "3;0" },
6217 { X(LOCALE_ICURRDIGITS), "2" },
6218 { X(LOCALE_IINTLCURRDIGITS), "2" },
6219 { X(LOCALE_ICURRENCY), "0" },
6220 { X(LOCALE_INEGCURR), "0" },
6221 { X(LOCALE_SDATE), "/" },
6222 { X(LOCALE_STIME), ":" },
6223 { X(LOCALE_SSHORTDATE), "MM/dd/yyyy" },
6224 { X(LOCALE_SLONGDATE), "dddd, dd MMMM yyyy" },
6225 { X(LOCALE_STIMEFORMAT), "HH:mm:ss" },
6226 { X(LOCALE_IDATE), "0" },
6227 { X(LOCALE_ILDATE), "1" },
6228 { X(LOCALE_ITIME), "1" },
6229 { X(LOCALE_ITIMEMARKPOSN), "0" },
6230 { X(LOCALE_ICENTURY), "1" },
6231 { X(LOCALE_ITLZERO), "1" },
6232 { X(LOCALE_IDAYLZERO), "1" },
6233 { X(LOCALE_IMONLZERO), "1" },
6234 { X(LOCALE_S1159), "AM" },
6235 { X(LOCALE_S2359), "PM" },
6236 { X(LOCALE_ICALENDARTYPE), "1" },
6237 { X(LOCALE_IOPTIONALCALENDAR), "0" },
6238 { X(LOCALE_IFIRSTDAYOFWEEK), "6" },
6239 { X(LOCALE_IFIRSTWEEKOFYEAR), "0" },
6240 { X(LOCALE_SDAYNAME1), "Monday" },
6241 { X(LOCALE_SDAYNAME2), "Tuesday" },
6242 { X(LOCALE_SDAYNAME3), "Wednesday" },
6243 { X(LOCALE_SDAYNAME4), "Thursday" },
6244 { X(LOCALE_SDAYNAME5), "Friday" },
6245 { X(LOCALE_SDAYNAME6), "Saturday" },
6246 { X(LOCALE_SDAYNAME7), "Sunday" },
6247 { X(LOCALE_SABBREVDAYNAME1), "Mon" },
6248 { X(LOCALE_SABBREVDAYNAME2), "Tue" },
6249 { X(LOCALE_SABBREVDAYNAME3), "Wed" },
6250 { X(LOCALE_SABBREVDAYNAME4), "Thu" },
6251 { X(LOCALE_SABBREVDAYNAME5), "Fri" },
6252 { X(LOCALE_SABBREVDAYNAME6), "Sat" },
6253 { X(LOCALE_SABBREVDAYNAME7), "Sun" },
6254 { X(LOCALE_SMONTHNAME1), "January" },
6255 { X(LOCALE_SMONTHNAME2), "February" },
6256 { X(LOCALE_SMONTHNAME3), "March" },
6257 { X(LOCALE_SMONTHNAME4), "April" },
6258 { X(LOCALE_SMONTHNAME5), "May" },
6259 { X(LOCALE_SMONTHNAME6), "June" },
6260 { X(LOCALE_SMONTHNAME7), "July" },
6261 { X(LOCALE_SMONTHNAME8), "August" },
6262 { X(LOCALE_SMONTHNAME9), "September" },
6263 { X(LOCALE_SMONTHNAME10), "October" },
6264 { X(LOCALE_SMONTHNAME11), "November" },
6265 { X(LOCALE_SMONTHNAME12), "December" },
6266 { X(LOCALE_SMONTHNAME13), "" },
6267 { X(LOCALE_SABBREVMONTHNAME1), "Jan" },
6268 { X(LOCALE_SABBREVMONTHNAME2), "Feb" },
6269 { X(LOCALE_SABBREVMONTHNAME3), "Mar" },
6270 { X(LOCALE_SABBREVMONTHNAME4), "Apr" },
6271 { X(LOCALE_SABBREVMONTHNAME5), "May" },
6272 { X(LOCALE_SABBREVMONTHNAME6), "Jun" },
6273 { X(LOCALE_SABBREVMONTHNAME7), "Jul" },
6274 { X(LOCALE_SABBREVMONTHNAME8), "Aug" },
6275 { X(LOCALE_SABBREVMONTHNAME9), "Sep" },
6276 { X(LOCALE_SABBREVMONTHNAME10), "Oct" },
6277 { X(LOCALE_SABBREVMONTHNAME11), "Nov" },
6278 { X(LOCALE_SABBREVMONTHNAME12), "Dec" },
6279 { X(LOCALE_SABBREVMONTHNAME13), "" },
6280 { X(LOCALE_SPOSITIVESIGN), "+" },
6281 { X(LOCALE_SNEGATIVESIGN), "-" },
6282 { X(LOCALE_IPOSSIGNPOSN), "3" },
6283 { X(LOCALE_INEGSIGNPOSN), "0" },
6284 { X(LOCALE_IPOSSYMPRECEDES), "1" },
6285 { X(LOCALE_IPOSSEPBYSPACE), "0" },
6286 { X(LOCALE_INEGSYMPRECEDES), "1" },
6287 { X(LOCALE_INEGSEPBYSPACE), "0" },
6288 { X(LOCALE_SISO639LANGNAME), "iv" },
6289 { X(LOCALE_SISO3166CTRYNAME), "IV" },
6290 { X(LOCALE_IDEFAULTEBCDICCODEPAGE), "037" },
6291 { X(LOCALE_IPAPERSIZE), "9" },
6292 { X(LOCALE_SENGCURRNAME), "International Monetary Fund" },
6293 { X(LOCALE_SNATIVECURRNAME), "International Monetary Fund" },
6294 { X(LOCALE_SYEARMONTH), "yyyy MMMM" },
6295 { X(LOCALE_IDIGITSUBSTITUTION), "1" },
6296 { X(LOCALE_SNAME), "" },
6297 { X(LOCALE_SSCRIPTS), "Latn;" },
6298 { 0 }
6300 #undef X
6302 static void test_invariant(void)
6304 int ret;
6305 int len;
6306 char buffer[BUFFER_SIZE];
6307 const struct invariant_entry *ptr = invariant_list;
6309 if (!GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer)))
6311 win_skip("GetLocaleInfoA(LOCALE_INVARIANT) not supported\n"); /* win2k */
6312 return;
6315 while (ptr->name)
6317 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|ptr->id, buffer, sizeof(buffer));
6318 if (!ret && (ptr->id == LOCALE_SNAME || ptr->id == LOCALE_SSCRIPTS))
6319 win_skip("not supported\n"); /* winxp/win2k3 */
6320 else
6322 len = strlen(ptr->expect)+1; /* include \0 */
6323 ok(ret == len || (ptr->expect2 && ret == strlen(ptr->expect2)+1),
6324 "For id %d, expected ret == %d, got %d, error %ld\n",
6325 ptr->id, len, ret, GetLastError());
6326 ok(!strcmp(buffer, ptr->expect) || (ptr->expect2 && !strcmp(buffer, ptr->expect2)),
6327 "For id %d, Expected %s, got '%s'\n",
6328 ptr->id, ptr->expect, buffer);
6331 ptr++;
6334 if ((LANGIDFROMLCID(GetSystemDefaultLCID()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)) ||
6335 (LANGIDFROMLCID(GetThreadLocale()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)))
6337 skip("Non US-English locale\n");
6339 else
6341 /* some locales translate these */
6342 static const char lang[] = "Invariant Language (Invariant Country)";
6343 static const char cntry[] = "Invariant Country";
6344 static const char sortm[] = "Math Alphanumerics";
6345 static const char sortms[] = "Maths Alphanumerics";
6346 static const char sortd[] = "Default"; /* win2k3 */
6348 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer));
6349 len = lstrlenA(lang) + 1;
6350 ok(ret == len, "Expected ret == %d, got %d, error %ld\n", len, ret, GetLastError());
6351 ok(!strcmp(buffer, lang), "Expected %s, got '%s'\n", lang, buffer);
6353 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SCOUNTRY, buffer, sizeof(buffer));
6354 len = lstrlenA(cntry) + 1;
6355 ok(ret == len, "Expected ret == %d, got %d, error %ld\n", len, ret, GetLastError());
6356 ok(!strcmp(buffer, cntry), "Expected %s, got '%s'\n", cntry, buffer);
6358 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SSORTNAME, buffer, sizeof(buffer));
6359 ok(ret, "Failed err %ld\n", GetLastError());
6360 ok(!strcmp(buffer, sortm) || !strcmp(buffer, sortd) || !strcmp(buffer, sortms), "Got '%s'\n", buffer);
6364 static void test_GetSystemPreferredUILanguages(void)
6366 BOOL ret;
6367 NTSTATUS status;
6368 ULONG i, count, size, size_id, size_name, size_buffer;
6369 WCHAR *buffer;
6371 if (!pGetSystemPreferredUILanguages)
6373 win_skip("GetSystemPreferredUILanguages is not available.\n");
6374 return;
6377 /* (in)valid first parameter */
6378 count = 0;
6379 size = 0;
6380 SetLastError(0xdeadbeef);
6381 ret = pGetSystemPreferredUILanguages(0, &count, NULL, &size);
6382 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6383 ok(count, "Expected count > 0\n");
6384 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6386 size = 0;
6387 SetLastError(0xdeadbeef);
6388 ret = pGetSystemPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
6389 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6390 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6391 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6393 size = 0;
6394 SetLastError(0xdeadbeef);
6395 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
6396 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6397 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6398 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6400 size = 0;
6401 SetLastError(0xdeadbeef);
6402 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME, &count, NULL, &size);
6403 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6404 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6405 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6407 count = 0;
6408 size = 0;
6409 SetLastError(0xdeadbeef);
6410 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
6411 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6412 ok(count, "Expected count > 0\n");
6413 ok(size % 5 == 1, "Expected size (%ld) %% 5 == 1\n", size);
6415 count = 0;
6416 size = 0;
6417 SetLastError(0xdeadbeef);
6418 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
6419 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6420 ok(count, "Expected count > 0\n");
6421 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6423 /* second parameter
6424 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, NULL, NULL, &size);
6425 * -> unhandled exception c0000005
6428 /* invalid third parameter */
6429 size = 1;
6430 SetLastError(0xdeadbeef);
6431 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
6432 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6433 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6434 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6436 /* fourth parameter
6437 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, NULL);
6438 * -> unhandled exception c0000005
6441 count = 0;
6442 size_id = 0;
6443 SetLastError(0xdeadbeef);
6444 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
6445 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6446 ok(count, "Expected count > 0\n");
6447 ok(size_id % 5 == 1, "Expected size (%ld) %% 5 == 1\n", size_id);
6449 count = 0;
6450 size_name = 0;
6451 SetLastError(0xdeadbeef);
6452 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
6453 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6454 ok(count, "Expected count > 0\n");
6455 ok(size_name % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size_name);
6457 size_buffer = max(size_id, size_name);
6458 if(!size_buffer)
6460 skip("No valid buffer size\n");
6461 return;
6464 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
6465 if (!buffer)
6467 skip("Failed to allocate memory for %ld chars\n", size_buffer);
6468 return;
6471 count = 0;
6472 size = size_buffer;
6473 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6474 SetLastError(0xdeadbeef);
6475 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
6476 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6477 ok(count, "Expected count > 0\n");
6478 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6479 if (ret && size % 6 == 1)
6480 ok(!buffer[size -2] && !buffer[size -1],
6481 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6482 buffer[size -2], buffer[size -1]);
6484 count = 0;
6485 size = size_buffer;
6486 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6487 SetLastError(0xdeadbeef);
6488 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
6489 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6490 ok(count, "Expected count > 0\n");
6491 ok(size % 5 == 1, "Expected size (%ld) %% 5 == 1\n", size);
6492 if (ret && size % 5 == 1)
6493 ok(!buffer[size -2] && !buffer[size -1],
6494 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6495 buffer[size -2], buffer[size -1]);
6496 for (i = 0; buffer[i]; i++)
6497 ok(('0' <= buffer[i] && buffer[i] <= '9') ||
6498 ('A' <= buffer[i] && buffer[i] <= 'F'),
6499 "MUI_LANGUAGE_ID [%ld] is bad in %s\n", i, wine_dbgstr_w(buffer));
6501 count = 0;
6502 size = size_buffer;
6503 SetLastError(0xdeadbeef);
6504 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
6505 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6506 ok(count, "Expected count > 0\n");
6507 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6508 if (ret && size % 5 == 1)
6509 ok(!buffer[size -2] && !buffer[size -1],
6510 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6511 buffer[size -2], buffer[size -1]);
6513 count = 0;
6514 size = 0;
6515 SetLastError(0xdeadbeef);
6516 ret = pGetSystemPreferredUILanguages(MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
6517 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
6518 ok(count, "Expected count > 0\n");
6519 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6520 if (ret && size % 6 == 1)
6521 ok(!buffer[size -2] && !buffer[size -1],
6522 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6523 buffer[size -2], buffer[size -1]);
6525 /* ntdll version is the same, but apparently takes an extra second parameter */
6526 count = 0;
6527 size = size_buffer;
6528 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6529 status = pRtlGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, 0, &count, buffer, &size);
6530 ok(!status, "got %lx\n", status);
6531 ok(count, "Expected count > 0\n");
6532 ok(size % 5 == 1, "Expected size (%ld) %% 5 == 1\n", size);
6533 if (ret && size % 5 == 1)
6534 ok(!buffer[size -2] && !buffer[size -1],
6535 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6536 buffer[size -2], buffer[size -1]);
6538 count = 0;
6539 size = size_buffer;
6540 status = pRtlGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, 0, &count, buffer, &size);
6541 ok(!status, "got %lx\n", status);
6542 ok(count, "Expected count > 0\n");
6543 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6544 if (ret && size % 5 == 1)
6545 ok(!buffer[size -2] && !buffer[size -1],
6546 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6547 buffer[size -2], buffer[size -1]);
6549 count = 0;
6550 size = 0;
6551 status = pRtlGetSystemPreferredUILanguages(MUI_MACHINE_LANGUAGE_SETTINGS, 0, &count, NULL, &size);
6552 ok(!status, "got %lx\n", status);
6553 ok(count, "Expected count > 0\n");
6554 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6555 if (ret && size % 6 == 1)
6556 ok(!buffer[size -2] && !buffer[size -1],
6557 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6558 buffer[size -2], buffer[size -1]);
6560 size = 0;
6561 SetLastError(0xdeadbeef);
6562 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
6563 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6564 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
6565 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6566 ok(size == size_id, "expected %lu, got %lu\n", size_id, size);
6568 size = 1;
6569 SetLastError(0xdeadbeef);
6570 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
6571 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6572 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
6573 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6574 ok(size == size_id, "expected %lu, got %lu\n", size_id, size);
6576 size = size_id -1;
6577 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6578 SetLastError(0xdeadbeef);
6579 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
6580 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6581 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
6582 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6583 ok(size == size_id, "expected %lu, got %lu\n", size_id, size);
6585 size = size_id -2;
6586 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6587 SetLastError(0xdeadbeef);
6588 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
6589 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
6590 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
6591 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6592 ok(size == size_id + 2 || size == size_id + 1 /* before win10 1809 */, "expected %lu, got %lu\n", size_id + 2, size);
6594 HeapFree(GetProcessHeap(), 0, buffer);
6597 static void test_GetThreadPreferredUILanguages(void)
6599 BOOL ret;
6600 NTSTATUS status;
6601 ULONG count, size, size_id;
6602 WCHAR *buf;
6604 if (!pGetThreadPreferredUILanguages)
6606 win_skip("GetThreadPreferredUILanguages is not available.\n");
6607 return;
6610 size = count = 0;
6611 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, NULL, &size);
6612 ok(ret, "got %lu\n", GetLastError());
6613 ok(count, "expected count > 0\n");
6614 ok(size, "expected size > 0\n");
6616 count = 0;
6617 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size * sizeof(WCHAR));
6618 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, buf, &size);
6619 ok(ret, "got %lu\n", GetLastError());
6620 ok(count, "expected count > 0\n");
6622 size_id = count = 0;
6623 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
6624 ok(ret, "got %lu\n", GetLastError());
6625 ok(count, "expected count > 0\n");
6626 ok(size_id, "expected size > 0\n");
6627 ok(size_id <= size, "expected size > 0\n");
6629 /* ntdll function is the same */
6630 size_id = count = 0;
6631 status = pRtlGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
6632 ok(!status, "got %lx\n", status);
6633 ok(count, "expected count > 0\n");
6634 ok(size_id, "expected size > 0\n");
6635 ok(size_id <= size, "expected size > 0\n");
6637 size = 0;
6638 SetLastError(0xdeadbeef);
6639 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, buf, &size);
6640 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
6641 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
6642 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6643 ok(size == size_id, "expected %lu, got %lu\n", size_id, size);
6645 size = 1;
6646 SetLastError(0xdeadbeef);
6647 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, buf, &size);
6648 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
6649 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
6650 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6651 ok(size == size_id, "expected %lu, got %lu\n", size_id, size);
6653 size = size_id - 1;
6654 SetLastError(0xdeadbeef);
6655 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID, &count, buf, &size);
6656 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
6657 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
6658 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6659 ok(size == size_id, "expected %lu, got %lu\n", size_id, size);
6661 size = size_id - 2;
6662 SetLastError(0xdeadbeef);
6663 ret = pGetThreadPreferredUILanguages(0, &count, buf, &size);
6664 ok(!ret, "Expected GetThreadPreferredUILanguages to fail\n");
6665 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
6666 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6667 todo_wine
6668 ok(size == size_id || size == size_id - 1 /* before win10 1809 */, "expected %lu, got %lu\n", size_id, size);
6670 HeapFree(GetProcessHeap(), 0, buf);
6673 static void test_GetUserPreferredUILanguages(void)
6675 BOOL ret;
6676 NTSTATUS status;
6677 ULONG count, size, size_id, size_name, size_buffer;
6678 WCHAR *buffer;
6680 if (!pGetUserPreferredUILanguages)
6682 win_skip("GetUserPreferredUILanguages is not available.\n");
6683 return;
6686 size = 0;
6687 SetLastError(0xdeadbeef);
6688 ret = pGetUserPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
6689 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
6690 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6691 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6693 size = 0;
6694 SetLastError(0xdeadbeef);
6695 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
6696 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
6697 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6698 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6700 size = 0;
6701 SetLastError(0xdeadbeef);
6702 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
6703 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
6704 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6705 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6707 size = 1;
6708 SetLastError(0xdeadbeef);
6709 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
6710 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
6711 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6712 "Expected error ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
6714 count = 0;
6715 size_id = 0;
6716 SetLastError(0xdeadbeef);
6717 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
6718 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
6719 ok(count, "Expected count > 0\n");
6720 ok(size_id % 5 == 1, "Expected size (%ld) %% 5 == 1\n", size_id);
6722 count = 0;
6723 size_name = 0;
6724 SetLastError(0xdeadbeef);
6725 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
6726 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
6727 ok(count, "Expected count > 0\n");
6728 ok(size_name % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size_name);
6730 size_buffer = max(size_id, size_name);
6731 if(!size_buffer)
6733 skip("No valid buffer size\n");
6734 return;
6737 /* ntdll version is the same, but apparently takes an extra second parameter */
6738 count = 0;
6739 size_id = 0;
6740 SetLastError(0xdeadbeef);
6741 status = pRtlGetUserPreferredUILanguages(MUI_LANGUAGE_ID, 0, &count, NULL, &size_id);
6742 ok(!status, "got %lx\n", status);
6743 ok(count, "Expected count > 0\n");
6744 ok(size_id % 5 == 1, "Expected size (%ld) %% 5 == 1\n", size_id);
6746 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
6748 count = 0;
6749 size = size_buffer;
6750 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6751 SetLastError(0xdeadbeef);
6752 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
6753 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
6754 ok(count, "Expected count > 0\n");
6755 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6756 if (ret && size % 6 == 1)
6757 ok(!buffer[size -2] && !buffer[size -1],
6758 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6759 buffer[size -2], buffer[size -1]);
6761 count = 0;
6762 size = size_buffer;
6763 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6764 SetLastError(0xdeadbeef);
6765 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
6766 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
6767 ok(count, "Expected count > 0\n");
6768 ok(size % 5 == 1, "Expected size (%ld) %% 5 == 1\n", size);
6769 if (ret && size % 5 == 1)
6770 ok(!buffer[size -2] && !buffer[size -1],
6771 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6772 buffer[size -2], buffer[size -1]);
6774 count = 0;
6775 size = size_buffer;
6776 SetLastError(0xdeadbeef);
6777 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
6778 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
6779 ok(count, "Expected count > 0\n");
6780 ok(size % 6 == 1, "Expected size (%ld) %% 6 == 1\n", size);
6781 if (ret && size % 5 == 1)
6782 ok(!buffer[size -2] && !buffer[size -1],
6783 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
6784 buffer[size -2], buffer[size -1]);
6786 size = 1;
6787 SetLastError(0xdeadbeef);
6788 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
6789 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
6790 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
6791 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6793 size = size_id -1;
6794 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6795 SetLastError(0xdeadbeef);
6796 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
6797 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
6798 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
6799 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6801 count = 0;
6802 size = size_id -2;
6803 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
6804 SetLastError(0xdeadbeef);
6805 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
6806 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
6807 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
6808 "Expected error ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
6810 HeapFree(GetProcessHeap(), 0, buffer);
6813 static void test_FindNLSStringEx(void)
6815 INT res;
6816 static const WCHAR comb_s_accent1W[] = {0x1e69, 'o','u','r','c','e',0};
6817 static const WCHAR comb_s_accent2W[] = {0x0073,0x323,0x307,'o','u','r','c','e',0};
6818 static const WCHAR comb_q_accent1W[] = {'a','b',0x0071,0x0307,0x323,'u','o','t','e','\n',0};
6819 static const WCHAR comb_q_accent2W[] = {0x0071,0x0323,0x307,'u','o','t','e',0};
6820 struct test_data {
6821 const WCHAR *locale;
6822 DWORD flags;
6823 const WCHAR *src;
6824 const WCHAR *value;
6825 INT expected_ret;
6826 INT expected_found;
6829 static struct test_data tests[] =
6831 { localeW, FIND_FROMSTART, L"SimpleSimple", L"Simple", 0, 6},
6832 { localeW, FIND_FROMEND, L"SimpleSimple", L"Simp", 6, 4},
6833 { localeW, FIND_STARTSWITH, L"SimpleSimple", L"Simp", 0, 4},
6834 { localeW, FIND_ENDSWITH, L"SimpleSimple", L"Simple", 6, 6},
6835 { localeW, FIND_ENDSWITH, L"SimpleSimple", L"Simp", -1, 0xdeadbeef},
6836 { localeW, FIND_FROMSTART, comb_s_accent1W, comb_s_accent2W, 0, 6 },
6837 { localeW, FIND_FROMSTART, comb_q_accent1W, comb_q_accent2W, 2, 7 },
6838 { localeW, FIND_STARTSWITH, L"--Option", L"--", 0, 2},
6839 { localeW, FIND_ENDSWITH, L"Option--", L"--", 6, 2},
6840 { localeW, FIND_FROMSTART, L"----", L"--", 0, 2},
6841 { localeW, FIND_FROMEND, L"----", L"--", 2, 2},
6842 { localeW, FIND_FROMSTART, L"opt1--opt2--opt3", L"--", 4, 2},
6843 { localeW, FIND_FROMEND, L"opt1--opt2--opt3", L"--", 10, 2},
6844 { localeW, FIND_FROMSTART, L"x-oss-security", L"x-oss-", 0, 6},
6845 { localeW, FIND_FROMSTART, L"x-oss-security", L"-oss", 1, 4},
6846 { localeW, FIND_FROMSTART, L"x-oss-security", L"-oss--", -1, 0xdeadbeef},
6847 { localeW, FIND_FROMEND, L"x-oss-oss2", L"-oss", 5, 4},
6849 unsigned int i;
6851 if (!pFindNLSStringEx)
6853 win_skip("FindNLSStringEx is not available.\n");
6854 return;
6857 SetLastError( 0xdeadbeef );
6858 res = pFindNLSStringEx(invalidW, FIND_FROMSTART, fooW, 3, fooW,
6859 3, NULL, NULL, NULL, 0);
6860 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
6861 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6862 "Expected ERROR_INVALID_PARAMETER as last error; got %ld\n", GetLastError());
6864 SetLastError( 0xdeadbeef );
6865 res = pFindNLSStringEx(localeW, FIND_FROMSTART, NULL, 3, fooW, 3,
6866 NULL, NULL, NULL, 0);
6867 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
6868 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6869 "Expected ERROR_INVALID_PARAMETER as last error; got %ld\n", GetLastError());
6871 SetLastError( 0xdeadbeef );
6872 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, -5, fooW, 3,
6873 NULL, NULL, NULL, 0);
6874 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
6875 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6876 "Expected ERROR_INVALID_PARAMETER as last error; got %ld\n", GetLastError());
6878 SetLastError( 0xdeadbeef );
6879 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, NULL, 3,
6880 NULL, NULL, NULL, 0);
6881 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
6882 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6883 "Expected ERROR_INVALID_PARAMETER as last error; got %ld\n", GetLastError());
6885 SetLastError( 0xdeadbeef );
6886 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, fooW, -5,
6887 NULL, NULL, NULL, 0);
6888 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
6889 ok(ERROR_INVALID_PARAMETER == GetLastError(),
6890 "Expected ERROR_INVALID_PARAMETER as last error; got %ld\n", GetLastError());
6892 for (i = 0; i < ARRAY_SIZE(tests); i++)
6894 int found = 0xdeadbeef;
6895 res = pFindNLSStringEx(tests[i].locale, tests[i].flags, tests[i].src, -1,
6896 tests[i].value, -1, &found, NULL, NULL, 0);
6897 ok(res == tests[i].expected_ret,
6898 "%u: Expected FindNLSStringEx to return %d. Returned value was %d\n", i,
6899 tests[i].expected_ret, res);
6900 ok(found == tests[i].expected_found,
6901 "%u: Expected FindNLSStringEx to output %d. Value was %d\n", i,
6902 tests[i].expected_found, found);
6906 static void test_FindStringOrdinal(void)
6908 static const WCHAR abc123aBcW[] = {'a', 'b', 'c', '1', '2', '3', 'a', 'B', 'c', 0};
6909 static const WCHAR abcW[] = {'a', 'b', 'c', 0};
6910 static const WCHAR aBcW[] = {'a', 'B', 'c', 0};
6911 static const WCHAR aaaW[] = {'a', 'a', 'a', 0};
6912 static const struct
6914 DWORD flag;
6915 const WCHAR *src;
6916 INT src_size;
6917 const WCHAR *val;
6918 INT val_size;
6919 BOOL ignore_case;
6920 INT ret;
6921 DWORD err;
6923 tests[] =
6925 /* Invalid */
6926 {1, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, ERROR_INVALID_FLAGS},
6927 {FIND_FROMSTART, NULL, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1,
6928 ERROR_INVALID_PARAMETER},
6929 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, NULL, ARRAY_SIZE(abcW) - 1, FALSE, -1,
6930 ERROR_INVALID_PARAMETER},
6931 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, NULL, 0, FALSE, -1, ERROR_INVALID_PARAMETER},
6932 {FIND_FROMSTART, NULL, 0, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, ERROR_INVALID_PARAMETER},
6933 {FIND_FROMSTART, NULL, 0, NULL, 0, FALSE, -1, ERROR_INVALID_PARAMETER},
6934 /* Case-insensitive */
6935 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
6936 {FIND_FROMEND, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
6937 {FIND_STARTSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
6938 {FIND_ENDSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, NO_ERROR},
6939 /* Case-sensitive */
6940 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 0, NO_ERROR},
6941 {FIND_FROMEND, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 6, NO_ERROR},
6942 {FIND_STARTSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 0, NO_ERROR},
6943 {FIND_ENDSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 6, NO_ERROR},
6944 /* Other */
6945 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aaaW, ARRAY_SIZE(aaaW) - 1, FALSE, -1, NO_ERROR},
6946 {FIND_FROMSTART, abc123aBcW, -1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
6947 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, -1, FALSE, 0, NO_ERROR},
6948 {FIND_FROMSTART, abc123aBcW, 0, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, NO_ERROR},
6949 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, 0, FALSE, 0, NO_ERROR},
6950 {FIND_FROMSTART, abc123aBcW, 0, abcW, 0, FALSE, 0, NO_ERROR},
6952 INT ret;
6953 DWORD err;
6954 INT i;
6956 if (!pFindStringOrdinal)
6958 win_skip("FindStringOrdinal is not available.\n");
6959 return;
6962 for (i = 0; i < ARRAY_SIZE(tests); i++)
6964 SetLastError(0xdeadbeef);
6965 ret = pFindStringOrdinal(tests[i].flag, tests[i].src, tests[i].src_size, tests[i].val, tests[i].val_size,
6966 tests[i].ignore_case);
6967 err = GetLastError();
6968 ok(ret == tests[i].ret, "Item %d expected %d, got %d\n", i, tests[i].ret, ret);
6969 ok(err == tests[i].err, "Item %d expected %#lx, got %#lx\n", i, tests[i].err, err);
6973 static void test_SetThreadUILanguage(void)
6975 LANGID res;
6977 if (!pGetThreadUILanguage)
6979 win_skip("GetThreadUILanguage isn't implemented, skipping SetThreadUILanguage tests for version < Vista\n");
6980 return; /* BTW SetThreadUILanguage is present on winxp/2003 but doesn`t set the LANGID anyway when tested */
6983 res = pSetThreadUILanguage(0);
6984 ok(res == pGetThreadUILanguage(), "expected %d got %d\n", pGetThreadUILanguage(), res);
6986 res = pSetThreadUILanguage(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN));
6987 ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
6988 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
6990 res = pSetThreadUILanguage(0);
6991 todo_wine ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
6992 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
6995 /* read a Unicode string from NormalizationTest.txt format; helper for test_NormalizeString */
6996 static int read_str( char *str, WCHAR res[32] )
6998 int pos = 0;
6999 char *end;
7001 while (*str && pos < 31)
7003 unsigned int c = strtoul( str, &end, 16 );
7004 pos += put_utf16( res + pos, c );
7005 while (*end == ' ') end++;
7006 str = end;
7008 res[pos] = 0;
7009 return pos;
7012 static void test_NormalizeString(void)
7014 /* part 0: specific cases */
7015 /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
7016 static const WCHAR part0_str1[] = {0x1e0a,0};
7017 static const WCHAR part0_nfd1[] = {0x0044,0x0307,0};
7019 /* LATIN CAPITAL LETTER D, COMBINING DOT BELOW, COMBINING DOT ABOVE */
7020 static const WCHAR part0_str2[] = {0x0044,0x0323,0x0307,0};
7021 static const WCHAR part0_nfc2[] = {0x1e0c,0x0307,0};
7023 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
7024 static const WCHAR part0_str3[] = {0x0044,0x031b,0x0323,0x0307,0};
7025 static const WCHAR part0_nfc3[] = {0x1e0c,0x031b,0x0307,0};
7027 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
7028 static const WCHAR part0_str4[] = {0x0044,0x031b,0x0323,0x0307,0};
7029 static const WCHAR part0_nfc4[] = {0x1e0c,0x031b,0x0307,0};
7032 * HEBREW ACCENT SEGOL, HEBREW POINT PATAH, HEBREW POINT DAGESH OR MAPIQ,
7033 * HEBREW ACCENT MERKHA, HEBREW POINT SHEVA, HEBREW PUNCTUATION PASEQ,
7034 * HEBREW MARK UPPER DOT, HEBREW ACCENT DEHI
7036 static const WCHAR part0_str5[] = {0x0592,0x05B7,0x05BC,0x05A5,0x05B0,0x05C0,0x05C4,0x05AD,0};
7037 static const WCHAR part0_nfc5[] = {0x05B0,0x05B7,0x05BC,0x05A5,0x0592,0x05C0,0x05AD,0x05C4,0};
7040 * HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT HATAF SEGOL,
7041 * HEBREW ACCENT ETNAHTA, HEBREW PUNCTUATION SOF PASUQ, HEBREW POINT SHEVA,
7042 * HEBREW ACCENT ILUY, HEBREW ACCENT QARNEY PARA
7044 static const WCHAR part0_str6[] = {0x05B8,0x05B9,0x05B1,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
7045 static const WCHAR part0_nfc6[] = {0x05B1,0x05B8,0x05B9,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
7047 /* LATIN CAPITAL LETTER D WITH DOT BELOW */
7048 static const WCHAR part0_str8[] = {0x1E0C,0};
7049 static const WCHAR part0_nfd8[] = {0x0044,0x0323,0};
7051 /* LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING DOT BELOW */
7052 static const WCHAR part0_str9[] = {0x1E0A,0x0323,0};
7053 static const WCHAR part0_nfc9[] = {0x1E0C,0x0307,0};
7054 static const WCHAR part0_nfd9[] = {0x0044,0x0323,0x0307,0};
7056 /* LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING DOT ABOVE */
7057 static const WCHAR part0_str10[] = {0x1E0C,0x0307,0};
7058 static const WCHAR part0_nfd10[] = {0x0044,0x0323,0x0307,0};
7060 /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE, COMBINING MACRON */
7061 static const WCHAR part0_str11[] = {0x1E14,0x0304,0};
7062 static const WCHAR part0_nfd11[] = {0x0045,0x0304,0x0300,0x0304,0};
7064 /* LATIN CAPITAL LETTER E WITH MACRON, COMBINING GRAVE ACCENT */
7065 static const WCHAR part0_str12[] = {0x0112,0x0300,0};
7066 static const WCHAR part0_nfc12[] = {0x1E14,0};
7067 static const WCHAR part0_nfd12[] = {0x0045,0x0304,0x0300,0};
7069 /* part 1: character by character */
7070 /* DIAERESIS */
7071 static const WCHAR part1_str1[] = {0x00a8,0};
7072 static const WCHAR part1_nfkc1[] = {0x0020,0x0308,0};
7074 /* VULGAR FRACTION ONE QUARTER */
7075 static const WCHAR part1_str2[] = {0x00bc,0};
7076 static const WCHAR part1_nfkc2[] = {0x0031,0x2044,0x0034,0};
7078 /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
7079 static const WCHAR part1_str3[] = {0x00ca,0};
7080 static const WCHAR part1_nfd3[] = {0x0045,0x0302,0};
7082 /* MODIFIER LETTER SMALL GAMMA */
7083 static const WCHAR part1_str4[] = {0x02e0,0};
7084 static const WCHAR part1_nfkc4[] = {0x0263,0};
7086 /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */
7087 static const WCHAR part1_str5[] = {0x0400,0};
7088 static const WCHAR part1_nfd5[] = {0x0415,0x0300,0};
7090 /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */
7091 static const WCHAR part1_str6[] = {0x0476,0};
7092 static const WCHAR part1_nfd6[] = {0x0474,0x030F,0};
7094 /* ARABIC LIGATURE HAH WITH JEEM INITIAL FORM */
7095 static const WCHAR part1_str7[] = {0xFCA9,0};
7096 static const WCHAR part1_nfkc7[] = {0x062D,0x062C,0};
7098 /* GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA */
7099 static const WCHAR part1_str8[] = {0x1F42,0};
7100 static const WCHAR part1_nfd8[] = {0x03BF,0x0313,0x0300,0};
7102 /* QUADRUPLE PRIME */
7103 static const WCHAR part1_str9[] = {0x2057,0};
7104 static const WCHAR part1_nfkc9[] = {0x2032,0x2032,0x2032,0x2032,0};
7106 /* KATAKANA-HIRAGANA VOICED SOUND MARK */
7107 static const WCHAR part1_str10[] = {0x309B,0};
7108 static const WCHAR part1_nfkc10[] = {0x20,0x3099,0};
7110 /* ANGSTROM SIGN */
7111 static const WCHAR part1_str11[] = {0x212B,0};
7112 static const WCHAR part1_nfc11[] = {0xC5,0};
7113 static const WCHAR part1_nfd11[] = {'A',0x030A,0};
7115 static const WCHAR composite_src[] =
7117 0x008a, 0x008e, 0x009a, 0x009e, 0x009f, 0x00c0, 0x00c1, 0x00c2,
7118 0x00c3, 0x00c4, 0x00c5, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb,
7119 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d1, 0x00d2, 0x00d3, 0x00d4,
7120 0x00d5, 0x00d6, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd,
7121 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e7, 0x00e8,
7122 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f1,
7123 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f8, 0x00f9, 0x00fa,
7124 0x00fb, 0x00fc, 0x00fd, 0x00ff, 0x212b
7127 struct test_data_normal {
7128 const WCHAR *str;
7129 const WCHAR *expected[4];
7131 static const struct test_data_normal test_arr[] =
7133 { part0_str1, { part0_str1, part0_nfd1, part0_str1, part0_nfd1 } },
7134 { part0_str2, { part0_nfc2, part0_str2, part0_nfc2, part0_str2 } },
7135 { part0_str3, { part0_nfc3, part0_str3, part0_nfc3, part0_str3 } },
7136 { part0_str4, { part0_nfc4, part0_str4, part0_nfc4, part0_str4 } },
7137 { part0_str5, { part0_nfc5, part0_nfc5, part0_nfc5, part0_nfc5 } },
7138 { part0_str6, { part0_nfc6, part0_nfc6, part0_nfc6, part0_nfc6 } },
7139 { part0_str8, { part0_str8, part0_nfd8, part0_str8, part0_nfd8 } },
7140 { part0_str9, { part0_nfc9, part0_nfd9, part0_nfc9, part0_nfd9 } },
7141 { part0_str10, { part0_str10, part0_nfd10, part0_str10, part0_nfd10 } },
7142 { part0_str11, { part0_str11, part0_nfd11, part0_str11, part0_nfd11 } },
7143 { part0_str12, { part0_nfc12, part0_nfd12, part0_nfc12, part0_nfd12 } },
7144 { part1_str1, { part1_str1, part1_str1, part1_nfkc1, part1_nfkc1 } },
7145 { part1_str2, { part1_str2, part1_str2, part1_nfkc2, part1_nfkc2 } },
7146 { part1_str3, { part1_str3, part1_nfd3, part1_str3, part1_nfd3 } },
7147 { part1_str4, { part1_str4, part1_str4, part1_nfkc4, part1_nfkc4 } },
7148 { part1_str5, { part1_str5, part1_nfd5, part1_str5, part1_nfd5 } },
7149 { part1_str6, { part1_str6, part1_nfd6, part1_str6, part1_nfd6 } },
7150 { part1_str7, { part1_str7, part1_str7, part1_nfkc7, part1_nfkc7 } },
7151 { part1_str8, { part1_str8, part1_nfd8, part1_str8, part1_nfd8 } },
7152 { part1_str9, { part1_str9, part1_str9, part1_nfkc9, part1_nfkc9 } },
7153 { part1_str10, { part1_str10, part1_str10, part1_nfkc10, part1_nfkc10 } },
7154 { part1_str11, { part1_nfc11, part1_nfd11, part1_nfc11, part1_nfd11 } },
7155 { 0 }
7157 const struct test_data_normal *ptest = test_arr;
7158 const int norm_forms[] = { NormalizationC, NormalizationD, NormalizationKC, NormalizationKD };
7159 WCHAR dst[256];
7160 BOOLEAN ret;
7161 NTSTATUS status;
7162 int dstlen, str_cmp, i, j;
7163 FILE *f;
7165 if (!pNormalizeString)
7167 win_skip("NormalizeString is not available.\n");
7168 return;
7170 if (!pRtlNormalizeString) win_skip("RtlNormalizeString is not available.\n");
7173 * For each string, first test passing -1 as srclen to NormalizeString,
7174 * thereby assuming a null-terminating string in src, and then test passing
7175 * explicitly the string length.
7176 * Do that for all 4 normalization forms.
7178 while (ptest->str)
7180 for (i = 0; i < 4; i++)
7182 SetLastError(0xdeadbeef);
7183 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, NULL, 0 );
7184 ok( dstlen > lstrlenW(ptest->str), "%s:%d: wrong len %d / %d\n",
7185 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW(ptest->str) );
7186 ok(GetLastError() == ERROR_SUCCESS, "%s:%d: got error %lu\n",
7187 wine_dbgstr_w(ptest->str), i, GetLastError());
7188 SetLastError(0xdeadbeef);
7189 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, dst, dstlen );
7190 ok(GetLastError() == ERROR_SUCCESS, "%s:%d: got error %lu\n",
7191 wine_dbgstr_w(ptest->str), i, GetLastError());
7192 ok(dstlen == lstrlenW( dst )+1, "%s:%d: Copied length differed: was %d, should be %d\n",
7193 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst )+1);
7194 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen+1 );
7195 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
7196 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
7198 dstlen = pNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), NULL, 0 );
7199 memset(dst, 0xcc, sizeof(dst));
7200 dstlen = pNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), dst, dstlen );
7201 ok(dstlen == lstrlenW( ptest->expected[i] ), "%s:%d: Copied length differed: was %d, should be %d\n",
7202 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst ));
7203 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen );
7204 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
7205 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
7207 if (pRtlNormalizeString)
7209 dstlen = 0;
7210 status = pRtlNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), NULL, &dstlen );
7211 ok( !status, "%s:%d: failed %lx\n", wine_dbgstr_w(ptest->str), i, status );
7212 ok( dstlen > lstrlenW(ptest->str), "%s:%d: wrong len %d / %d\n",
7213 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW(ptest->str) );
7214 memset(dst, 0, sizeof(dst));
7215 status = pRtlNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), dst, &dstlen );
7216 ok( !status, "%s:%d: failed %lx\n", wine_dbgstr_w(ptest->str), i, status );
7217 ok(dstlen == lstrlenW( dst ), "%s:%d: Copied length differed: was %d, should be %d\n",
7218 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst ));
7219 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen );
7220 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
7221 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
7222 ret = FALSE;
7223 status = pRtlIsNormalizedString( norm_forms[i], ptest->str, -1, &ret );
7224 ok( !status, "%s:%d: failed %lx\n", wine_dbgstr_w(ptest->str), i, status );
7225 if (!wcscmp( ptest->str, dst ))
7226 ok( ret, "%s:%d: not normalized\n", wine_dbgstr_w(ptest->str), i );
7227 else
7228 ok( !ret, "%s:%d: normalized (dst %s)\n", wine_dbgstr_w(ptest->str), i, wine_dbgstr_w(dst) );
7229 ret = FALSE;
7230 status = pRtlIsNormalizedString( norm_forms[i], dst, dstlen, &ret );
7231 ok( !status, "%s:%d: failed %lx\n", wine_dbgstr_w(ptest->str), i, status );
7232 ok( ret, "%s:%d: not normalized\n", wine_dbgstr_w(ptest->str), i );
7235 ptest++;
7238 /* buffer overflows */
7240 SetLastError(0xdeadbeef);
7241 dstlen = pNormalizeString( NormalizationD, part0_str1, -1, dst, 1 );
7242 ok( dstlen <= 0, "wrong len %d\n", dstlen );
7243 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %lu\n", GetLastError());
7245 SetLastError(0xdeadbeef);
7246 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, dst, 1 );
7247 ok( dstlen <= 0, "wrong len %d\n", dstlen );
7248 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %lu\n", GetLastError());
7250 SetLastError(0xdeadbeef);
7251 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, NULL, 0 );
7252 ok( dstlen == 12, "wrong len %d\n", dstlen );
7253 ok(GetLastError() == ERROR_SUCCESS, "got error %lu\n", GetLastError());
7255 SetLastError(0xdeadbeef);
7256 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, dst, 3 );
7257 ok( dstlen == 3, "wrong len %d\n", dstlen );
7258 ok(GetLastError() == ERROR_SUCCESS, "got error %lu\n", GetLastError());
7260 SetLastError(0xdeadbeef);
7261 dstlen = pNormalizeString( NormalizationC, part0_str2, 0, NULL, 0 );
7262 ok( dstlen == 0, "wrong len %d\n", dstlen );
7263 ok(GetLastError() == ERROR_SUCCESS, "got error %lu\n", GetLastError());
7265 SetLastError(0xdeadbeef);
7266 dstlen = pNormalizeString( NormalizationC, part0_str2, 0, dst, 3 );
7267 ok( dstlen == 0, "wrong len %d\n", dstlen );
7268 ok(GetLastError() == ERROR_SUCCESS, "got error %lu\n", GetLastError());
7270 /* size estimations */
7272 memset( dst, 'A', sizeof(dst) );
7273 for (j = 1; j < ARRAY_SIZE(dst); j++)
7275 for (i = 0; i < 4; i++)
7277 int expect = (i < 2) ? j * 3 : j * 18;
7278 if (expect > 64) expect = max( 64, j + j / 8 );
7279 dstlen = pNormalizeString( norm_forms[i], dst, j, NULL, 0 );
7280 ok( dstlen == expect, "%d: %d -> wrong len %d\n", i, j, dstlen );
7281 if (pRtlNormalizeString)
7283 dstlen = 0;
7284 status = pRtlNormalizeString( norm_forms[i], dst, j, NULL, &dstlen );
7285 ok( !status, "%d: failed %lx\n", i, status );
7286 ok( dstlen == expect, "%d: %d -> wrong len %d\n", i, j, dstlen );
7290 for (i = 0; i < 4; i++)
7292 int srclen = ARRAY_SIZE( composite_src );
7293 int expect = max( 64, srclen + srclen / 8 );
7294 dstlen = pNormalizeString( norm_forms[i], composite_src, srclen, NULL, 0 );
7295 ok( dstlen == expect, "%d: wrong len %d\n", i, dstlen );
7296 dstlen = pNormalizeString( norm_forms[i], composite_src, srclen, dst, dstlen );
7297 if (i == 0 || i == 2)
7299 ok( dstlen == srclen, "%d: wrong len %d\n", i, dstlen );
7300 ok(GetLastError() == ERROR_SUCCESS, "got error %lu\n", GetLastError());
7302 else
7304 ok( dstlen < -expect, "%d: wrong len %d\n", i, dstlen );
7305 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %lu\n", GetLastError());
7307 if (pRtlNormalizeString)
7309 dstlen = 0;
7310 status = pRtlNormalizeString( norm_forms[i], composite_src, srclen, NULL, &dstlen );
7311 ok( !status, "%d: failed %lx\n", i, status );
7312 ok( dstlen == expect, "%d: wrong len %d\n", i, dstlen );
7313 status = pRtlNormalizeString( norm_forms[i], composite_src, srclen, dst, &dstlen );
7314 if (i == 0 || i == 2)
7316 ok( !status, "%d: failed %lx\n", i, status );
7317 ok( dstlen == srclen, "%d: wrong len %d\n", i, dstlen );
7319 else
7321 ok( status == STATUS_BUFFER_TOO_SMALL, "%d: failed %lx\n", i, status );
7322 ok( dstlen > expect, "%d: wrong len %d\n", i, dstlen );
7327 /* invalid parameters */
7329 for (i = 0; i < 32; i++)
7331 SetLastError(0xdeadbeef);
7332 dstlen = pNormalizeString( i, L"ABC", -1, NULL, 0 );
7333 switch (i)
7335 case NormalizationC:
7336 case NormalizationD:
7337 case NormalizationKC:
7338 case NormalizationKD:
7339 case 13: /* Idn */
7340 ok( dstlen > 0, "%d: wrong len %d\n", i, dstlen );
7341 ok( GetLastError() == ERROR_SUCCESS, "%d: got error %lu\n", i, GetLastError());
7342 break;
7343 default:
7344 ok( dstlen <= 0, "%d: wrong len %d\n", i, dstlen );
7345 ok( GetLastError() == ERROR_INVALID_PARAMETER, "%d: got error %lu\n", i, GetLastError());
7346 break;
7348 if (pRtlNormalizeString)
7350 dstlen = 0;
7351 status = pRtlNormalizeString( i, L"ABC", -1, NULL, &dstlen );
7352 switch (i)
7354 case 0:
7355 ok( status == STATUS_INVALID_PARAMETER, "%d: failed %lx\n", i, status );
7356 break;
7357 case NormalizationC:
7358 case NormalizationD:
7359 case NormalizationKC:
7360 case NormalizationKD:
7361 case 13: /* Idn */
7362 ok( status == STATUS_SUCCESS, "%d: failed %lx\n", i, status );
7363 break;
7364 default:
7365 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%d: failed %lx\n", i, status );
7366 break;
7371 /* invalid sequences */
7373 for (i = 0; i < 4; i++)
7375 dstlen = pNormalizeString( norm_forms[i], L"AB\xd800Z", -1, NULL, 0 );
7376 ok( dstlen == (i < 2 ? 15 : 64), "%d: wrong len %d\n", i, dstlen );
7377 SetLastError( 0xdeadbeef );
7378 dstlen = pNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, ARRAY_SIZE(dst) );
7379 ok( dstlen == -3, "%d: wrong len %d\n", i, dstlen );
7380 ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%d: wrong error %ld\n", i, GetLastError() );
7381 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, NULL, 0 );
7382 ok( dstlen == (i < 2 ? 21 : 64), "%d: wrong len %d\n", i, dstlen );
7383 SetLastError( 0xdeadbeef );
7384 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, dst, ARRAY_SIZE(dst) );
7385 ok( dstlen == -4, "%d: wrong len %d\n", i, dstlen );
7386 ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%d: wrong error %ld\n", i, GetLastError() );
7387 SetLastError( 0xdeadbeef );
7388 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, dst, 2 );
7389 todo_wine
7390 ok( dstlen == (i < 2 ? -18 : -74), "%d: wrong len %d\n", i, dstlen );
7391 todo_wine_if (i == 0 || i == 2)
7392 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "%d: wrong error %ld\n", i, GetLastError() );
7393 if (pRtlNormalizeString)
7395 dstlen = 0;
7396 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, NULL, &dstlen );
7397 ok( !status, "%d: failed %lx\n", i, status );
7398 ok( dstlen == (i < 2 ? 15 : 64), "%d: wrong len %d\n", i, dstlen );
7399 dstlen = ARRAY_SIZE(dst);
7400 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
7401 ok( status == STATUS_NO_UNICODE_TRANSLATION, "%d: failed %lx\n", i, status );
7402 ok( dstlen == 3, "%d: wrong len %d\n", i, dstlen );
7403 dstlen = 1;
7404 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
7405 todo_wine_if( i == 0 || i == 2)
7406 ok( status == STATUS_BUFFER_TOO_SMALL, "%d: failed %lx\n", i, status );
7407 todo_wine_if( i != 3)
7408 ok( dstlen == (i < 2 ? 14 : 73), "%d: wrong len %d\n", i, dstlen );
7409 dstlen = 2;
7410 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
7411 ok( status == STATUS_NO_UNICODE_TRANSLATION, "%d: failed %lx\n", i, status );
7412 ok( dstlen == 3, "%d: wrong len %d\n", i, dstlen );
7416 /* optionally run the full test file from Unicode.org
7417 * available at http://www.unicode.org/Public/UCD/latest/ucd/NormalizationTest.txt
7419 if ((f = fopen( "NormalizationTest.txt", "r" )))
7421 char *p, buffer[1024];
7422 WCHAR str[3], srcW[32], dstW[32], resW[4][32];
7423 int line = 0, part = 0, ch;
7424 char tested[0x110000 / 8];
7426 while (fgets( buffer, sizeof(buffer), f ))
7428 line++;
7429 if ((p = strchr( buffer, '#' ))) *p = 0;
7430 if (!strncmp( buffer, "@Part", 5 ))
7432 part = atoi( buffer + 5 );
7433 continue;
7435 if (!(p = strtok( buffer, ";" ))) continue;
7436 read_str( p, srcW );
7437 for (i = 0; i < 4; i++)
7439 p = strtok( NULL, ";" );
7440 read_str( p, &resW[i][0] );
7442 if (part == 1)
7444 ch = srcW[0];
7445 if (ch >= 0xd800 && ch <= 0xdbff)
7446 ch = 0x10000 + ((srcW[0] & 0x3ff) << 10) + (srcW[1] & 0x3ff);
7447 tested[ch / 8] |= 1 << (ch % 8);
7449 for (i = 0; i < 4; i++)
7451 memset( dstW, 0xcc, sizeof(dstW) );
7452 dstlen = pNormalizeString( norm_forms[i], srcW, -1, dstW, ARRAY_SIZE(dstW) );
7453 ok( !wcscmp( dstW, resW[i] ),
7454 "line %u form %u: wrong result %s for %s expected %s\n", line, i,
7455 wine_dbgstr_w( dstW ), wine_dbgstr_w( srcW ), wine_dbgstr_w( resW[i] ));
7457 ret = FALSE;
7458 status = pRtlIsNormalizedString( norm_forms[i], srcW, -1, &ret );
7459 ok( !status, "line %u form %u: RtlIsNormalizedString failed %lx\n", line, i, status );
7460 if (!wcscmp( srcW, dstW ))
7461 ok( ret, "line %u form %u: source not normalized %s\n", line, i, wine_dbgstr_w(srcW) );
7462 else
7463 ok( !ret, "line %u form %u: source normalized %s\n", line, i, wine_dbgstr_w(srcW) );
7464 ret = FALSE;
7465 status = pRtlIsNormalizedString( norm_forms[i], dstW, -1, &ret );
7466 ok( !status, "line %u form %u: RtlIsNormalizedString failed %lx\n", line, i, status );
7467 ok( ret, "line %u form %u: dest not normalized %s\n", line, i, wine_dbgstr_w(dstW) );
7469 for (j = 0; j < 4; j++)
7471 int expect = i | (j & 2);
7472 memset( dstW, 0xcc, sizeof(dstW) );
7473 dstlen = pNormalizeString( norm_forms[i], resW[j], -1, dstW, ARRAY_SIZE(dstW) );
7474 ok( !wcscmp( dstW, resW[expect] ),
7475 "line %u form %u res %u: wrong result %s for %s expected %s\n", line, i, j,
7476 wine_dbgstr_w( dstW ), wine_dbgstr_w( resW[j] ), wine_dbgstr_w( resW[expect] ));
7480 fclose( f );
7482 /* test chars that are not in the @Part1 list */
7483 for (ch = 0; ch < 0x110000; ch++)
7485 if (tested[ch / 8] & (1 << (ch % 8))) continue;
7486 str[put_utf16( str, ch )] = 0;
7487 for (i = 0; i < 4; i++)
7489 memset( dstW, 0xcc, sizeof(dstW) );
7490 SetLastError( 0xdeadbeef );
7491 dstlen = pNormalizeString( norm_forms[i], str, -1, dstW, ARRAY_SIZE(dstW) );
7492 if ((ch >= 0xd800 && ch <= 0xdfff) ||
7493 (ch >= 0xfdd0 && ch <= 0xfdef) ||
7494 ((ch & 0xffff) >= 0xfffe))
7496 ok( dstlen <= 0, "char %04x form %u: wrong result %d %s expected error\n",
7497 ch, i, dstlen, wine_dbgstr_w( dstW ));
7498 ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION,
7499 "char %04x form %u: error %lu\n", str[0], i, GetLastError() );
7500 status = pRtlIsNormalizedString( norm_forms[i], str, -1, &ret );
7501 ok( status == STATUS_NO_UNICODE_TRANSLATION,
7502 "char %04x form %u: failed %lx\n", ch, i, status );
7504 else
7506 ok( !wcscmp( dstW, str ),
7507 "char %04x form %u: wrong result %s expected unchanged\n",
7508 ch, i, wine_dbgstr_w( dstW ));
7509 ret = FALSE;
7510 status = pRtlIsNormalizedString( norm_forms[i], str, -1, &ret );
7511 ok( !status, "char %04x form %u: failed %lx\n", ch, i, status );
7512 ok( ret, "char %04x form %u: not normalized\n", ch, i );
7519 static void test_SpecialCasing(void)
7521 int ret, i, len;
7522 UINT val = 0, exp;
7523 WCHAR src[8], buffer[8];
7524 static const struct test {
7525 const WCHAR *lang;
7526 DWORD flags;
7527 UINT ch;
7528 UINT exp; /* 0 if self */
7529 UINT exp_ling; /* 0 if exp */
7530 BOOL broken;
7531 } tests[] = {
7532 {L"de-DE", LCMAP_UPPERCASE, 0x00DF}, /* LATIN SMALL LETTER SHARP S */
7534 {L"en-US", LCMAP_UPPERCASE, 0xFB00}, /* LATIN SMALL LIGATURE FF */
7535 {L"en-US", LCMAP_UPPERCASE, 0xFB01}, /* LATIN SMALL LIGATURE FI */
7536 {L"en-US", LCMAP_UPPERCASE, 0xFB02}, /* LATIN SMALL LIGATURE FL */
7537 {L"en-US", LCMAP_UPPERCASE, 0xFB03}, /* LATIN SMALL LIGATURE FFI */
7538 {L"en-US", LCMAP_UPPERCASE, 0xFB04}, /* LATIN SMALL LIGATURE FFL */
7539 {L"en-US", LCMAP_UPPERCASE, 0xFB05}, /* LATIN SMALL LIGATURE LONG S T */
7540 {L"en-US", LCMAP_UPPERCASE, 0xFB06}, /* LATIN SMALL LIGATURE ST */
7542 {L"hy-AM", LCMAP_UPPERCASE, 0x0587}, /* ARMENIAN SMALL LIGATURE ECH YIWN */
7543 {L"hy-AM", LCMAP_UPPERCASE, 0xFB13}, /* ARMENIAN SMALL LIGATURE MEN NOW */
7544 {L"hy-AM", LCMAP_UPPERCASE, 0xFB14}, /* ARMENIAN SMALL LIGATURE MEN ECH */
7545 {L"hy-AM", LCMAP_UPPERCASE, 0xFB15}, /* ARMENIAN SMALL LIGATURE MEN INI */
7546 {L"hy-AM", LCMAP_UPPERCASE, 0xFB16}, /* ARMENIAN SMALL LIGATURE VEW NOW */
7547 {L"hy-AM", LCMAP_UPPERCASE, 0xFB17}, /* ARMENIAN SMALL LIGATURE MEN XEH */
7549 {L"en-US", LCMAP_UPPERCASE, 0x0149}, /* LATIN SMALL LETTER N PRECEDED BY APOSTROPHE */
7550 {L"el-GR", LCMAP_UPPERCASE, 0x0390,0,0,TRUE /*win7*/ }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
7551 {L"el-GR", LCMAP_UPPERCASE, 0x03B0,0,0,TRUE /*win7*/ }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
7552 {L"en-US", LCMAP_UPPERCASE, 0x01F0}, /* LATIN SMALL LETTER J WITH CARON */
7553 {L"en-US", LCMAP_UPPERCASE, 0x1E96}, /* LATIN SMALL LETTER H WITH LINE BELOW */
7554 {L"en-US", LCMAP_UPPERCASE, 0x1E97}, /* LATIN SMALL LETTER T WITH DIAERESIS */
7555 {L"en-US", LCMAP_UPPERCASE, 0x1E98}, /* LATIN SMALL LETTER W WITH RING ABOVE */
7556 {L"en-US", LCMAP_UPPERCASE, 0x1E99}, /* LATIN SMALL LETTER Y WITH RING ABOVE */
7557 {L"en-US", LCMAP_UPPERCASE, 0x1E9A}, /* LATIN SMALL LETTER A WITH RIGHT HALF RING */
7558 {L"el-GR", LCMAP_UPPERCASE, 0x1F50}, /* GREEK SMALL LETTER UPSILON WITH PSILI */
7559 {L"el-GR", LCMAP_UPPERCASE, 0x1F52}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA */
7560 {L"el-GR", LCMAP_UPPERCASE, 0x1F54}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA */
7561 {L"el-GR", LCMAP_UPPERCASE, 0x1F56}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI */
7562 {L"el-GR", LCMAP_UPPERCASE, 0x1FB6}, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI */
7563 {L"el-GR", LCMAP_UPPERCASE, 0x1FC6}, /* GREEK SMALL LETTER ETA WITH PERISPOMENI */
7564 {L"el-GR", LCMAP_UPPERCASE, 0x1FD2}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA */
7565 {L"el-GR", LCMAP_UPPERCASE, 0x1FD3}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA */
7566 {L"el-GR", LCMAP_UPPERCASE, 0x1FD6}, /* GREEK SMALL LETTER IOTA WITH PERISPOMENI */
7567 {L"el-GR", LCMAP_UPPERCASE, 0x1FD7}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI */
7568 {L"el-GR", LCMAP_UPPERCASE, 0x1FE2}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA */
7569 {L"el-GR", LCMAP_UPPERCASE, 0x1FE3}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA */
7570 {L"el-GR", LCMAP_UPPERCASE, 0x1FE4}, /* GREEK SMALL LETTER RHO WITH PSILI */
7571 {L"el-GR", LCMAP_UPPERCASE, 0x1FE6}, /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI */
7572 {L"el-GR", LCMAP_UPPERCASE, 0x1FE7}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI */
7573 {L"el-GR", LCMAP_UPPERCASE, 0x1FF6}, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI */
7575 {L"el-GR", LCMAP_UPPERCASE, 0x1F80,0x1F88}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI */
7576 {L"el-GR", LCMAP_UPPERCASE, 0x1F81,0x1F89}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI */
7577 {L"el-GR", LCMAP_UPPERCASE, 0x1F82,0x1F8A}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
7578 {L"el-GR", LCMAP_UPPERCASE, 0x1F83,0x1F8B}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
7579 {L"el-GR", LCMAP_UPPERCASE, 0x1F84,0x1F8C}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
7580 {L"el-GR", LCMAP_UPPERCASE, 0x1F85,0x1F8D}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
7581 {L"el-GR", LCMAP_UPPERCASE, 0x1F86,0x1F8E}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
7582 {L"el-GR", LCMAP_UPPERCASE, 0x1F87,0x1F8F}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
7584 {L"el-GR", LCMAP_LOWERCASE, 0x1F88,0x1F80}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */
7585 {L"el-GR", LCMAP_LOWERCASE, 0x1F89,0x1F81}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */
7586 {L"el-GR", LCMAP_LOWERCASE, 0x1F8A,0x1F82}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
7587 {L"el-GR", LCMAP_LOWERCASE, 0x1F8B,0x1F83}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
7588 {L"el-GR", LCMAP_LOWERCASE, 0x1F8C,0x1F84}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
7589 {L"el-GR", LCMAP_LOWERCASE, 0x1F8D,0x1F85}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
7590 {L"el-GR", LCMAP_LOWERCASE, 0x1F8E,0x1F86}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
7591 {L"el-GR", LCMAP_LOWERCASE, 0x1F8F,0x1F87}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
7593 {L"el-GR", LCMAP_UPPERCASE, 0x1F90,0x1F98}, /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI */
7594 {L"el-GR", LCMAP_UPPERCASE, 0x1F91,0x1F99}, /* GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI */
7595 {L"el-GR", LCMAP_UPPERCASE, 0x1F92,0x1F9A}, /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
7596 {L"el-GR", LCMAP_UPPERCASE, 0x1F93,0x1F9B}, /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
7597 {L"el-GR", LCMAP_UPPERCASE, 0x1F94,0x1F9C}, /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
7598 {L"el-GR", LCMAP_UPPERCASE, 0x1F95,0x1F9D}, /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
7599 {L"el-GR", LCMAP_UPPERCASE, 0x1F96,0x1F9E}, /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
7600 {L"el-GR", LCMAP_UPPERCASE, 0x1F97,0x1F9F}, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
7602 {L"el-GR", LCMAP_LOWERCASE, 0x1FA8,0x1FA0}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */
7603 {L"el-GR", LCMAP_LOWERCASE, 0x1FA9,0x1FA1}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */
7604 {L"el-GR", LCMAP_LOWERCASE, 0x1FAA,0x1FA2}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
7605 {L"el-GR", LCMAP_LOWERCASE, 0x1FAB,0x1FA3}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
7606 {L"el-GR", LCMAP_LOWERCASE, 0x1FAC,0x1FA4}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
7607 {L"el-GR", LCMAP_LOWERCASE, 0x1FAD,0x1FA5}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
7608 {L"el-GR", LCMAP_LOWERCASE, 0x1FAE,0x1FA6}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
7609 {L"el-GR", LCMAP_LOWERCASE, 0x1FAF,0x1FA7}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
7611 {L"el-GR", LCMAP_UPPERCASE, 0x1FB3,0x1FBC}, /* GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI */
7612 {L"el-GR", LCMAP_LOWERCASE, 0x1FBC,0x1FB3}, /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */
7613 {L"el-GR", LCMAP_UPPERCASE, 0x1FC3,0x1FCC}, /* GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI */
7614 {L"el-GR", LCMAP_LOWERCASE, 0x1FCC,0x1FC3}, /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */
7615 {L"el-GR", LCMAP_UPPERCASE, 0x1FF3,0x1FFC}, /* GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI */
7616 {L"el-GR", LCMAP_LOWERCASE, 0x1FFC,0x1FF3}, /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */
7618 {L"el-GR", LCMAP_UPPERCASE, 0x1FB2}, /* GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI */
7619 {L"el-GR", LCMAP_UPPERCASE, 0x1FB4}, /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI */
7620 {L"el-GR", LCMAP_UPPERCASE, 0x1FC2}, /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI */
7621 {L"el-GR", LCMAP_UPPERCASE, 0x1FC4}, /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI */
7622 {L"el-GR", LCMAP_UPPERCASE, 0x1FF2}, /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI */
7623 {L"el-GR", LCMAP_UPPERCASE, 0x1FF4}, /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI */
7625 {L"el-GR", LCMAP_UPPERCASE, 0x1FB7}, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI */
7626 {L"el-GR", LCMAP_UPPERCASE, 0x1FC7}, /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI */
7627 {L"el-GR", LCMAP_UPPERCASE, 0x1FF7}, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI */
7629 {L"el-GR", LCMAP_LOWERCASE, 0x03A3,0x03C3}, /* GREEK CAPITAL LETTER SIGMA */
7631 {L"lt-LT", LCMAP_LOWERCASE, 'J','j'}, /* LATIN CAPITAL LETTER J */
7632 {L"lt-LT", LCMAP_LOWERCASE, 0x012E,0x012F}, /* LATIN CAPITAL LETTER I WITH OGONEK */
7633 {L"lt-LT", LCMAP_LOWERCASE, 0x00CC,0x00EC}, /* LATIN CAPITAL LETTER I WITH GRAVE */
7634 {L"lt-LT", LCMAP_LOWERCASE, 0x00CD,0x00ED}, /* LATIN CAPITAL LETTER I WITH ACUTE */
7635 {L"lt-LT", LCMAP_LOWERCASE, 0x0128,0x0129}, /* LATIN CAPITAL LETTER I WITH TILDE */
7637 {L"en-US", LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
7638 {L"lt-LT", LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
7639 {L"tr-TR", LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
7640 {L"TR-TR", LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
7641 {L"az-Cyrl-az", LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE /*win7*/}, /* LATIN SMALL LETTER I */
7642 {L"az-Latn-az", LCMAP_UPPERCASE, 'i', 'I', 0x0130}, /* LATIN SMALL LETTER I */
7644 {L"en-US", LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
7645 {L"lt-LT", LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
7646 {L"tr-TR", LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
7647 {L"TR-TR", LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
7648 {L"az-Cyrl-az", LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE /*win7*/}, /* LATIN CAPITAL LETTER I */
7649 {L"az-Latn-az", LCMAP_LOWERCASE, 'I', 'i', 0x0131}, /* LATIN CAPITAL LETTER I */
7651 {L"en-US", LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
7652 {L"tr-TR", LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
7653 {L"TR-TR", LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
7654 {L"az-Cyrl-az", LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
7655 {L"az-Latn-az", LCMAP_LOWERCASE, 0x0130,0,'i'}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
7657 {L"en-US", LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
7658 {L"tr-TR", LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
7659 {L"TR-TR", LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
7660 {L"az-Cyrl-az", LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
7661 {L"az-Latn-az", LCMAP_UPPERCASE, 0x0131,0,'I'}, /* LATIN SMALL LETTER DOTLESS I */
7663 {L"en-US", LCMAP_LOWERCASE, 0x10418,0x10440,0,TRUE /*win7*/}, /* DESERET CAPITAL LETTER GAY */
7664 {L"en-US", LCMAP_UPPERCASE, 0x10431,0x10409,0,TRUE /*win7*/}, /* DESERET SMALL LETTER SHORT AH */
7667 if (!pLCMapStringEx)
7669 win_skip("LCMapStringEx not available\n");
7670 return;
7673 for (i = 0; i < ARRAY_SIZE(tests); i++)
7675 memset(buffer, 0, sizeof(buffer));
7676 len = put_utf16( src, tests[i].ch );
7677 ret = pLCMapStringEx(tests[i].lang, tests[i].flags,
7678 src, len, buffer, ARRAY_SIZE(buffer), NULL, NULL, 0);
7679 len = get_utf16( buffer, ret, &val );
7680 ok(ret == len, "got %d for %04x for %s\n", ret, tests[i].ch, wine_dbgstr_w(tests[i].lang));
7681 exp = tests[i].exp ? tests[i].exp : tests[i].ch;
7682 ok(val == exp || broken(tests[i].broken),
7683 "expected %04x, got %04x for %04x for %s\n",
7684 exp, val, tests[i].ch, wine_dbgstr_w(tests[i].lang));
7686 memset(buffer, 0, sizeof(buffer));
7687 len = put_utf16( src, tests[i].ch );
7688 ret = pLCMapStringEx(tests[i].lang, tests[i].flags|LCMAP_LINGUISTIC_CASING,
7689 src, len, buffer, ARRAY_SIZE(buffer), NULL, NULL, 0);
7690 len = get_utf16( buffer, ret, &val );
7691 ok(ret == len, "got %d for %04x for %s\n", ret, tests[i].ch, wine_dbgstr_w(tests[i].lang));
7692 exp = tests[i].exp_ling ? tests[i].exp_ling : exp;
7693 ok(val == exp || broken(tests[i].broken),
7694 "expected %04x, got %04x for %04x for %s\n",
7695 exp, val, tests[i].ch, wine_dbgstr_w(tests[i].lang));
7699 static void test_NLSVersion(void)
7701 static const GUID guid_null = { 0 };
7702 static const GUID guid_def = { 0x000000001, 0x57ee, 0x1e5c, {0x00,0xb4,0xd0,0x00,0x0b,0xb1,0xe1,0x1e}};
7703 static const GUID guid_fr = { 0x000000003, 0x57ee, 0x1e5c, {0x00,0xb4,0xd0,0x00,0x0b,0xb1,0xe1,0x1e}};
7704 static const GUID guid_ja = { 0x000000046, 0x57ee, 0x1e5c, {0x00,0xb4,0xd0,0x00,0x0b,0xb1,0xe1,0x1e}};
7705 BOOL ret;
7706 NLSVERSIONINFOEX info;
7708 if (!pGetNLSVersion)
7710 win_skip( "GetNLSVersion not available\n" );
7711 return;
7714 SetLastError( 0xdeadbeef );
7715 memset( &info, 0xcc, sizeof(info) );
7716 info.dwNLSVersionInfoSize = offsetof( NLSVERSIONINFO, dwEffectiveId );
7717 ret = pGetNLSVersion( COMPARE_STRING, MAKELANGID( LANG_FRENCH, SUBLANG_FRENCH_CANADIAN ),
7718 (NLSVERSIONINFO *)&info );
7719 ok( ret, "GetNLSVersion failed err %lu\n", GetLastError() );
7721 SetLastError( 0xdeadbeef );
7722 memset( &info, 0xcc, sizeof(info) );
7723 info.dwNLSVersionInfoSize = sizeof(info);
7724 ret = pGetNLSVersion( COMPARE_STRING, MAKELANGID( LANG_FRENCH, SUBLANG_FRENCH_CANADIAN ),
7725 (NLSVERSIONINFO *)&info );
7726 ok( ret || GetLastError() == ERROR_INSUFFICIENT_BUFFER /* < Vista */,
7727 "GetNLSVersion failed err %lu\n", GetLastError() );
7728 if (ret)
7730 ok( info.dwEffectiveId == MAKELANGID( LANG_FRENCH, SUBLANG_FRENCH_CANADIAN ),
7731 "wrong id %lx\n", info.dwEffectiveId );
7732 ok( IsEqualIID( &info.guidCustomVersion, &guid_fr ) ||
7733 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
7734 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
7737 SetLastError( 0xdeadbeef );
7738 info.dwNLSVersionInfoSize = 8;
7739 ret = pGetNLSVersion( COMPARE_STRING, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
7740 ok( !ret, "GetNLSVersion succeeded\n" );
7741 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %lu\n", GetLastError() );
7743 SetLastError( 0xdeadbeef );
7744 info.dwNLSVersionInfoSize = sizeof(info);
7745 ret = pGetNLSVersion( 2, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
7746 ok( !ret, "GetNLSVersion succeeded\n" );
7747 ok( GetLastError() == ERROR_INVALID_FLAGS ||
7748 broken( GetLastError() == ERROR_INSUFFICIENT_BUFFER ), /* win2003 */
7749 "wrong error %lu\n", GetLastError() );
7751 SetLastError( 0xdeadbeef );
7752 info.dwNLSVersionInfoSize = sizeof(info);
7753 ret = pGetNLSVersion( COMPARE_STRING, 0xdeadbeef, (NLSVERSIONINFO *)&info );
7754 ok( !ret, "GetNLSVersion succeeded\n" );
7755 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
7757 if (pGetNLSVersionEx)
7759 SetLastError( 0xdeadbeef );
7760 memset( &info, 0xcc, sizeof(info) );
7761 info.dwNLSVersionInfoSize = sizeof(info);
7762 ret = pGetNLSVersionEx( COMPARE_STRING, L"ja-JP", &info );
7763 ok( ret, "GetNLSVersionEx failed err %lu\n", GetLastError() );
7764 ok( info.dwEffectiveId == MAKELANGID( LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN ),
7765 "wrong id %lx\n", info.dwEffectiveId );
7766 ok( IsEqualIID( &info.guidCustomVersion, &guid_ja ) ||
7767 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
7768 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
7769 trace( "version %08lx %08lx %08lx %s\n", info.dwNLSVersion, info.dwDefinedVersion, info.dwEffectiveId,
7770 debugstr_guid(&info.guidCustomVersion) );
7772 SetLastError( 0xdeadbeef );
7773 memset( &info, 0xcc, sizeof(info) );
7774 info.dwNLSVersionInfoSize = sizeof(info);
7775 ret = pGetNLSVersionEx( COMPARE_STRING, L"fr", &info );
7776 ok( !ret == !pIsValidLocaleName(L"fr"), "GetNLSVersionEx doesn't match IsValidLocaleName\n" );
7777 if (ret)
7779 ok( info.dwEffectiveId == MAKELANGID( LANG_FRENCH, SUBLANG_DEFAULT ),
7780 "wrong id %lx\n", info.dwEffectiveId );
7781 ok( IsEqualIID( &info.guidCustomVersion, &guid_fr ) ||
7782 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
7783 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
7786 SetLastError( 0xdeadbeef );
7787 info.dwNLSVersionInfoSize = sizeof(info) - 1;
7788 ret = pGetNLSVersionEx( COMPARE_STRING, L"en-US", &info );
7789 ok( !ret, "GetNLSVersionEx succeeded\n" );
7790 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %lu\n", GetLastError() );
7792 SetLastError( 0xdeadbeef );
7793 memset( &info, 0xcc, sizeof(info) );
7794 info.dwNLSVersionInfoSize = offsetof( NLSVERSIONINFO, dwEffectiveId );
7795 ret = pGetNLSVersionEx( COMPARE_STRING, L"en-US", &info );
7796 ok( ret, "GetNLSVersionEx failed err %lu\n", GetLastError() );
7797 ok( info.dwEffectiveId == 0xcccccccc, "wrong id %lx\n", info.dwEffectiveId );
7799 SetLastError( 0xdeadbeef );
7800 info.dwNLSVersionInfoSize = sizeof(info);
7801 ret = pGetNLSVersionEx( 2, L"en-US", &info );
7802 ok( !ret, "GetNLSVersionEx succeeded\n" );
7803 ok( GetLastError() == ERROR_INVALID_FLAGS, "wrong error %lu\n", GetLastError() );
7805 SetLastError( 0xdeadbeef );
7806 info.dwNLSVersionInfoSize = sizeof(info);
7807 ret = pGetNLSVersionEx( COMPARE_STRING, L"foobar", &info );
7808 ok( !ret, "GetNLSVersionEx succeeded\n" );
7809 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
7811 SetLastError( 0xdeadbeef );
7812 memset( &info, 0xcc, sizeof(info) );
7813 info.dwNLSVersionInfoSize = sizeof(info);
7814 ret = pGetNLSVersionEx( COMPARE_STRING, L"zz-XX", &info );
7815 if (!ret) ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
7816 ok( !ret == !pIsValidLocaleName(L"zz-XX"), "GetNLSVersionEx doesn't match IsValidLocaleName\n" );
7817 if (ret)
7819 ok( info.dwEffectiveId == LOCALE_CUSTOM_UNSPECIFIED, "wrong id %lx\n", info.dwEffectiveId );
7820 ok( IsEqualIID( &info.guidCustomVersion, &guid_def ),
7821 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
7824 SetLastError( 0xdeadbeef );
7825 memset( &info, 0xcc, sizeof(info) );
7826 info.dwNLSVersionInfoSize = sizeof(info);
7827 ret = pGetNLSVersionEx( COMPARE_STRING, LOCALE_NAME_INVARIANT, &info );
7828 ok( ret, "GetNLSVersionEx failed err %lu\n", GetLastError() );
7829 if (ret)
7831 ok( info.dwEffectiveId == LOCALE_INVARIANT, "wrong id %lx\n", info.dwEffectiveId );
7832 ok( IsEqualIID( &info.guidCustomVersion, &guid_def ) ||
7833 broken( IsEqualIID( &info.guidCustomVersion, &guid_null )), /* <= win7 */
7834 "wrong guid %s\n", debugstr_guid(&info.guidCustomVersion) );
7836 else ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
7838 else win_skip( "GetNLSVersionEx not available\n" );
7840 if (pIsValidNLSVersion)
7842 info.dwNLSVersionInfoSize = sizeof(info);
7843 pGetNLSVersion( COMPARE_STRING, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
7845 SetLastError( 0xdeadbeef );
7846 info.dwNLSVersionInfoSize = sizeof(info);
7847 ret = pIsValidNLSVersion( COMPARE_STRING, L"ja-JP", &info );
7848 ok( ret, "IsValidNLSVersion failed err %lu\n", GetLastError() );
7849 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7851 SetLastError( 0xdeadbeef );
7852 info.dwNLSVersionInfoSize = offsetof( NLSVERSIONINFO, dwEffectiveId );
7853 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7854 ok( ret, "IsValidNLSVersion failed err %lu\n", GetLastError() );
7855 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7857 SetLastError( 0xdeadbeef );
7858 info.dwNLSVersionInfoSize = sizeof(info);
7859 ret = pIsValidNLSVersion( 2, L"en-US", &info );
7860 ok( !ret, "IsValidNLSVersion succeeded\n" );
7861 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
7863 SetLastError( 0xdeadbeef );
7864 info.dwNLSVersionInfoSize = sizeof(info);
7865 ret = pIsValidNLSVersion( COMPARE_STRING, L"foobar", &info );
7866 ok( !ret, "IsValidNLSVersion succeeded\n" );
7867 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError() );
7869 SetLastError( 0xdeadbeef );
7870 memset( &info, 0xcc, sizeof(info) );
7871 info.dwNLSVersionInfoSize = sizeof(info);
7872 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7873 ok( !ret, "IsValidNLSVersion succeeded\n" );
7874 ok( GetLastError() == ERROR_SUCCESS, "wrong error %lu\n", GetLastError() );
7876 info.dwNLSVersionInfoSize = sizeof(info);
7877 pGetNLSVersion( COMPARE_STRING, LOCALE_USER_DEFAULT, (NLSVERSIONINFO *)&info );
7878 info.dwNLSVersion++;
7879 SetLastError( 0xdeadbeef );
7880 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7881 ok( ret, "IsValidNLSVersion failed err %lu\n", GetLastError() );
7882 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7884 info.dwNLSVersion += 0x700; /* much higher ver -> surely invalid */
7885 SetLastError( 0xdeadbeef );
7886 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7887 ok( !ret, "IsValidNLSVersion succeeded\n" );
7888 ok( GetLastError() == 0, "wrong error %lu\n", GetLastError() );
7890 info.dwNLSVersion -= 2 * 0x700; /* much lower ver -> surely invalid */
7891 SetLastError( 0xdeadbeef );
7892 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7893 ok( !ret, "IsValidNLSVersion succeeded\n" );
7894 ok( GetLastError() == 0, "wrong error %lu\n", GetLastError() );
7896 info.dwNLSVersion += 0x700;
7897 info.dwDefinedVersion += 0x100;
7898 SetLastError( 0xdeadbeef );
7899 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7900 ok( ret, "IsValidNLSVersion failed err %lu\n", GetLastError() );
7901 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7903 info.dwDefinedVersion -= 0x100;
7904 info.guidCustomVersion.Data1 = 0x123;
7905 SetLastError( 0xdeadbeef );
7906 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7907 ok( !ret, "IsValidNLSVersion succeeded\n" );
7908 ok( GetLastError() == 0, "wrong error %lu\n", GetLastError() );
7910 info.guidCustomVersion = guid_null;
7911 SetLastError( 0xdeadbeef );
7912 ret = pIsValidNLSVersion( COMPARE_STRING, L"en-US", &info );
7913 ok( ret, "IsValidNLSVersion failed err %lu\n", GetLastError() );
7914 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7916 else win_skip( "IsValidNLSVersion not available\n" );
7918 if (pIsNLSDefinedString)
7920 SetLastError( 0xdeadbeef );
7921 info.dwNLSVersionInfoSize = sizeof(info);
7922 ret = pIsNLSDefinedString( COMPARE_STRING, 0, (NLSVERSIONINFO *)&info, L"A", 1 );
7923 if (ret)
7924 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7925 else
7926 ok( broken( GetLastError() == ERROR_INSUFFICIENT_BUFFER ), /* win7 */
7927 "wrong error %lu\n", GetLastError() );
7929 SetLastError( 0xdeadbeef );
7930 info.dwNLSVersionInfoSize = sizeof(info) + 1;
7931 ret = pIsNLSDefinedString( COMPARE_STRING, 0, (NLSVERSIONINFO *)&info, L"A", 1 );
7932 ok( !ret, "IsNLSDefinedString succeeded\n" );
7933 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %lu\n", GetLastError() );
7935 SetLastError( 0xdeadbeef );
7936 info.dwNLSVersionInfoSize = offsetof( NLSVERSIONINFO, dwEffectiveId );
7937 ret = pIsNLSDefinedString( COMPARE_STRING, 0, (NLSVERSIONINFO *)&info, L"A", 1 );
7938 ok( ret, "IsNLSDefinedString failed err %lu\n", GetLastError() );
7939 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7941 SetLastError( 0xdeadbeef );
7942 ret = pIsNLSDefinedString( 2, 0, (NLSVERSIONINFO *)&info, L"A", 1 );
7943 ok( !ret, "IsNLSDefinedString succeeded\n" );
7944 ok( GetLastError() == ERROR_INVALID_FLAGS, "wrong error %lu\n", GetLastError() );
7946 SetLastError( 0xdeadbeef );
7947 ret = pIsNLSDefinedString( COMPARE_STRING, 0, (NLSVERSIONINFO *)&info, L"ABC", -10 );
7948 ok( ret, "IsNLSDefinedString failed err %lu\n", GetLastError() );
7949 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7951 SetLastError( 0xdeadbeef );
7952 ret = pIsNLSDefinedString( COMPARE_STRING, 0, (NLSVERSIONINFO *)&info, L"ABC", -1 );
7953 ok( ret, "IsNLSDefinedString failed err %lu\n", GetLastError() );
7954 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7956 SetLastError( 0xdeadbeef );
7957 ret = pIsNLSDefinedString( COMPARE_STRING, 0, (NLSVERSIONINFO *)&info, L"\xd800", 1 );
7958 ok( !ret, "IsNLSDefinedString failed err %lu\n", GetLastError() );
7959 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7961 SetLastError( 0xdeadbeef );
7962 ret = pIsNLSDefinedString( COMPARE_STRING, 0, (NLSVERSIONINFO *)&info, L"\xd800", -20 );
7963 ok( !ret, "IsNLSDefinedString failed err %lu\n", GetLastError() );
7964 ok( GetLastError() == 0xdeadbeef, "wrong error %lu\n", GetLastError() );
7966 else win_skip( "IsNLSDefinedString not available\n" );
7969 static void test_locale_nls(void)
7971 NTSTATUS status;
7972 void *addr, *addr2;
7973 UINT *ptr;
7974 LCID lcid;
7975 LARGE_INTEGER size;
7977 if (!pNtInitializeNlsFiles || !pRtlGetLocaleFileMappingAddress)
7979 win_skip( "locale.nls functions not supported\n" );
7980 return;
7982 size.QuadPart = 0xdeadbeef;
7983 status = pNtInitializeNlsFiles( &addr, &lcid, &size );
7984 ok( !status, "NtInitializeNlsFiles failed %lx\n", status );
7985 trace( "locale %04lx size %I64x\n", lcid, size.QuadPart );
7986 ptr = addr;
7987 ok( size.QuadPart == 0xdeadbeef || size.QuadPart == ptr[4] || size.QuadPart == ptr[4] + 8,
7988 "wrong offset %x / %I64x\n", ptr[4], size.QuadPart );
7989 ptr = (UINT *)((char *)addr + ptr[4]);
7990 ok( ptr[0] == 8, "wrong offset %u\n", ptr[0] );
7991 ok( ptr[3] == 0x5344534e, "wrong magic %x\n", ptr[3] );
7993 status = pNtInitializeNlsFiles( &addr2, &lcid, &size );
7994 ok( !status, "NtInitializeNlsFiles failed %lx\n", status );
7995 ok( addr != addr2, "got same address %p\n", addr );
7996 ok( !memcmp( addr, addr2, ptr[4] ), "contents differ\n" );
7997 status = NtUnmapViewOfSection( GetCurrentProcess(), addr );
7998 ok( !status, "NtUnmapViewOfSection failed %lx\n", status );
7999 status = NtUnmapViewOfSection( GetCurrentProcess(), addr2 );
8000 ok( !status, "NtUnmapViewOfSection failed %lx\n", status );
8002 size.QuadPart = 0xdeadbeef;
8003 status = pRtlGetLocaleFileMappingAddress( &addr, &lcid, &size );
8004 ok( !status, "NtInitializeNlsFiles failed %lx\n", status );
8005 ptr = addr;
8006 ok( size.QuadPart == 0xdeadbeef || size.QuadPart == ptr[4] || size.QuadPart == ptr[4] + 8,
8007 "wrong offset %x / %I64x\n", ptr[4], size.QuadPart );
8008 ptr = (UINT *)((char *)addr + ptr[4]);
8009 ok( ptr[0] == 8, "wrong offset %u\n", ptr[0] );
8010 ok( ptr[3] == 0x5344534e, "wrong magic %x\n", ptr[3] );
8012 /* RtlGetLocaleFileMappingAddress caches the pointer */
8013 status = pRtlGetLocaleFileMappingAddress( &addr2, &lcid, &size );
8014 ok( !status, "NtInitializeNlsFiles failed %lx\n", status );
8015 ok( addr == addr2, "got different address %p / %p\n", addr, addr2 );
8018 static void test_geo_name(void)
8020 WCHAR reg_name[32], buf[32], set_name[32], nation[32], region[32];
8021 BOOL have_name = FALSE, have_region = FALSE, have_nation = FALSE;
8022 DWORD size, type, name_size;
8023 LSTATUS status;
8024 GEOID geoid;
8025 BOOL bret;
8026 HKEY key;
8027 int ret;
8029 if (!pSetUserGeoName || !pGetUserDefaultGeoName)
8031 win_skip("GetUserDefaultGeoName / SetUserGeoName is not available, skipping test.\n");
8032 return;
8035 status = RegOpenKeyExA(HKEY_CURRENT_USER, "Control Panel\\International\\Geo", 0, KEY_READ | KEY_WRITE, &key);
8036 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8038 size = sizeof(reg_name);
8039 if (!RegQueryValueExW(key, L"Name", NULL, &type, (BYTE *)reg_name, &size))
8040 have_name = TRUE;
8042 lstrcpyW(buf, L"QQ");
8043 RegSetValueExW(key, L"Name", 0, REG_SZ, (BYTE *)buf, (lstrlenW(buf) + 1) * sizeof(WCHAR));
8045 size = sizeof(reg_name);
8046 if ((ret = pGetUserDefaultGeoName(NULL, 0)) == 1)
8048 if (have_name)
8050 status = RegSetValueExW(key, L"Name", 0, REG_SZ, (BYTE *)reg_name, (lstrlenW(reg_name) + 1) * sizeof(*reg_name));
8051 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8053 else
8055 RegDeleteValueW(key, L"Name");
8057 win_skip("Geo names are not available, skipping test.\n");
8058 return;
8061 size = sizeof(nation);
8062 if (!RegQueryValueExW(key, L"Nation", NULL, &type, (BYTE *)nation, &size))
8063 have_nation = TRUE;
8064 size = sizeof(region);
8065 if (!RegQueryValueExW(key, L"Region", NULL, &type, (BYTE *)region, &size))
8066 have_region = TRUE;
8068 SetLastError(0xdeadbeef);
8069 ret = pGetUserDefaultGeoName(NULL, 0);
8070 ok((ret == 3 || ret == 4) && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8071 name_size = ret;
8073 SetLastError(0xdeadbeef);
8074 ret = pGetUserDefaultGeoName(buf, 0);
8075 ok(ret >= 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8077 SetLastError(0xdeadbeef);
8078 ret = pGetUserDefaultGeoName(buf, 2);
8079 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8081 SetLastError(0xdeadbeef);
8082 ret = pGetUserDefaultGeoName(NULL, 1);
8083 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8085 SetLastError(0xdeadbeef);
8086 ret = pGetUserDefaultGeoName(NULL, name_size);
8087 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8089 SetLastError(0xdeadbeef);
8090 ret = pGetUserDefaultGeoName(buf, name_size);
8091 ok(ret == name_size && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8092 ok(!lstrcmpW(buf, L"QQ"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8094 SetLastError(0xdeadbeef);
8095 bret = pSetUserGeoName(NULL);
8096 ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8098 lstrcpyW(set_name, L"QQ");
8099 SetLastError(0xdeadbeef);
8100 bret = pSetUserGeoName(set_name);
8101 ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8103 lstrcpyW(set_name, L"Xx");
8104 SetLastError(0xdeadbeef);
8105 bret = pSetUserGeoName(set_name);
8106 ok((bret && GetLastError() == 0xdeadbeef) || broken(bret && GetLastError() == 0),
8107 "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8109 SetLastError(0xdeadbeef);
8110 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8111 ok(ret == 4 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8112 ok(!lstrcmpW(buf, L"001"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8113 geoid = GetUserGeoID(GEOCLASS_REGION);
8114 ok(geoid == 39070, "Got unexpected geoid %lu.\n", geoid);
8115 size = sizeof(buf);
8116 status = RegQueryValueExW(key, L"Name", NULL, &type, (BYTE *)buf, &size);
8117 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8118 ok(type == REG_SZ, "Got unexpected type %#lx.\n", type);
8119 ok(!lstrcmpW(buf, L"001"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8121 lstrcpyW(set_name, L"ar");
8122 SetLastError(0xdeadbeef);
8123 bret = pSetUserGeoName(set_name);
8124 ok((bret && GetLastError() == 0xdeadbeef) || broken(bret && GetLastError() == 0),
8125 "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8126 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8127 ok((ret == 3 && GetLastError() == 0xdeadbeef) || broken(ret == 3 && GetLastError() == 0),
8128 "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8129 ok(!lstrcmpW(buf, L"AR"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8130 geoid = GetUserGeoID(GEOCLASS_NATION);
8131 ok(geoid == 11, "Got unexpected geoid %lu.\n", geoid);
8133 lstrcpyW(set_name, L"150");
8134 SetLastError(0xdeadbeef);
8135 bret = pSetUserGeoName(set_name);
8136 ok((bret && GetLastError() == 0xdeadbeef) || broken(bret && GetLastError() == 0),
8137 "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8138 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8139 ok((ret == 4 && GetLastError() == 0xdeadbeef) || broken(ret == 4 && GetLastError() == 0),
8140 "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8141 ok(!lstrcmpW(buf, L"150"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8142 geoid = GetUserGeoID(GEOCLASS_NATION);
8143 ok(geoid == 11, "Got unexpected geoid %lu.\n", geoid);
8145 lstrcpyW(set_name, L"150a");
8146 SetLastError(0xdeadbeef);
8147 bret = pSetUserGeoName(set_name);
8148 ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8150 bret = SetUserGeoID(21242);
8151 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8152 SetLastError(0xdeadbeef);
8153 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8154 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8155 ok(!lstrcmpW(buf, L"XX"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8157 bret = SetUserGeoID(42483);
8158 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8159 SetLastError(0xdeadbeef);
8160 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8161 ok(ret == 4 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8162 ok(!lstrcmpW(buf, L"011"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8164 bret = SetUserGeoID(333);
8165 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
8166 SetLastError(0xdeadbeef);
8167 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8168 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8169 ok(!lstrcmpW(buf, L"AN"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8171 RegDeleteValueW(key, L"Name");
8172 RegDeleteValueW(key, L"Region");
8173 lstrcpyW(buf, L"124");
8174 status = RegSetValueExW(key, L"Nation", 0, REG_SZ, (BYTE *)buf, (lstrlenW(buf) + 1) * sizeof(*buf));
8175 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8176 SetLastError(0xdeadbeef);
8177 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8178 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8179 ok(!lstrcmpW(buf, L"JM"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8181 lstrcpyW(buf, L"333");
8182 status = RegSetValueExW(key, L"Region", 0, REG_SZ, (BYTE *)buf, (lstrlenW(buf) + 1) * sizeof(*buf));
8183 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8184 SetLastError(0xdeadbeef);
8185 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8186 ok(ret == 3 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8187 ok(!lstrcmpW(buf, L"JM"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8189 RegDeleteValueW(key, L"Nation");
8190 SetLastError(0xdeadbeef);
8191 ret = pGetUserDefaultGeoName(buf, ARRAY_SIZE(buf));
8192 ok(ret == 4 && GetLastError() == 0xdeadbeef, "Got unexpected ret %u, GetLastError() %lu.\n", ret, GetLastError());
8193 ok(!lstrcmpW(buf, L"001"), "Got unexpected name %s.\n", wine_dbgstr_w(buf));
8195 /* Restore user geo data. */
8196 if (have_name)
8198 status = RegSetValueExW(key, L"Name", 0, REG_SZ, (BYTE *)reg_name, (lstrlenW(reg_name) + 1) * sizeof(*reg_name));
8199 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8201 else
8203 RegDeleteValueW(key, L"Name");
8205 if (have_nation)
8207 status = RegSetValueExW(key, L"Nation", 0, REG_SZ, (BYTE *)nation, (lstrlenW(nation) + 1) * sizeof(*nation));
8208 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8210 else
8212 RegDeleteValueW(key, L"Nation");
8214 if (have_region)
8216 status = RegSetValueExW(key, L"Region", 0, REG_SZ, (BYTE *)region, (lstrlenW(region) + 1) * sizeof(*region));
8217 ok(status == ERROR_SUCCESS, "Got unexpected status %#lx.\n", status);
8219 else
8221 RegDeleteValueW(key, L"Region");
8224 RegCloseKey(key);
8227 static const LCID locales_with_optional_calendars[] = {
8228 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT),
8229 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_LEBANON), SORT_DEFAULT),
8230 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_EGYPT), SORT_DEFAULT),
8231 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_ALGERIA), SORT_DEFAULT),
8232 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_BAHRAIN), SORT_DEFAULT),
8233 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_IRAQ), SORT_DEFAULT),
8234 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_JORDAN), SORT_DEFAULT),
8235 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_KUWAIT), SORT_DEFAULT),
8236 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_LIBYA), SORT_DEFAULT),
8237 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_MOROCCO), SORT_DEFAULT),
8238 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_OMAN), SORT_DEFAULT),
8239 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_QATAR), SORT_DEFAULT),
8240 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SYRIA), SORT_DEFAULT),
8241 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_TUNISIA), SORT_DEFAULT),
8242 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_UAE), SORT_DEFAULT),
8243 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_YEMEN), SORT_DEFAULT),
8244 MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL), SORT_DEFAULT),
8245 MAKELCID(MAKELANGID(LANG_DIVEHI, SUBLANG_DEFAULT), SORT_DEFAULT),
8246 MAKELCID(MAKELANGID(LANG_PERSIAN, SUBLANG_DEFAULT), SORT_DEFAULT),
8247 MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT),
8248 MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), SORT_DEFAULT),
8249 MAKELCID(MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN), SORT_DEFAULT),
8250 MAKELCID(MAKELANGID(LANG_THAI, SUBLANG_DEFAULT), SORT_DEFAULT),
8251 MAKELCID(MAKELANGID(LANG_URDU, SUBLANG_URDU_PAKISTAN), SORT_DEFAULT)
8254 static BOOL CALLBACK calinfo_procA(LPSTR calinfo)
8256 (void)calinfo;
8257 return TRUE;
8260 static void test_EnumCalendarInfoA(void)
8262 BOOL ret;
8263 INT i;
8265 ret = EnumCalendarInfoA( calinfo_procA, LOCALE_USER_DEFAULT, ENUM_ALL_CALENDARS,
8266 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8267 ok( ret, "EnumCalendarInfoA for user default locale failed: %lu\n", GetLastError() );
8269 for (i = 0; i < ARRAY_SIZE( locales_with_optional_calendars ); i++)
8271 LCID lcid = locales_with_optional_calendars[i];
8272 ret = EnumCalendarInfoA( calinfo_procA, lcid, ENUM_ALL_CALENDARS,
8273 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8274 ok( ret || broken( GetLastError() == ERROR_INVALID_FLAGS ) /* no locale */,
8275 "EnumCalendarInfoA for LCID %#06lx failed: %lu\n", lcid, GetLastError() );
8279 static BOOL CALLBACK calinfo_procW(LPWSTR calinfo)
8281 (void)calinfo;
8282 return TRUE;
8285 static void test_EnumCalendarInfoW(void)
8287 BOOL ret;
8288 INT i;
8290 ret = EnumCalendarInfoW( calinfo_procW, LOCALE_USER_DEFAULT, ENUM_ALL_CALENDARS,
8291 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8292 ok( ret, "EnumCalendarInfoW for user default locale failed: %lu\n", GetLastError() );
8294 for (i = 0; i < ARRAY_SIZE( locales_with_optional_calendars ); i++)
8296 LCID lcid = locales_with_optional_calendars[i];
8297 ret = EnumCalendarInfoW( calinfo_procW, lcid, ENUM_ALL_CALENDARS,
8298 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8299 ok( ret || broken( GetLastError() == ERROR_INVALID_FLAGS ) /* no locale */,
8300 "EnumCalendarInfoW for LCID %#06lx failed: %lu\n", lcid, GetLastError() );
8304 static BOOL CALLBACK calinfoex_procA(LPSTR calinfo, LCID calid)
8306 (void)calinfo;
8307 (void)calid;
8308 return TRUE;
8311 static void test_EnumCalendarInfoExA(void)
8313 BOOL ret;
8314 INT i;
8316 ret = EnumCalendarInfoExA( calinfoex_procA, LOCALE_USER_DEFAULT, ENUM_ALL_CALENDARS,
8317 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8318 ok( ret, "EnumCalendarInfoExA for user default locale failed: %lu\n", GetLastError() );
8320 for (i = 0; i < ARRAY_SIZE( locales_with_optional_calendars ); i++)
8322 LCID lcid = locales_with_optional_calendars[i];
8323 ret = EnumCalendarInfoExA( calinfoex_procA, lcid, ENUM_ALL_CALENDARS,
8324 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8325 ok( ret || broken( GetLastError() == ERROR_INVALID_FLAGS ) /* no locale */,
8326 "EnumCalendarInfoExA for LCID %#06lx failed: %lu\n", lcid, GetLastError() );
8330 static BOOL CALLBACK calinfoex_procW(LPWSTR calinfo, LCID calid)
8332 (void)calinfo;
8333 (void)calid;
8334 return TRUE;
8337 static void test_EnumCalendarInfoExW(void)
8339 BOOL ret;
8340 INT i;
8342 ret = EnumCalendarInfoExW( calinfoex_procW, LOCALE_USER_DEFAULT, ENUM_ALL_CALENDARS,
8343 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8344 ok( ret, "EnumCalendarInfoExW for user default locale failed: %lu\n", GetLastError() );
8346 for (i = 0; i < ARRAY_SIZE( locales_with_optional_calendars ); i++)
8348 LCID lcid = locales_with_optional_calendars[i];
8349 ret = EnumCalendarInfoExW( calinfoex_procW, lcid, ENUM_ALL_CALENDARS,
8350 CAL_RETURN_NUMBER | CAL_ICALINTVALUE );
8351 ok( ret || broken( GetLastError() == ERROR_INVALID_FLAGS ) /* no locale */,
8352 "EnumCalendarInfoExW for LCID %#06lx failed: %lu\n", lcid, GetLastError() );
8356 /* Generate sort keys for a list of Unicode code points.
8357 * Possible source files:
8358 * The Unicode collation test suite: https://www.unicode.org/Public/UCA/latest/CollationTest.zip
8359 * The list of supported char compressions: winedump nls/sortdefault.nls | grep \\-\>
8361 static void dump_sortkeys( char *argv[] )
8363 WCHAR data[128];
8364 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
8365 BYTE key[256];
8366 unsigned int i, val, pos, res, flags = 0;
8367 char *p, *end, buffer[1024];
8368 FILE *f = fopen( argv[1], "r" );
8370 locale[0] = 0;
8371 if (argv[2])
8373 MultiByteToWideChar( CP_ACP, 0, argv[2], -1, locale, LOCALE_NAME_MAX_LENGTH );
8374 if (argv[3]) flags = strtoul( argv[3], NULL, 0 );
8377 if (!f)
8379 fprintf( stderr, "cannot open %s\n", argv[1] );
8380 return;
8382 while (fgets( buffer, sizeof(buffer), f ))
8384 if (buffer[0] && buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = 0;
8385 p = buffer;
8386 while (*p == ' ' || *p == '\t') p++;
8387 if (*p == '#') continue;
8388 pos = 0;
8389 while (*p && *p != ';' && *p != '-')
8391 val = strtoul( p, &end, 16 );
8392 if (end == p) break;
8393 if (val >= 0x10000)
8395 data[pos++] = 0xd800 | (val >> 10);
8396 data[pos++] = 0xdc00 | (val & 0x3ff);
8398 else data[pos++] = val;
8399 p = end;
8400 while (*p == ' ' || *p == '\t') p++;
8402 *p = 0;
8403 res = LCMapStringEx( locale, flags | LCMAP_SORTKEY, data, pos,
8404 (WCHAR *)key, sizeof(key), NULL, NULL, 0 );
8405 printf( "%s:", buffer );
8406 for (i = 0; i < res; i++) printf( " %02x", key[i] );
8407 printf( "\n" );
8409 fclose( f );
8412 static BOOL CALLBACK EnumDateFormatsExEx_proc(LPWSTR date_format_string, CALID calendar_id, LPARAM lp)
8414 return TRUE;
8417 static void test_EnumDateFormatsExEx(void)
8419 DWORD error;
8420 BOOL ret;
8422 /* Invalid locale name */
8423 ret = EnumDateFormatsExEx(EnumDateFormatsExEx_proc, L"deadbeef", DATE_SHORTDATE, 0);
8424 error = GetLastError();
8425 ok(!ret, "EnumDateFormatsExEx succeeded.\n");
8426 ok(error == ERROR_INVALID_PARAMETER, "Got unexpected error %#lx.\n", error);
8428 /* yi-Hebr is missing on versions < Win10 */
8429 /* Running the following tests will cause other tests that use LOCALE_CUSTOM_UNSPECIFIED to
8430 * report yi-Hebr instead the default locale on Windows 10. So run them at the end */
8431 ret = EnumDateFormatsExEx(EnumDateFormatsExEx_proc, L"yi-Hebr", DATE_SHORTDATE, 0);
8432 error = GetLastError();
8433 ok(ret || (!ret && error == ERROR_INVALID_PARAMETER), /* < Win10 */
8434 "EnumDateFormatsExEx failed, error %#lx.\n", error);
8436 ret = EnumDateFormatsExEx(EnumDateFormatsExEx_proc, L"yi-Hebr", DATE_LONGDATE, 0);
8437 error = GetLastError();
8438 ok(ret || (!ret && error == ERROR_INVALID_PARAMETER), /* < Win10 */
8439 "EnumDateFormatsExEx failed, error %#lx.\n", error);
8442 START_TEST(locale)
8444 char **argv;
8445 int argc = winetest_get_mainargs( &argv );
8447 InitFunctionPointers();
8449 if (argc >= 4)
8451 if (!strcmp( argv[2], "sortkeys" ))
8453 dump_sortkeys( argv + 2 );
8454 return;
8458 test_EnumTimeFormatsA();
8459 test_EnumTimeFormatsW();
8460 test_EnumDateFormatsA();
8461 test_GetLocaleInfoA();
8462 test_GetLocaleInfoW();
8463 test_GetLocaleInfoEx();
8464 test_GetTimeFormatA();
8465 test_GetTimeFormatEx();
8466 test_GetDateFormatA();
8467 test_GetDateFormatEx();
8468 test_GetDateFormatW();
8469 test_GetCurrencyFormatA(); /* Also tests the W version */
8470 test_GetNumberFormatA(); /* Also tests the W version */
8471 test_GetNumberFormatEx();
8472 test_CompareStringA();
8473 test_CompareStringW();
8474 test_CompareStringEx();
8475 test_LCMapStringA();
8476 test_LCMapStringW();
8477 test_LCMapStringEx();
8478 test_LocaleNameToLCID();
8479 test_FoldStringA();
8480 test_FoldStringW();
8481 test_ConvertDefaultLocale();
8482 test_EnumSystemLanguageGroupsA();
8483 test_EnumSystemLocalesEx();
8484 test_EnumLanguageGroupLocalesA();
8485 test_SetLocaleInfo();
8486 test_EnumUILanguageA();
8487 test_GetCPInfo();
8488 test_GetStringTypeW();
8489 test_Idn();
8490 test_IsValidLocaleName();
8491 test_ResolveLocaleName();
8492 test_CompareStringOrdinal();
8493 test_GetGeoInfo();
8494 test_EnumSystemGeoID();
8495 test_invariant();
8496 test_GetSystemPreferredUILanguages();
8497 test_GetThreadPreferredUILanguages();
8498 test_GetUserPreferredUILanguages();
8499 test_FindNLSStringEx();
8500 test_FindStringOrdinal();
8501 test_SetThreadUILanguage();
8502 test_NormalizeString();
8503 test_SpecialCasing();
8504 test_NLSVersion();
8505 test_locale_nls();
8506 test_geo_name();
8507 test_sorting();
8508 test_unicode_sorting();
8509 test_EnumCalendarInfoA();
8510 test_EnumCalendarInfoW();
8511 test_EnumCalendarInfoExA();
8512 test_EnumCalendarInfoExW();
8514 /* Run this test at the end */
8515 test_EnumDateFormatsExEx();