kernel32/tests: Use the available ARRAY_SIZE() macro.
[wine.git] / dlls / kernel32 / tests / locale.c
blob06a7aa75e71c3a886915f4d50602f3172c4b0793
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 #include <assert.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <stdio.h>
33 #include "wine/test.h"
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winnls.h"
39 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};
40 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};
41 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};
42 static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
43 static const WCHAR localeW[] = {'e','n','-','U','S',0};
44 static const WCHAR fooW[] = {'f','o','o',0};
45 static const WCHAR emptyW[] = {0};
46 static const WCHAR invalidW[] = {'i','n','v','a','l','i','d',0};
48 static inline unsigned int strlenW( const WCHAR *str )
50 const WCHAR *s = str;
51 while (*s) s++;
52 return s - str;
55 static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
57 if (n <= 0) return 0;
58 while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
59 return *str1 - *str2;
62 static inline WCHAR *strchrW( const WCHAR *str, WCHAR ch )
64 do { if (*str == ch) return (WCHAR *)str; } while (*str++);
65 return NULL;
68 static inline BOOL isdigitW( WCHAR wc )
70 WORD type;
71 GetStringTypeW( CT_CTYPE1, &wc, 1, &type );
72 return type & C1_DIGIT;
75 /* Some functions are only in later versions of kernel32.dll */
76 static WORD enumCount;
78 static INT (WINAPI *pGetTimeFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT);
79 static INT (WINAPI *pGetDateFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT, LPCWSTR);
80 static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROCA, DWORD, LONG_PTR);
81 static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROCA, LGRPID, DWORD, LONG_PTR);
82 static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROCA, DWORD, LONG_PTR);
83 static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
84 static INT (WINAPI *pLCMapStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM);
85 static LCID (WINAPI *pLocaleNameToLCID)(LPCWSTR, DWORD);
86 static INT (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD);
87 static INT (WINAPI *pFoldStringA)(DWORD, LPCSTR, INT, LPSTR, INT);
88 static INT (WINAPI *pFoldStringW)(DWORD, LPCWSTR, INT, LPWSTR, INT);
89 static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
90 static INT (WINAPI *pIdnToNameprepUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
91 static INT (WINAPI *pIdnToAscii)(DWORD, LPCWSTR, INT, LPWSTR, INT);
92 static INT (WINAPI *pIdnToUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
93 static INT (WINAPI *pGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, INT);
94 static BOOL (WINAPI *pIsValidLocaleName)(LPCWSTR);
95 static INT (WINAPI *pCompareStringOrdinal)(const WCHAR *, INT, const WCHAR *, INT, BOOL);
96 static INT (WINAPI *pCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT,
97 LPNLSVERSIONINFO, LPVOID, LPARAM);
98 static INT (WINAPI *pGetGeoInfoA)(GEOID, GEOTYPE, LPSTR, INT, LANGID);
99 static INT (WINAPI *pGetGeoInfoW)(GEOID, GEOTYPE, LPWSTR, INT, LANGID);
100 static BOOL (WINAPI *pEnumSystemGeoID)(GEOCLASS, GEOID, GEO_ENUMPROC);
101 static BOOL (WINAPI *pGetSystemPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
102 static BOOL (WINAPI *pGetThreadPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
103 static BOOL (WINAPI *pGetUserPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
104 static WCHAR (WINAPI *pRtlUpcaseUnicodeChar)(WCHAR);
105 static INT (WINAPI *pGetNumberFormatEx)(LPCWSTR, DWORD, LPCWSTR, const NUMBERFMTW *, LPWSTR, int);
106 static INT (WINAPI *pFindNLSStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT, LPINT, LPNLSVERSIONINFO, LPVOID, LPARAM);
107 static LANGID (WINAPI *pSetThreadUILanguage)(LANGID);
108 static LANGID (WINAPI *pGetThreadUILanguage)(VOID);
109 static INT (WINAPI *pNormalizeString)(NORM_FORM, LPCWSTR, INT, LPWSTR, INT);
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(FoldStringA);
124 X(FoldStringW);
125 X(IsValidLanguageGroup);
126 X(EnumUILanguagesA);
127 X(EnumSystemLocalesEx);
128 X(IdnToNameprepUnicode);
129 X(IdnToAscii);
130 X(IdnToUnicode);
131 X(GetLocaleInfoEx);
132 X(IsValidLocaleName);
133 X(CompareStringOrdinal);
134 X(CompareStringEx);
135 X(GetGeoInfoA);
136 X(GetGeoInfoW);
137 X(EnumSystemGeoID);
138 X(GetSystemPreferredUILanguages);
139 X(GetThreadPreferredUILanguages);
140 X(GetUserPreferredUILanguages);
141 X(GetNumberFormatEx);
142 X(FindNLSStringEx);
143 X(SetThreadUILanguage);
144 X(GetThreadUILanguage);
145 X(NormalizeString);
147 mod = GetModuleHandleA("ntdll");
148 X(RtlUpcaseUnicodeChar);
149 #undef X
152 #define eq(received, expected, label, type) \
153 ok((received) == (expected), "%s: got " type " instead of " type "\n", \
154 (label), (received), (expected))
156 #define BUFFER_SIZE 128
158 #define STRINGSA(x,y) strcpy(input, x); strcpy(Expected, y); SetLastError(0xdeadbeef); buffer[0] = '\0'
159 #define EXPECT_LENA ok(ret == lstrlenA(Expected)+1, "Expected len %d, got %d\n", lstrlenA(Expected)+1, ret)
160 #define EXPECT_EQA ok(strncmp(buffer, Expected, strlen(Expected)) == 0, \
161 "Expected '%s', got '%s'\n", Expected, buffer)
163 #define STRINGSW(x,y) MultiByteToWideChar(CP_ACP,0,x,-1,input,ARRAY_SIZE(input)); \
164 MultiByteToWideChar(CP_ACP,0,y,-1,Expected,ARRAY_SIZE(Expected)); \
165 SetLastError(0xdeadbeef); buffer[0] = '\0'
166 #define EXPECT_LENW ok(ret == lstrlenW(Expected)+1, "Expected Len %d, got %d\n", lstrlenW(Expected)+1, ret)
167 #define EXPECT_EQW ok(strncmpW(buffer, Expected, strlenW(Expected)) == 0, "Bad conversion\n")
169 #define NUO LOCALE_NOUSEROVERRIDE
171 static void test_GetLocaleInfoA(void)
173 int ret;
174 int len;
175 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
176 char buffer[BUFFER_SIZE];
177 char expected[BUFFER_SIZE];
178 DWORD val;
180 ok(lcid == 0x409, "wrong LCID calculated - %d\n", lcid);
182 ret = GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (char*)&val, sizeof(val));
183 ok(ret, "got %d\n", ret);
184 ok(val == lcid, "got 0x%08x\n", val);
186 /* en and ar use SUBLANG_NEUTRAL, but GetLocaleInfo assume SUBLANG_DEFAULT
187 Same is true for zh on pre-Vista, but on Vista and higher GetLocaleInfo
188 assumes SUBLANG_NEUTRAL for zh */
189 memset(expected, 0, ARRAY_SIZE(expected));
190 len = GetLocaleInfoA(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
191 SetLastError(0xdeadbeef);
192 memset(buffer, 0, ARRAY_SIZE(buffer));
193 ret = GetLocaleInfoA(LANG_ENGLISH, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
194 ok((ret == len) && !lstrcmpA(buffer, expected),
195 "got %d with '%s' (expected %d with '%s')\n",
196 ret, buffer, len, expected);
198 memset(expected, 0, ARRAY_SIZE(expected));
199 len = GetLocaleInfoA(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
200 if (len) {
201 SetLastError(0xdeadbeef);
202 memset(buffer, 0, ARRAY_SIZE(buffer));
203 ret = GetLocaleInfoA(LANG_ARABIC, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
204 ok((ret == len) && !lstrcmpA(buffer, expected),
205 "got %d with '%s' (expected %d with '%s')\n",
206 ret, buffer, len, expected);
208 else
209 win_skip("LANG_ARABIC not installed\n");
211 /* SUBLANG_DEFAULT is required for mlang.dll, but optional for GetLocaleInfo */
212 memset(expected, 0, ARRAY_SIZE(expected));
213 len = GetLocaleInfoA(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
214 SetLastError(0xdeadbeef);
215 memset(buffer, 0, ARRAY_SIZE(buffer));
216 ret = GetLocaleInfoA(LANG_GERMAN, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
217 ok((ret == len) && !lstrcmpA(buffer, expected),
218 "got %d with '%s' (expected %d with '%s')\n",
219 ret, buffer, len, expected);
222 /* HTMLKit and "Font xplorer lite" expect GetLocaleInfoA to
223 * partially fill the buffer even if it is too short. See bug 637.
225 SetLastError(0xdeadbeef);
226 memset(buffer, 0, ARRAY_SIZE(buffer));
227 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 0);
228 ok(ret == 7 && !buffer[0], "Expected len=7, got %d\n", ret);
230 SetLastError(0xdeadbeef);
231 memset(buffer, 0, ARRAY_SIZE(buffer));
232 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 3);
233 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
234 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
235 ok(!strcmp(buffer, "Mon"), "Expected 'Mon', got '%s'\n", buffer);
237 SetLastError(0xdeadbeef);
238 memset(buffer, 0, ARRAY_SIZE(buffer));
239 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 10);
240 ok(ret == 7, "Expected ret == 7, got %d, error %d\n", ret, GetLastError());
241 ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer);
244 struct neutralsublang_name2_t {
245 WCHAR name[3];
246 WCHAR sname[15];
247 LCID lcid;
248 LCID lcid_broken;
249 WCHAR sname_broken[15];
250 int todo;
253 static const struct neutralsublang_name2_t neutralsublang_names2[] = {
254 { {'a','r',0}, {'a','r','-','S','A',0},
255 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
256 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0},
257 MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
258 { {'d','e',0}, {'d','e','-','D','E',0},
259 MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
260 { {'e','n',0}, {'e','n','-','U','S',0},
261 MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
262 { {'e','s',0}, {'e','s','-','E','S',0},
263 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT),
264 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), SORT_DEFAULT) /* vista */,
265 {'e','s','-','E','S','_','t','r','a','d','n','l',0} },
266 { {'g','a',0}, {'g','a','-','I','E',0},
267 MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 0, {0}, 0x3 },
268 { {'i','t',0}, {'i','t','-','I','T',0},
269 MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
270 { {'m','s',0}, {'m','s','-','M','Y',0},
271 MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
272 { {'n','l',0}, {'n','l','-','N','L',0},
273 MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
274 { {'p','t',0}, {'p','t','-','B','R',0},
275 MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
276 { {'s','r',0}, {'h','r','-','H','R',0},
277 MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CROATIA), SORT_DEFAULT) },
278 { {'s','v',0}, {'s','v','-','S','E',0},
279 MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
280 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0},
281 MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
282 { {'z','h',0}, {'z','h','-','C','N',0},
283 MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
284 { {0} }
287 static void test_GetLocaleInfoW(void)
289 LCID lcid_en = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
290 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
291 LCID lcid_en_neut = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT);
292 WCHAR bufferW[80], buffer2W[80];
293 CHAR bufferA[80];
294 DWORD val;
295 DWORD ret;
296 INT i;
298 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
299 if (!ret) {
300 win_skip("GetLocaleInfoW() isn't implemented\n");
301 return;
304 ret = GetLocaleInfoW(lcid_en, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
305 ok(ret, "got %d\n", ret);
306 ok(val == lcid_en, "got 0x%08x\n", val);
308 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
309 if (ret)
311 static const WCHAR slangW[] = {'E','n','g','l','i','s','h',' ','(','U','n','i','t','e','d',' ',
312 'S','t','a','t','e','s',')',0};
313 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
314 static const WCHAR enW[] = {'e','n','-','U','S',0};
315 const struct neutralsublang_name2_t *ptr = neutralsublang_names2;
317 ok(!lstrcmpW(bufferW, enW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
319 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
320 ok(ret, "got %d\n", ret);
321 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
322 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
324 skip("Non-English locale\n");
326 else
327 ok(!lstrcmpW(statesW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
329 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SLANGUAGE, bufferW, ARRAY_SIZE(bufferW));
330 ok(ret, "got %d\n", ret);
331 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
332 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
334 skip("Non-English locale\n");
336 else
337 ok(!lstrcmpW(slangW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
339 while (*ptr->name)
341 LANGID langid;
342 LCID lcid;
344 /* make neutral lcid */
345 langid = MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(ptr->lcid)), SUBLANG_NEUTRAL);
346 lcid = MAKELCID(langid, SORT_DEFAULT);
348 val = 0;
349 GetLocaleInfoW(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
350 todo_wine_if (ptr->todo & 0x1)
351 ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
352 wine_dbgstr_w(ptr->name), val, ptr->lcid);
354 /* now check LOCALE_SNAME */
355 GetLocaleInfoW(lcid, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
356 todo_wine_if (ptr->todo & 0x2)
357 ok(!lstrcmpW(bufferW, ptr->sname) ||
358 (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))),
359 "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
360 ptr++;
363 else
364 win_skip("English neutral locale not supported\n");
366 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
367 if (!ret) {
368 win_skip("LANG_RUSSIAN locale data unavailable\n");
369 return;
371 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
372 bufferW, ARRAY_SIZE(bufferW));
373 if (!ret) {
374 win_skip("LOCALE_RETURN_GENITIVE_NAMES isn't supported\n");
375 return;
378 /* LOCALE_RETURN_GENITIVE_NAMES isn't supported for GetLocaleInfoA */
379 bufferA[0] = 'a';
380 SetLastError(0xdeadbeef);
381 ret = GetLocaleInfoA(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
382 bufferA, ARRAY_SIZE(bufferA));
383 ok(ret == 0, "LOCALE_RETURN_GENITIVE_NAMES should fail with GetLocaleInfoA\n");
384 ok(bufferA[0] == 'a', "Expected buffer to be untouched\n");
385 ok(GetLastError() == ERROR_INVALID_FLAGS,
386 "Expected ERROR_INVALID_FLAGS, got %x\n", GetLastError());
388 bufferW[0] = 'a';
389 SetLastError(0xdeadbeef);
390 ret = GetLocaleInfoW(lcid_ru, LOCALE_RETURN_GENITIVE_NAMES, bufferW, ARRAY_SIZE(bufferW));
391 ok(ret == 0,
392 "LOCALE_RETURN_GENITIVE_NAMES itself doesn't return anything, got %d\n", ret);
393 ok(bufferW[0] == 'a', "Expected buffer to be untouched\n");
394 ok(GetLastError() == ERROR_INVALID_FLAGS,
395 "Expected ERROR_INVALID_FLAGS, got %x\n", GetLastError());
397 /* yes, test empty 13 month entry too */
398 for (i = 0; i < 12; i++) {
399 bufferW[0] = 0;
400 ret = GetLocaleInfoW(lcid_ru, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
401 bufferW, ARRAY_SIZE(bufferW));
402 ok(ret, "Expected non zero result\n");
403 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %d, length %d\n",
404 ret, lstrlenW(bufferW));
405 buffer2W[0] = 0;
406 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
407 ok(ret, "Expected non zero result\n");
408 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %d, length %d\n",
409 ret, lstrlenW(buffer2W));
411 ok(lstrcmpW(bufferW, buffer2W) != 0,
412 "Expected genitive name to differ, got the same for month %d\n", i+1);
414 /* for locale without genitive names nominative returned in both cases */
415 bufferW[0] = 0;
416 ret = GetLocaleInfoW(lcid_en, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
417 bufferW, ARRAY_SIZE(bufferW));
418 ok(ret, "Expected non zero result\n");
419 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %d, length %d\n",
420 ret, lstrlenW(bufferW));
421 buffer2W[0] = 0;
422 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
423 ok(ret, "Expected non zero result\n");
424 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %d, length %d\n",
425 ret, lstrlenW(buffer2W));
427 ok(lstrcmpW(bufferW, buffer2W) == 0,
428 "Expected same names, got different for month %d\n", i+1);
432 static void test_GetTimeFormatA(void)
434 int ret;
435 SYSTEMTIME curtime;
436 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
437 char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
439 memset(&curtime, 2, sizeof(SYSTEMTIME));
440 STRINGSA("tt HH':'mm'@'ss", ""); /* Invalid time */
441 SetLastError(0xdeadbeef);
442 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
443 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
444 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
446 curtime.wHour = 8;
447 curtime.wMinute = 56;
448 curtime.wSecond = 13;
449 curtime.wMilliseconds = 22;
450 STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
451 SetLastError(0xdeadbeef);
452 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
453 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
454 EXPECT_LENA; EXPECT_EQA;
456 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
457 SetLastError(0xdeadbeef);
458 ret = GetTimeFormatA(lcid, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
459 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
460 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
462 STRINGSA("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */
463 SetLastError(0xdeadbeef);
464 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
465 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
466 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
468 STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
469 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
470 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
471 EXPECT_LENA;
473 STRINGSA("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
474 ret = GetTimeFormatA(lcid, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
475 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
476 EXPECT_LENA; EXPECT_EQA;
478 STRINGSA("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
479 ret = GetTimeFormatA(lcid, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
480 ok(ret == strlen(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
481 ok( !strcmp( buffer, "" ) || broken( !strcmp( buffer, "4" )), /* win9x */
482 "Expected '', got '%s'\n", buffer );
484 STRINGSA("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
485 ret = GetTimeFormatA(lcid, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
486 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
487 EXPECT_LENA; EXPECT_EQA;
489 STRINGSA("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
490 strcpy(Expected, "8:56 AM");
491 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
492 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
493 EXPECT_LENA; EXPECT_EQA;
495 STRINGSA("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
496 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
497 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
498 ok( !strcmp( buffer, "8.@:56AM" ) || broken( !strcmp( buffer, "8.@:56.@:AM" )) /* win9x */,
499 "Expected '8.@:56AM', got '%s'\n", buffer );
501 STRINGSA("s1s2s3", ""); /* Duplicate tokens */
502 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
503 ok(ret == strlen(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
504 ok( !strcmp( buffer, "" ) || broken( !strcmp( buffer, "3" )), /* win9x */
505 "Expected '', got '%s'\n", buffer );
507 STRINGSA("t/tt", "A/AM"); /* AM time marker */
508 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
509 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
510 EXPECT_LENA; EXPECT_EQA;
512 curtime.wHour = 13;
513 STRINGSA("t/tt", "P/PM"); /* PM time marker */
514 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
515 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
516 EXPECT_LENA; EXPECT_EQA;
518 STRINGSA("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
519 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
520 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
521 EXPECT_LENA; EXPECT_EQA;
523 STRINGSA("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
524 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
525 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
526 EXPECT_LENA; EXPECT_EQA;
528 STRINGSA("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
529 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
530 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
531 EXPECT_LENA; EXPECT_EQA;
533 curtime.wHour = 14; /* change this to 14 or 2pm */
534 curtime.wMinute = 5;
535 curtime.wSecond = 3;
536 STRINGSA("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */
537 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
538 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
539 EXPECT_LENA; EXPECT_EQA;
541 curtime.wHour = 0;
542 STRINGSA("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
543 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
544 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
545 EXPECT_LENA; EXPECT_EQA;
547 STRINGSA("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
548 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
549 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
550 EXPECT_LENA; EXPECT_EQA;
552 /* try to convert formatting strings with more than two letters
553 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
554 * NOTE: We expect any letter for which there is an upper case value
555 * we should see a replacement. For letters that DO NOT have
556 * upper case values we should see NO REPLACEMENT.
558 curtime.wHour = 8;
559 curtime.wMinute = 56;
560 curtime.wSecond = 13;
561 curtime.wMilliseconds = 22;
562 STRINGSA("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
563 "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
564 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
565 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
566 EXPECT_LENA; EXPECT_EQA;
568 STRINGSA("h", "text"); /* Don't write to buffer if len is 0 */
569 strcpy(buffer, "text");
570 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, 0);
571 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
572 EXPECT_EQA;
574 STRINGSA("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
575 "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
576 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
577 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
578 EXPECT_LENA; EXPECT_EQA;
580 STRINGSA("'''", "'"); /* invalid quoted string */
581 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
582 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
583 EXPECT_LENA; EXPECT_EQA;
585 /* test that msdn suggested single quotation usage works as expected */
586 STRINGSA("''''", "'"); /* single quote mark */
587 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
588 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
589 EXPECT_LENA; EXPECT_EQA;
591 STRINGSA("''HHHHHH", "08"); /* Normal use */
592 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
593 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
594 EXPECT_LENA; EXPECT_EQA;
596 /* and test for normal use of the single quotation mark */
597 STRINGSA("'''HHHHHH'", "'HHHHHH"); /* Normal use */
598 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
599 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
600 EXPECT_LENA; EXPECT_EQA;
602 STRINGSA("'''HHHHHH", "'HHHHHH"); /* Odd use */
603 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
604 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
605 EXPECT_LENA; EXPECT_EQA;
607 STRINGSA("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
608 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
609 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
610 EXPECT_LENA; EXPECT_EQA;
612 curtime.wHour = 25;
613 STRINGSA("'123'tt", ""); /* Invalid time */
614 SetLastError(0xdeadbeef);
615 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
616 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
617 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
619 curtime.wHour = 12;
620 curtime.wMonth = 60; /* Invalid */
621 STRINGSA("h:m:s", "12:56:13"); /* Invalid date */
622 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
623 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
624 EXPECT_LENA; EXPECT_EQA;
627 static void test_GetTimeFormatEx(void)
629 int ret;
630 SYSTEMTIME curtime;
631 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
633 if (!pGetTimeFormatEx)
635 win_skip("GetTimeFormatEx not supported\n");
636 return;
639 memset(&curtime, 2, sizeof(SYSTEMTIME));
640 STRINGSW("tt HH':'mm'@'ss", ""); /* Invalid time */
641 SetLastError(0xdeadbeef);
642 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
643 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
644 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
646 curtime.wHour = 8;
647 curtime.wMinute = 56;
648 curtime.wSecond = 13;
649 curtime.wMilliseconds = 22;
650 STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
651 SetLastError(0xdeadbeef);
652 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
653 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
654 EXPECT_LENW; EXPECT_EQW;
656 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
657 SetLastError(0xdeadbeef);
658 ret = pGetTimeFormatEx(localeW, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
659 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
660 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
662 STRINGSW("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */
663 SetLastError(0xdeadbeef);
664 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
665 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
666 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
668 STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
669 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
670 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
671 EXPECT_LENW;
673 STRINGSW("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
674 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
675 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
676 EXPECT_LENW; EXPECT_EQW;
678 STRINGSW("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
679 ret = pGetTimeFormatEx(localeW, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
680 ok(ret == strlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
681 EXPECT_LENW; EXPECT_EQW;
683 STRINGSW("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
684 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
685 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
686 EXPECT_LENW; EXPECT_EQW;
688 STRINGSW("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
689 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
690 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
691 EXPECT_LENW; EXPECT_EQW;
693 STRINGSW("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
694 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
695 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
696 EXPECT_LENW; EXPECT_EQW;
698 STRINGSW("s1s2s3", ""); /* Duplicate tokens */
699 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
700 ok(ret == strlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
701 EXPECT_LENW; EXPECT_EQW;
703 STRINGSW("t/tt", "A/AM"); /* AM time marker */
704 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
705 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
706 EXPECT_LENW; EXPECT_EQW;
708 curtime.wHour = 13;
709 STRINGSW("t/tt", "P/PM"); /* PM time marker */
710 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
711 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
712 EXPECT_LENW; EXPECT_EQW;
714 STRINGSW("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
715 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
716 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
717 EXPECT_LENW; EXPECT_EQW;
719 STRINGSW("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
720 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
721 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
722 EXPECT_LENW; EXPECT_EQW;
724 STRINGSW("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
725 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
726 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
727 EXPECT_LENW; EXPECT_EQW;
729 curtime.wHour = 14; /* change this to 14 or 2pm */
730 curtime.wMinute = 5;
731 curtime.wSecond = 3;
732 STRINGSW("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */
733 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
734 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
735 EXPECT_LENW; EXPECT_EQW;
737 curtime.wHour = 0;
738 STRINGSW("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
739 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
740 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
741 EXPECT_LENW; EXPECT_EQW;
743 STRINGSW("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
744 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
745 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
746 EXPECT_LENW; EXPECT_EQW;
748 /* try to convert formatting strings with more than two letters
749 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
750 * NOTE: We expect any letter for which there is an upper case value
751 * we should see a replacement. For letters that DO NOT have
752 * upper case values we should see NO REPLACEMENT.
754 curtime.wHour = 8;
755 curtime.wMinute = 56;
756 curtime.wSecond = 13;
757 curtime.wMilliseconds = 22;
758 STRINGSW("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
759 "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
760 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
761 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
762 EXPECT_LENW; EXPECT_EQW;
764 STRINGSW("h", "text"); /* Don't write to buffer if len is 0 */
765 lstrcpyW(buffer, Expected);
766 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, 0);
767 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
768 EXPECT_EQW;
770 STRINGSW("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
771 "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
772 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
773 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
774 EXPECT_LENW; EXPECT_EQW;
776 STRINGSW("'''", "'"); /* invalid quoted string */
777 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
778 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
779 EXPECT_LENW; EXPECT_EQW;
781 /* test that msdn suggested single quotation usage works as expected */
782 STRINGSW("''''", "'"); /* single quote mark */
783 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
784 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
785 EXPECT_LENW; EXPECT_EQW;
787 STRINGSW("''HHHHHH", "08"); /* Normal use */
788 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
789 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
790 EXPECT_LENW; EXPECT_EQW;
792 /* and test for normal use of the single quotation mark */
793 STRINGSW("'''HHHHHH'", "'HHHHHH"); /* Normal use */
794 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
795 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
796 EXPECT_LENW; EXPECT_EQW;
798 STRINGSW("'''HHHHHH", "'HHHHHH"); /* Odd use */
799 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
800 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
801 EXPECT_LENW; EXPECT_EQW;
803 STRINGSW("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
804 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
805 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
806 EXPECT_LENW; EXPECT_EQW;
808 curtime.wHour = 25;
809 STRINGSW("'123'tt", ""); /* Invalid time */
810 SetLastError(0xdeadbeef);
811 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
812 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
813 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
815 curtime.wHour = 12;
816 curtime.wMonth = 60; /* Invalid */
817 STRINGSW("h:m:s", "12:56:13"); /* Invalid date */
818 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
819 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
820 EXPECT_LENW; EXPECT_EQW;
823 static void test_GetDateFormatA(void)
825 int ret;
826 SYSTEMTIME curtime;
827 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
828 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
829 char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
830 char Broken[BUFFER_SIZE];
831 char short_day[10], month[10], genitive_month[10];
833 memset(&curtime, 2, sizeof(SYSTEMTIME)); /* Invalid time */
834 STRINGSA("ddd',' MMM dd yy","");
835 SetLastError(0xdeadbeef);
836 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
837 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
838 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
840 curtime.wYear = 2002;
841 curtime.wMonth = 5;
842 curtime.wDay = 4;
843 curtime.wDayOfWeek = 3;
844 STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Simple case */
845 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
846 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
847 EXPECT_LENA; EXPECT_EQA;
849 /* Same as above but with LOCALE_NOUSEROVERRIDE */
850 STRINGSA("ddd',' MMM dd yy",""); /* Simple case */
851 SetLastError(0xdeadbeef);
852 ret = GetDateFormatA(lcid, NUO, &curtime, input, buffer, ARRAY_SIZE(buffer));
853 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
854 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
855 EXPECT_EQA;
857 STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Format containing "'" */
858 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
859 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
860 EXPECT_LENA; EXPECT_EQA;
862 curtime.wHour = 36; /* Invalid */
863 STRINGSA("ddd',' MMM dd ''''yy","Sat, May 04 '02"); /* Invalid time */
864 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
865 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
866 EXPECT_LENA; EXPECT_EQA;
868 STRINGSA("ddd',' MMM dd ''''yy",""); /* Get size only */
869 ret = GetDateFormatA(lcid, 0, &curtime, input, NULL, 0);
870 ok(ret == 16, "Expected ret == 16, got %d, error %d\n", ret, GetLastError());
871 EXPECT_EQA;
873 STRINGSA("ddd',' MMM dd ''''yy",""); /* Buffer too small */
874 SetLastError(0xdeadbeef);
875 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, 2);
876 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
877 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
879 STRINGSA("ddd',' MMM dd ''''yy","5/4/2002"); /* Default to DATE_SHORTDATE */
880 ret = GetDateFormatA(lcid, NUO, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
881 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
882 if (strncmp(buffer, Expected, strlen(Expected)) && strncmp(buffer, "5/4/02", strlen(Expected)) != 0)
883 ok (0, "Expected '%s' or '5/4/02', got '%s'\n", Expected, buffer);
885 SetLastError(0xdeadbeef); buffer[0] = '\0'; /* DATE_LONGDATE */
886 ret = GetDateFormatA(lcid, NUO|DATE_LONGDATE, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
887 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
888 ok(strcmp(buffer, "Saturday, May 04, 2002") == 0 ||
889 strcmp(buffer, "Saturday, May 4, 2002") == 0 /* Win 8 */,
890 "got an unexpected date string '%s'\n", buffer);
892 /* test for expected DATE_YEARMONTH behavior with null format */
893 /* NT4 returns ERROR_INVALID_FLAGS for DATE_YEARMONTH */
894 STRINGSA("ddd',' MMM dd ''''yy", ""); /* DATE_YEARMONTH */
895 SetLastError(0xdeadbeef);
896 ret = GetDateFormatA(lcid, NUO|DATE_YEARMONTH, &curtime, input, buffer, ARRAY_SIZE(buffer));
897 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
898 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
899 EXPECT_EQA;
901 /* Test that using invalid DATE_* flags results in the correct error */
902 /* and return values */
903 STRINGSA("m/d/y", ""); /* Invalid flags */
904 SetLastError(0xdeadbeef);
905 ret = GetDateFormatA(lcid, DATE_YEARMONTH|DATE_SHORTDATE|DATE_LONGDATE, &curtime, input,
906 buffer, ARRAY_SIZE(buffer));
907 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
908 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
910 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddMMMM", buffer, ARRAY_SIZE(buffer));
911 if (!ret)
913 win_skip("LANG_RUSSIAN locale data unavailable\n");
914 return;
917 /* month part should be in genitive form */
918 strcpy(genitive_month, buffer + 2);
919 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMM", buffer, ARRAY_SIZE(buffer));
920 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
921 strcpy(month, buffer);
922 ok(strcmp(genitive_month, month) != 0, "Expected different month forms\n");
924 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddd", buffer, ARRAY_SIZE(buffer));
925 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
926 strcpy(short_day, buffer);
928 STRINGSA("dd MMMMddd dd", "");
929 sprintf(Expected, "04 %s%s 04", genitive_month, short_day);
930 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
931 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
932 EXPECT_EQA;
934 STRINGSA("MMMMddd dd", "");
935 sprintf(Expected, "%s%s 04", month, short_day);
936 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
937 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
938 EXPECT_EQA;
940 STRINGSA("MMMMddd", "");
941 sprintf(Expected, "%s%s", month, short_day);
942 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
943 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
944 EXPECT_EQA;
946 STRINGSA("MMMMdd", "");
947 sprintf(Expected, "%s04", genitive_month);
948 sprintf(Broken, "%s04", month);
949 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
950 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
951 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
952 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
953 "Expected '%s', got '%s'\n", Expected, buffer);
955 STRINGSA("MMMMdd ddd", "");
956 sprintf(Expected, "%s04 %s", genitive_month, short_day);
957 sprintf(Broken, "%s04 %s", month, short_day);
958 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
959 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
960 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
961 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
962 "Expected '%s', got '%s'\n", Expected, buffer);
964 STRINGSA("dd dddMMMM", "");
965 sprintf(Expected, "04 %s%s", short_day, month);
966 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
967 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
968 EXPECT_EQA;
970 STRINGSA("dd dddMMMM ddd MMMMdd", "");
971 sprintf(Expected, "04 %s%s %s %s04", short_day, month, short_day, genitive_month);
972 sprintf(Broken, "04 %s%s %s %s04", short_day, month, short_day, month);
973 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
974 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
975 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
976 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
977 "Expected '%s', got '%s'\n", Expected, buffer);
979 /* with literal part */
980 STRINGSA("ddd',' MMMM dd", "");
981 sprintf(Expected, "%s, %s 04", short_day, genitive_month);
982 sprintf(Broken, "%s, %s 04", short_day, month);
983 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
984 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
985 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
986 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
987 "Expected '%s', got '%s'\n", Expected, buffer);
990 static void test_GetDateFormatEx(void)
992 int ret;
993 SYSTEMTIME curtime;
994 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
996 if (!pGetDateFormatEx)
998 win_skip("GetDateFormatEx not supported\n");
999 return;
1002 STRINGSW("",""); /* If flags are set, then format must be NULL */
1003 SetLastError(0xdeadbeef);
1004 ret = pGetDateFormatEx(localeW, DATE_LONGDATE, NULL, input, buffer, ARRAY_SIZE(buffer), NULL);
1005 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1006 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1007 EXPECT_EQW;
1009 STRINGSW("",""); /* NULL buffer, len > 0 */
1010 SetLastError(0xdeadbeef);
1011 ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, ARRAY_SIZE(buffer), NULL);
1012 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1013 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1015 STRINGSW("",""); /* NULL buffer, len == 0 */
1016 ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, 0, NULL);
1017 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1018 EXPECT_LENW; EXPECT_EQW;
1020 STRINGSW("",""); /* Invalid flag combination */
1021 SetLastError(0xdeadbeef);
1022 ret = pGetDateFormatEx(localeW, DATE_LONGDATE|DATE_SHORTDATE, NULL,
1023 input, NULL, 0, NULL);
1024 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1025 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1026 EXPECT_EQW;
1028 curtime.wYear = 2002;
1029 curtime.wMonth = 10;
1030 curtime.wDay = 23;
1031 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1032 curtime.wHour = 65432; /* Invalid */
1033 curtime.wMinute = 34512; /* Invalid */
1034 curtime.wSecond = 65535; /* Invalid */
1035 curtime.wMilliseconds = 12345;
1036 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
1037 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1038 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1039 EXPECT_LENW; EXPECT_EQW;
1041 curtime.wYear = 2002;
1042 curtime.wMonth = 10;
1043 curtime.wDay = 23;
1044 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1045 curtime.wHour = 65432; /* Invalid */
1046 curtime.wMinute = 34512; /* Invalid */
1047 curtime.wSecond = 65535; /* Invalid */
1048 curtime.wMilliseconds = 12345;
1049 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002");
1050 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), emptyW); /* Use reserved arg */
1051 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1052 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1054 /* Limit tests */
1056 curtime.wYear = 1601;
1057 curtime.wMonth = 1;
1058 curtime.wDay = 1;
1059 curtime.wDayOfWeek = 0; /* Irrelevant */
1060 curtime.wHour = 0;
1061 curtime.wMinute = 0;
1062 curtime.wSecond = 0;
1063 curtime.wMilliseconds = 0;
1064 STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601");
1065 SetLastError(0xdeadbeef);
1066 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1067 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1068 EXPECT_LENW; EXPECT_EQW;
1070 curtime.wYear = 1600;
1071 curtime.wMonth = 12;
1072 curtime.wDay = 31;
1073 curtime.wDayOfWeek = 0; /* Irrelevant */
1074 curtime.wHour = 23;
1075 curtime.wMinute = 59;
1076 curtime.wSecond = 59;
1077 curtime.wMilliseconds = 999;
1078 STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600");
1079 SetLastError(0xdeadbeef);
1080 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1081 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1082 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1085 static void test_GetDateFormatW(void)
1087 int ret;
1088 SYSTEMTIME curtime;
1089 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
1090 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1092 STRINGSW("",""); /* If flags is not zero then format must be NULL */
1093 ret = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, NULL, input, buffer, ARRAY_SIZE(buffer));
1094 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1096 win_skip("GetDateFormatW is not implemented\n");
1097 return;
1099 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1100 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1101 EXPECT_EQW;
1103 STRINGSW("",""); /* NULL buffer, len > 0 */
1104 SetLastError(0xdeadbeef);
1105 ret = GetDateFormatW (lcid, 0, NULL, input, NULL, ARRAY_SIZE(buffer));
1106 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1107 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1109 STRINGSW("",""); /* NULL buffer, len == 0 */
1110 ret = GetDateFormatW (lcid, 0, NULL, input, NULL, 0);
1111 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1112 EXPECT_LENW; EXPECT_EQW;
1114 curtime.wYear = 2002;
1115 curtime.wMonth = 10;
1116 curtime.wDay = 23;
1117 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1118 curtime.wHour = 65432; /* Invalid */
1119 curtime.wMinute = 34512; /* Invalid */
1120 curtime.wSecond = 65535; /* Invalid */
1121 curtime.wMilliseconds = 12345;
1122 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
1123 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1124 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1125 EXPECT_LENW; EXPECT_EQW;
1127 /* Limit tests */
1129 curtime.wYear = 1601;
1130 curtime.wMonth = 1;
1131 curtime.wDay = 1;
1132 curtime.wDayOfWeek = 0; /* Irrelevant */
1133 curtime.wHour = 0;
1134 curtime.wMinute = 0;
1135 curtime.wSecond = 0;
1136 curtime.wMilliseconds = 0;
1137 STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601");
1138 SetLastError(0xdeadbeef);
1139 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1140 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1141 EXPECT_LENW; EXPECT_EQW;
1143 curtime.wYear = 1600;
1144 curtime.wMonth = 12;
1145 curtime.wDay = 31;
1146 curtime.wDayOfWeek = 0; /* Irrelevant */
1147 curtime.wHour = 23;
1148 curtime.wMinute = 59;
1149 curtime.wSecond = 59;
1150 curtime.wMilliseconds = 999;
1151 STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600");
1152 SetLastError(0xdeadbeef);
1153 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1154 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1155 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1159 #define CY_POS_LEFT 0
1160 #define CY_POS_RIGHT 1
1161 #define CY_POS_LEFT_SPACE 2
1162 #define CY_POS_RIGHT_SPACE 3
1164 static void test_GetCurrencyFormatA(void)
1166 static char szDot[] = { '.', '\0' };
1167 static char szComma[] = { ',', '\0' };
1168 static char szDollar[] = { '$', '\0' };
1169 int ret;
1170 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1171 char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
1172 CURRENCYFMTA format;
1174 memset(&format, 0, sizeof(format));
1176 STRINGSA("23",""); /* NULL output, length > 0 --> Error */
1177 SetLastError(0xdeadbeef);
1178 ret = GetCurrencyFormatA(lcid, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1179 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1180 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1182 STRINGSA("23,53",""); /* Invalid character --> Error */
1183 SetLastError(0xdeadbeef);
1184 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1185 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1186 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1188 STRINGSA("--",""); /* Double '-' --> Error */
1189 SetLastError(0xdeadbeef);
1190 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1191 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1192 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1194 STRINGSA("0-",""); /* Trailing '-' --> Error */
1195 SetLastError(0xdeadbeef);
1196 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1197 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1198 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1200 STRINGSA("0..",""); /* Double '.' --> Error */
1201 SetLastError(0xdeadbeef);
1202 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1203 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1204 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1206 STRINGSA(" 0.1",""); /* Leading space --> Error */
1207 SetLastError(0xdeadbeef);
1208 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1209 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1210 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1212 STRINGSA("1234","$"); /* Length too small --> Write up to length chars */
1213 SetLastError(0xdeadbeef);
1214 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, 2);
1215 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1216 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1218 STRINGSA("2353",""); /* Format and flags given --> Error */
1219 SetLastError(0xdeadbeef);
1220 ret = GetCurrencyFormatA(lcid, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1221 ok( !ret, "Expected ret == 0, got %d\n", ret);
1222 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1223 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1225 STRINGSA("2353",""); /* Invalid format --> Error */
1226 SetLastError(0xdeadbeef);
1227 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1228 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1229 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1231 STRINGSA("2353","$2,353.00"); /* Valid number */
1232 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1233 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1234 EXPECT_LENA; EXPECT_EQA;
1236 STRINGSA("-2353","($2,353.00)"); /* Valid negative number */
1237 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1238 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1239 EXPECT_LENA; EXPECT_EQA;
1241 STRINGSA("2353.1","$2,353.10"); /* Valid real number */
1242 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1243 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1244 EXPECT_LENA; EXPECT_EQA;
1246 STRINGSA("2353.111","$2,353.11"); /* Too many DP --> Truncated */
1247 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1248 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1249 EXPECT_LENA; EXPECT_EQA;
1251 STRINGSA("2353.119","$2,353.12"); /* Too many DP --> Rounded */
1252 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1253 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1254 EXPECT_LENA; EXPECT_EQA;
1256 format.NumDigits = 0; /* No decimal separator */
1257 format.LeadingZero = 0;
1258 format.Grouping = 0; /* No grouping char */
1259 format.NegativeOrder = 0;
1260 format.PositiveOrder = CY_POS_LEFT;
1261 format.lpDecimalSep = szDot;
1262 format.lpThousandSep = szComma;
1263 format.lpCurrencySymbol = szDollar;
1265 STRINGSA("2353","$2353"); /* No decimal or grouping chars expected */
1266 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1267 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1268 EXPECT_LENA; EXPECT_EQA;
1270 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1271 STRINGSA("2353","$2353.0");
1272 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1273 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1274 EXPECT_LENA; EXPECT_EQA;
1276 format.Grouping = 2; /* Group by 100's */
1277 STRINGSA("2353","$23,53.0");
1278 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1279 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1280 EXPECT_LENA; EXPECT_EQA;
1282 STRINGSA("235","$235.0"); /* Grouping of a positive number */
1283 format.Grouping = 3;
1284 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1285 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1286 EXPECT_LENA; EXPECT_EQA;
1288 STRINGSA("-235","$-235.0"); /* Grouping of a negative number */
1289 format.NegativeOrder = 2;
1290 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1291 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1292 EXPECT_LENA; EXPECT_EQA;
1294 format.LeadingZero = 1; /* Always provide leading zero */
1295 STRINGSA(".5","$0.5");
1296 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1297 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1298 EXPECT_LENA; EXPECT_EQA;
1300 format.PositiveOrder = CY_POS_RIGHT;
1301 STRINGSA("1","1.0$");
1302 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1303 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1304 EXPECT_LENA; EXPECT_EQA;
1306 format.PositiveOrder = CY_POS_LEFT_SPACE;
1307 STRINGSA("1","$ 1.0");
1308 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1309 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1310 EXPECT_LENA; EXPECT_EQA;
1312 format.PositiveOrder = CY_POS_RIGHT_SPACE;
1313 STRINGSA("1","1.0 $");
1314 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1315 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1316 EXPECT_LENA; EXPECT_EQA;
1318 format.NegativeOrder = 0;
1319 STRINGSA("-1","($1.0)");
1320 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1321 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1322 EXPECT_LENA; EXPECT_EQA;
1324 format.NegativeOrder = 1;
1325 STRINGSA("-1","-$1.0");
1326 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1327 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1328 EXPECT_LENA; EXPECT_EQA;
1330 format.NegativeOrder = 2;
1331 STRINGSA("-1","$-1.0");
1332 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1333 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1334 EXPECT_LENA; EXPECT_EQA;
1336 format.NegativeOrder = 3;
1337 STRINGSA("-1","$1.0-");
1338 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1339 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1340 EXPECT_LENA; EXPECT_EQA;
1342 format.NegativeOrder = 4;
1343 STRINGSA("-1","(1.0$)");
1344 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1345 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1346 EXPECT_LENA; EXPECT_EQA;
1348 format.NegativeOrder = 5;
1349 STRINGSA("-1","-1.0$");
1350 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1351 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1352 EXPECT_LENA; EXPECT_EQA;
1354 format.NegativeOrder = 6;
1355 STRINGSA("-1","1.0-$");
1356 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1357 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1358 EXPECT_LENA; EXPECT_EQA;
1360 format.NegativeOrder = 7;
1361 STRINGSA("-1","1.0$-");
1362 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1363 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1364 EXPECT_LENA; EXPECT_EQA;
1366 format.NegativeOrder = 8;
1367 STRINGSA("-1","-1.0 $");
1368 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1369 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1370 EXPECT_LENA; EXPECT_EQA;
1372 format.NegativeOrder = 9;
1373 STRINGSA("-1","-$ 1.0");
1374 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1375 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1376 EXPECT_LENA; EXPECT_EQA;
1378 format.NegativeOrder = 10;
1379 STRINGSA("-1","1.0 $-");
1380 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1381 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1382 EXPECT_LENA; EXPECT_EQA;
1384 format.NegativeOrder = 11;
1385 STRINGSA("-1","$ 1.0-");
1386 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1387 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1388 EXPECT_LENA; EXPECT_EQA;
1390 format.NegativeOrder = 12;
1391 STRINGSA("-1","$ -1.0");
1392 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1393 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1394 EXPECT_LENA; EXPECT_EQA;
1396 format.NegativeOrder = 13;
1397 STRINGSA("-1","1.0- $");
1398 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1399 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1400 EXPECT_LENA; EXPECT_EQA;
1402 format.NegativeOrder = 14;
1403 STRINGSA("-1","($ 1.0)");
1404 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1405 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1406 EXPECT_LENA; EXPECT_EQA;
1408 format.NegativeOrder = 15;
1409 STRINGSA("-1","(1.0 $)");
1410 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1411 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1412 EXPECT_LENA; EXPECT_EQA;
1415 #define NEG_PARENS 0 /* "(1.1)" */
1416 #define NEG_LEFT 1 /* "-1.1" */
1417 #define NEG_LEFT_SPACE 2 /* "- 1.1" */
1418 #define NEG_RIGHT 3 /* "1.1-" */
1419 #define NEG_RIGHT_SPACE 4 /* "1.1 -" */
1421 static void test_GetNumberFormatA(void)
1423 static char szDot[] = { '.', '\0' };
1424 static char szComma[] = { ',', '\0' };
1425 int ret;
1426 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1427 char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
1428 NUMBERFMTA format;
1430 memset(&format, 0, sizeof(format));
1432 STRINGSA("23",""); /* NULL output, length > 0 --> Error */
1433 SetLastError(0xdeadbeef);
1434 ret = GetNumberFormatA(lcid, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1435 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1436 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1438 STRINGSA("23,53",""); /* Invalid character --> Error */
1439 SetLastError(0xdeadbeef);
1440 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1441 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1442 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1444 STRINGSA("--",""); /* Double '-' --> Error */
1445 SetLastError(0xdeadbeef);
1446 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1447 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1448 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1450 STRINGSA("0-",""); /* Trailing '-' --> Error */
1451 SetLastError(0xdeadbeef);
1452 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1453 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1454 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1456 STRINGSA("0..",""); /* Double '.' --> Error */
1457 SetLastError(0xdeadbeef);
1458 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1459 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1460 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1462 STRINGSA(" 0.1",""); /* Leading space --> Error */
1463 SetLastError(0xdeadbeef);
1464 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1465 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1466 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1468 STRINGSA("1234","1"); /* Length too small --> Write up to length chars */
1469 SetLastError(0xdeadbeef);
1470 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, 2);
1471 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1472 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1474 STRINGSA("2353",""); /* Format and flags given --> Error */
1475 SetLastError(0xdeadbeef);
1476 ret = GetNumberFormatA(lcid, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1477 ok( !ret, "Expected ret == 0, got %d\n", ret);
1478 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1479 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1481 STRINGSA("2353",""); /* Invalid format --> Error */
1482 SetLastError(0xdeadbeef);
1483 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1484 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1485 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1487 STRINGSA("2353","2,353.00"); /* Valid number */
1488 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1489 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1490 EXPECT_LENA; EXPECT_EQA;
1492 STRINGSA("-2353","-2,353.00"); /* Valid negative number */
1493 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1494 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1495 EXPECT_LENA; EXPECT_EQA;
1497 STRINGSA("-353","-353.00"); /* test for off by one error in grouping */
1498 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1499 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1500 EXPECT_LENA; EXPECT_EQA;
1502 STRINGSA("2353.1","2,353.10"); /* Valid real number */
1503 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1504 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1505 EXPECT_LENA; EXPECT_EQA;
1507 STRINGSA("2353.111","2,353.11"); /* Too many DP --> Truncated */
1508 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1509 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1510 EXPECT_LENA; EXPECT_EQA;
1512 STRINGSA("2353.119","2,353.12"); /* Too many DP --> Rounded */
1513 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1514 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1515 EXPECT_LENA; EXPECT_EQA;
1517 format.NumDigits = 0; /* No decimal separator */
1518 format.LeadingZero = 0;
1519 format.Grouping = 0; /* No grouping char */
1520 format.NegativeOrder = 0;
1521 format.lpDecimalSep = szDot;
1522 format.lpThousandSep = szComma;
1524 STRINGSA("2353","2353"); /* No decimal or grouping chars expected */
1525 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1526 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1527 EXPECT_LENA; EXPECT_EQA;
1529 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1530 STRINGSA("2353","2353.0");
1531 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1532 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1533 EXPECT_LENA; EXPECT_EQA;
1535 format.Grouping = 2; /* Group by 100's */
1536 STRINGSA("2353","23,53.0");
1537 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1538 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1539 EXPECT_LENA; EXPECT_EQA;
1541 STRINGSA("235","235.0"); /* Grouping of a positive number */
1542 format.Grouping = 3;
1543 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1544 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1545 EXPECT_LENA; EXPECT_EQA;
1547 STRINGSA("-235","-235.0"); /* Grouping of a negative number */
1548 format.NegativeOrder = NEG_LEFT;
1549 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1550 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1551 EXPECT_LENA; EXPECT_EQA;
1553 format.LeadingZero = 1; /* Always provide leading zero */
1554 STRINGSA(".5","0.5");
1555 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1556 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1557 EXPECT_LENA; EXPECT_EQA;
1559 format.NegativeOrder = NEG_PARENS;
1560 STRINGSA("-1","(1.0)");
1561 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1562 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1563 EXPECT_LENA; EXPECT_EQA;
1565 format.NegativeOrder = NEG_LEFT;
1566 STRINGSA("-1","-1.0");
1567 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1568 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1569 EXPECT_LENA; EXPECT_EQA;
1571 format.NegativeOrder = NEG_LEFT_SPACE;
1572 STRINGSA("-1","- 1.0");
1573 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1574 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1575 EXPECT_LENA; EXPECT_EQA;
1577 format.NegativeOrder = NEG_RIGHT;
1578 STRINGSA("-1","1.0-");
1579 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1580 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1581 EXPECT_LENA; EXPECT_EQA;
1583 format.NegativeOrder = NEG_RIGHT_SPACE;
1584 STRINGSA("-1","1.0 -");
1585 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1586 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1587 EXPECT_LENA; EXPECT_EQA;
1589 lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1591 if (IsValidLocale(lcid, 0))
1593 STRINGSA("-12345","-12 345,00"); /* Try French formatting */
1594 Expected[3] = 160; /* Non breaking space */
1595 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1596 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1597 EXPECT_LENA; EXPECT_EQA;
1601 static void test_GetNumberFormatEx(void)
1603 int ret;
1604 NUMBERFMTW format;
1605 static WCHAR dotW[] = {'.',0};
1606 static WCHAR commaW[] = {',',0};
1607 static const WCHAR enW[] = {'e','n','-','U','S',0};
1608 static const WCHAR frW[] = {'f','r','-','F','R',0};
1609 static const WCHAR bogusW[] = {'b','o','g','u','s',0};
1610 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
1612 if (!pGetNumberFormatEx)
1614 win_skip("GetNumberFormatEx is not available.\n");
1615 return;
1618 STRINGSW("23",""); /* NULL output, length > 0 --> Error */
1619 ret = pGetNumberFormatEx(enW, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1620 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1621 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1623 STRINGSW("23,53",""); /* Invalid character --> Error */
1624 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1625 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1626 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1628 STRINGSW("--",""); /* Double '-' --> Error */
1629 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1630 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1631 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1633 STRINGSW("0-",""); /* Trailing '-' --> Error */
1634 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1635 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1636 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1638 STRINGSW("0..",""); /* Double '.' --> Error */
1639 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1640 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1641 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1643 STRINGSW(" 0.1",""); /* Leading space --> Error */
1644 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1645 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1646 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1648 STRINGSW("1234","1"); /* Length too small --> Write up to length chars */
1649 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, 2);
1650 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1651 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1653 STRINGSW("23",""); /* Bogus locale --> Error */
1654 ret = pGetNumberFormatEx(bogusW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1655 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1656 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1658 memset(&format, 0, sizeof(format));
1660 STRINGSW("2353",""); /* Format and flags given --> Error */
1661 ret = pGetNumberFormatEx(enW, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1662 ok( !ret, "Expected ret == 0, got %d\n", ret);
1663 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1664 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1666 STRINGSW("2353",""); /* Invalid format --> Error */
1667 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1668 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1669 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1671 STRINGSW("2353","2,353.00"); /* Valid number */
1672 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1673 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1674 EXPECT_LENW; EXPECT_EQW;
1676 STRINGSW("-2353","-2,353.00"); /* Valid negative number */
1677 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1678 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1679 EXPECT_LENW; EXPECT_EQW;
1681 STRINGSW("-353","-353.00"); /* test for off by one error in grouping */
1682 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1683 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1684 EXPECT_LENW; EXPECT_EQW;
1686 STRINGSW("2353.1","2,353.10"); /* Valid real number */
1687 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1688 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1689 EXPECT_LENW; EXPECT_EQW;
1691 STRINGSW("2353.111","2,353.11"); /* Too many DP --> Truncated */
1692 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1693 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1694 EXPECT_LENW; EXPECT_EQW;
1696 STRINGSW("2353.119","2,353.12"); /* Too many DP --> Rounded */
1697 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1698 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1699 EXPECT_LENW; EXPECT_EQW;
1701 format.NumDigits = 0; /* No decimal separator */
1702 format.LeadingZero = 0;
1703 format.Grouping = 0; /* No grouping char */
1704 format.NegativeOrder = 0;
1705 format.lpDecimalSep = dotW;
1706 format.lpThousandSep = commaW;
1708 STRINGSW("2353","2353"); /* No decimal or grouping chars expected */
1709 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1710 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1711 EXPECT_LENW; EXPECT_EQW;
1713 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1714 STRINGSW("2353","2353.0");
1715 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1716 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1717 EXPECT_LENW; EXPECT_EQW;
1719 format.Grouping = 2; /* Group by 100's */
1720 STRINGSW("2353","23,53.0");
1721 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1722 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1723 EXPECT_LENW; EXPECT_EQW;
1725 STRINGSW("235","235.0"); /* Grouping of a positive number */
1726 format.Grouping = 3;
1727 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1728 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1729 EXPECT_LENW; EXPECT_EQW;
1731 STRINGSW("-235","-235.0"); /* Grouping of a negative number */
1732 format.NegativeOrder = NEG_LEFT;
1733 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1734 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1735 EXPECT_LENW; EXPECT_EQW;
1737 format.LeadingZero = 1; /* Always provide leading zero */
1738 STRINGSW(".5","0.5");
1739 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1740 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1741 EXPECT_LENW; EXPECT_EQW;
1743 format.NegativeOrder = NEG_PARENS;
1744 STRINGSW("-1","(1.0)");
1745 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1746 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1747 EXPECT_LENW; EXPECT_EQW;
1749 format.NegativeOrder = NEG_LEFT;
1750 STRINGSW("-1","-1.0");
1751 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1752 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1753 EXPECT_LENW; EXPECT_EQW;
1755 format.NegativeOrder = NEG_LEFT_SPACE;
1756 STRINGSW("-1","- 1.0");
1757 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1758 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1759 EXPECT_LENW; EXPECT_EQW;
1761 format.NegativeOrder = NEG_RIGHT;
1762 STRINGSW("-1","1.0-");
1763 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1764 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1765 EXPECT_LENW; EXPECT_EQW;
1767 format.NegativeOrder = NEG_RIGHT_SPACE;
1768 STRINGSW("-1","1.0 -");
1769 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1770 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1771 EXPECT_LENW; EXPECT_EQW;
1773 if (pIsValidLocaleName(frW))
1775 STRINGSW("-12345","-12 345,00"); /* Try French formatting */
1776 Expected[3] = 160; /* Non breaking space */
1777 ret = pGetNumberFormatEx(frW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1778 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1779 EXPECT_LENW; EXPECT_EQW;
1783 struct comparestringa_entry {
1784 LCID lcid;
1785 DWORD flags;
1786 const char *first;
1787 int first_len;
1788 const char *second;
1789 int second_len;
1790 int ret;
1793 static const struct comparestringa_entry comparestringa_data[] = {
1794 { LOCALE_SYSTEM_DEFAULT, 0, "EndDialog", -1, "_Property", -1, CSTR_GREATER_THAN },
1795 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0070", -1, "_IEWWBrowserComp", -1, CSTR_GREATER_THAN },
1796 { LOCALE_SYSTEM_DEFAULT, 0, "r", -1, "\\", -1, CSTR_GREATER_THAN },
1797 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0031", -1, "OriginalDatabase", -1, CSTR_GREATER_THAN },
1798 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1, CSTR_GREATER_THAN },
1799 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1, CSTR_LESS_THAN },
1800 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1801 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1802 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1, CSTR_LESS_THAN },
1803 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1, CSTR_LESS_THAN },
1804 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1, CSTR_LESS_THAN },
1805 { LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1, CSTR_GREATER_THAN },
1806 { LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1, CSTR_LESS_THAN },
1807 { LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1, CSTR_LESS_THAN },
1808 { LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1, CSTR_GREATER_THAN },
1809 { LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1, CSTR_GREATER_THAN },
1810 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1, CSTR_LESS_THAN },
1811 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1, CSTR_LESS_THAN },
1812 { LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1, CSTR_LESS_THAN },
1813 /* hyphen and apostrophe are treated differently depending on whether SORT_STRINGSORT specified or not */
1814 { LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1, CSTR_GREATER_THAN },
1815 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1, CSTR_LESS_THAN },
1816 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1, CSTR_LESS_THAN },
1817 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1, CSTR_GREATER_THAN },
1818 { LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1, CSTR_GREATER_THAN },
1819 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1, CSTR_LESS_THAN },
1820 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1, CSTR_LESS_THAN },
1821 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1, CSTR_GREATER_THAN },
1822 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9, CSTR_EQUAL },
1823 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10, CSTR_LESS_THAN },
1824 { LOCALE_SYSTEM_DEFAULT, 0, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1825 { LOCALE_SYSTEM_DEFAULT, 0, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1826 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1827 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1828 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a.", 3, "a\0", 3, CSTR_EQUAL },
1829 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a ", 3, "a\0", 3, CSTR_EQUAL },
1830 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0\0", 4, CSTR_EQUAL },
1831 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0\0", 4, CSTR_EQUAL },
1832 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 1, CSTR_EQUAL },
1833 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 2, CSTR_EQUAL },
1834 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0x", 4, CSTR_LESS_THAN },
1835 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0x", 4, CSTR_LESS_THAN },
1836 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 1, CSTR_GREATER_THAN },
1837 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 2, CSTR_GREATER_THAN },
1840 static void test_CompareStringA(void)
1842 int ret, i;
1843 char a[256];
1844 LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1846 for (i = 0; i < ARRAY_SIZE(comparestringa_data); i++)
1848 const struct comparestringa_entry *entry = &comparestringa_data[i];
1850 ret = CompareStringA(entry->lcid, entry->flags, entry->first, entry->first_len,
1851 entry->second, entry->second_len);
1852 ok(ret == entry->ret, "%d: got %d, expected %d\n", i, ret, entry->ret);
1855 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
1856 ok (ret == CSTR_LESS_THAN, "(Salut/Salute) Expected CSTR_LESS_THAN, got %d\n", ret);
1858 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
1859 ok (ret == CSTR_EQUAL, "(Salut/SaLuT) Expected CSTR_EQUAL, got %d\n", ret);
1861 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
1862 ok (ret == CSTR_GREATER_THAN, "(Salut/hola) Expected CSTR_GREATER_THAN, got %d\n", ret);
1864 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1865 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1867 lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1869 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1870 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1872 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
1873 ok (ret == CSTR_GREATER_THAN, "(haha/hoho) Expected CSTR_GREATER_THAN, got %d\n", ret);
1875 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1);
1876 ok (ret == CSTR_EQUAL, "(Salut/saLuT) Expected CSTR_EQUAL, got %d\n", ret);
1878 /* test for CompareStringA flags */
1879 SetLastError(0xdeadbeef);
1880 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0x8, "NULL", -1, "NULL", -1);
1881 ok(GetLastError() == ERROR_INVALID_FLAGS,
1882 "unexpected error code %d\n", GetLastError());
1883 ok(!ret, "CompareStringA must fail with invalid flag\n");
1885 SetLastError(0xdeadbeef);
1886 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, LOCALE_USE_CP_ACP, "NULL", -1, "NULL", -1);
1887 ok(GetLastError() == 0xdeadbeef, "unexpected error code %d\n", GetLastError());
1888 ok(ret == CSTR_EQUAL, "CompareStringA error: %d != CSTR_EQUAL\n", ret);
1889 /* end of test for CompareStringA flags */
1891 ret = lstrcmpA("", "");
1892 ok (ret == 0, "lstrcmpA(\"\", \"\") should return 0, got %d\n", ret);
1894 ret = lstrcmpA(NULL, NULL);
1895 ok (ret == 0 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, NULL) should return 0, got %d\n", ret);
1897 ret = lstrcmpA("", NULL);
1898 ok (ret == 1 || broken(ret == -2) /* win9x */, "lstrcmpA(\"\", NULL) should return 1, got %d\n", ret);
1900 ret = lstrcmpA(NULL, "");
1901 ok (ret == -1 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, \"\") should return -1, got %d\n", ret);
1904 if (0) { /* this requires collation table patch to make it MS compatible */
1905 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
1906 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1908 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 );
1909 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1911 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 );
1912 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1914 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 );
1915 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1917 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 );
1918 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1920 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 );
1921 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1923 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 );
1924 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1926 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 );
1927 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1929 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 );
1930 ok(ret == CSTR_LESS_THAN, "`o vs -m expected CSTR_LESS_THAN, got %d\n", ret);
1932 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 );
1933 ok(ret == CSTR_GREATER_THAN, "-m vs `o CSTR_GREATER_THAN got %d\n", ret);
1935 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 );
1936 ok(ret == CSTR_GREATER_THAN, "`o vs -m CSTR_GREATER_THAN got %d\n", ret);
1938 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 );
1939 ok(ret == CSTR_LESS_THAN, "-m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1943 /* WinXP handles embedded NULLs differently than earlier versions */
1944 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
1945 ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLuZkUtZ vs aLuZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret);
1947 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10);
1948 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);
1950 ret = CompareStringA(lcid, 0, "a\0b", -1, "a", -1);
1951 ok(ret == CSTR_EQUAL, "a vs a expected CSTR_EQUAL, got %d\n", ret);
1953 ret = CompareStringA(lcid, 0, "a\0b", 4, "a", 2);
1954 ok(ret == CSTR_EQUAL || /* win2k */
1955 ret == CSTR_GREATER_THAN,
1956 "a\\0b vs a expected CSTR_EQUAL or CSTR_GREATER_THAN, got %d\n", ret);
1958 ret = CompareStringA(lcid, 0, "\2", 2, "\1", 2);
1959 todo_wine ok(ret != CSTR_EQUAL, "\\2 vs \\1 expected unequal\n");
1961 ret = CompareStringA(lcid, NORM_IGNORECASE | LOCALE_USE_CP_ACP, "#", -1, ".", -1);
1962 todo_wine ok(ret == CSTR_LESS_THAN, "\"#\" vs \".\" expected CSTR_LESS_THAN, got %d\n", ret);
1964 ret = CompareStringA(lcid, NORM_IGNORECASE, "_", -1, ".", -1);
1965 todo_wine ok(ret == CSTR_GREATER_THAN, "\"_\" vs \".\" expected CSTR_GREATER_THAN, got %d\n", ret);
1967 ret = lstrcmpiA("#", ".");
1968 todo_wine ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret);
1970 lcid = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT);
1972 /* \xB9 character lies between a and b */
1973 ret = CompareStringA(lcid, 0, "a", 1, "\xB9", 1);
1974 todo_wine ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be greater than \'a\'\n");
1975 ret = CompareStringA(lcid, 0, "\xB9", 1, "b", 1);
1976 ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be smaller than \'b\'\n");
1978 memset(a, 'a', sizeof(a));
1979 SetLastError(0xdeadbeef);
1980 ret = CompareStringA(lcid, 0, a, sizeof(a), a, sizeof(a));
1981 ok (GetLastError() == 0xdeadbeef && ret == CSTR_EQUAL,
1982 "ret %d, error %d, expected value %d\n", ret, GetLastError(), CSTR_EQUAL);
1985 static void test_CompareStringW(void)
1987 WCHAR *str1, *str2;
1988 SYSTEM_INFO si;
1989 DWORD old_prot;
1990 BOOL success;
1991 char *buf;
1992 int ret;
1994 GetSystemInfo(&si);
1995 buf = VirtualAlloc(NULL, si.dwPageSize * 4, MEM_COMMIT, PAGE_READWRITE);
1996 ok(buf != NULL, "VirtualAlloc failed with %u\n", GetLastError());
1997 success = VirtualProtect(buf + si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
1998 ok(success, "VirtualProtect failed with %u\n", GetLastError());
1999 success = VirtualProtect(buf + 3 * si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
2000 ok(success, "VirtualProtect failed with %u\n", GetLastError());
2002 str1 = (WCHAR *)(buf + si.dwPageSize - sizeof(WCHAR));
2003 str2 = (WCHAR *)(buf + 3 * si.dwPageSize - sizeof(WCHAR));
2004 *str1 = 'A';
2005 *str2 = 'B';
2007 /* CompareStringW should abort on the first non-matching character */
2008 ret = CompareStringW(CP_ACP, 0, str1, 100, str2, 100);
2009 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2011 success = VirtualFree(buf, 0, MEM_RELEASE);
2012 ok(success, "VirtualFree failed with %u\n", GetLastError());
2015 struct comparestringex_test {
2016 const char *locale;
2017 DWORD flags;
2018 const WCHAR first[2];
2019 const WCHAR second[2];
2020 INT ret;
2021 INT broken;
2022 BOOL todo;
2025 static const struct comparestringex_test comparestringex_tests[] = {
2026 /* default behavior */
2027 { /* 0 */
2028 "tr-TR", 0,
2029 {'i',0}, {'I',0}, CSTR_LESS_THAN, -1, FALSE
2031 { /* 1 */
2032 "tr-TR", 0,
2033 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2035 { /* 2 */
2036 "tr-TR", 0,
2037 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2039 { /* 3 */
2040 "tr-TR", 0,
2041 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, TRUE
2043 { /* 4 */
2044 "tr-TR", 0,
2045 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2047 { /* 5 */
2048 "tr-TR", 0,
2049 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2051 /* with NORM_IGNORECASE */
2052 { /* 6 */
2053 "tr-TR", NORM_IGNORECASE,
2054 {'i',0}, {'I',0}, CSTR_EQUAL, -1, FALSE
2056 { /* 7 */
2057 "tr-TR", NORM_IGNORECASE,
2058 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, TRUE
2060 { /* 8 */
2061 "tr-TR", NORM_IGNORECASE,
2062 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2064 { /* 9 */
2065 "tr-TR", NORM_IGNORECASE,
2066 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, TRUE
2068 { /* 10 */
2069 "tr-TR", NORM_IGNORECASE,
2070 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2072 { /* 11 */
2073 "tr-TR", NORM_IGNORECASE,
2074 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2076 /* with NORM_LINGUISTIC_CASING */
2077 { /* 12 */
2078 "tr-TR", NORM_LINGUISTIC_CASING,
2079 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2081 { /* 13 */
2082 "tr-TR", NORM_LINGUISTIC_CASING,
2083 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2085 { /* 14 */
2086 "tr-TR", NORM_LINGUISTIC_CASING,
2087 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2089 { /* 15 */
2090 "tr-TR", NORM_LINGUISTIC_CASING,
2091 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, TRUE
2093 { /* 16 */
2094 "tr-TR", NORM_LINGUISTIC_CASING,
2095 {'I',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2097 { /* 17 */
2098 "tr-TR", NORM_LINGUISTIC_CASING,
2099 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2101 /* with LINGUISTIC_IGNORECASE */
2102 { /* 18 */
2103 "tr-TR", LINGUISTIC_IGNORECASE,
2104 {'i',0}, {'I',0}, CSTR_EQUAL, -1, TRUE
2106 { /* 19 */
2107 "tr-TR", LINGUISTIC_IGNORECASE,
2108 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2110 { /* 20 */
2111 "tr-TR", LINGUISTIC_IGNORECASE,
2112 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2114 { /* 21 */
2115 "tr-TR", LINGUISTIC_IGNORECASE,
2116 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, TRUE
2118 { /* 22 */
2119 "tr-TR", LINGUISTIC_IGNORECASE,
2120 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2122 { /* 23 */
2123 "tr-TR", LINGUISTIC_IGNORECASE,
2124 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2126 /* with NORM_LINGUISTIC_CASING | NORM_IGNORECASE */
2127 { /* 24 */
2128 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2129 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL, TRUE
2131 { /* 25 */
2132 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2133 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN, FALSE
2135 { /* 26 */
2136 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2137 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2139 { /* 27 */
2140 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2141 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, TRUE
2143 { /* 28 */
2144 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2145 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2147 { /* 29 */
2148 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2149 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2151 /* with NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE */
2152 { /* 30 */
2153 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2154 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL, TRUE
2156 { /* 31 */
2157 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2158 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2160 { /* 32 */
2161 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2162 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2164 { /* 33 */
2165 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2166 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, TRUE
2168 { /* 34 */
2169 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2170 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2172 { /* 35 */
2173 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2174 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2178 static void test_CompareStringEx(void)
2180 const char *op[] = {"ERROR", "CSTR_LESS_THAN", "CSTR_EQUAL", "CSTR_GREATER_THAN"};
2181 WCHAR locale[6];
2182 INT ret, i;
2184 /* CompareStringEx is only available on Vista+ */
2185 if (!pCompareStringEx)
2187 win_skip("CompareStringEx not supported\n");
2188 return;
2191 for (i = 0; i < ARRAY_SIZE(comparestringex_tests); i++)
2193 const struct comparestringex_test *e = &comparestringex_tests[i];
2195 MultiByteToWideChar(CP_ACP, 0, e->locale, -1, locale, ARRAY_SIZE(locale));
2196 ret = pCompareStringEx(locale, e->flags, e->first, -1, e->second, -1, NULL, NULL, 0);
2197 todo_wine_if (e->todo)
2198 ok(ret == e->ret || broken(ret == e->broken),
2199 "%d: got %s, expected %s\n", i, op[ret], op[e->ret]);
2204 static const DWORD lcmap_invalid_flags[] = {
2206 LCMAP_HIRAGANA | LCMAP_KATAKANA,
2207 LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
2208 LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
2209 LCMAP_LOWERCASE | SORT_STRINGSORT,
2210 LCMAP_UPPERCASE | NORM_IGNORESYMBOLS,
2211 LCMAP_LOWERCASE | NORM_IGNORESYMBOLS,
2212 LCMAP_UPPERCASE | NORM_IGNORENONSPACE,
2213 LCMAP_LOWERCASE | NORM_IGNORENONSPACE,
2214 LCMAP_HIRAGANA | NORM_IGNORENONSPACE,
2215 LCMAP_HIRAGANA | NORM_IGNORESYMBOLS,
2216 LCMAP_HIRAGANA | LCMAP_SIMPLIFIED_CHINESE,
2217 LCMAP_HIRAGANA | LCMAP_TRADITIONAL_CHINESE,
2218 LCMAP_KATAKANA | NORM_IGNORENONSPACE,
2219 LCMAP_KATAKANA | NORM_IGNORESYMBOLS,
2220 LCMAP_KATAKANA | LCMAP_SIMPLIFIED_CHINESE,
2221 LCMAP_KATAKANA | LCMAP_TRADITIONAL_CHINESE,
2222 LCMAP_FULLWIDTH | NORM_IGNORENONSPACE,
2223 LCMAP_FULLWIDTH | NORM_IGNORESYMBOLS,
2224 LCMAP_FULLWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2225 LCMAP_FULLWIDTH | LCMAP_TRADITIONAL_CHINESE,
2226 LCMAP_HALFWIDTH | NORM_IGNORENONSPACE,
2227 LCMAP_HALFWIDTH | NORM_IGNORESYMBOLS,
2228 LCMAP_HALFWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2229 LCMAP_HALFWIDTH | LCMAP_TRADITIONAL_CHINESE,
2232 static void test_LCMapStringA(void)
2234 int ret, ret2, i;
2235 char buf[256], buf2[256];
2236 static const char upper_case[] = "\tJUST! A, TEST; STRING 1/*+-.\r\n";
2237 static const char lower_case[] = "\tjust! a, test; string 1/*+-.\r\n";
2238 static const char symbols_stripped[] = "justateststring1";
2240 SetLastError(0xdeadbeef);
2241 ret = LCMapStringA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP | LCMAP_LOWERCASE,
2242 lower_case, -1, buf, sizeof(buf));
2243 ok(ret == lstrlenA(lower_case) + 1,
2244 "ret %d, error %d, expected value %d\n",
2245 ret, GetLastError(), lstrlenA(lower_case) + 1);
2246 ok(!memcmp(buf, lower_case, ret), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2248 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
2249 upper_case, -1, buf, sizeof(buf));
2250 ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
2251 ok(GetLastError() == ERROR_INVALID_FLAGS,
2252 "unexpected error code %d\n", GetLastError());
2254 /* test invalid flag combinations */
2255 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2256 lstrcpyA(buf, "foo");
2257 SetLastError(0xdeadbeef);
2258 ret = LCMapStringA(LOCALE_USER_DEFAULT, lcmap_invalid_flags[i],
2259 lower_case, -1, buf, sizeof(buf));
2260 ok(GetLastError() == ERROR_INVALID_FLAGS,
2261 "LCMapStringA (flag %08x) unexpected error code %d\n",
2262 lcmap_invalid_flags[i], GetLastError());
2263 ok(!ret, "LCMapStringA (flag %08x) should return 0, got %d\n",
2264 lcmap_invalid_flags[i], ret);
2267 /* test LCMAP_LOWERCASE */
2268 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2269 upper_case, -1, buf, sizeof(buf));
2270 ok(ret == lstrlenA(upper_case) + 1,
2271 "ret %d, error %d, expected value %d\n",
2272 ret, GetLastError(), lstrlenA(upper_case) + 1);
2273 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2275 /* test LCMAP_UPPERCASE */
2276 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2277 lower_case, -1, buf, sizeof(buf));
2278 ok(ret == lstrlenA(lower_case) + 1,
2279 "ret %d, error %d, expected value %d\n",
2280 ret, GetLastError(), lstrlenA(lower_case) + 1);
2281 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2283 /* test buffer overflow */
2284 SetLastError(0xdeadbeef);
2285 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2286 lower_case, -1, buf, 4);
2287 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2288 "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
2290 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2291 lstrcpyA(buf, lower_case);
2292 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2293 buf, -1, buf, sizeof(buf));
2294 if (!ret) /* Win9x */
2295 trace("Ignoring LCMapStringA(LCMAP_UPPERCASE, buf, buf) error on Win9x\n");
2296 else
2298 ok(ret == lstrlenA(lower_case) + 1,
2299 "ret %d, error %d, expected value %d\n",
2300 ret, GetLastError(), lstrlenA(lower_case) + 1);
2301 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2303 lstrcpyA(buf, upper_case);
2304 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2305 buf, -1, buf, sizeof(buf));
2306 if (!ret) /* Win9x */
2307 trace("Ignoring LCMapStringA(LCMAP_LOWERCASE, buf, buf) error on Win9x\n");
2308 else
2310 ok(ret == lstrlenA(upper_case) + 1,
2311 "ret %d, error %d, expected value %d\n",
2312 ret, GetLastError(), lstrlenA(lower_case) + 1);
2313 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2316 /* otherwise src == dst should fail */
2317 SetLastError(0xdeadbeef);
2318 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
2319 buf, 10, buf, sizeof(buf));
2320 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2321 GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
2322 "unexpected error code %d\n", GetLastError());
2323 ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
2325 /* test whether '\0' is always appended */
2326 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2327 upper_case, -1, buf, sizeof(buf));
2328 ok(ret, "LCMapStringA must succeed\n");
2329 ok(buf[ret-1] == 0, "LCMapStringA not null-terminated\n");
2330 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2331 upper_case, lstrlenA(upper_case), buf2, sizeof(buf2));
2332 ok(ret2, "LCMapStringA must succeed\n");
2333 ok(buf2[ret2-1] == 0, "LCMapStringA not null-terminated\n" );
2334 ok(ret == ret2, "lengths of sort keys must be equal\n");
2335 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2337 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2338 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
2339 upper_case, -1, buf, sizeof(buf));
2340 ok(ret, "LCMapStringA must succeed\n");
2341 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2342 lower_case, -1, buf2, sizeof(buf2));
2343 ok(ret2, "LCMapStringA must succeed\n");
2344 ok(ret == ret2, "lengths of sort keys must be equal\n");
2345 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2347 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2348 results from plain LCMAP_SORTKEY on Vista */
2350 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2351 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2352 lower_case, -1, buf, sizeof(buf));
2353 ok(ret, "LCMapStringA must succeed\n");
2354 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2355 symbols_stripped, -1, buf2, sizeof(buf2));
2356 ok(ret2, "LCMapStringA must succeed\n");
2357 ok(ret == ret2, "lengths of sort keys must be equal\n");
2358 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2360 /* test NORM_IGNORENONSPACE */
2361 lstrcpyA(buf, "foo");
2362 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
2363 lower_case, -1, buf, sizeof(buf));
2364 ok(ret == lstrlenA(lower_case) + 1, "LCMapStringA should return %d, ret = %d\n",
2365 lstrlenA(lower_case) + 1, ret);
2366 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2368 /* test NORM_IGNORESYMBOLS */
2369 lstrcpyA(buf, "foo");
2370 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
2371 lower_case, -1, buf, sizeof(buf));
2372 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2373 lstrlenA(symbols_stripped) + 1, ret);
2374 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2376 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2377 lstrcpyA(buf, "foo");
2378 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE,
2379 lower_case, -1, buf, sizeof(buf));
2380 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2381 lstrlenA(symbols_stripped) + 1, ret);
2382 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2384 /* test srclen = 0 */
2385 SetLastError(0xdeadbeef);
2386 ret = LCMapStringA(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
2387 ok(!ret, "LCMapStringA should fail with srclen = 0\n");
2388 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2389 "unexpected error code %d\n", GetLastError());
2392 typedef INT (*lcmapstring_wrapper)(DWORD, LPCWSTR, INT, LPWSTR, INT);
2394 static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *func_name)
2396 const static WCHAR japanese_text[] = {
2397 0x3044, 0x309d, 0x3084, 0x3001, 0x30a4, 0x30fc, 0x30cf,
2398 0x30c8, 0x30fc, 0x30f4, 0x30a9, 0x306e, 0x2026, 0
2400 const static WCHAR hiragana_text[] = {
2401 0x3044, 0x309d, 0x3084, 0x3001, 0x3044, 0x30fc, 0x306f,
2402 0x3068, 0x30fc, 0x3094, 0x3049, 0x306e, 0x2026, 0
2404 const static WCHAR katakana_text[] = {
2405 0x30a4, 0x30fd, 0x30e4, 0x3001, 0x30a4, 0x30fc, 0x30cf,
2406 0x30c8, 0x30fc, 0x30f4, 0x30a9, 0x30ce, 0x2026, 0
2408 const static WCHAR halfwidth_text[] = {
2409 0x3044, 0x309d, 0x3084, 0xff64, 0xff72, 0xff70, 0xff8a,
2410 0xff84, 0xff70, 0xff73, 0xff9e, 0xff6b, 0x306e, 0x2026, 0
2412 int ret, ret2, i;
2413 WCHAR buf[256], buf2[256];
2414 char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
2416 /* LCMAP_LOWERCASE | LCMAP_UPPERCASE makes LCMAP_TITLECASE, so it's valid now. */
2417 ret = func_ptr(LCMAP_LOWERCASE | LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2418 todo_wine ok(ret == lstrlenW(title_case) + 1 || broken(!ret),
2419 "%s ret %d, error %d, expected value %d\n", func_name,
2420 ret, GetLastError(), lstrlenW(title_case) + 1);
2421 todo_wine ok(lstrcmpW(buf, title_case) == 0 || broken(!ret),
2422 "Expected title case string\n");
2424 /* test invalid flag combinations */
2425 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2426 lstrcpyW(buf, fooW);
2427 SetLastError(0xdeadbeef);
2428 ret = func_ptr(lcmap_invalid_flags[i],
2429 lower_case, -1, buf, sizeof(buf));
2430 ok(GetLastError() == ERROR_INVALID_FLAGS,
2431 "%s (flag %08x) unexpected error code %d\n",
2432 func_name, lcmap_invalid_flags[i], GetLastError());
2433 ok(!ret, "%s (flag %08x) should return 0, got %d\n",
2434 func_name, lcmap_invalid_flags[i], ret);
2437 /* test LCMAP_LOWERCASE */
2438 ret = func_ptr(LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2439 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2440 ret, GetLastError(), lstrlenW(upper_case) + 1);
2441 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2443 /* test LCMAP_UPPERCASE */
2444 ret = func_ptr(LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2445 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2446 ret, GetLastError(), lstrlenW(lower_case) + 1);
2447 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2449 /* test LCMAP_HIRAGANA */
2450 ret = func_ptr(LCMAP_HIRAGANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2451 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2452 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2453 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2455 buf[0] = 0x30f5; /* KATAKANA LETTER SMALL KA */
2456 ret = func_ptr(LCMAP_HIRAGANA, buf, 1, buf2, 1);
2457 ok(ret == 1, "%s ret %d, error %d, expected value 1\n", func_name,
2458 ret, GetLastError());
2459 /* U+3095: HIRAGANA LETTER SMALL KA was added in Unicode 3.2 */
2460 ok(buf2[0] == 0x3095 || broken(buf2[0] == 0x30f5 /* Vista and earlier versions */),
2461 "%s expected %04x, got %04x\n", func_name, 0x3095, buf2[0]);
2463 /* test LCMAP_KATAKANA | LCMAP_LOWERCASE */
2464 ret = func_ptr(LCMAP_KATAKANA | LCMAP_LOWERCASE, japanese_text, -1, buf, ARRAY_SIZE(buf));
2465 ok(ret == lstrlenW(katakana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2466 ret, GetLastError(), lstrlenW(katakana_text) + 1);
2467 ok(!lstrcmpW(buf, katakana_text), "%s string compare mismatch\n", func_name);
2469 /* test LCMAP_FULLWIDTH */
2470 ret = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2471 ok(ret == lstrlenW(japanese_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2472 ret, GetLastError(), lstrlenW(japanese_text) + 1);
2473 ok(!lstrcmpW(buf, japanese_text), "%s string compare mismatch\n", func_name);
2475 ret2 = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, NULL, 0);
2476 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret2, ret);
2478 /* test LCMAP_FULLWIDTH | LCMAP_HIRAGANA
2479 (half-width katakana is converted into full-width hiragana) */
2480 ret = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2481 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2482 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2483 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2485 ret2 = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, NULL, 0);
2486 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2488 /* test LCMAP_HALFWIDTH */
2489 ret = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, buf, ARRAY_SIZE(buf));
2490 ok(ret == lstrlenW(halfwidth_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2491 ret, GetLastError(), lstrlenW(halfwidth_text) + 1);
2492 ok(!lstrcmpW(buf, halfwidth_text), "%s string compare mismatch\n", func_name);
2494 ret2 = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, NULL, 0);
2495 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2497 /* test buffer overflow */
2498 SetLastError(0xdeadbeef);
2499 ret = func_ptr(LCMAP_UPPERCASE,
2500 lower_case, -1, buf, 4);
2501 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2502 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2504 /* KATAKANA LETTER GA (U+30AC) is converted into two half-width characters.
2505 Thus, it requires two WCHARs. */
2506 buf[0] = 0x30ac;
2507 SetLastError(0xdeadbeef);
2508 ret = func_ptr(LCMAP_HALFWIDTH, buf, 1, buf2, 1);
2509 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2510 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2512 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2513 lstrcpyW(buf, lower_case);
2514 ret = func_ptr(LCMAP_UPPERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2515 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2516 ret, GetLastError(), lstrlenW(lower_case) + 1);
2517 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2519 lstrcpyW(buf, upper_case);
2520 ret = func_ptr(LCMAP_LOWERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2521 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2522 ret, GetLastError(), lstrlenW(lower_case) + 1);
2523 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2525 /* otherwise src == dst should fail */
2526 SetLastError(0xdeadbeef);
2527 ret = func_ptr(LCMAP_SORTKEY | LCMAP_UPPERCASE,
2528 buf, 10, buf, sizeof(buf));
2529 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2530 GetLastError() == ERROR_INVALID_PARAMETER /* Win7+ */,
2531 "%s unexpected error code %d\n", func_name, GetLastError());
2532 ok(!ret, "%s src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n", func_name);
2534 /* test whether '\0' is always appended */
2535 ret = func_ptr(LCMAP_SORTKEY,
2536 upper_case, -1, buf, sizeof(buf));
2537 ok(ret, "%s func_ptr must succeed\n", func_name);
2538 ret2 = func_ptr(LCMAP_SORTKEY,
2539 upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
2540 ok(ret, "%s func_ptr must succeed\n", func_name);
2541 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2542 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2544 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2545 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORECASE,
2546 upper_case, -1, buf, sizeof(buf));
2547 ok(ret, "%s func_ptr must succeed\n", func_name);
2548 ret2 = func_ptr(LCMAP_SORTKEY,
2549 lower_case, -1, buf2, sizeof(buf2));
2550 ok(ret2, "%s func_ptr must succeed\n", func_name);
2551 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2552 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2554 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2555 results from plain LCMAP_SORTKEY on Vista */
2557 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2558 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2559 lower_case, -1, buf, sizeof(buf));
2560 ok(ret, "%s func_ptr must succeed\n", func_name);
2561 ret2 = func_ptr(LCMAP_SORTKEY,
2562 symbols_stripped, -1, buf2, sizeof(buf2));
2563 ok(ret2, "%s func_ptr must succeed\n", func_name);
2564 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2565 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2567 /* test NORM_IGNORENONSPACE */
2568 lstrcpyW(buf, fooW);
2569 ret = func_ptr(NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2570 ok(ret == lstrlenW(lower_case) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2571 lstrlenW(lower_case) + 1, ret);
2572 ok(!lstrcmpW(buf, lower_case), "%s string comparison mismatch\n", func_name);
2574 /* test NORM_IGNORESYMBOLS */
2575 lstrcpyW(buf, fooW);
2576 ret = func_ptr(NORM_IGNORESYMBOLS, lower_case, -1, buf, ARRAY_SIZE(buf));
2577 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2578 lstrlenW(symbols_stripped) + 1, ret);
2579 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2581 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2582 lstrcpyW(buf, fooW);
2583 ret = func_ptr(NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2584 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2585 lstrlenW(symbols_stripped) + 1, ret);
2586 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2588 /* test srclen = 0 */
2589 SetLastError(0xdeadbeef);
2590 ret = func_ptr(0, upper_case, 0, buf, ARRAY_SIZE(buf));
2591 ok(!ret, "%s func_ptr should fail with srclen = 0\n", func_name);
2592 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2593 "%s unexpected error code %d\n", func_name, GetLastError());
2596 static INT LCMapStringW_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2598 return LCMapStringW(LOCALE_USER_DEFAULT, flags, src, srclen, dst, dstlen);
2601 static void test_LCMapStringW(void)
2603 int ret;
2604 WCHAR buf[256];
2606 trace("testing LCMapStringW\n");
2608 SetLastError(0xdeadbeef);
2609 ret = LCMapStringW((LCID)-1, LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2610 todo_wine {
2611 ok(!ret, "LCMapStringW should fail with bad lcid\n");
2612 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
2615 test_lcmapstring_unicode(LCMapStringW_wrapper, "LCMapStringW:");
2618 static INT LCMapStringEx_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2620 return pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2623 static void test_LCMapStringEx(void)
2625 int ret;
2626 WCHAR buf[256];
2628 if (!pLCMapStringEx)
2630 win_skip( "LCMapStringEx not available\n" );
2631 return;
2634 trace("testing LCMapStringEx\n");
2636 SetLastError(0xdeadbeef);
2637 ret = pLCMapStringEx(invalidW, LCMAP_LOWERCASE,
2638 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 0);
2639 todo_wine {
2640 ok(!ret, "LCMapStringEx should fail with bad locale name\n");
2641 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
2644 /* test reserved parameters */
2645 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2646 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 1);
2647 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
2648 ret, GetLastError(), lstrlenW(upper_case) + 1);
2649 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2651 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2652 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, (void*)1, 0);
2653 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
2654 ret, GetLastError(), lstrlenW(upper_case) + 1);
2655 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2657 /* crashes on native */
2658 if(0)
2659 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2660 upper_case, -1, buf, ARRAY_SIZE(buf), (void*)1, NULL, 0);
2662 test_lcmapstring_unicode(LCMapStringEx_wrapper, "LCMapStringEx:");
2665 struct neutralsublang_name_t {
2666 WCHAR name[3];
2667 WCHAR sname[16];
2668 LCID lcid;
2669 int todo;
2672 static const struct neutralsublang_name_t neutralsublang_names[] = {
2673 { {'a','r',0}, {'a','r','-','S','A',0}, MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
2674 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0}, MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
2675 { {'d','e',0}, {'d','e','-','D','E',0}, MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
2676 { {'e','n',0}, {'e','n','-','U','S',0}, MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
2677 { {'e','s',0}, {'e','s','-','E','S',0}, MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT) },
2678 { {'g','a',0}, {'g','a','-','I','E',0}, MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT) },
2679 { {'i','t',0}, {'i','t','-','I','T',0}, MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
2680 { {'m','s',0}, {'m','s','-','M','Y',0}, MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
2681 { {'n','l',0}, {'n','l','-','N','L',0}, MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
2682 { {'p','t',0}, {'p','t','-','B','R',0}, MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
2683 { {'s','r',0}, {'s','r','-','L','a','t','n','-','R','S',0}, MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN), SORT_DEFAULT), 1 },
2684 { {'s','v',0}, {'s','v','-','S','E',0}, MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
2685 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0}, MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
2686 { {'z','h',0}, {'z','h','-','C','N',0}, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
2687 { {0} }
2690 static void test_LocaleNameToLCID(void)
2692 LCID lcid;
2693 INT ret;
2694 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
2695 static const WCHAR enW[] = {'e','n',0};
2696 static const WCHAR esesW[] = {'e','s','-','e','s',0};
2697 static const WCHAR zhHansW[] = {'z','h','-','H','a','n','s',0};
2698 static const WCHAR zhhansW[] = {'z','h','-','h','a','n','s',0};
2699 static const WCHAR zhHantW[] = {'z','h','-','H','a','n','t',0};
2700 static const WCHAR zhhantW[] = {'z','h','-','h','a','n','t',0};
2701 static const WCHAR zhcnW[] = {'z','h','-','C','N',0};
2702 static const WCHAR zhhkW[] = {'z','h','-','H','K',0};
2704 if (!pLocaleNameToLCID)
2706 win_skip( "LocaleNameToLCID not available\n" );
2707 return;
2710 /* special cases */
2711 buffer[0] = 0;
2712 SetLastError(0xdeadbeef);
2713 lcid = pLocaleNameToLCID(LOCALE_NAME_USER_DEFAULT, 0);
2714 ok(lcid == GetUserDefaultLCID() || broken(GetLastError() == ERROR_INVALID_PARAMETER /* Vista */),
2715 "Expected lcid == %08x, got %08x, error %d\n", GetUserDefaultLCID(), lcid, GetLastError());
2716 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2717 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2718 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2720 buffer[0] = 0;
2721 SetLastError(0xdeadbeef);
2722 lcid = pLocaleNameToLCID(LOCALE_NAME_SYSTEM_DEFAULT, 0);
2723 ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
2724 "Expected lcid == 0, got %08x, error %d\n", lcid, GetLastError());
2725 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2726 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2727 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2729 buffer[0] = 0;
2730 SetLastError(0xdeadbeef);
2731 lcid = pLocaleNameToLCID(LOCALE_NAME_INVARIANT, 0);
2732 ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08x, error %d\n", lcid, GetLastError());
2733 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2734 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2735 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2737 /* bad name */
2738 SetLastError(0xdeadbeef);
2739 lcid = pLocaleNameToLCID(invalidW, 0);
2740 ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
2741 "Expected lcid == 0, got %08x, error %d\n", lcid, GetLastError());
2743 /* lower-case */
2744 lcid = pLocaleNameToLCID(esesW, 0);
2745 ok(lcid == MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT), "Got wrong lcid for es-es: 0x%x\n", lcid);
2747 /* english neutral name */
2748 lcid = pLocaleNameToLCID(enW, 0);
2749 ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) ||
2750 broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid);
2751 if (lcid)
2753 const struct neutralsublang_name_t *ptr = neutralsublang_names;
2755 while (*ptr->name)
2757 lcid = pLocaleNameToLCID(ptr->name, 0);
2758 todo_wine_if (ptr->todo)
2759 ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
2760 wine_dbgstr_w(ptr->name), lcid, ptr->lcid);
2762 *buffer = 0;
2763 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2764 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(ptr->name), ret);
2765 ok(!lstrcmpW(ptr->sname, buffer), "%s: got wrong locale name %s\n",
2766 wine_dbgstr_w(ptr->name), wine_dbgstr_w(buffer));
2768 ptr++;
2771 /* zh-Hant has LCID 0x7c04, but LocaleNameToLCID actually returns 0x0c04, which is the LCID of zh-HK */
2772 lcid = pLocaleNameToLCID(zhHantW, 0);
2773 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
2774 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhHantW), lcid);
2775 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2776 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
2777 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2778 wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
2779 /* check that 0x7c04 also works and is mapped to zh-HK */
2780 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE_TRADITIONAL, SUBLANG_CHINESE_TRADITIONAL),
2781 buffer, ARRAY_SIZE(buffer), 0);
2782 todo_wine ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
2783 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2784 wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
2786 /* zh-hant */
2787 lcid = pLocaleNameToLCID(zhhantW, 0);
2788 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
2789 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhhantW), lcid);
2790 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2791 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhantW), ret);
2792 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2793 wine_dbgstr_w(zhhantW), wine_dbgstr_w(buffer));
2795 /* zh-Hans has LCID 0x0004, but LocaleNameToLCID actually returns 0x0804, which is the LCID of zh-CN */
2796 lcid = pLocaleNameToLCID(zhHansW, 0);
2797 /* check that LocaleNameToLCID actually returns 0x0804 */
2798 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
2799 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhHansW), lcid);
2800 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2801 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
2802 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2803 wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
2804 /* check that 0x0004 also works and is mapped to zh-CN */
2805 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE, SUBLANG_NEUTRAL), buffer, ARRAY_SIZE(buffer), 0);
2806 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
2807 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2808 wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
2810 /* zh-hans */
2811 lcid = pLocaleNameToLCID(zhhansW, 0);
2812 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
2813 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhhansW), lcid);
2814 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2815 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhansW), ret);
2816 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2817 wine_dbgstr_w(zhhansW), wine_dbgstr_w(buffer));
2821 /* this requires collation table patch to make it MS compatible */
2822 static const char * const strings_sorted[] =
2824 "'",
2825 "-",
2826 "!",
2827 "\"",
2828 ".",
2829 ":",
2830 "\\",
2831 "_",
2832 "`",
2833 "{",
2834 "}",
2835 "+",
2836 "0",
2837 "1",
2838 "2",
2839 "3",
2840 "4",
2841 "5",
2842 "6",
2843 "7",
2844 "8",
2845 "9",
2846 "a",
2847 "A",
2848 "b",
2849 "B",
2850 "c",
2854 static const char * const strings[] =
2856 "C",
2857 "\"",
2858 "9",
2859 "'",
2860 "}",
2861 "-",
2862 "7",
2863 "+",
2864 "`",
2865 "1",
2866 "a",
2867 "5",
2868 "\\",
2869 "8",
2870 "B",
2871 "3",
2872 "_",
2873 "6",
2874 "{",
2875 "2",
2876 "c",
2877 "4",
2878 "!",
2879 "0",
2880 "A",
2881 ":",
2882 "b",
2886 static int compare_string1(const void *e1, const void *e2)
2888 const char *s1 = *(const char *const *)e1;
2889 const char *s2 = *(const char *const *)e2;
2891 return lstrcmpA(s1, s2);
2894 static int compare_string2(const void *e1, const void *e2)
2896 const char *s1 = *(const char *const *)e1;
2897 const char *s2 = *(const char *const *)e2;
2899 return CompareStringA(0, 0, s1, -1, s2, -1) - 2;
2902 static int compare_string3(const void *e1, const void *e2)
2904 const char *s1 = *(const char *const *)e1;
2905 const char *s2 = *(const char *const *)e2;
2906 char key1[256], key2[256];
2908 LCMapStringA(0, LCMAP_SORTKEY, s1, -1, key1, sizeof(key1));
2909 LCMapStringA(0, LCMAP_SORTKEY, s2, -1, key2, sizeof(key2));
2910 return strcmp(key1, key2);
2913 static void test_sorting(void)
2915 char buf[256];
2916 char **str_buf = (char **)buf;
2917 int i;
2919 assert(sizeof(buf) >= sizeof(strings));
2921 /* 1. sort using lstrcmpA */
2922 memcpy(buf, strings, sizeof(strings));
2923 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string1);
2924 for (i = 0; i < ARRAY_SIZE(strings); i++)
2926 ok(!strcmp(strings_sorted[i], str_buf[i]),
2927 "qsort using lstrcmpA failed for element %d\n", i);
2929 /* 2. sort using CompareStringA */
2930 memcpy(buf, strings, sizeof(strings));
2931 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string2);
2932 for (i = 0; i < ARRAY_SIZE(strings); i++)
2934 ok(!strcmp(strings_sorted[i], str_buf[i]),
2935 "qsort using CompareStringA failed for element %d\n", i);
2937 /* 3. sort using sort keys */
2938 memcpy(buf, strings, sizeof(strings));
2939 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string3);
2940 for (i = 0; i < ARRAY_SIZE(strings); i++)
2942 ok(!strcmp(strings_sorted[i], str_buf[i]),
2943 "qsort using sort keys failed for element %d\n", i);
2947 static void test_FoldStringA(void)
2949 int ret, i, j;
2950 BOOL is_special;
2951 char src[256], dst[256];
2952 static const char digits_src[] = { 0xB9,0xB2,0xB3,'\0' };
2953 static const char digits_dst[] = { '1','2','3','\0' };
2954 static const char composite_src[] =
2956 0x8a,0x8e,0x9a,0x9e,0x9f,0xc0,0xc1,0xc2,
2957 0xc3,0xc4,0xc5,0xc7,0xc8,0xc9,0xca,0xcb,
2958 0xcc,0xcd,0xce,0xcf,0xd1,0xd2,0xd3,0xd4,
2959 0xd5,0xd6,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,
2960 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe7,0xe8,
2961 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf1,
2962 0xf2,0xf3,0xf4,0xf5,0xf6,0xf8,0xf9,0xfa,
2963 0xfb,0xfc,0xfd,0xff,'\0'
2965 static const char composite_dst[] =
2967 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
2968 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
2969 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
2970 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
2971 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
2972 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
2973 0x4f,0x7e,0x4f,0xa8,0x4f,0x3f,0x55,0x60,
2974 0x55,0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,
2975 0x61,0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,
2976 0x61,0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,
2977 0x65,0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,
2978 0x69,0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,
2979 0x6f,0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,
2980 0x6f,0xa8,0x6f,0x3f,0x75,0x60,0x75,0xb4,
2981 0x75,0x5e,0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
2983 static const char composite_dst_alt[] =
2985 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
2986 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
2987 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
2988 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
2989 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
2990 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
2991 0x4f,0x7e,0x4f,0xa8,0xd8,0x55,0x60,0x55,
2992 0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,0x61,
2993 0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,0x61,
2994 0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,0x65,
2995 0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,0x69,
2996 0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,0x6f,
2997 0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,0x6f,
2998 0xa8,0xf8,0x75,0x60,0x75,0xb4,0x75,0x5e,
2999 0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
3001 static const char ligatures_src[] =
3003 0x8C,0x9C,0xC6,0xDE,0xDF,0xE6,0xFE,'\0'
3005 static const char ligatures_dst[] =
3007 'O','E','o','e','A','E','T','H','s','s','a','e','t','h','\0'
3009 static const struct special
3011 char src;
3012 char dst[4];
3013 } foldczone_special[] =
3015 /* src dst */
3016 { 0x85, { 0x2e, 0x2e, 0x2e, 0x00 } },
3017 { 0x98, { 0x20, 0x7e, 0x00 } },
3018 { 0x99, { 0x54, 0x4d, 0x00 } },
3019 { 0xa0, { 0x20, 0x00 } },
3020 { 0xa8, { 0x20, 0xa8, 0x00 } },
3021 { 0xaa, { 0x61, 0x00 } },
3022 { 0xaf, { 0x20, 0xaf, 0x00 } },
3023 { 0xb2, { 0x32, 0x00 } },
3024 { 0xb3, { 0x33, 0x00 } },
3025 { 0xb4, { 0x20, 0xb4, 0x00 } },
3026 { 0xb8, { 0x20, 0xb8, 0x00 } },
3027 { 0xb9, { 0x31, 0x00 } },
3028 { 0xba, { 0x6f, 0x00 } },
3029 { 0xbc, { 0x31, 0x2f, 0x34, 0x00 } },
3030 { 0xbd, { 0x31, 0x2f, 0x32, 0x00 } },
3031 { 0xbe, { 0x33, 0x2f, 0x34, 0x00 } },
3032 { 0x00 }
3035 if (!pFoldStringA)
3036 return; /* FoldString is present in NT v3.1+, but not 95/98/Me */
3038 /* these tests are locale specific */
3039 if (GetACP() != 1252)
3041 trace("Skipping FoldStringA tests for a not Latin 1 locale\n");
3042 return;
3045 /* MAP_FOLDDIGITS */
3046 SetLastError(0);
3047 ret = pFoldStringA(MAP_FOLDDIGITS, digits_src, -1, dst, 256);
3048 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3050 win_skip("FoldStringA is not implemented\n");
3051 return;
3053 ok(ret == 4, "Expected ret == 4, got %d, error %d\n", ret, GetLastError());
3054 ok(strcmp(dst, digits_dst) == 0,
3055 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", digits_dst, dst);
3056 for (i = 1; i < 256; i++)
3058 if (!strchr(digits_src, i))
3060 src[0] = i;
3061 src[1] = '\0';
3062 SetLastError(0);
3063 ret = pFoldStringA(MAP_FOLDDIGITS, src, -1, dst, 256);
3064 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3065 ok(dst[0] == src[0],
3066 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", src, dst);
3070 /* MAP_EXPAND_LIGATURES */
3071 SetLastError(0);
3072 ret = pFoldStringA(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
3073 /* NT 4.0 doesn't support MAP_EXPAND_LIGATURES */
3074 if (!(ret == 0 && GetLastError() == ERROR_INVALID_FLAGS)) {
3075 ok(ret == sizeof(ligatures_dst), "Got %d, error %d\n", ret, GetLastError());
3076 ok(strcmp(dst, ligatures_dst) == 0,
3077 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", ligatures_dst, dst);
3078 for (i = 1; i < 256; i++)
3080 if (!strchr(ligatures_src, i))
3082 src[0] = i;
3083 src[1] = '\0';
3084 SetLastError(0);
3085 ret = pFoldStringA(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
3086 if (ret == 3)
3088 /* Vista */
3089 ok((i == 0xDC && lstrcmpA(dst, "UE") == 0) ||
3090 (i == 0xFC && lstrcmpA(dst, "ue") == 0),
3091 "Got %s for %d\n", dst, i);
3093 else
3095 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3096 ok(dst[0] == src[0],
3097 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", src, dst);
3103 /* MAP_COMPOSITE */
3104 SetLastError(0);
3105 ret = pFoldStringA(MAP_COMPOSITE, composite_src, -1, dst, 256);
3106 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3107 ok(ret == 121 || ret == 119, "Expected 121 or 119, got %d\n", ret);
3108 ok(strcmp(dst, composite_dst) == 0 || strcmp(dst, composite_dst_alt) == 0,
3109 "MAP_COMPOSITE: Mismatch, got '%s'\n", dst);
3111 for (i = 1; i < 256; i++)
3113 if (!strchr(composite_src, i))
3115 src[0] = i;
3116 src[1] = '\0';
3117 SetLastError(0);
3118 ret = pFoldStringA(MAP_COMPOSITE, src, -1, dst, 256);
3119 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3120 ok(dst[0] == src[0],
3121 "0x%02x, 0x%02x,0x%02x,0x%02x,\n", (unsigned char)src[0],
3122 (unsigned char)dst[0],(unsigned char)dst[1],(unsigned char)dst[2]);
3126 /* MAP_FOLDCZONE */
3127 for (i = 1; i < 256; i++)
3129 src[0] = i;
3130 src[1] = '\0';
3131 SetLastError(0);
3132 ret = pFoldStringA(MAP_FOLDCZONE, src, -1, dst, 256);
3133 is_special = FALSE;
3134 for (j = 0; foldczone_special[j].src != 0 && ! is_special; j++)
3136 if (foldczone_special[j].src == src[0])
3138 ok(ret == 2 || ret == lstrlenA(foldczone_special[j].dst) + 1,
3139 "Expected ret == 2 or %d, got %d, error %d\n",
3140 lstrlenA(foldczone_special[j].dst) + 1, ret, GetLastError());
3141 ok(src[0] == dst[0] || lstrcmpA(foldczone_special[j].dst, dst) == 0,
3142 "MAP_FOLDCZONE: string mismatch for 0x%02x\n",
3143 (unsigned char)src[0]);
3144 is_special = TRUE;
3147 if (! is_special)
3149 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3150 ok(src[0] == dst[0],
3151 "MAP_FOLDCZONE: Expected 0x%02x, got 0x%02x\n",
3152 (unsigned char)src[0], (unsigned char)dst[0]);
3156 /* MAP_PRECOMPOSED */
3157 for (i = 1; i < 256; i++)
3159 src[0] = i;
3160 src[1] = '\0';
3161 SetLastError(0);
3162 ret = pFoldStringA(MAP_PRECOMPOSED, src, -1, dst, 256);
3163 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3164 ok(src[0] == dst[0],
3165 "MAP_PRECOMPOSED: Expected 0x%02x, got 0x%02x\n",
3166 (unsigned char)src[0], (unsigned char)dst[0]);
3170 static void test_FoldStringW(void)
3172 int ret;
3173 unsigned int i, j;
3174 WCHAR src[256], dst[256], ch, prev_ch = 1;
3175 static const DWORD badFlags[] =
3178 MAP_PRECOMPOSED|MAP_COMPOSITE,
3179 MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES,
3180 MAP_COMPOSITE|MAP_EXPAND_LIGATURES
3182 /* Ranges of digits 0-9 : Must be sorted! */
3183 static const WCHAR digitRanges[] =
3185 0x0030, /* '0'-'9' */
3186 0x0660, /* Eastern Arabic */
3187 0x06F0, /* Arabic - Hindu */
3188 0x07C0, /* Nko */
3189 0x0966, /* Devengari */
3190 0x09E6, /* Bengalii */
3191 0x0A66, /* Gurmukhi */
3192 0x0AE6, /* Gujarati */
3193 0x0B66, /* Oriya */
3194 0x0BE6, /* Tamil - No 0 */
3195 0x0C66, /* Telugu */
3196 0x0CE6, /* Kannada */
3197 0x0D66, /* Maylayalam */
3198 0x0DE6, /* Sinhala Lith */
3199 0x0E50, /* Thai */
3200 0x0ED0, /* Laos */
3201 0x0F20, /* Tibet */
3202 0x0F29, /* Tibet half - 0 is out of sequence */
3203 0x1040, /* Myanmar */
3204 0x1090, /* Myanmar Shan */
3205 0x1368, /* Ethiopic - no 0 */
3206 0x17E0, /* Khmer */
3207 0x1810, /* Mongolian */
3208 0x1946, /* Limbu */
3209 0x19D0, /* New Tai Lue */
3210 0x1A80, /* Tai Tham Hora */
3211 0x1A90, /* Tai Tham Tham */
3212 0x1B50, /* Balinese */
3213 0x1BB0, /* Sundanese */
3214 0x1C40, /* Lepcha */
3215 0x1C50, /* Ol Chiki */
3216 0x2070, /* Superscript - 1, 2, 3 are out of sequence */
3217 0x2080, /* Subscript */
3218 0x245F, /* Circled - 0 is out of sequence */
3219 0x2473, /* Bracketed */
3220 0x2487, /* Full stop */
3221 0x24F4, /* Double Circled */
3222 0x2775, /* Inverted circled - No 0 */
3223 0x277F, /* Patterned circled - No 0 */
3224 0x2789, /* Inverted Patterned circled - No 0 */
3225 0x3020, /* Hangzhou */
3226 0xA620, /* Vai */
3227 0xA8D0, /* Saurashtra */
3228 0xA900, /* Kayah Li */
3229 0xA9D0, /* Javanese */
3230 0xA9F0, /* Myanmar Tai Laing */
3231 0xAA50, /* Cham */
3232 0xABF0, /* Meetei Mayek */
3233 0xff10, /* Pliene chasse (?) */
3234 0xffff /* Terminator */
3236 /* Digits which are represented, but out of sequence */
3237 static const WCHAR outOfSequenceDigits[] =
3239 0xB9, /* Superscript 1 */
3240 0xB2, /* Superscript 2 */
3241 0xB3, /* Superscript 3 */
3242 0x0C78, /* Telugu Fraction 0 */
3243 0x0C79, /* Telugu Fraction 1 */
3244 0x0C7A, /* Telugu Fraction 2 */
3245 0x0C7B, /* Telugu Fraction 3 */
3246 0x0C7C, /* Telugu Fraction 1 */
3247 0x0C7D, /* Telugu Fraction 2 */
3248 0x0C7E, /* Telugu Fraction 3 */
3249 0x0F33, /* Tibetan half zero */
3250 0x19DA, /* New Tai Lue Tham 1 */
3251 0x24EA, /* Circled 0 */
3252 0x24FF, /* Negative Circled 0 */
3253 0x3007, /* Ideographic number zero */
3254 '\0' /* Terminator */
3256 /* Digits in digitRanges for which no representation is available */
3257 static const WCHAR noDigitAvailable[] =
3259 0x0BE6, /* No Tamil 0 */
3260 0x0F29, /* No Tibetan half zero (out of sequence) */
3261 0x1368, /* No Ethiopic 0 */
3262 0x2473, /* No Bracketed 0 */
3263 0x2487, /* No 0 Full stop */
3264 0x24F4, /* No double circled 0 */
3265 0x2775, /* No inverted circled 0 */
3266 0x277F, /* No patterned circled */
3267 0x2789, /* No inverted Patterned circled */
3268 0x3020, /* No Hangzhou 0 */
3269 '\0' /* Terminator */
3271 static const WCHAR foldczone_src[] =
3273 'W', 'i', 'n', 'e', 0x0348, 0x0551, 0x1323, 0x280d,
3274 0xff37, 0xff49, 0xff4e, 0xff45, '\0'
3276 static const WCHAR foldczone_dst[] =
3278 'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e','\0'
3280 static const WCHAR foldczone_todo_src[] =
3282 0x3c5,0x308,0x6a,0x30c,0xa0,0xaa,0
3284 static const WCHAR foldczone_todo_dst[] =
3286 0x3cb,0x1f0,' ','a',0
3288 static const WCHAR foldczone_todo_broken_dst[] =
3290 0x3cb,0x1f0,0xa0,0xaa,0
3292 static const WCHAR ligatures_src[] =
3294 'W', 'i', 'n', 'e', 0x03a6, 0x03b9, 0x03bd, 0x03b5,
3295 0x00c6, 0x00de, 0x00df, 0x00e6, 0x00fe, 0x0132, 0x0133, 0x0152,
3296 0x0153, 0x01c4, 0x01c5, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01ca,
3297 0x01cb, 0x01cc, 0x01e2, 0x01e3, 0x01f1, 0x01f2, 0x01f3, 0x01fc,
3298 0x01fd, 0x05f0, 0x05f1, 0x05f2, 0xfb00, 0xfb01, 0xfb02, 0xfb03,
3299 0xfb04, 0xfb05, 0xfb06, '\0'
3301 static const WCHAR ligatures_dst[] =
3303 'W','i','n','e',0x03a6,0x03b9,0x03bd,0x03b5,
3304 'A','E','T','H','s','s','a','e','t','h','I','J','i','j','O','E','o','e',
3305 'D',0x017d,'D',0x017e,'d',0x017e,'L','J','L','j','l','j','N','J','N','j',
3306 'n','j',0x0100,0x0112,0x0101,0x0113,'D','Z','D','z','d','z',0x00c1,0x00c9,
3307 0x00e1,0x00e9,0x05d5,0x05d5,0x05d5,0x05d9,0x05d9,0x05d9,'f','f','f','i',
3308 'f','l','f','f','i','f','f','l',0x017f,'t','s','t','\0'
3311 if (!pFoldStringW)
3313 win_skip("FoldStringW is not available\n");
3314 return; /* FoldString is present in NT v3.1+, but not 95/98/Me */
3317 /* Invalid flag combinations */
3318 for (i = 0; i < ARRAY_SIZE(badFlags); i++)
3320 src[0] = dst[0] = '\0';
3321 SetLastError(0);
3322 ret = pFoldStringW(badFlags[i], src, 256, dst, 256);
3323 if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
3325 win_skip("FoldStringW is not implemented\n");
3326 return;
3328 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
3329 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3332 /* src & dst cannot be the same */
3333 SetLastError(0);
3334 ret = pFoldStringW(MAP_FOLDCZONE, src, -1, src, 256);
3335 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3336 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3338 /* src can't be NULL */
3339 SetLastError(0);
3340 ret = pFoldStringW(MAP_FOLDCZONE, NULL, -1, dst, 256);
3341 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3342 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3344 /* srclen can't be 0 */
3345 SetLastError(0);
3346 ret = pFoldStringW(MAP_FOLDCZONE, src, 0, dst, 256);
3347 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3348 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3350 /* dstlen can't be < 0 */
3351 SetLastError(0);
3352 ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, -1);
3353 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3354 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3356 /* Ret includes terminating NUL which is appended if srclen = -1 */
3357 SetLastError(0);
3358 src[0] = 'A';
3359 src[1] = '\0';
3360 dst[0] = '\0';
3361 ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
3362 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3363 ok(dst[0] == 'A' && dst[1] == '\0',
3364 "srclen=-1: Expected ret=2 [%d,%d], got ret=%d [%d,%d], err=%d\n",
3365 'A', '\0', ret, dst[0], dst[1], GetLastError());
3367 /* If size is given, result is not NUL terminated */
3368 SetLastError(0);
3369 src[0] = 'A';
3370 src[1] = 'A';
3371 dst[0] = 'X';
3372 dst[1] = 'X';
3373 ret = pFoldStringW(MAP_FOLDCZONE, src, 1, dst, 256);
3374 ok(ret == 1, "Expected ret == 1, got %d, error %d\n", ret, GetLastError());
3375 ok(dst[0] == 'A' && dst[1] == 'X',
3376 "srclen=1: Expected ret=1, [%d,%d], got ret=%d,[%d,%d], err=%d\n",
3377 'A','X', ret, dst[0], dst[1], GetLastError());
3379 /* MAP_FOLDDIGITS */
3380 for (j = 0; j < ARRAY_SIZE(digitRanges); j++)
3382 /* Check everything before this range */
3383 for (ch = prev_ch; ch < digitRanges[j]; ch++)
3385 SetLastError(0);
3386 src[0] = ch;
3387 src[1] = dst[0] = '\0';
3388 ret = pFoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
3389 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3391 ok(dst[0] == ch || strchrW(outOfSequenceDigits, ch) ||
3392 (ch >= 0xa8e0 && ch <= 0xa8e9), /* combining Devanagari on Win8 */
3393 "MAP_FOLDDIGITS: ch 0x%04x Expected unchanged got %04x\n", ch, dst[0]);
3394 ok(!isdigitW(ch) || strchrW(outOfSequenceDigits, ch) ||
3395 broken( ch >= 0xbf0 && ch <= 0xbf2 ), /* win2k */
3396 "char %04x should not be a digit\n", ch );
3399 if (digitRanges[j] == 0xffff)
3400 break; /* Finished the whole code point space */
3402 for (ch = digitRanges[j]; ch < digitRanges[j] + 10; ch++)
3404 WCHAR c;
3406 /* Map out of sequence characters */
3407 if (ch == 0x2071) c = 0x00B9; /* Superscript 1 */
3408 else if (ch == 0x2072) c = 0x00B2; /* Superscript 2 */
3409 else if (ch == 0x2073) c = 0x00B3; /* Superscript 3 */
3410 else if (ch == 0x245F) c = 0x24EA; /* Circled 0 */
3411 else c = ch;
3412 SetLastError(0);
3413 src[0] = c;
3414 src[1] = dst[0] = '\0';
3415 ret = pFoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
3416 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3418 ok((dst[0] == '0' + ch - digitRanges[j] && dst[1] == '\0') ||
3419 broken( dst[0] == ch ) || /* old Windows versions don't have all mappings */
3420 (digitRanges[j] == 0x3020 && dst[0] == ch) || /* Hangzhou not present in all Windows versions */
3421 (digitRanges[j] == 0x0F29 && dst[0] == ch) || /* Tibetan not present in all Windows versions */
3422 strchrW(noDigitAvailable, c),
3423 "MAP_FOLDDIGITS: ch %04x Expected %04x got %04x\n",
3424 ch, '0' + digitRanges[j] - ch, dst[0]);
3426 prev_ch = ch;
3429 /* MAP_FOLDCZONE */
3430 SetLastError(0);
3431 ret = pFoldStringW(MAP_FOLDCZONE, foldczone_src, -1, dst, 256);
3432 ok(ret == ARRAY_SIZE(foldczone_dst), "Got %d, error %d\n", ret, GetLastError());
3433 ok(!memcmp(dst, foldczone_dst, sizeof(foldczone_dst)),
3434 "MAP_FOLDCZONE: Expanded incorrectly\n");
3436 ret = pFoldStringW(MAP_FOLDCZONE|MAP_PRECOMPOSED, foldczone_todo_src, -1, dst, 256);
3437 todo_wine ok(ret == ARRAY_SIZE(foldczone_todo_dst), "Got %d, error %d\n", ret, GetLastError());
3438 todo_wine ok(!memcmp(dst, foldczone_todo_dst, sizeof(foldczone_todo_dst))
3439 || broken(!memcmp(dst, foldczone_todo_broken_dst, sizeof(foldczone_todo_broken_dst))),
3440 "MAP_FOLDCZONE: Expanded incorrectly (%s)\n", wine_dbgstr_w(dst));
3442 /* MAP_EXPAND_LIGATURES */
3443 SetLastError(0);
3444 ret = pFoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
3445 /* NT 4.0 doesn't support MAP_EXPAND_LIGATURES */
3446 if (!(ret == 0 && GetLastError() == ERROR_INVALID_FLAGS)) {
3447 ok(ret == ARRAY_SIZE(ligatures_dst), "Got %d, error %d\n", ret, GetLastError());
3448 ok(!memcmp(dst, ligatures_dst, sizeof(ligatures_dst)),
3449 "MAP_EXPAND_LIGATURES: Expanded incorrectly\n");
3452 /* FIXME: MAP_PRECOMPOSED : MAP_COMPOSITE */
3457 #define LCID_OK(l) \
3458 ok(lcid == l, "Expected lcid = %08x, got %08x\n", l, lcid)
3459 #define MKLCID(x,y,z) MAKELCID(MAKELANGID(x, y), z)
3460 #define LCID_RES(src, res) lcid = ConvertDefaultLocale(src); LCID_OK(res)
3461 #define TEST_LCIDLANG(a,b) LCID_RES(MAKELCID(a,b), MAKELCID(a,b))
3462 #define TEST_LCID(a,b,c) LCID_RES(MKLCID(a,b,c), MKLCID(a,b,c))
3464 static void test_ConvertDefaultLocale(void)
3466 LCID lcid;
3468 /* Doesn't change lcid, even if non default sublang/sort used */
3469 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_US, SORT_DEFAULT);
3470 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_UK, SORT_DEFAULT);
3471 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT);
3472 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE);
3474 /* SUBLANG_NEUTRAL -> SUBLANG_DEFAULT */
3475 LCID_RES(MKLCID(LANG_ENGLISH, SUBLANG_NEUTRAL, SORT_DEFAULT),
3476 MKLCID(LANG_ENGLISH, SUBLANG_DEFAULT, SORT_DEFAULT));
3477 LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_DEFAULT),
3478 MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT));
3480 /* Invariant language is not treated specially */
3481 TEST_LCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT);
3483 /* User/system default languages alone are not mapped */
3484 TEST_LCIDLANG(LANG_SYSTEM_DEFAULT, SORT_JAPANESE_UNICODE);
3485 TEST_LCIDLANG(LANG_USER_DEFAULT, SORT_JAPANESE_UNICODE);
3487 /* Default lcids */
3488 LCID_RES(LOCALE_SYSTEM_DEFAULT, GetSystemDefaultLCID());
3489 LCID_RES(LOCALE_USER_DEFAULT, GetUserDefaultLCID());
3490 LCID_RES(LOCALE_NEUTRAL, GetUserDefaultLCID());
3491 lcid = ConvertDefaultLocale(LOCALE_INVARIANT);
3492 ok(lcid == LOCALE_INVARIANT || broken(lcid == 0x47f) /* win2k[3]/winxp */,
3493 "Expected lcid = %08x, got %08x\n", LOCALE_INVARIANT, lcid);
3496 static BOOL CALLBACK langgrp_procA(LGRPID lgrpid, LPSTR lpszNum, LPSTR lpszName,
3497 DWORD dwFlags, LONG_PTR lParam)
3499 if (winetest_debug > 1)
3500 trace("%08x, %s, %s, %08x, %08lx\n",
3501 lgrpid, lpszNum, lpszName, dwFlags, lParam);
3503 ok(pIsValidLanguageGroup(lgrpid, dwFlags) == TRUE,
3504 "Enumerated grp %d not valid (flags %d)\n", lgrpid, dwFlags);
3506 /* If lParam is one, we are calling with flags defaulted from 0 */
3507 ok(!lParam || (dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED),
3508 "Expected dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED, got %d\n", dwFlags);
3510 return TRUE;
3513 static void test_EnumSystemLanguageGroupsA(void)
3515 BOOL ret;
3517 if (!pEnumSystemLanguageGroupsA || !pIsValidLanguageGroup)
3519 win_skip("EnumSystemLanguageGroupsA and/or IsValidLanguageGroup are not available\n");
3520 return;
3523 /* No enumeration proc */
3524 SetLastError(0);
3525 ret = pEnumSystemLanguageGroupsA(0, LGRPID_INSTALLED, 0);
3526 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3528 win_skip("EnumSystemLanguageGroupsA is not implemented\n");
3529 return;
3531 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3532 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3534 /* Invalid flags */
3535 SetLastError(0);
3536 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED|LGRPID_SUPPORTED, 0);
3537 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3539 /* No flags - defaults to LGRPID_INSTALLED */
3540 SetLastError(0xdeadbeef);
3541 pEnumSystemLanguageGroupsA(langgrp_procA, 0, 1);
3542 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
3544 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED, 0);
3545 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
3548 static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam )
3550 if (winetest_debug > 1)
3551 trace( "%s %x\n", wine_dbgstr_w(name), flags );
3552 return TRUE;
3555 static void test_EnumSystemLocalesEx(void)
3557 BOOL ret;
3559 if (!pEnumSystemLocalesEx)
3561 win_skip( "EnumSystemLocalesEx not available\n" );
3562 return;
3564 SetLastError( 0xdeadbeef );
3565 ret = pEnumSystemLocalesEx( enum_func, LOCALE_ALL, 0, (void *)1 );
3566 ok( !ret, "should have failed\n" );
3567 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
3568 SetLastError( 0xdeadbeef );
3569 ret = pEnumSystemLocalesEx( enum_func, 0, 0, NULL );
3570 ok( ret, "failed err %u\n", GetLastError() );
3573 static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
3574 LONG_PTR lParam)
3576 if (winetest_debug > 1)
3577 trace("%08x, %08x, %s, %08lx\n", lgrpid, lcid, lpszNum, lParam);
3579 /* invalid locale enumerated on some platforms */
3580 if (lcid == 0)
3581 return TRUE;
3583 ok(pIsValidLanguageGroup(lgrpid, LGRPID_SUPPORTED) == TRUE,
3584 "Enumerated grp %d not valid\n", lgrpid);
3585 ok(IsValidLocale(lcid, LCID_SUPPORTED) == TRUE,
3586 "Enumerated grp locale %04x not valid\n", lcid);
3587 return TRUE;
3590 static void test_EnumLanguageGroupLocalesA(void)
3592 BOOL ret;
3594 if (!pEnumLanguageGroupLocalesA || !pIsValidLanguageGroup)
3596 win_skip("EnumLanguageGroupLocalesA and/or IsValidLanguageGroup are not available\n");
3597 return;
3600 /* No enumeration proc */
3601 SetLastError(0);
3602 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0, 0);
3603 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3605 win_skip("EnumLanguageGroupLocalesA is not implemented\n");
3606 return;
3608 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3609 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3611 /* lgrpid too small */
3612 SetLastError(0);
3613 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, 0, 0, 0);
3614 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3615 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3617 /* lgrpid too big */
3618 SetLastError(0);
3619 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_ARMENIAN + 1, 0, 0);
3620 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3621 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3623 /* dwFlags is reserved */
3624 SetLastError(0);
3625 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0x1, 0);
3626 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3627 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3629 pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_WESTERN_EUROPE, 0, 0);
3632 static void test_SetLocaleInfoA(void)
3634 BOOL bRet;
3635 LCID lcid = GetUserDefaultLCID();
3637 /* Null data */
3638 SetLastError(0);
3639 bRet = SetLocaleInfoA(lcid, LOCALE_SDATE, 0);
3640 ok( !bRet && GetLastError() == ERROR_INVALID_PARAMETER,
3641 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3643 /* IDATE */
3644 SetLastError(0);
3645 bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, "test_SetLocaleInfoA");
3646 ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
3647 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3649 /* ILDATE */
3650 SetLastError(0);
3651 bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, "test_SetLocaleInfoA");
3652 ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
3653 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3656 static BOOL CALLBACK luilocale_proc1A(LPSTR value, LONG_PTR lParam)
3658 if (winetest_debug > 1)
3659 trace("%s %08lx\n", value, lParam);
3660 return(TRUE);
3663 static BOOL CALLBACK luilocale_proc2A(LPSTR value, LONG_PTR lParam)
3665 ok(!enumCount, "callback called again unexpected\n");
3666 enumCount++;
3667 return(FALSE);
3670 static BOOL CALLBACK luilocale_proc3A(LPSTR value, LONG_PTR lParam)
3672 ok(0,"callback called unexpected\n");
3673 return(FALSE);
3676 static void test_EnumUILanguageA(void)
3678 BOOL ret;
3679 if (!pEnumUILanguagesA) {
3680 win_skip("EnumUILanguagesA is not available on Win9x or NT4\n");
3681 return;
3684 SetLastError(ERROR_SUCCESS);
3685 ret = pEnumUILanguagesA(luilocale_proc1A, 0, 0);
3686 if (ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3688 win_skip("EnumUILanguagesA is not implemented\n");
3689 return;
3691 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3693 enumCount = 0;
3694 SetLastError(ERROR_SUCCESS);
3695 ret = pEnumUILanguagesA(luilocale_proc2A, 0, 0);
3696 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3698 SetLastError(ERROR_SUCCESS);
3699 ret = pEnumUILanguagesA(NULL, 0, 0);
3700 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3701 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3702 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3704 SetLastError(ERROR_SUCCESS);
3705 ret = pEnumUILanguagesA(luilocale_proc3A, 0x5a5a5a5a, 0);
3706 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3707 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3709 SetLastError(ERROR_SUCCESS);
3710 ret = pEnumUILanguagesA(NULL, 0x5a5a5a5a, 0);
3711 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3712 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3713 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3716 static char date_fmt_buf[1024];
3717 static WCHAR date_fmt_bufW[1024];
3719 static BOOL CALLBACK enum_datetime_procA(LPSTR fmt)
3721 lstrcatA(date_fmt_buf, fmt);
3722 lstrcatA(date_fmt_buf, "\n");
3723 return TRUE;
3726 static BOOL CALLBACK enum_datetime_procW(WCHAR *fmt)
3728 lstrcatW(date_fmt_bufW, fmt);
3729 return FALSE;
3732 static void test_EnumDateFormatsA(void)
3734 char *p, buf[256];
3735 BOOL ret;
3736 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
3738 date_fmt_buf[0] = 0;
3739 SetLastError(0xdeadbeef);
3740 ret = EnumDateFormatsA(enum_datetime_procA, lcid, 0);
3741 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3743 win_skip("0 for dwFlags is not supported\n");
3745 else
3747 ok(ret, "EnumDateFormatsA(0) error %d\n", GetLastError());
3748 trace("EnumDateFormatsA(0): %s\n", date_fmt_buf);
3749 /* test the 1st enumerated format */
3750 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3751 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3752 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3753 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3756 date_fmt_buf[0] = 0;
3757 SetLastError(0xdeadbeef);
3758 ret = EnumDateFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
3759 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3761 win_skip("LOCALE_USE_CP_ACP is not supported\n");
3763 else
3765 ok(ret, "EnumDateFormatsA(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
3766 trace("EnumDateFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
3767 /* test the 1st enumerated format */
3768 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3769 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3770 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3771 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3774 date_fmt_buf[0] = 0;
3775 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_SHORTDATE);
3776 ok(ret, "EnumDateFormatsA(DATE_SHORTDATE) error %d\n", GetLastError());
3777 trace("EnumDateFormatsA(DATE_SHORTDATE): %s\n", date_fmt_buf);
3778 /* test the 1st enumerated format */
3779 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3780 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3781 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3782 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3784 date_fmt_buf[0] = 0;
3785 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_LONGDATE);
3786 ok(ret, "EnumDateFormatsA(DATE_LONGDATE) error %d\n", GetLastError());
3787 trace("EnumDateFormatsA(DATE_LONGDATE): %s\n", date_fmt_buf);
3788 /* test the 1st enumerated format */
3789 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3790 ret = GetLocaleInfoA(lcid, LOCALE_SLONGDATE, buf, sizeof(buf));
3791 ok(ret, "GetLocaleInfoA(LOCALE_SLONGDATE) error %d\n", GetLastError());
3792 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3794 date_fmt_buf[0] = 0;
3795 SetLastError(0xdeadbeef);
3796 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_YEARMONTH);
3797 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3799 win_skip("DATE_YEARMONTH is only present on W2K and later\n");
3800 return;
3802 ok(ret, "EnumDateFormatsA(DATE_YEARMONTH) error %d\n", GetLastError());
3803 trace("EnumDateFormatsA(DATE_YEARMONTH): %s\n", date_fmt_buf);
3804 /* test the 1st enumerated format */
3805 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3806 ret = GetLocaleInfoA(lcid, LOCALE_SYEARMONTH, buf, sizeof(buf));
3807 ok(ret, "GetLocaleInfoA(LOCALE_SYEARMONTH) error %d\n", GetLastError());
3808 ok(!lstrcmpA(date_fmt_buf, buf) || broken(!buf[0]) /* win9x */,
3809 "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3812 static void test_EnumTimeFormatsA(void)
3814 char *p, buf[256];
3815 BOOL ret;
3816 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
3818 date_fmt_buf[0] = 0;
3819 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, 0);
3820 ok(ret, "EnumTimeFormatsA(0) error %d\n", GetLastError());
3821 trace("EnumTimeFormatsA(0): %s\n", date_fmt_buf);
3822 /* test the 1st enumerated format */
3823 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3824 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
3825 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
3826 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3828 date_fmt_buf[0] = 0;
3829 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
3830 ok(ret, "EnumTimeFormatsA(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
3831 trace("EnumTimeFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
3832 /* test the 1st enumerated format */
3833 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3834 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
3835 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
3836 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3839 static void test_EnumTimeFormatsW(void)
3841 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
3842 WCHAR bufW[256];
3843 BOOL ret;
3845 date_fmt_bufW[0] = 0;
3846 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, 0);
3847 ok(ret, "EnumTimeFormatsW(0) error %d\n", GetLastError());
3848 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
3849 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
3850 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
3851 wine_dbgstr_w(bufW));
3853 date_fmt_bufW[0] = 0;
3854 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, LOCALE_USE_CP_ACP);
3855 ok(ret, "EnumTimeFormatsW(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
3856 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
3857 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
3858 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
3859 wine_dbgstr_w(bufW));
3861 /* TIME_NOSECONDS is Win7+ feature */
3862 date_fmt_bufW[0] = 0;
3863 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, TIME_NOSECONDS);
3864 if (!ret && GetLastError() == ERROR_INVALID_FLAGS)
3865 win_skip("EnumTimeFormatsW doesn't support TIME_NOSECONDS\n");
3866 else {
3867 char buf[256];
3869 ok(ret, "EnumTimeFormatsW(TIME_NOSECONDS) error %d\n", GetLastError());
3870 ret = GetLocaleInfoW(lcid, LOCALE_SSHORTTIME, bufW, ARRAY_SIZE(bufW));
3871 ok(ret, "GetLocaleInfoW(LOCALE_SSHORTTIME) error %d\n", GetLastError());
3872 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
3873 wine_dbgstr_w(bufW));
3875 /* EnumTimeFormatsA doesn't support this flag */
3876 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, TIME_NOSECONDS);
3877 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
3878 GetLastError());
3880 ret = EnumTimeFormatsA(NULL, lcid, TIME_NOSECONDS);
3881 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
3882 GetLastError());
3884 /* And it's not supported by GetLocaleInfoA either */
3885 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTTIME, buf, ARRAY_SIZE(buf));
3886 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "GetLocaleInfoA(LOCALE_SSHORTTIME) ret %d, error %d\n", ret,
3887 GetLastError());
3890 static void test_GetCPInfo(void)
3892 BOOL ret;
3893 CPINFO cpinfo;
3895 SetLastError(0xdeadbeef);
3896 ret = GetCPInfo(CP_SYMBOL, &cpinfo);
3897 ok(!ret, "GetCPInfo(CP_SYMBOL) should fail\n");
3898 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3899 "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
3901 SetLastError(0xdeadbeef);
3902 ret = GetCPInfo(CP_UTF7, &cpinfo);
3903 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
3905 win_skip("Codepage CP_UTF7 is not installed/available\n");
3907 else
3909 ok(ret, "GetCPInfo(CP_UTF7) error %u\n", GetLastError());
3910 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
3911 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
3912 ok(cpinfo.LeadByte[0] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[0]);
3913 ok(cpinfo.LeadByte[1] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[1]);
3914 ok(cpinfo.MaxCharSize == 5, "expected 5, got 0x%x\n", cpinfo.MaxCharSize);
3917 SetLastError(0xdeadbeef);
3918 ret = GetCPInfo(CP_UTF8, &cpinfo);
3919 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
3921 win_skip("Codepage CP_UTF8 is not installed/available\n");
3923 else
3925 ok(ret, "GetCPInfo(CP_UTF8) error %u\n", GetLastError());
3926 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
3927 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
3928 ok(cpinfo.LeadByte[0] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[0]);
3929 ok(cpinfo.LeadByte[1] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[1]);
3930 ok(cpinfo.MaxCharSize == 4 || broken(cpinfo.MaxCharSize == 3) /* win9x */,
3931 "expected 4, got %u\n", cpinfo.MaxCharSize);
3936 * The CT_TYPE1 has varied over windows version.
3937 * The current target for correct behavior is windows 7.
3938 * There was a big shift between windows 2000 (first introduced) and windows Xp
3939 * Most of the old values below are from windows 2000.
3940 * A smaller subset of changes happened between windows Xp and Window vista/7
3942 static void test_GetStringTypeW(void)
3944 static const WCHAR blanks[] = {0x9, 0x20, 0xa0, 0x3000, 0xfeff};
3945 static const WORD blanks_new[] = {C1_SPACE | C1_CNTRL | C1_BLANK | C1_DEFINED,
3946 C1_SPACE | C1_BLANK | C1_DEFINED,
3947 C1_SPACE | C1_BLANK | C1_DEFINED,
3948 C1_SPACE | C1_BLANK | C1_DEFINED,
3949 C1_CNTRL | C1_BLANK | C1_DEFINED};
3950 static const WORD blanks_old[] ={C1_SPACE | C1_CNTRL | C1_BLANK,
3951 C1_SPACE | C1_BLANK,
3952 C1_SPACE | C1_BLANK,
3953 C1_SPACE | C1_BLANK,
3954 C1_SPACE | C1_BLANK};
3956 static const WCHAR undefined[] = {0x378, 0x379, 0x5ff, 0xfff8, 0xfffe};
3958 /* Lu, Ll, Lt */
3959 static const WCHAR alpha[] = {0x47, 0x67, 0x1c5};
3960 static const WORD alpha_old[] = {C1_UPPER | C1_ALPHA,
3961 C1_LOWER | C1_ALPHA,
3962 C1_UPPER | C1_LOWER | C1_ALPHA,
3963 C1_ALPHA};
3965 /* Sk, Sk, Mn, So, Me */
3966 static const WCHAR oldpunc[] = { 0x2c2, 0x2e5, 0x322, 0x482, 0x6de,
3967 /* Sc, Sm, No,*/
3968 0xffe0, 0xffe9, 0x2153};
3970 /* Lm, Nl, Cf, 0xad(Cf), 0x1f88 (Lt), Lo, Mc */
3971 static const WCHAR changed[] = {0x2b0, 0x2160, 0x600, 0xad, 0x1f88, 0x294, 0x903};
3972 static const WORD changed_old[] = { C1_PUNCT, C1_PUNCT, 0, C1_PUNCT, C1_UPPER | C1_ALPHA, C1_ALPHA, C1_PUNCT };
3973 static const WORD changed_xp[] = {C1_ALPHA | C1_DEFINED,
3974 C1_ALPHA | C1_DEFINED,
3975 C1_CNTRL | C1_DEFINED,
3976 C1_PUNCT | C1_DEFINED,
3977 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
3978 C1_ALPHA | C1_LOWER | C1_DEFINED,
3979 C1_ALPHA | C1_DEFINED };
3980 static const WORD changed_new[] = { C1_ALPHA | C1_DEFINED,
3981 C1_ALPHA | C1_DEFINED,
3982 C1_CNTRL | C1_DEFINED,
3983 C1_PUNCT | C1_CNTRL | C1_DEFINED,
3984 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
3985 C1_ALPHA | C1_DEFINED,
3986 C1_DEFINED
3988 /* Pc, Pd, Ps, Pe, Pi, Pf, Po*/
3989 static const WCHAR punct[] = { 0x5f, 0x2d, 0x28, 0x29, 0xab, 0xbb, 0x21 };
3991 static const WCHAR punct_special[] = {0x24, 0x2b, 0x3c, 0x3e, 0x5e, 0x60,
3992 0x7c, 0x7e, 0xa2, 0xbe, 0xd7, 0xf7};
3993 static const WCHAR digit_special[] = {0xb2, 0xb3, 0xb9};
3994 static const WCHAR lower_special[] = {0x2071, 0x207f};
3995 static const WCHAR cntrl_special[] = {0x070f, 0x200c, 0x200d,
3996 0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
3997 0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
3998 0xfff9, 0xfffa, 0xfffb};
3999 static const WCHAR space_special[] = {0x09, 0x0d, 0x85};
4001 WORD types[20];
4002 WCHAR ch[2];
4003 BOOL ret;
4004 int i;
4006 /* NULL src */
4007 SetLastError(0xdeadbeef);
4008 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, NULL);
4009 ok(!ret, "got %d\n", ret);
4010 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4012 SetLastError(0xdeadbeef);
4013 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, types);
4014 ok(!ret, "got %d\n", ret);
4015 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4017 SetLastError(0xdeadbeef);
4018 ret = GetStringTypeW(CT_CTYPE1, NULL, 5, types);
4019 ok(!ret, "got %d\n", ret);
4020 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4022 memset(types,0,sizeof(types));
4023 GetStringTypeW(CT_CTYPE1, blanks, 5, types);
4024 for (i = 0; i < 5; i++)
4025 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]);
4027 memset(types,0,sizeof(types));
4028 GetStringTypeW(CT_CTYPE1, alpha, 3, types);
4029 for (i = 0; i < 3; i++)
4030 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]));
4031 memset(types,0,sizeof(types));
4032 GetStringTypeW(CT_CTYPE1, undefined, 5, types);
4033 for (i = 0; i < 5; i++)
4034 ok(types[i] == 0, "incorrect types returned for %x -> (%x != 0)\n",undefined[i], types[i]);
4036 memset(types,0,sizeof(types));
4037 GetStringTypeW(CT_CTYPE1, oldpunc, 8, types);
4038 for (i = 0; i < 8; i++)
4039 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);
4041 memset(types,0,sizeof(types));
4042 GetStringTypeW(CT_CTYPE1, changed, 7, types);
4043 for (i = 0; i < 7; i++)
4044 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]);
4046 memset(types,0,sizeof(types));
4047 GetStringTypeW(CT_CTYPE1, punct, 7, types);
4048 for (i = 0; i < 7; i++)
4049 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));
4052 memset(types,0,sizeof(types));
4053 GetStringTypeW(CT_CTYPE1, punct_special, 12, types);
4054 for (i = 0; i < 12; i++)
4055 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);
4057 memset(types,0,sizeof(types));
4058 GetStringTypeW(CT_CTYPE1, digit_special, 3, types);
4059 for (i = 0; i < 3; i++)
4060 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);
4062 memset(types,0,sizeof(types));
4063 GetStringTypeW(CT_CTYPE1, lower_special, 2, types);
4064 for (i = 0; i < 2; i++)
4065 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);
4067 memset(types,0,sizeof(types));
4068 GetStringTypeW(CT_CTYPE1, cntrl_special, 20, types);
4069 for (i = 0; i < 20; i++)
4070 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);
4072 memset(types,0,sizeof(types));
4073 GetStringTypeW(CT_CTYPE1, space_special, 3, types);
4074 for (i = 0; i < 3; i++)
4075 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 );
4077 /* surrogate pairs */
4078 ch[0] = 0xd800;
4079 memset(types, 0, sizeof(types));
4080 GetStringTypeW(CT_CTYPE3, ch, 1, types);
4081 if (types[0] == C3_NOTAPPLICABLE)
4082 win_skip("C3_HIGHSURROGATE/C3_LOWSURROGATE are not supported.\n");
4083 else {
4084 ok(types[0] == C3_HIGHSURROGATE, "got %x\n", types[0]);
4086 ch[0] = 0xdc00;
4087 memset(types, 0, sizeof(types));
4088 GetStringTypeW(CT_CTYPE3, ch, 1, types);
4089 ok(types[0] == C3_LOWSURROGATE, "got %x\n", types[0]);
4092 /* Zl, Zp categories */
4093 ch[0] = 0x2028;
4094 ch[1] = 0x2029;
4095 memset(types, 0, sizeof(types));
4096 GetStringTypeW(CT_CTYPE1, ch, 2, types);
4097 ok(types[0] == (C1_DEFINED|C1_SPACE), "got %x\n", types[0]);
4098 ok(types[1] == (C1_DEFINED|C1_SPACE), "got %x\n", types[1]);
4100 /* check Arabic range for kashida flag */
4101 for (ch[0] = 0x600; ch[0] <= 0x6ff; ch[0] += 1)
4103 types[0] = 0;
4104 ret = GetStringTypeW(CT_CTYPE3, ch, 1, types);
4105 ok(ret, "%#x: failed %d\n", ch[0], ret);
4106 if (ch[0] == 0x640) /* ARABIC TATWEEL (Kashida) */
4107 ok(types[0] & C3_KASHIDA, "%#x: type %#x\n", ch[0], types[0]);
4108 else
4109 ok(!(types[0] & C3_KASHIDA), "%#x: type %#x\n", ch[0], types[0]);
4113 static void test_IdnToNameprepUnicode(void)
4115 struct {
4116 DWORD in_len;
4117 const WCHAR in[64];
4118 DWORD ret;
4119 DWORD broken_ret;
4120 const WCHAR out[64];
4121 DWORD flags;
4122 DWORD err;
4123 DWORD todo;
4124 } test_data[] = {
4126 5, {'t','e','s','t',0},
4127 5, 5, {'t','e','s','t',0},
4128 0, 0xdeadbeef
4131 3, {'a',0xe111,'b'},
4132 0, 0, {0},
4133 0, ERROR_INVALID_NAME
4136 4, {'t',0,'e',0},
4137 0, 0, {0},
4138 0, ERROR_INVALID_NAME
4141 1, {'T',0},
4142 1, 1, {'T',0},
4143 0, 0xdeadbeef
4146 1, {0},
4147 0, 0, {0},
4148 0, ERROR_INVALID_NAME
4151 6, {' ','-','/','[',']',0},
4152 6, 6, {' ','-','/','[',']',0},
4153 0, 0xdeadbeef
4156 3, {'a','-','a'},
4157 3, 3, {'a','-','a'},
4158 IDN_USE_STD3_ASCII_RULES, 0xdeadbeef
4161 3, {'a','a','-'},
4162 0, 0, {0},
4163 IDN_USE_STD3_ASCII_RULES, ERROR_INVALID_NAME
4165 { /* FoldString is not working as expected when MAP_FOLDCZONE is specified (composition+compatibility) */
4166 10, {'T',0xdf,0x130,0x143,0x37a,0x6a,0x30c,' ',0xaa,0},
4167 12, 12, {'t','s','s','i',0x307,0x144,' ',0x3b9,0x1f0,' ','a',0},
4168 0, 0xdeadbeef, TRUE
4171 11, {'t',0xad,0x34f,0x1806,0x180b,0x180c,0x180d,0x200b,0x200c,0x200d,0},
4172 2, 0, {'t',0},
4173 0, 0xdeadbeef
4175 { /* Another example of incorrectly working FoldString (composition) */
4176 2, {0x3b0, 0},
4177 2, 2, {0x3b0, 0},
4178 0, 0xdeadbeef, TRUE
4181 2, {0x221, 0},
4182 0, 2, {0},
4183 0, ERROR_NO_UNICODE_TRANSLATION
4186 2, {0x221, 0},
4187 2, 2, {0x221, 0},
4188 IDN_ALLOW_UNASSIGNED, 0xdeadbeef
4191 5, {'a','.','.','a',0},
4192 0, 0, {0},
4193 0, ERROR_INVALID_NAME
4196 3, {'a','.',0},
4197 3, 3, {'a','.',0},
4198 0, 0xdeadbeef
4202 WCHAR buf[1024];
4203 DWORD i, ret, err;
4205 if (!pIdnToNameprepUnicode)
4207 win_skip("IdnToNameprepUnicode is not available\n");
4208 return;
4211 ret = pIdnToNameprepUnicode(0, test_data[0].in,
4212 test_data[0].in_len, NULL, 0);
4213 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4215 SetLastError(0xdeadbeef);
4216 ret = pIdnToNameprepUnicode(0, test_data[1].in,
4217 test_data[1].in_len, NULL, 0);
4218 err = GetLastError();
4219 ok(ret == test_data[1].ret, "ret = %d\n", ret);
4220 ok(err == test_data[1].err, "err = %d\n", err);
4222 SetLastError(0xdeadbeef);
4223 ret = pIdnToNameprepUnicode(0, test_data[0].in, -1, buf, ARRAY_SIZE(buf));
4224 err = GetLastError();
4225 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4226 ok(err == 0xdeadbeef, "err = %d\n", err);
4228 SetLastError(0xdeadbeef);
4229 ret = pIdnToNameprepUnicode(0, test_data[0].in, -2, buf, ARRAY_SIZE(buf));
4230 err = GetLastError();
4231 ok(ret == 0, "ret = %d\n", ret);
4232 ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
4234 SetLastError(0xdeadbeef);
4235 ret = pIdnToNameprepUnicode(0, test_data[0].in, 0, buf, ARRAY_SIZE(buf));
4236 err = GetLastError();
4237 ok(ret == 0, "ret = %d\n", ret);
4238 ok(err == ERROR_INVALID_NAME, "err = %d\n", err);
4240 ret = pIdnToNameprepUnicode(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES,
4241 test_data[0].in, -1, buf, ARRAY_SIZE(buf));
4242 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4244 SetLastError(0xdeadbeef);
4245 ret = pIdnToNameprepUnicode(0, NULL, 0, NULL, 0);
4246 err = GetLastError();
4247 ok(ret == 0, "ret = %d\n", ret);
4248 ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
4250 SetLastError(0xdeadbeef);
4251 ret = pIdnToNameprepUnicode(4, NULL, 0, NULL, 0);
4252 err = GetLastError();
4253 ok(ret == 0, "ret = %d\n", ret);
4254 ok(err == ERROR_INVALID_FLAGS || err == ERROR_INVALID_PARAMETER /* Win8 */,
4255 "err = %d\n", err);
4257 for (i=0; i<ARRAY_SIZE(test_data); i++)
4259 SetLastError(0xdeadbeef);
4260 ret = pIdnToNameprepUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len,
4261 buf, ARRAY_SIZE(buf));
4262 err = GetLastError();
4264 todo_wine_if (test_data[i].todo)
4265 ok(ret == test_data[i].ret ||
4266 broken(ret == test_data[i].broken_ret), "%d) ret = %d\n", i, ret);
4268 if(ret != test_data[i].ret)
4269 continue;
4271 ok(err == test_data[i].err, "%d) err = %d\n", i, err);
4272 ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
4273 "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4277 static void test_IdnToAscii(void)
4279 struct {
4280 DWORD in_len;
4281 const WCHAR in[64];
4282 DWORD ret;
4283 const WCHAR out[64];
4284 DWORD flags;
4285 DWORD err;
4286 } test_data[] = {
4288 5, {'T','e','s','t',0},
4289 5, {'T','e','s','t',0},
4290 0, 0xdeadbeef
4293 5, {'T','e',0x017c,'s','t',0},
4294 12, {'x','n','-','-','t','e','s','t','-','c','b','b',0},
4295 0, 0xdeadbeef
4298 12, {'t','e',0x0105,'s','t','.','t','e',0x017c,'s','t',0},
4299 26, {'x','n','-','-','t','e','s','t','-','c','t','a','.','x','n','-','-','t','e','s','t','-','c','b','b',0},
4300 0, 0xdeadbeef
4303 3, {0x0105,'.',0},
4304 9, {'x','n','-','-','2','d','a','.',0},
4305 0, 0xdeadbeef
4308 10, {'h','t','t','p',':','/','/','t',0x0106,0},
4309 17, {'x','n','-','-','h','t','t','p',':','/','/','t','-','7','8','a',0},
4310 0, 0xdeadbeef
4313 10, {0x4e3a,0x8bf4,0x4e0d,0x4ed6,0x5011,0x10d,0x11b,0x305c,0x306a,0},
4314 35, {'x','n','-','-','b','e','a','2','a','1','6','3','1','a','v','b','a',
4315 'v','4','4','t','y','h','a','3','2','b','9','1','e','g','s','2','t',0},
4316 0, 0xdeadbeef
4319 2, {0x221,0},
4320 8, {'x','n','-','-','6','l','a',0},
4321 IDN_ALLOW_UNASSIGNED, 0xdeadbeef
4325 WCHAR buf[1024];
4326 DWORD i, ret, err;
4328 if (!pIdnToAscii)
4330 win_skip("IdnToAscii is not available\n");
4331 return;
4334 for (i=0; i<ARRAY_SIZE(test_data); i++)
4336 SetLastError(0xdeadbeef);
4337 ret = pIdnToAscii(test_data[i].flags, test_data[i].in,
4338 test_data[i].in_len, buf, sizeof(buf));
4339 err = GetLastError();
4340 ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
4341 ok(err == test_data[i].err, "%d) err = %d\n", i, err);
4342 ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
4343 "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4347 static void test_IdnToUnicode(void)
4349 struct {
4350 DWORD in_len;
4351 const WCHAR in[64];
4352 DWORD ret;
4353 const WCHAR out[64];
4354 DWORD flags;
4355 DWORD err;
4356 } test_data[] = {
4358 5, {'T','e','s','.',0},
4359 5, {'T','e','s','.',0},
4360 0, 0xdeadbeef
4363 2, {0x105,0},
4364 0, {0},
4365 0, ERROR_INVALID_NAME
4368 33, {'x','n','-','-','4','d','b','c','a','g','d','a','h','y','m','b',
4369 'x','e','k','h','e','h','6','e','0','a','7','f','e','i','0','b',0},
4370 23, {0x05dc,0x05de,0x05d4,0x05d4,0x05dd,0x05e4,0x05e9,0x05d5,0x05d8,
4371 0x05dc,0x05d0,0x05de,0x05d3,0x05d1,0x05e8,0x05d9,0x05dd,0x05e2,
4372 0x05d1,0x05e8,0x05d9,0x05ea,0},
4373 0, 0xdeadbeef
4376 34, {'t','e','s','t','.','x','n','-','-','k','d','a','9','a','g','5','e',
4377 '9','j','n','f','s','j','.','x','n','-','-','p','d','-','f','n','a'},
4378 16, {'t','e','s','t','.',0x0105,0x0119,0x015b,0x0107,
4379 0x0142,0x00f3,0x017c,'.','p',0x0119,'d'},
4380 0, 0xdeadbeef
4383 64, {'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
4384 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
4385 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
4386 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a'},
4387 0, {0},
4388 0, ERROR_INVALID_NAME
4391 8, {'x','n','-','-','6','l','a',0},
4392 2, {0x221,0},
4393 IDN_ALLOW_UNASSIGNED, 0xdeadbeef
4397 WCHAR buf[1024];
4398 DWORD i, ret, err;
4400 if (!pIdnToUnicode)
4402 win_skip("IdnToUnicode is not available\n");
4403 return;
4406 for (i=0; i<ARRAY_SIZE(test_data); i++)
4408 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in,
4409 test_data[i].in_len, NULL, 0);
4410 ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
4412 SetLastError(0xdeadbeef);
4413 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in,
4414 test_data[i].in_len, buf, sizeof(buf));
4415 err = GetLastError();
4416 ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
4417 ok(err == test_data[i].err, "%d) err = %d\n", i, err);
4418 ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
4419 "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4423 static void test_GetLocaleInfoEx(void)
4425 static const WCHAR enW[] = {'e','n',0};
4426 WCHAR bufferW[80], buffer2[80];
4427 INT ret;
4429 if (!pGetLocaleInfoEx)
4431 win_skip("GetLocaleInfoEx not supported\n");
4432 return;
4435 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4436 ok(ret || broken(ret == 0) /* Vista */, "got %d\n", ret);
4437 if (ret)
4439 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
4440 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
4441 static const WCHAR enusW[] = {'e','n','-','U','S',0};
4442 static const WCHAR usaW[] = {'U','S','A',0};
4443 static const WCHAR enuW[] = {'E','N','U',0};
4444 const struct neutralsublang_name_t *ptr = neutralsublang_names;
4445 DWORD val;
4447 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4448 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
4450 SetLastError(0xdeadbeef);
4451 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, 2);
4452 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, %d\n", ret, GetLastError());
4454 SetLastError(0xdeadbeef);
4455 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, NULL, 0);
4456 ok(ret == 3 && GetLastError() == 0xdeadbeef, "got %d, %d\n", ret, GetLastError());
4458 ret = pGetLocaleInfoEx(enusW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4459 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4460 ok(!lstrcmpW(bufferW, enusW), "got %s\n", wine_dbgstr_w(bufferW));
4462 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVCTRYNAME, bufferW, ARRAY_SIZE(bufferW));
4463 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4464 ok(!lstrcmpW(bufferW, usaW), "got %s\n", wine_dbgstr_w(bufferW));
4466 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVLANGNAME, bufferW, ARRAY_SIZE(bufferW));
4467 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4468 ok(!lstrcmpW(bufferW, enuW), "got %s\n", wine_dbgstr_w(bufferW));
4470 ret = pGetLocaleInfoEx(enusW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
4471 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4472 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
4474 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
4475 ok(ret == 1, "got %d\n", ret);
4476 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
4478 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT | LOCALE_NOUSEROVERRIDE, bufferW, ARRAY_SIZE(bufferW));
4479 ok(ret == 1, "got %d\n", ret);
4480 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
4482 ret = pGetLocaleInfoEx(enW, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
4483 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4484 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
4485 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
4487 skip("Non-English locale\n");
4489 else
4490 ok(!lstrcmpW(bufferW, statesW), "got %s\n", wine_dbgstr_w(bufferW));
4492 bufferW[0] = 0;
4493 SetLastError(0xdeadbeef);
4494 ret = pGetLocaleInfoEx(dummyW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4495 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
4497 while (*ptr->name)
4499 val = 0;
4500 pGetLocaleInfoEx(ptr->name, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
4501 todo_wine_if (ptr->todo)
4502 ok(val == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", wine_dbgstr_w(ptr->name), val, ptr->lcid);
4503 bufferW[0] = 0;
4504 ret = pGetLocaleInfoEx(ptr->name, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4505 ok(ret == lstrlenW(bufferW)+1, "%s: got ret value %d\n", wine_dbgstr_w(ptr->name), ret);
4506 ok(!lstrcmpW(bufferW, ptr->name), "%s: got wrong LOCALE_SNAME %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
4507 ptr++;
4510 ret = pGetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4511 ok(ret && ret == lstrlenW(bufferW)+1, "got ret value %d\n", ret);
4512 ret = GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SNAME, buffer2, ARRAY_SIZE(buffer2));
4513 ok(ret && ret == lstrlenW(buffer2)+1, "got ret value %d\n", ret);
4514 ok(!lstrcmpW(bufferW, buffer2), "LOCALE_SNAMEs don't match %s %s\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(buffer2));
4518 static void test_IsValidLocaleName(void)
4520 static const WCHAR enusW[] = {'e','n','-','U','S',0};
4521 static const WCHAR zzW[] = {'z','z',0};
4522 static const WCHAR zz_zzW[] = {'z','z','-','Z','Z',0};
4523 static const WCHAR zzzzW[] = {'z','z','z','z',0};
4524 BOOL ret;
4526 if (!pIsValidLocaleName)
4528 win_skip("IsValidLocaleName not supported\n");
4529 return;
4532 ret = pIsValidLocaleName(enusW);
4533 ok(ret, "IsValidLocaleName failed\n");
4534 ret = pIsValidLocaleName(zzW);
4535 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
4536 ret = pIsValidLocaleName(zz_zzW);
4537 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
4538 ret = pIsValidLocaleName(zzzzW);
4539 ok(!ret, "IsValidLocaleName should have failed\n");
4540 ret = pIsValidLocaleName(LOCALE_NAME_INVARIANT);
4541 ok(ret, "IsValidLocaleName failed\n");
4542 ret = pIsValidLocaleName(NULL);
4543 ok(!ret, "IsValidLocaleName should have failed\n");
4546 static void test_CompareStringOrdinal(void)
4548 INT ret;
4549 WCHAR test1[] = { 't','e','s','t',0 };
4550 WCHAR test2[] = { 'T','e','S','t',0 };
4551 WCHAR test3[] = { 't','e','s','t','3',0 };
4552 WCHAR null1[] = { 'a',0,'a',0 };
4553 WCHAR null2[] = { 'a',0,'b',0 };
4554 WCHAR bills1[] = { 'b','i','l','l','\'','s',0 };
4555 WCHAR bills2[] = { 'b','i','l','l','s',0 };
4556 WCHAR coop1[] = { 'c','o','-','o','p',0 };
4557 WCHAR coop2[] = { 'c','o','o','p',0 };
4558 WCHAR nonascii1[] = { 0x0102,0 };
4559 WCHAR nonascii2[] = { 0x0201,0 };
4560 WCHAR ch1, ch2;
4562 if (!pCompareStringOrdinal)
4564 win_skip("CompareStringOrdinal not supported\n");
4565 return;
4568 /* Check errors */
4569 SetLastError(0xdeadbeef);
4570 ret = pCompareStringOrdinal(NULL, 0, NULL, 0, FALSE);
4571 ok(!ret, "Got %u, expected 0\n", ret);
4572 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4573 SetLastError(0xdeadbeef);
4574 ret = pCompareStringOrdinal(test1, -1, NULL, 0, FALSE);
4575 ok(!ret, "Got %u, expected 0\n", ret);
4576 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4577 SetLastError(0xdeadbeef);
4578 ret = pCompareStringOrdinal(NULL, 0, test1, -1, FALSE);
4579 ok(!ret, "Got %u, expected 0\n", ret);
4580 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4582 /* Check case */
4583 ret = pCompareStringOrdinal(test1, -1, test1, -1, FALSE);
4584 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
4585 ret = pCompareStringOrdinal(test1, -1, test2, -1, FALSE);
4586 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4587 ret = pCompareStringOrdinal(test2, -1, test1, -1, FALSE);
4588 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4589 ret = pCompareStringOrdinal(test1, -1, test2, -1, TRUE);
4590 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
4592 /* Check different sizes */
4593 ret = pCompareStringOrdinal(test1, 3, test2, -1, TRUE);
4594 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4595 ret = pCompareStringOrdinal(test1, -1, test2, 3, TRUE);
4596 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4598 /* Check null character */
4599 ret = pCompareStringOrdinal(null1, 3, null2, 3, FALSE);
4600 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4601 ret = pCompareStringOrdinal(null1, 3, null2, 3, TRUE);
4602 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4603 ret = pCompareStringOrdinal(test1, 5, test3, 5, FALSE);
4604 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4605 ret = pCompareStringOrdinal(test1, 4, test1, 5, FALSE);
4606 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4608 /* Check ordinal behaviour */
4609 ret = pCompareStringOrdinal(bills1, -1, bills2, -1, FALSE);
4610 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4611 ret = pCompareStringOrdinal(coop2, -1, coop1, -1, FALSE);
4612 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4613 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, FALSE);
4614 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4615 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, TRUE);
4616 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4618 for (ch1 = 0; ch1 < 512; ch1++)
4620 for (ch2 = 0; ch2 < 1024; ch2++)
4622 int diff = ch1 - ch2;
4623 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, FALSE );
4624 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
4625 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
4626 diff = pRtlUpcaseUnicodeChar( ch1 ) - pRtlUpcaseUnicodeChar( ch2 );
4627 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, TRUE );
4628 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
4629 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
4634 static void test_GetGeoInfo(void)
4636 char buffA[20];
4637 INT ret;
4639 if (!pGetGeoInfoA)
4641 win_skip("GetGeoInfo is not available.\n");
4642 return;
4645 /* unassigned id */
4646 SetLastError(0xdeadbeef);
4647 ret = pGetGeoInfoA(344, GEO_ISO2, NULL, 0, 0);
4648 ok(ret == 0, "got %d\n", ret);
4649 ok(GetLastError() == ERROR_INVALID_PARAMETER ||
4650 broken(GetLastError() == 0xdeadbeef /* Win10 */), "got %d\n", GetLastError());
4652 ret = pGetGeoInfoA(203, GEO_ISO2, NULL, 0, 0);
4653 ok(ret == 3, "got %d\n", ret);
4655 ret = pGetGeoInfoA(203, GEO_ISO3, NULL, 0, 0);
4656 ok(ret == 4, "got %d\n", ret);
4658 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 3, 0);
4659 ok(ret == 3, "got %d\n", ret);
4660 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
4662 /* buffer pointer not NULL, length is 0 - return required length */
4663 buffA[0] = 'a';
4664 SetLastError(0xdeadbeef);
4665 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 0, 0);
4666 ok(ret == 3, "got %d\n", ret);
4667 ok(buffA[0] == 'a', "got %c\n", buffA[0]);
4669 ret = pGetGeoInfoA(203, GEO_ISO3, buffA, 4, 0);
4670 ok(ret == 4, "got %d\n", ret);
4671 ok(!strcmp(buffA, "RUS"), "got %s\n", buffA);
4673 /* shorter buffer */
4674 SetLastError(0xdeadbeef);
4675 buffA[1] = buffA[2] = 0;
4676 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 2, 0);
4677 ok(ret == 0, "got %d\n", ret);
4678 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
4679 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d\n", GetLastError());
4681 /* GEO_NATION returns GEOID in a string form */
4682 buffA[0] = 0;
4683 ret = pGetGeoInfoA(203, GEO_NATION, buffA, 20, 0);
4684 ok(ret == 4, "got %d\n", ret);
4685 ok(!strcmp(buffA, "203"), "got %s\n", buffA);
4687 /* GEO_PARENT */
4688 buffA[0] = 0;
4689 ret = pGetGeoInfoA(203, GEO_PARENT, buffA, 20, 0);
4690 if (ret == 0)
4691 win_skip("GEO_PARENT not supported.\n");
4692 else
4694 ok(ret == 6, "got %d\n", ret);
4695 ok(!strcmp(buffA, "47609"), "got %s\n", buffA);
4698 buffA[0] = 0;
4699 ret = pGetGeoInfoA(203, GEO_ISO_UN_NUMBER, buffA, 20, 0);
4700 if (ret == 0)
4701 win_skip("GEO_ISO_UN_NUMBER not supported.\n");
4702 else
4704 ok(ret == 4, "got %d\n", ret);
4705 ok(!strcmp(buffA, "643"), "got %s\n", buffA);
4708 /* try invalid type value */
4709 SetLastError(0xdeadbeef);
4710 ret = pGetGeoInfoA(203, GEO_ID + 1, NULL, 0, 0);
4711 ok(ret == 0, "got %d\n", ret);
4712 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError());
4715 static int geoidenum_count;
4716 static BOOL CALLBACK test_geoid_enumproc(GEOID geoid)
4718 INT ret = pGetGeoInfoA(geoid, GEO_ISO2, NULL, 0, 0);
4719 ok(ret == 3, "got %d for %d\n", ret, geoid);
4720 /* valid geoid starts at 2 */
4721 ok(geoid >= 2, "got geoid %d\n", geoid);
4723 return geoidenum_count++ < 5;
4726 static BOOL CALLBACK test_geoid_enumproc2(GEOID geoid)
4728 geoidenum_count++;
4729 return TRUE;
4732 static void test_EnumSystemGeoID(void)
4734 BOOL ret;
4736 if (!pEnumSystemGeoID)
4738 win_skip("EnumSystemGeoID is not available.\n");
4739 return;
4742 SetLastError(0xdeadbeef);
4743 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, NULL);
4744 ok(!ret, "got %d\n", ret);
4745 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %d\n", GetLastError());
4747 SetLastError(0xdeadbeef);
4748 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, test_geoid_enumproc);
4749 ok(!ret, "got %d\n", ret);
4750 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError());
4752 SetLastError(0xdeadbeef);
4753 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, NULL);
4754 ok(!ret, "got %d\n", ret);
4755 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %d\n", GetLastError());
4757 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, test_geoid_enumproc);
4758 ok(ret, "got %d\n", ret);
4760 /* only the first level is enumerated, not the whole hierarchy */
4761 geoidenum_count = 0;
4762 ret = pEnumSystemGeoID(GEOCLASS_NATION, 39070, test_geoid_enumproc2);
4763 if (ret == 0)
4764 win_skip("Parent GEOID is not supported in EnumSystemGeoID.\n");
4765 else
4766 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
4768 geoidenum_count = 0;
4769 ret = pEnumSystemGeoID(GEOCLASS_REGION, 39070, test_geoid_enumproc2);
4770 if (ret == 0)
4771 win_skip("GEOCLASS_REGION is not supported in EnumSystemGeoID.\n");
4772 else
4774 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
4776 geoidenum_count = 0;
4777 ret = pEnumSystemGeoID(GEOCLASS_REGION, 0, test_geoid_enumproc2);
4778 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
4782 struct invariant_entry {
4783 const char *name;
4784 int id;
4785 const char *expect, *expect2;
4788 #define X(x) #x, x
4789 static const struct invariant_entry invariant_list[] = {
4790 { X(LOCALE_ILANGUAGE), "007f" },
4791 { X(LOCALE_SENGLANGUAGE), "Invariant Language" },
4792 { X(LOCALE_SABBREVLANGNAME), "IVL" },
4793 { X(LOCALE_SNATIVELANGNAME), "Invariant Language" },
4794 { X(LOCALE_ICOUNTRY), "1" },
4795 { X(LOCALE_SENGCOUNTRY), "Invariant Country" },
4796 { X(LOCALE_SABBREVCTRYNAME), "IVC", "" },
4797 { X(LOCALE_SNATIVECTRYNAME), "Invariant Country" },
4798 { X(LOCALE_IDEFAULTLANGUAGE), "0409" },
4799 { X(LOCALE_IDEFAULTCOUNTRY), "1" },
4800 { X(LOCALE_IDEFAULTCODEPAGE), "437" },
4801 { X(LOCALE_IDEFAULTANSICODEPAGE), "1252" },
4802 { X(LOCALE_IDEFAULTMACCODEPAGE), "10000" },
4803 { X(LOCALE_SLIST), "," },
4804 { X(LOCALE_IMEASURE), "0" },
4805 { X(LOCALE_SDECIMAL), "." },
4806 { X(LOCALE_STHOUSAND), "," },
4807 { X(LOCALE_SGROUPING), "3;0" },
4808 { X(LOCALE_IDIGITS), "2" },
4809 { X(LOCALE_ILZERO), "1" },
4810 { X(LOCALE_INEGNUMBER), "1" },
4811 { X(LOCALE_SNATIVEDIGITS), "0123456789" },
4812 { X(LOCALE_SCURRENCY), "\x00a4" },
4813 { X(LOCALE_SINTLSYMBOL), "XDR" },
4814 { X(LOCALE_SMONDECIMALSEP), "." },
4815 { X(LOCALE_SMONTHOUSANDSEP), "," },
4816 { X(LOCALE_SMONGROUPING), "3;0" },
4817 { X(LOCALE_ICURRDIGITS), "2" },
4818 { X(LOCALE_IINTLCURRDIGITS), "2" },
4819 { X(LOCALE_ICURRENCY), "0" },
4820 { X(LOCALE_INEGCURR), "0" },
4821 { X(LOCALE_SDATE), "/" },
4822 { X(LOCALE_STIME), ":" },
4823 { X(LOCALE_SSHORTDATE), "MM/dd/yyyy" },
4824 { X(LOCALE_SLONGDATE), "dddd, dd MMMM yyyy" },
4825 { X(LOCALE_STIMEFORMAT), "HH:mm:ss" },
4826 { X(LOCALE_IDATE), "0" },
4827 { X(LOCALE_ILDATE), "1" },
4828 { X(LOCALE_ITIME), "1" },
4829 { X(LOCALE_ITIMEMARKPOSN), "0" },
4830 { X(LOCALE_ICENTURY), "1" },
4831 { X(LOCALE_ITLZERO), "1" },
4832 { X(LOCALE_IDAYLZERO), "1" },
4833 { X(LOCALE_IMONLZERO), "1" },
4834 { X(LOCALE_S1159), "AM" },
4835 { X(LOCALE_S2359), "PM" },
4836 { X(LOCALE_ICALENDARTYPE), "1" },
4837 { X(LOCALE_IOPTIONALCALENDAR), "0" },
4838 { X(LOCALE_IFIRSTDAYOFWEEK), "6" },
4839 { X(LOCALE_IFIRSTWEEKOFYEAR), "0" },
4840 { X(LOCALE_SDAYNAME1), "Monday" },
4841 { X(LOCALE_SDAYNAME2), "Tuesday" },
4842 { X(LOCALE_SDAYNAME3), "Wednesday" },
4843 { X(LOCALE_SDAYNAME4), "Thursday" },
4844 { X(LOCALE_SDAYNAME5), "Friday" },
4845 { X(LOCALE_SDAYNAME6), "Saturday" },
4846 { X(LOCALE_SDAYNAME7), "Sunday" },
4847 { X(LOCALE_SABBREVDAYNAME1), "Mon" },
4848 { X(LOCALE_SABBREVDAYNAME2), "Tue" },
4849 { X(LOCALE_SABBREVDAYNAME3), "Wed" },
4850 { X(LOCALE_SABBREVDAYNAME4), "Thu" },
4851 { X(LOCALE_SABBREVDAYNAME5), "Fri" },
4852 { X(LOCALE_SABBREVDAYNAME6), "Sat" },
4853 { X(LOCALE_SABBREVDAYNAME7), "Sun" },
4854 { X(LOCALE_SMONTHNAME1), "January" },
4855 { X(LOCALE_SMONTHNAME2), "February" },
4856 { X(LOCALE_SMONTHNAME3), "March" },
4857 { X(LOCALE_SMONTHNAME4), "April" },
4858 { X(LOCALE_SMONTHNAME5), "May" },
4859 { X(LOCALE_SMONTHNAME6), "June" },
4860 { X(LOCALE_SMONTHNAME7), "July" },
4861 { X(LOCALE_SMONTHNAME8), "August" },
4862 { X(LOCALE_SMONTHNAME9), "September" },
4863 { X(LOCALE_SMONTHNAME10), "October" },
4864 { X(LOCALE_SMONTHNAME11), "November" },
4865 { X(LOCALE_SMONTHNAME12), "December" },
4866 { X(LOCALE_SMONTHNAME13), "" },
4867 { X(LOCALE_SABBREVMONTHNAME1), "Jan" },
4868 { X(LOCALE_SABBREVMONTHNAME2), "Feb" },
4869 { X(LOCALE_SABBREVMONTHNAME3), "Mar" },
4870 { X(LOCALE_SABBREVMONTHNAME4), "Apr" },
4871 { X(LOCALE_SABBREVMONTHNAME5), "May" },
4872 { X(LOCALE_SABBREVMONTHNAME6), "Jun" },
4873 { X(LOCALE_SABBREVMONTHNAME7), "Jul" },
4874 { X(LOCALE_SABBREVMONTHNAME8), "Aug" },
4875 { X(LOCALE_SABBREVMONTHNAME9), "Sep" },
4876 { X(LOCALE_SABBREVMONTHNAME10), "Oct" },
4877 { X(LOCALE_SABBREVMONTHNAME11), "Nov" },
4878 { X(LOCALE_SABBREVMONTHNAME12), "Dec" },
4879 { X(LOCALE_SABBREVMONTHNAME13), "" },
4880 { X(LOCALE_SPOSITIVESIGN), "+" },
4881 { X(LOCALE_SNEGATIVESIGN), "-" },
4882 { X(LOCALE_IPOSSIGNPOSN), "3" },
4883 { X(LOCALE_INEGSIGNPOSN), "0" },
4884 { X(LOCALE_IPOSSYMPRECEDES), "1" },
4885 { X(LOCALE_IPOSSEPBYSPACE), "0" },
4886 { X(LOCALE_INEGSYMPRECEDES), "1" },
4887 { X(LOCALE_INEGSEPBYSPACE), "0" },
4888 { X(LOCALE_SISO639LANGNAME), "iv" },
4889 { X(LOCALE_SISO3166CTRYNAME), "IV" },
4890 { X(LOCALE_IDEFAULTEBCDICCODEPAGE), "037" },
4891 { X(LOCALE_IPAPERSIZE), "9" },
4892 { X(LOCALE_SENGCURRNAME), "International Monetary Fund" },
4893 { X(LOCALE_SNATIVECURRNAME), "International Monetary Fund" },
4894 { X(LOCALE_SYEARMONTH), "yyyy MMMM" },
4895 { X(LOCALE_IDIGITSUBSTITUTION), "1" },
4896 { X(LOCALE_SNAME), "" },
4897 { X(LOCALE_SSCRIPTS), "Latn;" },
4898 { 0 }
4900 #undef X
4902 static void test_invariant(void)
4904 int ret;
4905 int len;
4906 char buffer[BUFFER_SIZE];
4907 const struct invariant_entry *ptr = invariant_list;
4909 if (!GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer)))
4911 win_skip("GetLocaleInfoA(LOCALE_INVARIANT) not supported\n"); /* win2k */
4912 return;
4915 while (ptr->name)
4917 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|ptr->id, buffer, sizeof(buffer));
4918 if (!ret && (ptr->id == LOCALE_SNAME || ptr->id == LOCALE_SSCRIPTS))
4919 win_skip("not supported\n"); /* winxp/win2k3 */
4920 else
4922 len = strlen(ptr->expect)+1; /* include \0 */
4923 ok(ret == len || (ptr->expect2 && ret == strlen(ptr->expect2)+1),
4924 "For id %d, expected ret == %d, got %d, error %d\n",
4925 ptr->id, len, ret, GetLastError());
4926 ok(!strcmp(buffer, ptr->expect) || (ptr->expect2 && !strcmp(buffer, ptr->expect2)),
4927 "For id %d, Expected %s, got '%s'\n",
4928 ptr->id, ptr->expect, buffer);
4931 ptr++;
4934 if ((LANGIDFROMLCID(GetSystemDefaultLCID()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)) ||
4935 (LANGIDFROMLCID(GetThreadLocale()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)))
4937 skip("Non US-English locale\n");
4939 else
4941 /* some locales translate these */
4942 static const char lang[] = "Invariant Language (Invariant Country)";
4943 static const char cntry[] = "Invariant Country";
4944 static const char sortm[] = "Math Alphanumerics";
4945 static const char sortd[] = "Default"; /* win2k3 */
4947 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer));
4948 len = lstrlenA(lang) + 1;
4949 ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
4950 ok(!strcmp(buffer, lang), "Expected %s, got '%s'\n", lang, buffer);
4952 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SCOUNTRY, buffer, sizeof(buffer));
4953 len = lstrlenA(cntry) + 1;
4954 ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
4955 ok(!strcmp(buffer, cntry), "Expected %s, got '%s'\n", cntry, buffer);
4957 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SSORTNAME, buffer, sizeof(buffer));
4958 if (ret == lstrlenA(sortm)+1)
4959 ok(!strcmp(buffer, sortm), "Expected %s, got '%s'\n", sortm, buffer);
4960 else if (ret == lstrlenA(sortd)+1) /* win2k3 */
4961 ok(!strcmp(buffer, sortd), "Expected %s, got '%s'\n", sortd, buffer);
4962 else
4963 ok(0, "Expected ret == %d or %d, got %d, error %d\n",
4964 lstrlenA(sortm)+1, lstrlenA(sortd)+1, ret, GetLastError());
4968 static void test_GetSystemPreferredUILanguages(void)
4970 BOOL ret;
4971 ULONG count, size, size_id, size_name, size_buffer;
4972 WCHAR *buffer;
4975 if (!pGetSystemPreferredUILanguages)
4977 win_skip("GetSystemPreferredUILanguages is not available.\n");
4978 return;
4981 /* (in)valid first parameter */
4982 count = 0xdeadbeef;
4983 size = 0;
4984 SetLastError(0xdeadbeef);
4985 ret = pGetSystemPreferredUILanguages(0, &count, NULL, &size);
4986 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
4987 ok(count, "Expected count > 0\n");
4988 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
4990 count = 0xdeadbeef;
4991 size = 0;
4992 SetLastError(0xdeadbeef);
4993 ret = pGetSystemPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
4994 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
4995 ok(ERROR_INVALID_PARAMETER == GetLastError(),
4996 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
4998 count = 0xdeadbeef;
4999 size = 0;
5000 SetLastError(0xdeadbeef);
5001 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
5002 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5003 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5004 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5006 count = 0xdeadbeef;
5007 size = 0;
5008 SetLastError(0xdeadbeef);
5009 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME, &count, NULL, &size);
5010 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5011 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5012 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5014 count = 0xdeadbeef;
5015 size = 0;
5016 SetLastError(0xdeadbeef);
5017 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5018 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5019 ok(count, "Expected count > 0\n");
5020 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5022 count = 0xdeadbeef;
5023 size = 0;
5024 SetLastError(0xdeadbeef);
5025 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5026 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5027 ok(count, "Expected count > 0\n");
5028 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5030 /* second parameter
5031 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, NULL, NULL, &size);
5032 * -> unhandled exception c0000005
5035 /* invalid third parameter */
5036 count = 0xdeadbeef;
5037 size = 1;
5038 SetLastError(0xdeadbeef);
5039 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
5040 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5041 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5042 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5044 /* fourth parameter
5045 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, NULL);
5046 * -> unhandled exception c0000005
5049 count = 0xdeadbeef;
5050 size_id = 0;
5051 SetLastError(0xdeadbeef);
5052 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5053 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5054 ok(count, "Expected count > 0\n");
5055 ok(size_id % 5 == 1, "Expected size (%d) %% 5 == 1\n", size_id);
5057 count = 0xdeadbeef;
5058 size_name = 0;
5059 SetLastError(0xdeadbeef);
5060 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
5061 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5062 ok(count, "Expected count > 0\n");
5063 ok(size_name % 6 == 1, "Expected size (%d) %% 6 == 1\n", size_name);
5065 size_buffer = max(size_id, size_name);
5066 if(!size_buffer)
5068 skip("No valid buffer size\n");
5069 return;
5072 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
5073 if (!buffer)
5075 skip("Failed to allocate memory for %d chars\n", size_buffer);
5076 return;
5079 count = 0xdeadbeef;
5080 size = size_buffer;
5081 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5082 SetLastError(0xdeadbeef);
5083 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
5084 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5085 ok(count, "Expected count > 0\n");
5086 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5087 if (ret && size % 6 == 1)
5088 ok(!buffer[size -2] && !buffer[size -1],
5089 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5090 buffer[size -2], buffer[size -1]);
5092 count = 0xdeadbeef;
5093 size = size_buffer;
5094 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5095 SetLastError(0xdeadbeef);
5096 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5097 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5098 ok(count, "Expected count > 0\n");
5099 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5100 if (ret && size % 5 == 1)
5101 ok(!buffer[size -2] && !buffer[size -1],
5102 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5103 buffer[size -2], buffer[size -1]);
5105 count = 0xdeadbeef;
5106 size = size_buffer;
5107 SetLastError(0xdeadbeef);
5108 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
5109 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5110 ok(count, "Expected count > 0\n");
5111 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5112 if (ret && size % 5 == 1)
5113 ok(!buffer[size -2] && !buffer[size -1],
5114 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5115 buffer[size -2], buffer[size -1]);
5117 count = 0xdeadbeef;
5118 size = 0;
5119 SetLastError(0xdeadbeef);
5120 ret = pGetSystemPreferredUILanguages(MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5121 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5122 ok(count, "Expected count > 0\n");
5123 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5124 if (ret && size % 6 == 1)
5125 ok(!buffer[size -2] && !buffer[size -1],
5126 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5127 buffer[size -2], buffer[size -1]);
5129 count = 0xdeadbeef;
5130 size = 1;
5131 SetLastError(0xdeadbeef);
5132 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5133 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5134 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5135 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5137 count = 0xdeadbeef;
5138 size = size_id -1;
5139 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5140 SetLastError(0xdeadbeef);
5141 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5142 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5143 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5144 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5146 count = 0xdeadbeef;
5147 size = size_id -2;
5148 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5149 SetLastError(0xdeadbeef);
5150 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
5151 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5152 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5153 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5155 HeapFree(GetProcessHeap(), 0, buffer);
5158 static void test_GetThreadPreferredUILanguages(void)
5160 BOOL ret;
5161 ULONG count, size;
5162 WCHAR *buf;
5164 if (!pGetThreadPreferredUILanguages)
5166 win_skip("GetThreadPreferredUILanguages is not available.\n");
5167 return;
5170 size = count = 0;
5171 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, NULL, &size);
5172 ok(ret, "got %u\n", GetLastError());
5173 ok(count, "expected count > 0\n");
5174 ok(size, "expected size > 0\n");
5176 count = 0;
5177 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size * sizeof(WCHAR));
5178 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, buf, &size);
5179 ok(ret, "got %u\n", GetLastError());
5180 ok(count, "expected count > 0\n");
5181 HeapFree(GetProcessHeap(), 0, buf);
5184 static void test_GetUserPreferredUILanguages(void)
5186 BOOL ret;
5187 ULONG count, size, size_id, size_name, size_buffer;
5188 WCHAR *buffer;
5191 if (!pGetUserPreferredUILanguages)
5193 win_skip("GetUserPreferredUILanguages is not available.\n");
5194 return;
5197 count = 0xdeadbeef;
5198 size = 0;
5199 SetLastError(0xdeadbeef);
5200 ret = pGetUserPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
5201 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5202 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5203 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5205 count = 0xdeadbeef;
5206 size = 0;
5207 SetLastError(0xdeadbeef);
5208 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
5209 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5210 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5211 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5213 count = 0xdeadbeef;
5214 size = 0;
5215 SetLastError(0xdeadbeef);
5216 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5217 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5218 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5219 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5221 count = 0xdeadbeef;
5222 size = 1;
5223 SetLastError(0xdeadbeef);
5224 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
5225 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5226 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5227 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5229 count = 0xdeadbeef;
5230 size_id = 0;
5231 SetLastError(0xdeadbeef);
5232 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5233 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5234 ok(count, "Expected count > 0\n");
5235 ok(size_id % 5 == 1, "Expected size (%d) %% 5 == 1\n", size_id);
5237 count = 0xdeadbeef;
5238 size_name = 0;
5239 SetLastError(0xdeadbeef);
5240 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
5241 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5242 ok(count, "Expected count > 0\n");
5243 ok(size_name % 6 == 1, "Expected size (%d) %% 6 == 1\n", size_name);
5245 size_buffer = max(size_id, size_name);
5246 if(!size_buffer)
5248 skip("No valid buffer size\n");
5249 return;
5252 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
5254 count = 0xdeadbeef;
5255 size = size_buffer;
5256 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5257 SetLastError(0xdeadbeef);
5258 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
5259 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5260 ok(count, "Expected count > 0\n");
5261 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5262 if (ret && size % 6 == 1)
5263 ok(!buffer[size -2] && !buffer[size -1],
5264 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5265 buffer[size -2], buffer[size -1]);
5267 count = 0xdeadbeef;
5268 size = size_buffer;
5269 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5270 SetLastError(0xdeadbeef);
5271 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5272 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5273 ok(count, "Expected count > 0\n");
5274 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5275 if (ret && size % 5 == 1)
5276 ok(!buffer[size -2] && !buffer[size -1],
5277 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5278 buffer[size -2], buffer[size -1]);
5280 count = 0xdeadbeef;
5281 size = size_buffer;
5282 SetLastError(0xdeadbeef);
5283 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
5284 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5285 ok(count, "Expected count > 0\n");
5286 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5287 if (ret && size % 5 == 1)
5288 ok(!buffer[size -2] && !buffer[size -1],
5289 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5290 buffer[size -2], buffer[size -1]);
5292 count = 0xdeadbeef;
5293 size = 1;
5294 SetLastError(0xdeadbeef);
5295 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5296 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5297 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5298 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5300 count = 0xdeadbeef;
5301 size = size_id -1;
5302 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5303 SetLastError(0xdeadbeef);
5304 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5305 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5306 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5307 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5309 count = 0xdeadbeef;
5310 size = size_id -2;
5311 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5312 SetLastError(0xdeadbeef);
5313 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
5314 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5315 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5316 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5318 HeapFree(GetProcessHeap(), 0, buffer);
5321 static void test_FindNLSStringEx(void)
5323 INT res;
5324 static WCHAR en_simpsimpW[] = {'S','i','m','p','l','e','S','i','m','p','l','e',0};
5325 static WCHAR en_simpW[] = {'S','i','m','p','l','e',0};
5326 static WCHAR comb_s_accent1W[] = {0x1e69, 'o','u','r','c','e',0};
5327 static WCHAR comb_s_accent2W[] = {0x0073,0x323,0x307,'o','u','r','c','e',0};
5328 static WCHAR comb_q_accent1W[] = {0x0071,0x0307,0x323,'u','o','t','e',0};
5329 static WCHAR comb_q_accent2W[] = {0x0071,0x0323,0x307,'u','o','t','e',0};
5330 struct test_data {
5331 const WCHAR *locale;
5332 DWORD flags;
5333 WCHAR *src;
5334 INT src_size;
5335 WCHAR *value;
5336 INT val_size;
5337 INT found;
5338 INT expected_ret;
5339 INT expected_found;
5340 int todo;
5341 BOOL broken_vista_servers;
5344 static struct test_data test_arr[] =
5346 { localeW, FIND_FROMSTART, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5347 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 0, 6, 0, FALSE},
5348 { localeW, FIND_FROMEND, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5349 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 6, 6, 0, FALSE},
5350 { localeW, FIND_STARTSWITH, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5351 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 0, 6, 0, FALSE},
5352 { localeW, FIND_ENDSWITH, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5353 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 6, 6, 0, FALSE},
5354 { localeW, FIND_FROMSTART, comb_s_accent1W, ARRAY_SIZE(comb_s_accent1W)-1,
5355 comb_s_accent2W, ARRAY_SIZE(comb_s_accent2W)-1, 0, 0, 6, 1, TRUE },
5356 { localeW, FIND_FROMSTART, comb_q_accent1W, ARRAY_SIZE(comb_q_accent1W)-1,
5357 comb_q_accent2W, ARRAY_SIZE(comb_q_accent2W)-1, 0, 0, 7, 1, FALSE },
5358 { 0 }
5360 struct test_data *ptest;
5362 if (!pFindNLSStringEx)
5364 win_skip("FindNLSStringEx is not available.\n");
5365 return;
5368 SetLastError( 0xdeadbeef );
5369 res = pFindNLSStringEx(invalidW, FIND_FROMSTART, fooW, 3, fooW,
5370 3, NULL, NULL, NULL, 0);
5371 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5372 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5373 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5375 SetLastError( 0xdeadbeef );
5376 res = pFindNLSStringEx(localeW, FIND_FROMSTART, NULL, 3, fooW, 3,
5377 NULL, NULL, NULL, 0);
5378 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5379 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5380 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5382 SetLastError( 0xdeadbeef );
5383 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, -5, fooW, 3,
5384 NULL, NULL, NULL, 0);
5385 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5386 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5387 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5389 SetLastError( 0xdeadbeef );
5390 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, NULL, 3,
5391 NULL, NULL, NULL, 0);
5392 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5393 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5394 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5396 SetLastError( 0xdeadbeef );
5397 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, fooW, -5,
5398 NULL, NULL, NULL, 0);
5399 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5400 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5401 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5403 for (ptest = test_arr; ptest->src != NULL; ptest++)
5405 todo_wine_if(ptest->todo)
5407 res = pFindNLSStringEx(ptest->locale, ptest->flags, ptest->src, ptest->src_size,
5408 ptest->value, ptest->val_size, &ptest->found, NULL, NULL, 0);
5409 if (ptest->broken_vista_servers)
5411 ok(res == ptest->expected_ret || /* Win 7 onwards */
5412 broken(res == -1), /* Win Vista, Server 2003 and 2008 */
5413 "Expected FindNLSStringEx to return %d. Returned value was %d\n",
5414 ptest->expected_ret, res);
5415 ok(ptest->found == ptest->expected_found || /* Win 7 onwards */
5416 broken(ptest->found == 0), /* Win Vista, Server 2003 and 2008 */
5417 "Expected FindNLSStringEx to output %d. Value was %d\n",
5418 ptest->expected_found, ptest->found);
5420 else
5422 ok(res == ptest->expected_ret,
5423 "Expected FindNLSStringEx to return %d. Returned value was %d\n",
5424 ptest->expected_ret, res);
5425 ok(ptest->found == ptest->expected_found,
5426 "Expected FindNLSStringEx to output %d. Value was %d\n",
5427 ptest->expected_found, ptest->found);
5433 static void test_SetThreadUILanguage(void)
5435 LANGID res;
5437 if (!pGetThreadUILanguage)
5439 win_skip("GetThreadUILanguage isn't implemented, skipping SetThreadUILanguage tests for version < Vista\n");
5440 return; /* BTW SetThreadUILanguage is present on winxp/2003 but doesn`t set the LANGID anyway when tested */
5443 res = pSetThreadUILanguage(0);
5444 ok(res == pGetThreadUILanguage(), "expected %d got %d\n", pGetThreadUILanguage(), res);
5446 res = pSetThreadUILanguage(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN));
5447 ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
5448 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
5450 res = pSetThreadUILanguage(0);
5451 todo_wine ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
5452 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
5455 static void test_NormalizeString(void)
5457 /* part 0: specific cases */
5458 /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
5459 static const WCHAR part0_str1[] = {0x1e0a,0};
5460 static const WCHAR part0_nfd1[] = {0x0044,0x0307,0};
5462 /* LATIN CAPITAL LETTER D, COMBINING DOT BELOW, COMBINING DOT ABOVE */
5463 static const WCHAR part0_str2[] = {0x0044,0x0323,0x0307,0};
5464 static const WCHAR part0_nfc2[] = {0x1e0c,0x0307,0};
5466 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
5467 static const WCHAR part0_str3[] = {0x0044,0x031b,0x0323,0x0307,0};
5468 static const WCHAR part0_nfc3[] = {0x1e0c,0x031b,0x0307,0};
5470 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
5471 static const WCHAR part0_str4[] = {0x0044,0x031b,0x0323,0x0307,0};
5472 static const WCHAR part0_nfc4[] = {0x1e0c,0x031b,0x0307,0};
5475 * HEBREW ACCENT SEGOL, HEBREW POINT PATAH, HEBREW POINT DAGESH OR MAPIQ,
5476 * HEBREW ACCENT MERKHA, HEBREW POINT SHEVA, HEBREW PUNCTUATION PASEQ,
5477 * HEBREW MARK UPPER DOT, HEBREW ACCENT DEHI
5479 static const WCHAR part0_str5[] = {0x0592,0x05B7,0x05BC,0x05A5,0x05B0,0x05C0,0x05C4,0x05AD,0};
5480 static const WCHAR part0_nfc5[] = {0x05B0,0x05B7,0x05BC,0x05A5,0x0592,0x05C0,0x05AD,0x05C4,0};
5483 * HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT HATAF SEGOL,
5484 * HEBREW ACCENT ETNAHTA, HEBREW PUNCTUATION SOF PASUQ, HEBREW POINT SHEVA,
5485 * HEBREW ACCENT ILUY, HEBREW ACCENT QARNEY PARA
5487 static const WCHAR part0_str6[] = {0x05B8,0x05B9,0x05B1,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
5488 static const WCHAR part0_nfc6[] = {0x05B1,0x05B8,0x05B9,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
5490 /* LATIN CAPITAL LETTER D WITH DOT BELOW */
5491 static const WCHAR part0_str8[] = {0x1E0C,0};
5492 static const WCHAR part0_nfd8[] = {0x0044,0x0323,0};
5494 /* LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING DOT BELOW */
5495 static const WCHAR part0_str9[] = {0x1E0A,0x0323,0};
5496 static const WCHAR part0_nfc9[] = {0x1E0C,0x0307,0};
5497 static const WCHAR part0_nfd9[] = {0x0044,0x0323,0x0307,0};
5499 /* LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING DOT ABOVE */
5500 static const WCHAR part0_str10[] = {0x1E0C,0x0307,0};
5501 static const WCHAR part0_nfd10[] = {0x0044,0x0323,0x0307,0};
5503 /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE, COMBINING MACRON */
5504 static const WCHAR part0_str11[] = {0x1E14,0x0304,0};
5505 static const WCHAR part0_nfd11[] = {0x0045,0x0304,0x0300,0x0304,0};
5507 /* LATIN CAPITAL LETTER E WITH MACRON, COMBINING GRAVE ACCENT */
5508 static const WCHAR part0_str12[] = {0x0112,0x0300,0};
5509 static const WCHAR part0_nfc12[] = {0x1E14,0};
5510 static const WCHAR part0_nfd12[] = {0x0045,0x0304,0x0300,0};
5512 /* part 1: character by character */
5513 /* DIAERESIS */
5514 static const WCHAR part1_str1[] = {0x00a8,0};
5515 static const WCHAR part1_nfkc1[] = {0x0020,0x0308,0};
5517 /* VULGAR FRACTION ONE QUARTER */
5518 static const WCHAR part1_str2[] = {0x00bc,0};
5519 static const WCHAR part1_nfkc2[] = {0x0031,0x2044,0x0034,0};
5521 /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
5522 static const WCHAR part1_str3[] = {0x00ca,0};
5523 static const WCHAR part1_nfd3[] = {0x0045,0x0302,0};
5525 /* MODIFIER LETTER SMALL GAMMA */
5526 static const WCHAR part1_str4[] = {0x02e0,0};
5527 static const WCHAR part1_nfkc4[] = {0x0263,0};
5529 /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */
5530 static const WCHAR part1_str5[] = {0x0400,0};
5531 static const WCHAR part1_nfd5[] = {0x0415,0x0300,0};
5533 /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */
5534 static const WCHAR part1_str6[] = {0x0476,0};
5535 static const WCHAR part1_nfd6[] = {0x0474,0x030F,0};
5537 /* ARABIC LIGATURE HAH WITH JEEM INITIAL FORM */
5538 static const WCHAR part1_str7[] = {0xFCA9,0};
5539 static const WCHAR part1_nfkc7[] = {0x062D,0x062C,0};
5541 /* GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA */
5542 static const WCHAR part1_str8[] = {0x1F42,0};
5543 static const WCHAR part1_nfd8[] = {0x03BF,0x0313,0x0300,0};
5545 /* QUADRUPLE PRIME */
5546 static const WCHAR part1_str9[] = {0x2057,0};
5547 static const WCHAR part1_nfkc9[] = {0x2032,0x2032,0x2032,0x2032,0};
5549 /* KATAKANA-HIRAGANA VOICED SOUND MARK */
5550 static const WCHAR part1_str10[] = {0x309B,0};
5551 static const WCHAR part1_nfkc10[] = {0x20,0x3099,0};
5553 struct test_data_normal {
5554 const WCHAR *str;
5555 const WCHAR *expected[4];
5557 static const struct test_data_normal test_arr[] =
5559 { part0_str1, { part0_str1, part0_nfd1, part0_str1, part0_nfd1 } },
5560 { part0_str2, { part0_nfc2, part0_str2, part0_nfc2, part0_str2 } },
5561 { part0_str3, { part0_nfc3, part0_str3, part0_nfc3, part0_str3 } },
5562 { part0_str4, { part0_nfc4, part0_str4, part0_nfc4, part0_str4 } },
5563 { part0_str5, { part0_nfc5, part0_nfc5, part0_nfc5, part0_nfc5 } },
5564 { part0_str6, { part0_nfc6, part0_nfc6, part0_nfc6, part0_nfc6 } },
5565 { part0_str8, { part0_str8, part0_nfd8, part0_str8, part0_nfd8 } },
5566 { part0_str9, { part0_nfc9, part0_nfd9, part0_nfc9, part0_nfd9 } },
5567 { part0_str10, { part0_str10, part0_nfd10, part0_str10, part0_nfd10 } },
5568 { part0_str11, { part0_str11, part0_nfd11, part0_str11, part0_nfd11 } },
5569 { part0_str12, { part0_nfc12, part0_nfd12, part0_nfc12, part0_nfd12 } },
5570 { part1_str1, { part1_str1, part1_str1, part1_nfkc1, part1_nfkc1 } },
5571 { part1_str2, { part1_str2, part1_str2, part1_nfkc2, part1_nfkc2 } },
5572 { part1_str3, { part1_str3, part1_nfd3, part1_str3, part1_nfd3 } },
5573 { part1_str4, { part1_str4, part1_str4, part1_nfkc4, part1_nfkc4 } },
5574 { part1_str5, { part1_str5, part1_nfd5, part1_str5, part1_nfd5 } },
5575 { part1_str6, { part1_str6, part1_nfd6, part1_str6, part1_nfd6 } },
5576 { part1_str7, { part1_str7, part1_str7, part1_nfkc7, part1_nfkc7 } },
5577 { part1_str8, { part1_str8, part1_nfd8, part1_str8, part1_nfd8 } },
5578 { part1_str9, { part1_str9, part1_str9, part1_nfkc9, part1_nfkc9 } },
5579 { part1_str10, { part1_str10, part1_str10, part1_nfkc10, part1_nfkc10 } },
5580 { 0 }
5582 const struct test_data_normal *ptest = test_arr;
5583 const int norm_forms[] = { NormalizationC, NormalizationD, NormalizationKC, NormalizationKD };
5584 WCHAR dst[80];
5585 int dstlen;
5587 if (!pNormalizeString)
5589 win_skip("NormalizeString is not available.\n");
5590 return;
5593 todo_wine {
5594 dstlen = pNormalizeString( NormalizationD, ptest->str, -1, dst, 1 );
5595 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Should have failed with ERROR_INSUFFICIENT_BUFFER\n");
5599 * For each string, first test passing -1 as srclen to NormalizeString,
5600 * thereby assuming a null-terminating string in src, and then test passing
5601 * explicitly the string length.
5602 * Do that for all 4 normalization forms.
5604 while (ptest->str)
5606 int str_cmp, i;
5608 for (i = 0; i < 4; i++)
5610 todo_wine {
5611 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, NULL, 0 );
5612 if (dstlen)
5614 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, dst, dstlen );
5615 ok(dstlen == strlenW( ptest->expected[i] )+1, "Copied length differed: was %d, should be %d\n",
5616 dstlen, strlenW( ptest->expected[i] )+1);
5617 str_cmp = strncmpW( ptest->expected[i], dst, dstlen+1 );
5618 ok( str_cmp == 0, "test failed: returned value was %d\n", str_cmp );
5621 dstlen = pNormalizeString( norm_forms[i], ptest->str, strlenW(ptest->str), NULL, 0 );
5622 if (dstlen)
5624 dstlen = pNormalizeString( norm_forms[i], ptest->str, strlenW(ptest->str), dst, dstlen );
5625 ok(dstlen == strlenW( ptest->expected[i] ), "Copied length differed: was %d, should be %d\n",
5626 dstlen, strlenW( ptest->expected[i] ));
5627 str_cmp = strncmpW( ptest->expected[i], dst, dstlen );
5628 ok( str_cmp == 0, "test failed: returned value was %d\n", str_cmp );
5632 ptest++;
5636 START_TEST(locale)
5638 InitFunctionPointers();
5640 test_EnumTimeFormatsA();
5641 test_EnumTimeFormatsW();
5642 test_EnumDateFormatsA();
5643 test_GetLocaleInfoA();
5644 test_GetLocaleInfoW();
5645 test_GetLocaleInfoEx();
5646 test_GetTimeFormatA();
5647 test_GetTimeFormatEx();
5648 test_GetDateFormatA();
5649 test_GetDateFormatEx();
5650 test_GetDateFormatW();
5651 test_GetCurrencyFormatA(); /* Also tests the W version */
5652 test_GetNumberFormatA(); /* Also tests the W version */
5653 test_GetNumberFormatEx();
5654 test_CompareStringA();
5655 test_CompareStringW();
5656 test_CompareStringEx();
5657 test_LCMapStringA();
5658 test_LCMapStringW();
5659 test_LCMapStringEx();
5660 test_LocaleNameToLCID();
5661 test_FoldStringA();
5662 test_FoldStringW();
5663 test_ConvertDefaultLocale();
5664 test_EnumSystemLanguageGroupsA();
5665 test_EnumSystemLocalesEx();
5666 test_EnumLanguageGroupLocalesA();
5667 test_SetLocaleInfoA();
5668 test_EnumUILanguageA();
5669 test_GetCPInfo();
5670 test_GetStringTypeW();
5671 test_IdnToNameprepUnicode();
5672 test_IdnToAscii();
5673 test_IdnToUnicode();
5674 test_IsValidLocaleName();
5675 test_CompareStringOrdinal();
5676 test_GetGeoInfo();
5677 test_EnumSystemGeoID();
5678 test_invariant();
5679 test_GetSystemPreferredUILanguages();
5680 test_GetThreadPreferredUILanguages();
5681 test_GetUserPreferredUILanguages();
5682 test_FindNLSStringEx();
5683 test_SetThreadUILanguage();
5684 test_NormalizeString();
5685 /* this requires collation table patch to make it MS compatible */
5686 if (0) test_sorting();