kernelbase: Fix NormalizeString() return value and last error.
[wine.git] / dlls / kernel32 / tests / locale.c
blob76a993e6c3c6e776373d2288c0356b7fde636b61
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 "ntstatus.h"
34 #define WIN32_NO_STATUS
35 #include "wine/test.h"
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winerror.h"
39 #include "winnls.h"
40 #include "winternl.h"
42 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};
43 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};
44 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};
45 static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
46 static const WCHAR localeW[] = {'e','n','-','U','S',0};
47 static const WCHAR fooW[] = {'f','o','o',0};
48 static const WCHAR emptyW[] = {0};
49 static const WCHAR invalidW[] = {'i','n','v','a','l','i','d',0};
51 /* Some functions are only in later versions of kernel32.dll */
52 static WORD enumCount;
54 static INT (WINAPI *pGetTimeFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT);
55 static INT (WINAPI *pGetDateFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT, LPCWSTR);
56 static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROCA, DWORD, LONG_PTR);
57 static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROCA, LGRPID, DWORD, LONG_PTR);
58 static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROCA, DWORD, LONG_PTR);
59 static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
60 static INT (WINAPI *pLCMapStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM);
61 static LCID (WINAPI *pLocaleNameToLCID)(LPCWSTR, DWORD);
62 static NTSTATUS (WINAPI *pRtlLocaleNameToLcid)(LPCWSTR, LCID *, DWORD);
63 static INT (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD);
64 static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
65 static INT (WINAPI *pIdnToNameprepUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
66 static INT (WINAPI *pIdnToAscii)(DWORD, LPCWSTR, INT, LPWSTR, INT);
67 static INT (WINAPI *pIdnToUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
68 static INT (WINAPI *pGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, INT);
69 static BOOL (WINAPI *pIsValidLocaleName)(LPCWSTR);
70 static INT (WINAPI *pCompareStringOrdinal)(const WCHAR *, INT, const WCHAR *, INT, BOOL);
71 static INT (WINAPI *pCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT,
72 LPNLSVERSIONINFO, LPVOID, LPARAM);
73 static INT (WINAPI *pGetGeoInfoA)(GEOID, GEOTYPE, LPSTR, INT, LANGID);
74 static INT (WINAPI *pGetGeoInfoW)(GEOID, GEOTYPE, LPWSTR, INT, LANGID);
75 static BOOL (WINAPI *pEnumSystemGeoID)(GEOCLASS, GEOID, GEO_ENUMPROC);
76 static BOOL (WINAPI *pGetSystemPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
77 static BOOL (WINAPI *pGetThreadPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
78 static BOOL (WINAPI *pGetUserPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
79 static WCHAR (WINAPI *pRtlUpcaseUnicodeChar)(WCHAR);
80 static INT (WINAPI *pGetNumberFormatEx)(LPCWSTR, DWORD, LPCWSTR, const NUMBERFMTW *, LPWSTR, int);
81 static INT (WINAPI *pFindNLSStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPCWSTR, INT, LPINT, LPNLSVERSIONINFO, LPVOID, LPARAM);
82 static LANGID (WINAPI *pSetThreadUILanguage)(LANGID);
83 static LANGID (WINAPI *pGetThreadUILanguage)(VOID);
84 static INT (WINAPI *pNormalizeString)(NORM_FORM, LPCWSTR, INT, LPWSTR, INT);
85 static INT (WINAPI *pFindStringOrdinal)(DWORD, LPCWSTR lpStringSource, INT, LPCWSTR, INT, BOOL);
86 static NTSTATUS (WINAPI *pRtlNormalizeString)(ULONG, LPCWSTR, INT, LPWSTR, INT*);
87 static NTSTATUS (WINAPI *pRtlIsNormalizedString)(ULONG, LPCWSTR, INT, BOOLEAN*);
88 static NTSTATUS (WINAPI *pNtGetNlsSectionPtr)(ULONG,ULONG,void*,void**,SIZE_T*);
89 static void (WINAPI *pRtlInitCodePageTable)(USHORT*,CPTABLEINFO*);
90 static NTSTATUS (WINAPI *pRtlCustomCPToUnicodeN)(CPTABLEINFO*,WCHAR*,DWORD,DWORD*,const char*,DWORD);
92 static void InitFunctionPointers(void)
94 HMODULE mod = GetModuleHandleA("kernel32");
96 #define X(f) p##f = (void*)GetProcAddress(mod, #f)
97 X(GetTimeFormatEx);
98 X(GetDateFormatEx);
99 X(EnumSystemLanguageGroupsA);
100 X(EnumLanguageGroupLocalesA);
101 X(LocaleNameToLCID);
102 X(LCIDToLocaleName);
103 X(LCMapStringEx);
104 X(IsValidLanguageGroup);
105 X(EnumUILanguagesA);
106 X(EnumSystemLocalesEx);
107 X(IdnToNameprepUnicode);
108 X(IdnToAscii);
109 X(IdnToUnicode);
110 X(GetLocaleInfoEx);
111 X(IsValidLocaleName);
112 X(CompareStringOrdinal);
113 X(CompareStringEx);
114 X(GetGeoInfoA);
115 X(GetGeoInfoW);
116 X(EnumSystemGeoID);
117 X(GetSystemPreferredUILanguages);
118 X(GetThreadPreferredUILanguages);
119 X(GetUserPreferredUILanguages);
120 X(GetNumberFormatEx);
121 X(FindNLSStringEx);
122 X(SetThreadUILanguage);
123 X(GetThreadUILanguage);
124 X(NormalizeString);
125 X(FindStringOrdinal);
127 mod = GetModuleHandleA("ntdll");
128 X(RtlUpcaseUnicodeChar);
129 X(RtlLocaleNameToLcid);
130 X(RtlNormalizeString);
131 X(RtlIsNormalizedString);
132 X(NtGetNlsSectionPtr);
133 X(RtlInitCodePageTable);
134 X(RtlCustomCPToUnicodeN);
135 #undef X
138 #define eq(received, expected, label, type) \
139 ok((received) == (expected), "%s: got " type " instead of " type "\n", \
140 (label), (received), (expected))
142 #define BUFFER_SIZE 128
144 #define STRINGSA(x,y) strcpy(input, x); strcpy(Expected, y); SetLastError(0xdeadbeef); buffer[0] = '\0'
145 #define EXPECT_LENA ok(ret == lstrlenA(Expected)+1, "Expected len %d, got %d\n", lstrlenA(Expected)+1, ret)
146 #define EXPECT_EQA ok(strncmp(buffer, Expected, strlen(Expected)) == 0, \
147 "Expected '%s', got '%s'\n", Expected, buffer)
149 #define STRINGSW(x,y) MultiByteToWideChar(CP_ACP,0,x,-1,input,ARRAY_SIZE(input)); \
150 MultiByteToWideChar(CP_ACP,0,y,-1,Expected,ARRAY_SIZE(Expected)); \
151 SetLastError(0xdeadbeef); buffer[0] = '\0'
152 #define EXPECT_LENW ok(ret == lstrlenW(Expected)+1, "Expected Len %d, got %d\n", lstrlenW(Expected)+1, ret)
153 #define EXPECT_EQW ok(wcsncmp(buffer, Expected, lstrlenW(Expected)) == 0, "Bad conversion\n")
155 #define NUO LOCALE_NOUSEROVERRIDE
157 static void test_GetLocaleInfoA(void)
159 int ret;
160 int len;
161 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
162 char buffer[BUFFER_SIZE];
163 char expected[BUFFER_SIZE];
164 DWORD val;
166 ok(lcid == 0x409, "wrong LCID calculated - %d\n", lcid);
168 ret = GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (char*)&val, sizeof(val));
169 ok(ret, "got %d\n", ret);
170 ok(val == lcid, "got 0x%08x\n", val);
172 /* en and ar use SUBLANG_NEUTRAL, but GetLocaleInfo assume SUBLANG_DEFAULT
173 Same is true for zh on pre-Vista, but on Vista and higher GetLocaleInfo
174 assumes SUBLANG_NEUTRAL for zh */
175 memset(expected, 0, ARRAY_SIZE(expected));
176 len = GetLocaleInfoA(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
177 SetLastError(0xdeadbeef);
178 memset(buffer, 0, ARRAY_SIZE(buffer));
179 ret = GetLocaleInfoA(LANG_ENGLISH, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
180 ok((ret == len) && !lstrcmpA(buffer, expected),
181 "got %d with '%s' (expected %d with '%s')\n",
182 ret, buffer, len, expected);
184 memset(expected, 0, ARRAY_SIZE(expected));
185 len = GetLocaleInfoA(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
186 if (len) {
187 SetLastError(0xdeadbeef);
188 memset(buffer, 0, ARRAY_SIZE(buffer));
189 ret = GetLocaleInfoA(LANG_ARABIC, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
190 ok((ret == len) && !lstrcmpA(buffer, expected),
191 "got %d with '%s' (expected %d with '%s')\n",
192 ret, buffer, len, expected);
194 else
195 win_skip("LANG_ARABIC not installed\n");
197 /* SUBLANG_DEFAULT is required for mlang.dll, but optional for GetLocaleInfo */
198 memset(expected, 0, ARRAY_SIZE(expected));
199 len = GetLocaleInfoA(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT), LOCALE_SLANGUAGE, expected, ARRAY_SIZE(expected));
200 SetLastError(0xdeadbeef);
201 memset(buffer, 0, ARRAY_SIZE(buffer));
202 ret = GetLocaleInfoA(LANG_GERMAN, LOCALE_SLANGUAGE, buffer, ARRAY_SIZE(buffer));
203 ok((ret == len) && !lstrcmpA(buffer, expected),
204 "got %d with '%s' (expected %d with '%s')\n",
205 ret, buffer, len, expected);
208 /* HTMLKit and "Font xplorer lite" expect GetLocaleInfoA to
209 * partially fill the buffer even if it is too short. See bug 637.
211 SetLastError(0xdeadbeef);
212 memset(buffer, 0, ARRAY_SIZE(buffer));
213 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 0);
214 ok(ret == 7 && !buffer[0], "Expected len=7, got %d\n", ret);
216 SetLastError(0xdeadbeef);
217 memset(buffer, 0, ARRAY_SIZE(buffer));
218 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 3);
219 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
220 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
221 ok(!strcmp(buffer, "Mon"), "Expected 'Mon', got '%s'\n", buffer);
223 SetLastError(0xdeadbeef);
224 memset(buffer, 0, ARRAY_SIZE(buffer));
225 ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 10);
226 ok(ret == 7, "Expected ret == 7, got %d, error %d\n", ret, GetLastError());
227 ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer);
230 struct neutralsublang_name2_t {
231 WCHAR name[3];
232 WCHAR sname[15];
233 LCID lcid;
234 LCID lcid_broken;
235 WCHAR sname_broken[15];
238 static const struct neutralsublang_name2_t neutralsublang_names2[] = {
239 { {'a','r',0}, {'a','r','-','S','A',0},
240 MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
241 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0},
242 MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
243 { {'d','e',0}, {'d','e','-','D','E',0},
244 MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
245 { {'e','n',0}, {'e','n','-','U','S',0},
246 MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
247 { {'e','s',0}, {'e','s','-','E','S',0},
248 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT),
249 MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), SORT_DEFAULT) /* vista */,
250 {'e','s','-','E','S','_','t','r','a','d','n','l',0} },
251 { {'g','a',0}, {'g','a','-','I','E',0},
252 MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 0, {0} },
253 { {'i','t',0}, {'i','t','-','I','T',0},
254 MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
255 { {'m','s',0}, {'m','s','-','M','Y',0},
256 MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
257 { {'n','l',0}, {'n','l','-','N','L',0},
258 MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
259 { {'p','t',0}, {'p','t','-','B','R',0},
260 MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
261 { {'s','r',0}, {'h','r','-','H','R',0},
262 MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CROATIA), SORT_DEFAULT) },
263 { {'s','v',0}, {'s','v','-','S','E',0},
264 MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
265 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0},
266 MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
267 { {'z','h',0}, {'z','h','-','C','N',0},
268 MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
269 { {0} }
272 static void test_GetLocaleInfoW(void)
274 LCID lcid_en = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
275 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
276 LCID lcid_en_neut = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT);
277 WCHAR bufferW[80], buffer2W[80];
278 CHAR bufferA[80];
279 DWORD val;
280 DWORD ret;
281 INT i;
283 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
284 if (!ret) {
285 win_skip("GetLocaleInfoW() isn't implemented\n");
286 return;
289 ret = GetLocaleInfoW(lcid_en, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
290 ok(ret, "got %d\n", ret);
291 ok(val == lcid_en, "got 0x%08x\n", val);
293 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
294 if (ret)
296 static const WCHAR slangW[] = {'E','n','g','l','i','s','h',' ','(','U','n','i','t','e','d',' ',
297 'S','t','a','t','e','s',')',0};
298 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
299 static const WCHAR enW[] = {'e','n','-','U','S',0};
300 const struct neutralsublang_name2_t *ptr = neutralsublang_names2;
302 ok(!lstrcmpW(bufferW, enW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
304 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
305 ok(ret, "got %d\n", ret);
306 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
307 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
309 skip("Non-English locale\n");
311 else
312 ok(!lstrcmpW(statesW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
314 ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SLANGUAGE, bufferW, ARRAY_SIZE(bufferW));
315 ok(ret, "got %d\n", ret);
316 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
317 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
319 skip("Non-English locale\n");
321 else
322 ok(!lstrcmpW(slangW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
324 while (*ptr->name)
326 LANGID langid;
327 LCID lcid;
329 /* make neutral lcid */
330 langid = MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(ptr->lcid)), SUBLANG_NEUTRAL);
331 lcid = MAKELCID(langid, SORT_DEFAULT);
333 val = 0;
334 GetLocaleInfoW(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
335 ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
336 wine_dbgstr_w(ptr->name), val, ptr->lcid);
338 /* now check LOCALE_SNAME */
339 GetLocaleInfoW(lcid, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
340 ok(!lstrcmpW(bufferW, ptr->sname) ||
341 (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))),
342 "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
343 ptr++;
346 else
347 win_skip("English neutral locale not supported\n");
349 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1, bufferW, ARRAY_SIZE(bufferW));
350 if (!ret) {
351 win_skip("LANG_RUSSIAN locale data unavailable\n");
352 return;
354 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
355 bufferW, ARRAY_SIZE(bufferW));
356 if (!ret) {
357 win_skip("LOCALE_RETURN_GENITIVE_NAMES isn't supported\n");
358 return;
361 /* LOCALE_RETURN_GENITIVE_NAMES isn't supported for GetLocaleInfoA */
362 bufferA[0] = 'a';
363 SetLastError(0xdeadbeef);
364 ret = GetLocaleInfoA(lcid_ru, LOCALE_SMONTHNAME1|LOCALE_RETURN_GENITIVE_NAMES,
365 bufferA, ARRAY_SIZE(bufferA));
366 ok(ret == 0, "LOCALE_RETURN_GENITIVE_NAMES should fail with GetLocaleInfoA\n");
367 ok(bufferA[0] == 'a', "Expected buffer to be untouched\n");
368 ok(GetLastError() == ERROR_INVALID_FLAGS,
369 "Expected ERROR_INVALID_FLAGS, got %x\n", GetLastError());
371 bufferW[0] = 'a';
372 SetLastError(0xdeadbeef);
373 ret = GetLocaleInfoW(lcid_ru, LOCALE_RETURN_GENITIVE_NAMES, bufferW, ARRAY_SIZE(bufferW));
374 ok(ret == 0,
375 "LOCALE_RETURN_GENITIVE_NAMES itself doesn't return anything, got %d\n", ret);
376 ok(bufferW[0] == 'a', "Expected buffer to be untouched\n");
377 ok(GetLastError() == ERROR_INVALID_FLAGS,
378 "Expected ERROR_INVALID_FLAGS, got %x\n", GetLastError());
380 /* yes, test empty 13 month entry too */
381 for (i = 0; i < 12; i++) {
382 bufferW[0] = 0;
383 ret = GetLocaleInfoW(lcid_ru, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
384 bufferW, ARRAY_SIZE(bufferW));
385 ok(ret, "Expected non zero result\n");
386 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %d, length %d\n",
387 ret, lstrlenW(bufferW));
388 buffer2W[0] = 0;
389 ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
390 ok(ret, "Expected non zero result\n");
391 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %d, length %d\n",
392 ret, lstrlenW(buffer2W));
394 ok(lstrcmpW(bufferW, buffer2W) != 0,
395 "Expected genitive name to differ, got the same for month %d\n", i+1);
397 /* for locale without genitive names nominative returned in both cases */
398 bufferW[0] = 0;
399 ret = GetLocaleInfoW(lcid_en, (LOCALE_SMONTHNAME1+i)|LOCALE_RETURN_GENITIVE_NAMES,
400 bufferW, ARRAY_SIZE(bufferW));
401 ok(ret, "Expected non zero result\n");
402 ok(ret == lstrlenW(bufferW)+1, "Expected actual length, got %d, length %d\n",
403 ret, lstrlenW(bufferW));
404 buffer2W[0] = 0;
405 ret = GetLocaleInfoW(lcid_en, LOCALE_SMONTHNAME1+i, buffer2W, ARRAY_SIZE(buffer2W));
406 ok(ret, "Expected non zero result\n");
407 ok(ret == lstrlenW(buffer2W)+1, "Expected actual length, got %d, length %d\n",
408 ret, lstrlenW(buffer2W));
410 ok(lstrcmpW(bufferW, buffer2W) == 0,
411 "Expected same names, got different for month %d\n", i+1);
415 static void test_GetTimeFormatA(void)
417 int ret;
418 SYSTEMTIME curtime;
419 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
420 char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
422 memset(&curtime, 2, sizeof(SYSTEMTIME));
423 STRINGSA("tt HH':'mm'@'ss", ""); /* Invalid time */
424 SetLastError(0xdeadbeef);
425 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
426 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
427 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
429 curtime.wHour = 8;
430 curtime.wMinute = 56;
431 curtime.wSecond = 13;
432 curtime.wMilliseconds = 22;
433 STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
434 SetLastError(0xdeadbeef);
435 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
436 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
437 EXPECT_LENA; EXPECT_EQA;
439 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
440 SetLastError(0xdeadbeef);
441 ret = GetTimeFormatA(lcid, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
442 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
443 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
445 STRINGSA("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */
446 SetLastError(0xdeadbeef);
447 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
448 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
449 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
451 STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
452 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
453 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
454 EXPECT_LENA;
456 STRINGSA("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
457 ret = GetTimeFormatA(lcid, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
458 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
459 EXPECT_LENA; EXPECT_EQA;
461 STRINGSA("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
462 ret = GetTimeFormatA(lcid, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
463 ok(ret == strlen(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
464 ok( !strcmp( buffer, "" ) || broken( !strcmp( buffer, "4" )), /* win9x */
465 "Expected '', got '%s'\n", buffer );
467 STRINGSA("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
468 ret = GetTimeFormatA(lcid, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
469 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
470 EXPECT_LENA; EXPECT_EQA;
472 STRINGSA("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
473 strcpy(Expected, "8:56 AM");
474 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
475 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
476 EXPECT_LENA; EXPECT_EQA;
478 STRINGSA("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
479 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
480 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
481 ok( !strcmp( buffer, "8.@:56AM" ) || broken( !strcmp( buffer, "8.@:56.@:AM" )) /* win9x */,
482 "Expected '8.@:56AM', got '%s'\n", buffer );
484 STRINGSA("s1s2s3", ""); /* Duplicate tokens */
485 ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
486 ok(ret == strlen(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
487 ok( !strcmp( buffer, "" ) || broken( !strcmp( buffer, "3" )), /* win9x */
488 "Expected '', got '%s'\n", buffer );
490 STRINGSA("t/tt", "A/AM"); /* AM time marker */
491 ret = GetTimeFormatA(lcid, 0, &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 curtime.wHour = 13;
496 STRINGSA("t/tt", "P/PM"); /* PM time marker */
497 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
498 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
499 EXPECT_LENA; EXPECT_EQA;
501 STRINGSA("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
502 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
503 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
504 EXPECT_LENA; EXPECT_EQA;
506 STRINGSA("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
507 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
508 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
509 EXPECT_LENA; EXPECT_EQA;
511 STRINGSA("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
512 ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
513 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
514 EXPECT_LENA; EXPECT_EQA;
516 curtime.wHour = 14; /* change this to 14 or 2pm */
517 curtime.wMinute = 5;
518 curtime.wSecond = 3;
519 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 */
520 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
521 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
522 EXPECT_LENA; EXPECT_EQA;
524 curtime.wHour = 0;
525 STRINGSA("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
526 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
527 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
528 EXPECT_LENA; EXPECT_EQA;
530 STRINGSA("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
531 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
532 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
533 EXPECT_LENA; EXPECT_EQA;
535 /* try to convert formatting strings with more than two letters
536 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
537 * NOTE: We expect any letter for which there is an upper case value
538 * we should see a replacement. For letters that DO NOT have
539 * upper case values we should see NO REPLACEMENT.
541 curtime.wHour = 8;
542 curtime.wMinute = 56;
543 curtime.wSecond = 13;
544 curtime.wMilliseconds = 22;
545 STRINGSA("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
546 "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
547 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
548 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
549 EXPECT_LENA; EXPECT_EQA;
551 STRINGSA("h", "text"); /* Don't write to buffer if len is 0 */
552 strcpy(buffer, "text");
553 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, 0);
554 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
555 EXPECT_EQA;
557 STRINGSA("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
558 "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
559 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
560 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
561 EXPECT_LENA; EXPECT_EQA;
563 STRINGSA("'''", "'"); /* invalid quoted string */
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 /* test that msdn suggested single quotation usage works as expected */
569 STRINGSA("''''", "'"); /* single quote mark */
570 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
571 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
572 EXPECT_LENA; EXPECT_EQA;
574 STRINGSA("''HHHHHH", "08"); /* Normal use */
575 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
576 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
577 EXPECT_LENA; EXPECT_EQA;
579 /* and test for normal use of the single quotation mark */
580 STRINGSA("'''HHHHHH'", "'HHHHHH"); /* Normal use */
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 STRINGSA("'''HHHHHH", "'HHHHHH"); /* Odd use */
586 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
587 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
588 EXPECT_LENA; EXPECT_EQA;
590 STRINGSA("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
591 ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
592 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
593 EXPECT_LENA; EXPECT_EQA;
595 curtime.wHour = 25;
596 STRINGSA("'123'tt", ""); /* Invalid time */
597 SetLastError(0xdeadbeef);
598 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
599 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
600 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
602 curtime.wHour = 12;
603 curtime.wMonth = 60; /* Invalid */
604 STRINGSA("h:m:s", "12:56:13"); /* Invalid date */
605 ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
606 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
607 EXPECT_LENA; EXPECT_EQA;
610 static void test_GetTimeFormatEx(void)
612 int ret;
613 SYSTEMTIME curtime;
614 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
616 if (!pGetTimeFormatEx)
618 win_skip("GetTimeFormatEx not supported\n");
619 return;
622 memset(&curtime, 2, sizeof(SYSTEMTIME));
623 STRINGSW("tt HH':'mm'@'ss", ""); /* Invalid time */
624 SetLastError(0xdeadbeef);
625 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
626 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
627 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
629 curtime.wHour = 8;
630 curtime.wMinute = 56;
631 curtime.wSecond = 13;
632 curtime.wMilliseconds = 22;
633 STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
634 SetLastError(0xdeadbeef);
635 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
636 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
637 EXPECT_LENW; EXPECT_EQW;
639 /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */
640 SetLastError(0xdeadbeef);
641 ret = pGetTimeFormatEx(localeW, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
642 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
643 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
645 STRINGSW("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */
646 SetLastError(0xdeadbeef);
647 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
648 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
649 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
651 STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
652 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
653 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
654 EXPECT_LENW;
656 STRINGSW("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
657 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
658 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
659 EXPECT_LENW; EXPECT_EQW;
661 STRINGSW("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
662 ret = pGetTimeFormatEx(localeW, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
663 ok(ret == lstrlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
664 EXPECT_LENW; EXPECT_EQW;
666 STRINGSW("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
667 ret = pGetTimeFormatEx(localeW, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
668 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
669 EXPECT_LENW; EXPECT_EQW;
671 STRINGSW("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
672 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
673 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
674 EXPECT_LENW; EXPECT_EQW;
676 STRINGSW("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
677 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
678 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
679 EXPECT_LENW; EXPECT_EQW;
681 STRINGSW("s1s2s3", ""); /* Duplicate tokens */
682 ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, ARRAY_SIZE(buffer));
683 ok(ret == lstrlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
684 EXPECT_LENW; EXPECT_EQW;
686 STRINGSW("t/tt", "A/AM"); /* AM time marker */
687 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
688 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
689 EXPECT_LENW; EXPECT_EQW;
691 curtime.wHour = 13;
692 STRINGSW("t/tt", "P/PM"); /* PM time marker */
693 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
694 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
695 EXPECT_LENW; EXPECT_EQW;
697 STRINGSW("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
698 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
699 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
700 EXPECT_LENW; EXPECT_EQW;
702 STRINGSW("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
703 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
704 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
705 EXPECT_LENW; EXPECT_EQW;
707 STRINGSW("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
708 ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, ARRAY_SIZE(buffer));
709 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
710 EXPECT_LENW; EXPECT_EQW;
712 curtime.wHour = 14; /* change this to 14 or 2pm */
713 curtime.wMinute = 5;
714 curtime.wSecond = 3;
715 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 */
716 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
717 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
718 EXPECT_LENW; EXPECT_EQW;
720 curtime.wHour = 0;
721 STRINGSW("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
722 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
723 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
724 EXPECT_LENW; EXPECT_EQW;
726 STRINGSW("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
727 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
728 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
729 EXPECT_LENW; EXPECT_EQW;
731 /* try to convert formatting strings with more than two letters
732 * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
733 * NOTE: We expect any letter for which there is an upper case value
734 * we should see a replacement. For letters that DO NOT have
735 * upper case values we should see NO REPLACEMENT.
737 curtime.wHour = 8;
738 curtime.wMinute = 56;
739 curtime.wSecond = 13;
740 curtime.wMilliseconds = 22;
741 STRINGSW("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
742 "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
743 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
744 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
745 EXPECT_LENW; EXPECT_EQW;
747 STRINGSW("h", "text"); /* Don't write to buffer if len is 0 */
748 lstrcpyW(buffer, Expected);
749 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, 0);
750 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
751 EXPECT_EQW;
753 STRINGSW("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
754 "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
755 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
756 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
757 EXPECT_LENW; EXPECT_EQW;
759 STRINGSW("'''", "'"); /* invalid quoted string */
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 /* test that msdn suggested single quotation usage works as expected */
765 STRINGSW("''''", "'"); /* single quote mark */
766 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
767 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
768 EXPECT_LENW; EXPECT_EQW;
770 STRINGSW("''HHHHHH", "08"); /* Normal use */
771 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
772 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
773 EXPECT_LENW; EXPECT_EQW;
775 /* and test for normal use of the single quotation mark */
776 STRINGSW("'''HHHHHH'", "'HHHHHH"); /* Normal use */
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 STRINGSW("'''HHHHHH", "'HHHHHH"); /* Odd use */
782 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
783 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
784 EXPECT_LENW; EXPECT_EQW;
786 STRINGSW("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
787 ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, ARRAY_SIZE(buffer));
788 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
789 EXPECT_LENW; EXPECT_EQW;
791 curtime.wHour = 25;
792 STRINGSW("'123'tt", ""); /* Invalid time */
793 SetLastError(0xdeadbeef);
794 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
795 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
796 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
798 curtime.wHour = 12;
799 curtime.wMonth = 60; /* Invalid */
800 STRINGSW("h:m:s", "12:56:13"); /* Invalid date */
801 ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
802 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
803 EXPECT_LENW; EXPECT_EQW;
806 static void test_GetDateFormatA(void)
808 int ret;
809 SYSTEMTIME curtime;
810 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
811 LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
812 char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
813 char Broken[BUFFER_SIZE];
814 char short_day[10], month[10], genitive_month[10];
816 memset(&curtime, 2, sizeof(SYSTEMTIME)); /* Invalid time */
817 STRINGSA("ddd',' MMM dd yy","");
818 SetLastError(0xdeadbeef);
819 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
820 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
821 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
823 curtime.wYear = 2002;
824 curtime.wMonth = 5;
825 curtime.wDay = 4;
826 curtime.wDayOfWeek = 3;
827 STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Simple case */
828 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
829 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
830 EXPECT_LENA; EXPECT_EQA;
832 /* Same as above but with LOCALE_NOUSEROVERRIDE */
833 STRINGSA("ddd',' MMM dd yy",""); /* Simple case */
834 SetLastError(0xdeadbeef);
835 ret = GetDateFormatA(lcid, NUO, &curtime, input, buffer, ARRAY_SIZE(buffer));
836 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
837 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
838 EXPECT_EQA;
840 STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Format containing "'" */
841 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
842 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
843 EXPECT_LENA; EXPECT_EQA;
845 curtime.wHour = 36; /* Invalid */
846 STRINGSA("ddd',' MMM dd ''''yy","Sat, May 04 '02"); /* Invalid time */
847 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
848 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
849 EXPECT_LENA; EXPECT_EQA;
851 STRINGSA("ddd',' MMM dd ''''yy",""); /* Get size only */
852 ret = GetDateFormatA(lcid, 0, &curtime, input, NULL, 0);
853 ok(ret == 16, "Expected ret == 16, got %d, error %d\n", ret, GetLastError());
854 EXPECT_EQA;
856 STRINGSA("ddd',' MMM dd ''''yy",""); /* Buffer too small */
857 SetLastError(0xdeadbeef);
858 ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, 2);
859 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
860 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
862 STRINGSA("ddd',' MMM dd ''''yy","5/4/2002"); /* Default to DATE_SHORTDATE */
863 ret = GetDateFormatA(lcid, NUO, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
864 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
865 if (strncmp(buffer, Expected, strlen(Expected)) && strncmp(buffer, "5/4/02", strlen(Expected)) != 0)
866 ok (0, "Expected '%s' or '5/4/02', got '%s'\n", Expected, buffer);
868 SetLastError(0xdeadbeef); buffer[0] = '\0'; /* DATE_LONGDATE */
869 ret = GetDateFormatA(lcid, NUO|DATE_LONGDATE, &curtime, NULL, buffer, ARRAY_SIZE(buffer));
870 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
871 ok(strcmp(buffer, "Saturday, May 04, 2002") == 0 ||
872 strcmp(buffer, "Saturday, May 4, 2002") == 0 /* Win 8 */,
873 "got an unexpected date string '%s'\n", buffer);
875 /* test for expected DATE_YEARMONTH behavior with null format */
876 /* NT4 returns ERROR_INVALID_FLAGS for DATE_YEARMONTH */
877 STRINGSA("ddd',' MMM dd ''''yy", ""); /* DATE_YEARMONTH */
878 SetLastError(0xdeadbeef);
879 ret = GetDateFormatA(lcid, NUO|DATE_YEARMONTH, &curtime, input, buffer, ARRAY_SIZE(buffer));
880 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
881 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
882 EXPECT_EQA;
884 /* Test that using invalid DATE_* flags results in the correct error */
885 /* and return values */
886 STRINGSA("m/d/y", ""); /* Invalid flags */
887 SetLastError(0xdeadbeef);
888 ret = GetDateFormatA(lcid, DATE_YEARMONTH|DATE_SHORTDATE|DATE_LONGDATE, &curtime, input,
889 buffer, ARRAY_SIZE(buffer));
890 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
891 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
893 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddMMMM", buffer, ARRAY_SIZE(buffer));
894 if (!ret)
896 win_skip("LANG_RUSSIAN locale data unavailable\n");
897 return;
900 /* month part should be in genitive form */
901 strcpy(genitive_month, buffer + 2);
902 ret = GetDateFormatA(lcid_ru, 0, &curtime, "MMMM", buffer, ARRAY_SIZE(buffer));
903 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
904 strcpy(month, buffer);
905 ok(strcmp(genitive_month, month) != 0, "Expected different month forms\n");
907 ret = GetDateFormatA(lcid_ru, 0, &curtime, "ddd", buffer, ARRAY_SIZE(buffer));
908 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
909 strcpy(short_day, buffer);
911 STRINGSA("dd MMMMddd dd", "");
912 sprintf(Expected, "04 %s%s 04", genitive_month, short_day);
913 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
914 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
915 EXPECT_EQA;
917 STRINGSA("MMMMddd dd", "");
918 sprintf(Expected, "%s%s 04", month, short_day);
919 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
920 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
921 EXPECT_EQA;
923 STRINGSA("MMMMddd", "");
924 sprintf(Expected, "%s%s", month, short_day);
925 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
926 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
927 EXPECT_EQA;
929 STRINGSA("MMMMdd", "");
930 sprintf(Expected, "%s04", genitive_month);
931 sprintf(Broken, "%s04", month);
932 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
933 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
934 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
935 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
936 "Expected '%s', got '%s'\n", Expected, buffer);
938 STRINGSA("MMMMdd ddd", "");
939 sprintf(Expected, "%s04 %s", genitive_month, short_day);
940 sprintf(Broken, "%s04 %s", month, short_day);
941 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
942 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
943 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
944 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
945 "Expected '%s', got '%s'\n", Expected, buffer);
947 STRINGSA("dd dddMMMM", "");
948 sprintf(Expected, "04 %s%s", short_day, 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 EXPECT_EQA;
953 STRINGSA("dd dddMMMM ddd MMMMdd", "");
954 sprintf(Expected, "04 %s%s %s %s04", short_day, month, short_day, genitive_month);
955 sprintf(Broken, "04 %s%s %s %s04", short_day, month, short_day, month);
956 ret = GetDateFormatA(lcid_ru, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
957 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
958 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
959 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
960 "Expected '%s', got '%s'\n", Expected, buffer);
962 /* with literal part */
963 STRINGSA("ddd',' MMMM dd", "");
964 sprintf(Expected, "%s, %s 04", short_day, genitive_month);
965 sprintf(Broken, "%s, %s 04", 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 ok(strncmp(buffer, Expected, strlen(Expected)) == 0 ||
969 broken(strncmp(buffer, Broken, strlen(Broken)) == 0) /* nt4 */,
970 "Expected '%s', got '%s'\n", Expected, buffer);
973 static void test_GetDateFormatEx(void)
975 int ret;
976 SYSTEMTIME curtime;
977 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
979 if (!pGetDateFormatEx)
981 win_skip("GetDateFormatEx not supported\n");
982 return;
985 STRINGSW("",""); /* If flags are set, then format must be NULL */
986 SetLastError(0xdeadbeef);
987 ret = pGetDateFormatEx(localeW, DATE_LONGDATE, NULL, input, buffer, ARRAY_SIZE(buffer), NULL);
988 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
989 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
990 EXPECT_EQW;
992 STRINGSW("",""); /* NULL buffer, len > 0 */
993 SetLastError(0xdeadbeef);
994 ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, ARRAY_SIZE(buffer), NULL);
995 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
996 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
998 STRINGSW("",""); /* NULL buffer, len == 0 */
999 ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, 0, NULL);
1000 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1001 EXPECT_LENW; EXPECT_EQW;
1003 STRINGSW("",""); /* Invalid flag combination */
1004 SetLastError(0xdeadbeef);
1005 ret = pGetDateFormatEx(localeW, DATE_LONGDATE|DATE_SHORTDATE, NULL,
1006 input, NULL, 0, NULL);
1007 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1008 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1009 EXPECT_EQW;
1011 curtime.wYear = 2002;
1012 curtime.wMonth = 10;
1013 curtime.wDay = 23;
1014 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1015 curtime.wHour = 65432; /* Invalid */
1016 curtime.wMinute = 34512; /* Invalid */
1017 curtime.wSecond = 65535; /* Invalid */
1018 curtime.wMilliseconds = 12345;
1019 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
1020 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1021 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1022 EXPECT_LENW; EXPECT_EQW;
1024 curtime.wYear = 2002;
1025 curtime.wMonth = 10;
1026 curtime.wDay = 23;
1027 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1028 curtime.wHour = 65432; /* Invalid */
1029 curtime.wMinute = 34512; /* Invalid */
1030 curtime.wSecond = 65535; /* Invalid */
1031 curtime.wMilliseconds = 12345;
1032 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002");
1033 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), emptyW); /* Use reserved arg */
1034 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1035 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1037 /* Limit tests */
1039 curtime.wYear = 1601;
1040 curtime.wMonth = 1;
1041 curtime.wDay = 1;
1042 curtime.wDayOfWeek = 0; /* Irrelevant */
1043 curtime.wHour = 0;
1044 curtime.wMinute = 0;
1045 curtime.wSecond = 0;
1046 curtime.wMilliseconds = 0;
1047 STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601");
1048 SetLastError(0xdeadbeef);
1049 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1050 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1051 EXPECT_LENW; EXPECT_EQW;
1053 curtime.wYear = 1600;
1054 curtime.wMonth = 12;
1055 curtime.wDay = 31;
1056 curtime.wDayOfWeek = 0; /* Irrelevant */
1057 curtime.wHour = 23;
1058 curtime.wMinute = 59;
1059 curtime.wSecond = 59;
1060 curtime.wMilliseconds = 999;
1061 STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600");
1062 SetLastError(0xdeadbeef);
1063 ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, ARRAY_SIZE(buffer), NULL);
1064 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1065 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1068 static void test_GetDateFormatW(void)
1070 int ret;
1071 SYSTEMTIME curtime;
1072 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
1073 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1075 STRINGSW("",""); /* If flags is not zero then format must be NULL */
1076 ret = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, NULL, input, buffer, ARRAY_SIZE(buffer));
1077 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1079 win_skip("GetDateFormatW is not implemented\n");
1080 return;
1082 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
1083 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1084 EXPECT_EQW;
1086 STRINGSW("",""); /* NULL buffer, len > 0 */
1087 SetLastError(0xdeadbeef);
1088 ret = GetDateFormatW (lcid, 0, NULL, input, NULL, ARRAY_SIZE(buffer));
1089 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1090 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1092 STRINGSW("",""); /* NULL buffer, len == 0 */
1093 ret = GetDateFormatW (lcid, 0, NULL, input, NULL, 0);
1094 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1095 EXPECT_LENW; EXPECT_EQW;
1097 curtime.wYear = 2002;
1098 curtime.wMonth = 10;
1099 curtime.wDay = 23;
1100 curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
1101 curtime.wHour = 65432; /* Invalid */
1102 curtime.wMinute = 34512; /* Invalid */
1103 curtime.wSecond = 65535; /* Invalid */
1104 curtime.wMilliseconds = 12345;
1105 STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
1106 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1107 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1108 EXPECT_LENW; EXPECT_EQW;
1110 /* Limit tests */
1112 curtime.wYear = 1601;
1113 curtime.wMonth = 1;
1114 curtime.wDay = 1;
1115 curtime.wDayOfWeek = 0; /* Irrelevant */
1116 curtime.wHour = 0;
1117 curtime.wMinute = 0;
1118 curtime.wSecond = 0;
1119 curtime.wMilliseconds = 0;
1120 STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601");
1121 SetLastError(0xdeadbeef);
1122 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1123 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1124 EXPECT_LENW; EXPECT_EQW;
1126 curtime.wYear = 1600;
1127 curtime.wMonth = 12;
1128 curtime.wDay = 31;
1129 curtime.wDayOfWeek = 0; /* Irrelevant */
1130 curtime.wHour = 23;
1131 curtime.wMinute = 59;
1132 curtime.wSecond = 59;
1133 curtime.wMilliseconds = 999;
1134 STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600");
1135 SetLastError(0xdeadbeef);
1136 ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, ARRAY_SIZE(buffer));
1137 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1138 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1142 #define CY_POS_LEFT 0
1143 #define CY_POS_RIGHT 1
1144 #define CY_POS_LEFT_SPACE 2
1145 #define CY_POS_RIGHT_SPACE 3
1147 static void test_GetCurrencyFormatA(void)
1149 static char szDot[] = { '.', '\0' };
1150 static char szComma[] = { ',', '\0' };
1151 static char szDollar[] = { '$', '\0' };
1152 int ret;
1153 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1154 char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
1155 CURRENCYFMTA format;
1157 memset(&format, 0, sizeof(format));
1159 STRINGSA("23",""); /* NULL output, length > 0 --> Error */
1160 SetLastError(0xdeadbeef);
1161 ret = GetCurrencyFormatA(lcid, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1162 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1163 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1165 STRINGSA("23,53",""); /* Invalid character --> Error */
1166 SetLastError(0xdeadbeef);
1167 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1168 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1169 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1171 STRINGSA("--",""); /* Double '-' --> Error */
1172 SetLastError(0xdeadbeef);
1173 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1174 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1175 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1177 STRINGSA("0-",""); /* Trailing '-' --> Error */
1178 SetLastError(0xdeadbeef);
1179 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1180 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1181 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1183 STRINGSA("0..",""); /* Double '.' --> Error */
1184 SetLastError(0xdeadbeef);
1185 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1186 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1187 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1189 STRINGSA(" 0.1",""); /* Leading space --> Error */
1190 SetLastError(0xdeadbeef);
1191 ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1192 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1193 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1195 STRINGSA("1234","$"); /* Length too small --> Write up to length chars */
1196 SetLastError(0xdeadbeef);
1197 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, 2);
1198 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1199 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1201 STRINGSA("2353",""); /* Format and flags given --> Error */
1202 SetLastError(0xdeadbeef);
1203 ret = GetCurrencyFormatA(lcid, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1204 ok( !ret, "Expected ret == 0, got %d\n", ret);
1205 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1206 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1208 STRINGSA("2353",""); /* Invalid format --> Error */
1209 SetLastError(0xdeadbeef);
1210 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1211 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1212 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1214 STRINGSA("2353","$2,353.00"); /* Valid number */
1215 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1216 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1217 EXPECT_LENA; EXPECT_EQA;
1219 STRINGSA("-2353","($2,353.00)"); /* Valid negative number */
1220 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1221 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1222 EXPECT_LENA; EXPECT_EQA;
1224 STRINGSA("2353.1","$2,353.10"); /* Valid real number */
1225 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1226 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1227 EXPECT_LENA; EXPECT_EQA;
1229 STRINGSA("2353.111","$2,353.11"); /* Too many DP --> Truncated */
1230 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1231 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1232 EXPECT_LENA; EXPECT_EQA;
1234 STRINGSA("2353.119","$2,353.12"); /* Too many DP --> Rounded */
1235 ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1236 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1237 EXPECT_LENA; EXPECT_EQA;
1239 format.NumDigits = 0; /* No decimal separator */
1240 format.LeadingZero = 0;
1241 format.Grouping = 0; /* No grouping char */
1242 format.NegativeOrder = 0;
1243 format.PositiveOrder = CY_POS_LEFT;
1244 format.lpDecimalSep = szDot;
1245 format.lpThousandSep = szComma;
1246 format.lpCurrencySymbol = szDollar;
1248 STRINGSA("2353","$2353"); /* No decimal or grouping chars expected */
1249 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1250 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1251 EXPECT_LENA; EXPECT_EQA;
1253 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1254 STRINGSA("2353","$2353.0");
1255 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1256 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1257 EXPECT_LENA; EXPECT_EQA;
1259 format.Grouping = 2; /* Group by 100's */
1260 STRINGSA("2353","$23,53.0");
1261 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1262 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1263 EXPECT_LENA; EXPECT_EQA;
1265 STRINGSA("235","$235.0"); /* Grouping of a positive number */
1266 format.Grouping = 3;
1267 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1268 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1269 EXPECT_LENA; EXPECT_EQA;
1271 STRINGSA("-235","$-235.0"); /* Grouping of a negative number */
1272 format.NegativeOrder = 2;
1273 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1274 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1275 EXPECT_LENA; EXPECT_EQA;
1277 format.LeadingZero = 1; /* Always provide leading zero */
1278 STRINGSA(".5","$0.5");
1279 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1280 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1281 EXPECT_LENA; EXPECT_EQA;
1283 format.PositiveOrder = CY_POS_RIGHT;
1284 STRINGSA("1","1.0$");
1285 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1286 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1287 EXPECT_LENA; EXPECT_EQA;
1289 format.PositiveOrder = CY_POS_LEFT_SPACE;
1290 STRINGSA("1","$ 1.0");
1291 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1292 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1293 EXPECT_LENA; EXPECT_EQA;
1295 format.PositiveOrder = CY_POS_RIGHT_SPACE;
1296 STRINGSA("1","1.0 $");
1297 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1298 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1299 EXPECT_LENA; EXPECT_EQA;
1301 format.NegativeOrder = 0;
1302 STRINGSA("-1","($1.0)");
1303 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1304 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1305 EXPECT_LENA; EXPECT_EQA;
1307 format.NegativeOrder = 1;
1308 STRINGSA("-1","-$1.0");
1309 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1310 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1311 EXPECT_LENA; EXPECT_EQA;
1313 format.NegativeOrder = 2;
1314 STRINGSA("-1","$-1.0");
1315 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1316 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1317 EXPECT_LENA; EXPECT_EQA;
1319 format.NegativeOrder = 3;
1320 STRINGSA("-1","$1.0-");
1321 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1322 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1323 EXPECT_LENA; EXPECT_EQA;
1325 format.NegativeOrder = 4;
1326 STRINGSA("-1","(1.0$)");
1327 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1328 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1329 EXPECT_LENA; EXPECT_EQA;
1331 format.NegativeOrder = 5;
1332 STRINGSA("-1","-1.0$");
1333 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1334 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1335 EXPECT_LENA; EXPECT_EQA;
1337 format.NegativeOrder = 6;
1338 STRINGSA("-1","1.0-$");
1339 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1340 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1341 EXPECT_LENA; EXPECT_EQA;
1343 format.NegativeOrder = 7;
1344 STRINGSA("-1","1.0$-");
1345 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1346 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1347 EXPECT_LENA; EXPECT_EQA;
1349 format.NegativeOrder = 8;
1350 STRINGSA("-1","-1.0 $");
1351 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1352 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1353 EXPECT_LENA; EXPECT_EQA;
1355 format.NegativeOrder = 9;
1356 STRINGSA("-1","-$ 1.0");
1357 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1358 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1359 EXPECT_LENA; EXPECT_EQA;
1361 format.NegativeOrder = 10;
1362 STRINGSA("-1","1.0 $-");
1363 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1364 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1365 EXPECT_LENA; EXPECT_EQA;
1367 format.NegativeOrder = 11;
1368 STRINGSA("-1","$ 1.0-");
1369 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1370 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1371 EXPECT_LENA; EXPECT_EQA;
1373 format.NegativeOrder = 12;
1374 STRINGSA("-1","$ -1.0");
1375 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1376 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1377 EXPECT_LENA; EXPECT_EQA;
1379 format.NegativeOrder = 13;
1380 STRINGSA("-1","1.0- $");
1381 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1382 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1383 EXPECT_LENA; EXPECT_EQA;
1385 format.NegativeOrder = 14;
1386 STRINGSA("-1","($ 1.0)");
1387 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1388 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1389 EXPECT_LENA; EXPECT_EQA;
1391 format.NegativeOrder = 15;
1392 STRINGSA("-1","(1.0 $)");
1393 ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1394 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1395 EXPECT_LENA; EXPECT_EQA;
1398 #define NEG_PARENS 0 /* "(1.1)" */
1399 #define NEG_LEFT 1 /* "-1.1" */
1400 #define NEG_LEFT_SPACE 2 /* "- 1.1" */
1401 #define NEG_RIGHT 3 /* "1.1-" */
1402 #define NEG_RIGHT_SPACE 4 /* "1.1 -" */
1404 static void test_GetNumberFormatA(void)
1406 static char szDot[] = { '.', '\0' };
1407 static char szComma[] = { ',', '\0' };
1408 int ret;
1409 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1410 char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
1411 NUMBERFMTA format;
1413 memset(&format, 0, sizeof(format));
1415 STRINGSA("23",""); /* NULL output, length > 0 --> Error */
1416 SetLastError(0xdeadbeef);
1417 ret = GetNumberFormatA(lcid, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1418 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1419 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1421 STRINGSA("23,53",""); /* Invalid character --> Error */
1422 SetLastError(0xdeadbeef);
1423 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1424 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1425 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1427 STRINGSA("--",""); /* Double '-' --> Error */
1428 SetLastError(0xdeadbeef);
1429 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1430 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1431 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1433 STRINGSA("0-",""); /* Trailing '-' --> Error */
1434 SetLastError(0xdeadbeef);
1435 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1436 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1437 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1439 STRINGSA("0..",""); /* Double '.' --> Error */
1440 SetLastError(0xdeadbeef);
1441 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1442 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1443 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1445 STRINGSA(" 0.1",""); /* Leading space --> Error */
1446 SetLastError(0xdeadbeef);
1447 ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1448 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1449 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1451 STRINGSA("1234","1"); /* Length too small --> Write up to length chars */
1452 SetLastError(0xdeadbeef);
1453 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, 2);
1454 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1455 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1457 STRINGSA("2353",""); /* Format and flags given --> Error */
1458 SetLastError(0xdeadbeef);
1459 ret = GetNumberFormatA(lcid, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1460 ok( !ret, "Expected ret == 0, got %d\n", ret);
1461 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1462 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1464 STRINGSA("2353",""); /* Invalid format --> Error */
1465 SetLastError(0xdeadbeef);
1466 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1467 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1468 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1470 STRINGSA("2353","2,353.00"); /* Valid number */
1471 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1472 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1473 EXPECT_LENA; EXPECT_EQA;
1475 STRINGSA("-2353","-2,353.00"); /* Valid negative number */
1476 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1477 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1478 EXPECT_LENA; EXPECT_EQA;
1480 STRINGSA("-353","-353.00"); /* test for off by one error in grouping */
1481 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1482 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1483 EXPECT_LENA; EXPECT_EQA;
1485 STRINGSA("2353.1","2,353.10"); /* Valid real number */
1486 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1487 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1488 EXPECT_LENA; EXPECT_EQA;
1490 STRINGSA("2353.111","2,353.11"); /* Too many DP --> Truncated */
1491 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1492 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1493 EXPECT_LENA; EXPECT_EQA;
1495 STRINGSA("2353.119","2,353.12"); /* Too many DP --> Rounded */
1496 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1497 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1498 EXPECT_LENA; EXPECT_EQA;
1500 format.NumDigits = 0; /* No decimal separator */
1501 format.LeadingZero = 0;
1502 format.Grouping = 0; /* No grouping char */
1503 format.NegativeOrder = 0;
1504 format.lpDecimalSep = szDot;
1505 format.lpThousandSep = szComma;
1507 STRINGSA("2353","2353"); /* No decimal or grouping chars expected */
1508 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1509 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1510 EXPECT_LENA; EXPECT_EQA;
1512 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1513 STRINGSA("2353","2353.0");
1514 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1515 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1516 EXPECT_LENA; EXPECT_EQA;
1518 format.Grouping = 2; /* Group by 100's */
1519 STRINGSA("2353","23,53.0");
1520 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1521 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1522 EXPECT_LENA; EXPECT_EQA;
1524 STRINGSA("235","235.0"); /* Grouping of a positive number */
1525 format.Grouping = 3;
1526 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1527 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1528 EXPECT_LENA; EXPECT_EQA;
1530 STRINGSA("-235","-235.0"); /* Grouping of a negative number */
1531 format.NegativeOrder = NEG_LEFT;
1532 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1533 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1534 EXPECT_LENA; EXPECT_EQA;
1536 format.LeadingZero = 1; /* Always provide leading zero */
1537 STRINGSA(".5","0.5");
1538 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1539 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1540 EXPECT_LENA; EXPECT_EQA;
1542 format.NegativeOrder = NEG_PARENS;
1543 STRINGSA("-1","(1.0)");
1544 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1545 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1546 EXPECT_LENA; EXPECT_EQA;
1548 format.NegativeOrder = NEG_LEFT;
1549 STRINGSA("-1","-1.0");
1550 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1551 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1552 EXPECT_LENA; EXPECT_EQA;
1554 format.NegativeOrder = NEG_LEFT_SPACE;
1555 STRINGSA("-1","- 1.0");
1556 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1557 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1558 EXPECT_LENA; EXPECT_EQA;
1560 format.NegativeOrder = NEG_RIGHT;
1561 STRINGSA("-1","1.0-");
1562 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1563 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1564 EXPECT_LENA; EXPECT_EQA;
1566 format.NegativeOrder = NEG_RIGHT_SPACE;
1567 STRINGSA("-1","1.0 -");
1568 ret = GetNumberFormatA(lcid, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1569 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1570 EXPECT_LENA; EXPECT_EQA;
1572 lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1574 if (IsValidLocale(lcid, 0))
1576 STRINGSA("-12345","-12 345,00"); /* Try French formatting */
1577 Expected[3] = (char)160; /* Non breaking space */
1578 ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1579 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1580 EXPECT_LENA; EXPECT_EQA;
1584 static void test_GetNumberFormatEx(void)
1586 int ret;
1587 NUMBERFMTW format;
1588 static WCHAR dotW[] = {'.',0};
1589 static WCHAR commaW[] = {',',0};
1590 static const WCHAR enW[] = {'e','n','-','U','S',0};
1591 static const WCHAR frW[] = {'f','r','-','F','R',0};
1592 static const WCHAR bogusW[] = {'b','o','g','u','s',0};
1593 WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
1595 if (!pGetNumberFormatEx)
1597 win_skip("GetNumberFormatEx is not available.\n");
1598 return;
1601 STRINGSW("23",""); /* NULL output, length > 0 --> Error */
1602 ret = pGetNumberFormatEx(enW, 0, input, NULL, NULL, ARRAY_SIZE(buffer));
1603 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1604 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1606 STRINGSW("23,53",""); /* Invalid character --> Error */
1607 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1608 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1609 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1611 STRINGSW("--",""); /* Double '-' --> Error */
1612 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1613 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1614 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1616 STRINGSW("0-",""); /* Trailing '-' --> Error */
1617 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1618 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1619 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1621 STRINGSW("0..",""); /* Double '.' --> Error */
1622 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1623 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1624 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1626 STRINGSW(" 0.1",""); /* Leading space --> Error */
1627 ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, ARRAY_SIZE(buffer));
1628 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1629 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1631 STRINGSW("1234","1"); /* Length too small --> Write up to length chars */
1632 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, 2);
1633 ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
1634 "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
1636 STRINGSW("23",""); /* Bogus locale --> Error */
1637 ret = pGetNumberFormatEx(bogusW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1638 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1639 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1641 memset(&format, 0, sizeof(format));
1643 STRINGSW("2353",""); /* Format and flags given --> Error */
1644 ret = pGetNumberFormatEx(enW, NUO, input, &format, buffer, ARRAY_SIZE(buffer));
1645 ok( !ret, "Expected ret == 0, got %d\n", ret);
1646 ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
1647 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
1649 STRINGSW("2353",""); /* Invalid format --> Error */
1650 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1651 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
1652 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
1654 STRINGSW("2353","2,353.00"); /* Valid number */
1655 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1656 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1657 EXPECT_LENW; EXPECT_EQW;
1659 STRINGSW("-2353","-2,353.00"); /* Valid negative number */
1660 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1661 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1662 EXPECT_LENW; EXPECT_EQW;
1664 STRINGSW("-353","-353.00"); /* test for off by one error in grouping */
1665 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1666 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1667 EXPECT_LENW; EXPECT_EQW;
1669 STRINGSW("2353.1","2,353.10"); /* Valid real number */
1670 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1671 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1672 EXPECT_LENW; EXPECT_EQW;
1674 STRINGSW("2353.111","2,353.11"); /* Too many DP --> Truncated */
1675 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1676 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1677 EXPECT_LENW; EXPECT_EQW;
1679 STRINGSW("2353.119","2,353.12"); /* Too many DP --> Rounded */
1680 ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1681 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1682 EXPECT_LENW; EXPECT_EQW;
1684 format.NumDigits = 0; /* No decimal separator */
1685 format.LeadingZero = 0;
1686 format.Grouping = 0; /* No grouping char */
1687 format.NegativeOrder = 0;
1688 format.lpDecimalSep = dotW;
1689 format.lpThousandSep = commaW;
1691 STRINGSW("2353","2353"); /* No decimal or grouping chars expected */
1692 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1693 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1694 EXPECT_LENW; EXPECT_EQW;
1696 format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
1697 STRINGSW("2353","2353.0");
1698 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1699 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1700 EXPECT_LENW; EXPECT_EQW;
1702 format.Grouping = 2; /* Group by 100's */
1703 STRINGSW("2353","23,53.0");
1704 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1705 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1706 EXPECT_LENW; EXPECT_EQW;
1708 STRINGSW("235","235.0"); /* Grouping of a positive number */
1709 format.Grouping = 3;
1710 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1711 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1712 EXPECT_LENW; EXPECT_EQW;
1714 STRINGSW("-235","-235.0"); /* Grouping of a negative number */
1715 format.NegativeOrder = NEG_LEFT;
1716 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1717 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1718 EXPECT_LENW; EXPECT_EQW;
1720 format.LeadingZero = 1; /* Always provide leading zero */
1721 STRINGSW(".5","0.5");
1722 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1723 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1724 EXPECT_LENW; EXPECT_EQW;
1726 format.NegativeOrder = NEG_PARENS;
1727 STRINGSW("-1","(1.0)");
1728 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1729 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1730 EXPECT_LENW; EXPECT_EQW;
1732 format.NegativeOrder = NEG_LEFT;
1733 STRINGSW("-1","-1.0");
1734 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1735 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1736 EXPECT_LENW; EXPECT_EQW;
1738 format.NegativeOrder = NEG_LEFT_SPACE;
1739 STRINGSW("-1","- 1.0");
1740 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1741 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1742 EXPECT_LENW; EXPECT_EQW;
1744 format.NegativeOrder = NEG_RIGHT;
1745 STRINGSW("-1","1.0-");
1746 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1747 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1748 EXPECT_LENW; EXPECT_EQW;
1750 format.NegativeOrder = NEG_RIGHT_SPACE;
1751 STRINGSW("-1","1.0 -");
1752 ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, ARRAY_SIZE(buffer));
1753 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1754 EXPECT_LENW; EXPECT_EQW;
1756 if (pIsValidLocaleName(frW))
1758 STRINGSW("-12345","-12 345,00"); /* Try French formatting */
1759 Expected[3] = 160; /* Non breaking space */
1760 ret = pGetNumberFormatEx(frW, NUO, input, NULL, buffer, ARRAY_SIZE(buffer));
1761 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
1762 EXPECT_LENW; EXPECT_EQW;
1766 struct comparestringa_entry {
1767 LCID lcid;
1768 DWORD flags;
1769 const char *first;
1770 int first_len;
1771 const char *second;
1772 int second_len;
1773 int ret;
1776 static const struct comparestringa_entry comparestringa_data[] = {
1777 { LOCALE_SYSTEM_DEFAULT, 0, "EndDialog", -1, "_Property", -1, CSTR_GREATER_THAN },
1778 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0070", -1, "_IEWWBrowserComp", -1, CSTR_GREATER_THAN },
1779 { LOCALE_SYSTEM_DEFAULT, 0, "r", -1, "\\", -1, CSTR_GREATER_THAN },
1780 { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0031", -1, "OriginalDatabase", -1, CSTR_GREATER_THAN },
1781 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1, CSTR_GREATER_THAN },
1782 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1, CSTR_LESS_THAN },
1783 { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1784 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1, CSTR_LESS_THAN },
1785 { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1, CSTR_LESS_THAN },
1786 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1, CSTR_LESS_THAN },
1787 { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1, CSTR_LESS_THAN },
1788 { LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1, CSTR_GREATER_THAN },
1789 { LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1, CSTR_LESS_THAN },
1790 { LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1, CSTR_LESS_THAN },
1791 { LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1, CSTR_GREATER_THAN },
1792 { LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1, CSTR_GREATER_THAN },
1793 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1, CSTR_LESS_THAN },
1794 { LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1, CSTR_LESS_THAN },
1795 { LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1, CSTR_LESS_THAN },
1796 /* hyphen and apostrophe are treated differently depending on whether SORT_STRINGSORT specified or not */
1797 { LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1, CSTR_GREATER_THAN },
1798 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1, CSTR_LESS_THAN },
1799 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1, CSTR_LESS_THAN },
1800 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1, CSTR_GREATER_THAN },
1801 { LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1, CSTR_GREATER_THAN },
1802 { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1, CSTR_LESS_THAN },
1803 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1, CSTR_LESS_THAN },
1804 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1, CSTR_GREATER_THAN },
1805 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9, CSTR_EQUAL },
1806 { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10, CSTR_LESS_THAN },
1807 { LOCALE_SYSTEM_DEFAULT, 0, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1808 { LOCALE_SYSTEM_DEFAULT, 0, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1809 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
1810 { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
1811 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a.", 3, "a\0", 3, CSTR_EQUAL },
1812 { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a ", 3, "a\0", 3, CSTR_EQUAL },
1813 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0\0", 4, CSTR_EQUAL },
1814 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0\0", 4, CSTR_EQUAL },
1815 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 1, CSTR_EQUAL },
1816 { LOCALE_SYSTEM_DEFAULT, 0, "a\0\0", 4, "a", 2, CSTR_EQUAL },
1817 { LOCALE_SYSTEM_DEFAULT, 0, "a", 1, "a\0x", 4, CSTR_LESS_THAN },
1818 { LOCALE_SYSTEM_DEFAULT, 0, "a", 2, "a\0x", 4, CSTR_LESS_THAN },
1819 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 1, CSTR_GREATER_THAN },
1820 { LOCALE_SYSTEM_DEFAULT, 0, "a\0x", 4, "a", 2, CSTR_GREATER_THAN },
1823 static void test_CompareStringA(void)
1825 static const char ABC_EE[] = {'A','B','C',0,0xEE};
1826 static const char ABC_FF[] = {'A','B','C',0,0xFF};
1827 int ret, i;
1828 char a[256];
1829 LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
1831 for (i = 0; i < ARRAY_SIZE(comparestringa_data); i++)
1833 const struct comparestringa_entry *entry = &comparestringa_data[i];
1835 ret = CompareStringA(entry->lcid, entry->flags, entry->first, entry->first_len,
1836 entry->second, entry->second_len);
1837 ok(ret == entry->ret, "%d: got %d, expected %d\n", i, ret, entry->ret);
1840 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
1841 ok (ret == CSTR_LESS_THAN, "(Salut/Salute) Expected CSTR_LESS_THAN, got %d\n", ret);
1843 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
1844 ok (ret == CSTR_EQUAL, "(Salut/SaLuT) Expected CSTR_EQUAL, got %d\n", ret);
1846 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
1847 ok (ret == CSTR_GREATER_THAN, "(Salut/hola) Expected CSTR_GREATER_THAN, got %d\n", ret);
1849 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1850 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1852 lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1854 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
1855 ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
1857 ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
1858 ok (ret == CSTR_GREATER_THAN, "(haha/hoho) Expected CSTR_GREATER_THAN, got %d\n", ret);
1860 ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1);
1861 ok (ret == CSTR_EQUAL, "(Salut/saLuT) Expected CSTR_EQUAL, got %d\n", ret);
1863 /* test for CompareStringA flags */
1864 SetLastError(0xdeadbeef);
1865 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0x8, "NULL", -1, "NULL", -1);
1866 ok(GetLastError() == ERROR_INVALID_FLAGS,
1867 "unexpected error code %d\n", GetLastError());
1868 ok(!ret, "CompareStringA must fail with invalid flag\n");
1870 SetLastError(0xdeadbeef);
1871 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, LOCALE_USE_CP_ACP, "NULL", -1, "NULL", -1);
1872 ok(GetLastError() == 0xdeadbeef, "unexpected error code %d\n", GetLastError());
1873 ok(ret == CSTR_EQUAL, "CompareStringA error: %d != CSTR_EQUAL\n", ret);
1874 /* end of test for CompareStringA flags */
1876 ret = lstrcmpA("", "");
1877 ok (ret == 0, "lstrcmpA(\"\", \"\") should return 0, got %d\n", ret);
1879 ret = lstrcmpA(NULL, NULL);
1880 ok (ret == 0 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, NULL) should return 0, got %d\n", ret);
1882 ret = lstrcmpA("", NULL);
1883 ok (ret == 1 || broken(ret == -2) /* win9x */, "lstrcmpA(\"\", NULL) should return 1, got %d\n", ret);
1885 ret = lstrcmpA(NULL, "");
1886 ok (ret == -1 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, \"\") should return -1, got %d\n", ret);
1889 if (0) { /* this requires collation table patch to make it MS compatible */
1890 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
1891 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1893 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 );
1894 ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
1896 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 );
1897 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1899 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 );
1900 ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
1902 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 );
1903 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1905 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 );
1906 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1908 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 );
1909 ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
1911 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 );
1912 ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1914 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 );
1915 ok(ret == CSTR_LESS_THAN, "`o vs -m expected CSTR_LESS_THAN, got %d\n", ret);
1917 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 );
1918 ok(ret == CSTR_GREATER_THAN, "-m vs `o CSTR_GREATER_THAN got %d\n", ret);
1920 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 );
1921 ok(ret == CSTR_GREATER_THAN, "`o vs -m CSTR_GREATER_THAN got %d\n", ret);
1923 ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 );
1924 ok(ret == CSTR_LESS_THAN, "-m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
1928 /* WinXP handles embedded NULLs differently than earlier versions */
1929 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
1930 ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLuZkUtZ vs aLuZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret);
1932 ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10);
1933 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);
1935 ret = CompareStringA(lcid, 0, "a\0b", -1, "a", -1);
1936 ok(ret == CSTR_EQUAL, "a vs a expected CSTR_EQUAL, got %d\n", ret);
1938 ret = CompareStringA(lcid, 0, "a\0b", 4, "a", 2);
1939 ok(ret == CSTR_EQUAL || /* win2k */
1940 ret == CSTR_GREATER_THAN,
1941 "a\\0b vs a expected CSTR_EQUAL or CSTR_GREATER_THAN, got %d\n", ret);
1943 ret = CompareStringA(lcid, 0, "\2", 2, "\1", 2);
1944 todo_wine ok(ret != CSTR_EQUAL, "\\2 vs \\1 expected unequal\n");
1946 ret = CompareStringA(lcid, NORM_IGNORECASE | LOCALE_USE_CP_ACP, "#", -1, ".", -1);
1947 todo_wine ok(ret == CSTR_LESS_THAN, "\"#\" vs \".\" expected CSTR_LESS_THAN, got %d\n", ret);
1949 ret = CompareStringA(lcid, NORM_IGNORECASE, "_", -1, ".", -1);
1950 todo_wine ok(ret == CSTR_GREATER_THAN, "\"_\" vs \".\" expected CSTR_GREATER_THAN, got %d\n", ret);
1952 ret = lstrcmpiA("#", ".");
1953 todo_wine ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret);
1955 lcid = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT);
1957 /* \xB9 character lies between a and b */
1958 ret = CompareStringA(lcid, 0, "a", 1, "\xB9", 1);
1959 ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be greater than \'a\'\n");
1960 ret = CompareStringA(lcid, 0, "\xB9", 1, "b", 1);
1961 ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be smaller than \'b\'\n");
1963 memset(a, 'a', sizeof(a));
1964 SetLastError(0xdeadbeef);
1965 ret = CompareStringA(lcid, 0, a, sizeof(a), a, sizeof(a));
1966 ok (GetLastError() == 0xdeadbeef && ret == CSTR_EQUAL,
1967 "ret %d, error %d, expected value %d\n", ret, GetLastError(), CSTR_EQUAL);
1969 ret = CompareStringA(CP_ACP, 0, ABC_EE, 3, ABC_FF, 3);
1970 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
1971 ret = CompareStringA(CP_ACP, 0, ABC_EE, 5, ABC_FF, 3);
1972 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
1973 ret = CompareStringA(CP_ACP, 0, ABC_EE, 3, ABC_FF, 5);
1974 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
1975 ret = CompareStringA(CP_ACP, 0, ABC_EE, 5, ABC_FF, 5);
1976 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
1977 ret = CompareStringA(CP_ACP, 0, ABC_FF, 5, ABC_EE, 5);
1978 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
1981 static void test_CompareStringW(void)
1983 static const WCHAR ABC_EE[] = {'A','B','C',0,0xEE};
1984 static const WCHAR ABC_FF[] = {'A','B','C',0,0xFF};
1985 static const WCHAR A_ACUTE_BC[] = {0xc1,'B','C',0};
1986 static const WCHAR A_ACUTE_BC_DECOMP[] = {'A',0x301,'B','C',0};
1987 static const WCHAR A_NULL_BC[] = {'A',0,'B','C',0};
1988 WCHAR *str1, *str2;
1989 SYSTEM_INFO si;
1990 DWORD old_prot;
1991 BOOL success;
1992 char *buf;
1993 int ret;
1995 GetSystemInfo(&si);
1996 buf = VirtualAlloc(NULL, si.dwPageSize * 4, MEM_COMMIT, PAGE_READWRITE);
1997 ok(buf != NULL, "VirtualAlloc failed with %u\n", GetLastError());
1998 success = VirtualProtect(buf + si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
1999 ok(success, "VirtualProtect failed with %u\n", GetLastError());
2000 success = VirtualProtect(buf + 3 * si.dwPageSize, si.dwPageSize, PAGE_NOACCESS, &old_prot);
2001 ok(success, "VirtualProtect failed with %u\n", GetLastError());
2003 str1 = (WCHAR *)(buf + si.dwPageSize - sizeof(WCHAR));
2004 str2 = (WCHAR *)(buf + 3 * si.dwPageSize - sizeof(WCHAR));
2005 *str1 = 'A';
2006 *str2 = 'B';
2008 /* CompareStringW should abort on the first non-matching character */
2009 ret = CompareStringW(CP_ACP, 0, str1, 100, str2, 100);
2010 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2012 success = VirtualFree(buf, 0, MEM_RELEASE);
2013 ok(success, "VirtualFree failed with %u\n", GetLastError());
2015 ret = CompareStringW(CP_ACP, 0, ABC_EE, 3, ABC_FF, 3);
2016 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2017 ret = CompareStringW(CP_ACP, 0, ABC_EE, 5, ABC_FF, 3);
2018 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
2019 ret = CompareStringW(CP_ACP, 0, ABC_EE, 3, ABC_FF, 5);
2020 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2021 ret = CompareStringW(CP_ACP, 0, ABC_EE, 5, ABC_FF, 5);
2022 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2023 ret = CompareStringW(CP_ACP, 0, ABC_FF, 5, ABC_EE, 5);
2024 ok(ret == CSTR_GREATER_THAN, "expected CSTR_GREATER_THAN, got %d\n", ret);
2026 ret = CompareStringW(CP_ACP, 0, ABC_EE, 4, A_ACUTE_BC, 4);
2027 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2028 ret = CompareStringW(CP_ACP, 0, ABC_EE, 4, A_ACUTE_BC_DECOMP, 5);
2029 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2030 ret = CompareStringW(CP_ACP, 0, A_ACUTE_BC, 4, A_ACUTE_BC_DECOMP, 5);
2031 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2033 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, ABC_EE, 3, A_ACUTE_BC, 4);
2034 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2035 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, ABC_EE, 4, A_ACUTE_BC_DECOMP, 5);
2036 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2037 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, A_ACUTE_BC, 4, A_ACUTE_BC_DECOMP, 5);
2038 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2040 ret = CompareStringW(CP_ACP, 0, ABC_EE, 4, A_NULL_BC, 4);
2041 ok(ret == CSTR_EQUAL, "expected CSTR_LESS_THAN, got %d\n", ret);
2042 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, ABC_EE, 4, A_NULL_BC, 4);
2043 ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2045 ret = CompareStringW(CP_ACP, 0, A_NULL_BC, 4, A_ACUTE_BC, 4);
2046 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2047 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, A_NULL_BC, 4, A_ACUTE_BC, 4);
2048 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2050 ret = CompareStringW(CP_ACP, 0, A_NULL_BC, 4, A_ACUTE_BC_DECOMP, 5);
2051 ok(ret == CSTR_LESS_THAN, "expected CSTR_LESS_THAN, got %d\n", ret);
2052 ret = CompareStringW(CP_ACP, NORM_IGNORENONSPACE, A_NULL_BC, 4, A_ACUTE_BC_DECOMP, 5);
2053 todo_wine ok(ret == CSTR_EQUAL, "expected CSTR_EQUAL, got %d\n", ret);
2056 struct comparestringex_test {
2057 const char *locale;
2058 DWORD flags;
2059 const WCHAR first[2];
2060 const WCHAR second[2];
2061 INT ret;
2062 INT broken;
2063 BOOL todo;
2066 static const struct comparestringex_test comparestringex_tests[] = {
2067 /* default behavior */
2068 { /* 0 */
2069 "tr-TR", 0,
2070 {'i',0}, {'I',0}, CSTR_LESS_THAN, -1, FALSE
2072 { /* 1 */
2073 "tr-TR", 0,
2074 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2076 { /* 2 */
2077 "tr-TR", 0,
2078 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2080 { /* 3 */
2081 "tr-TR", 0,
2082 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2084 { /* 4 */
2085 "tr-TR", 0,
2086 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2088 { /* 5 */
2089 "tr-TR", 0,
2090 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2092 /* with NORM_IGNORECASE */
2093 { /* 6 */
2094 "tr-TR", NORM_IGNORECASE,
2095 {'i',0}, {'I',0}, CSTR_EQUAL, -1, FALSE
2097 { /* 7 */
2098 "tr-TR", NORM_IGNORECASE,
2099 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2101 { /* 8 */
2102 "tr-TR", NORM_IGNORECASE,
2103 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2105 { /* 9 */
2106 "tr-TR", NORM_IGNORECASE,
2107 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2109 { /* 10 */
2110 "tr-TR", NORM_IGNORECASE,
2111 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2113 { /* 11 */
2114 "tr-TR", NORM_IGNORECASE,
2115 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2117 /* with NORM_LINGUISTIC_CASING */
2118 { /* 12 */
2119 "tr-TR", NORM_LINGUISTIC_CASING,
2120 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2122 { /* 13 */
2123 "tr-TR", NORM_LINGUISTIC_CASING,
2124 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2126 { /* 14 */
2127 "tr-TR", NORM_LINGUISTIC_CASING,
2128 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2130 { /* 15 */
2131 "tr-TR", NORM_LINGUISTIC_CASING,
2132 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2134 { /* 16 */
2135 "tr-TR", NORM_LINGUISTIC_CASING,
2136 {'I',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2138 { /* 17 */
2139 "tr-TR", NORM_LINGUISTIC_CASING,
2140 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2142 /* with LINGUISTIC_IGNORECASE */
2143 { /* 18 */
2144 "tr-TR", LINGUISTIC_IGNORECASE,
2145 {'i',0}, {'I',0}, CSTR_EQUAL, -1, TRUE
2147 { /* 19 */
2148 "tr-TR", LINGUISTIC_IGNORECASE,
2149 {'i',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2151 { /* 20 */
2152 "tr-TR", LINGUISTIC_IGNORECASE,
2153 {'i',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2155 { /* 21 */
2156 "tr-TR", LINGUISTIC_IGNORECASE,
2157 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2159 { /* 22 */
2160 "tr-TR", LINGUISTIC_IGNORECASE,
2161 {'I',0}, {0x131,0}, CSTR_LESS_THAN, -1, FALSE
2163 { /* 23 */
2164 "tr-TR", LINGUISTIC_IGNORECASE,
2165 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2167 /* with NORM_LINGUISTIC_CASING | NORM_IGNORECASE */
2168 { /* 24 */
2169 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2170 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL, TRUE
2172 { /* 25 */
2173 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2174 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2176 { /* 26 */
2177 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2178 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2180 { /* 27 */
2181 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2182 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2184 { /* 28 */
2185 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2186 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2188 { /* 29 */
2189 "tr-TR", NORM_LINGUISTIC_CASING | NORM_IGNORECASE,
2190 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, -1, TRUE
2192 /* with NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE */
2193 { /* 30 */
2194 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2195 {'i',0}, {'I',0}, CSTR_GREATER_THAN, CSTR_EQUAL, TRUE
2197 { /* 31 */
2198 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2199 {'i',0}, {0x130,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2201 { /* 32 */
2202 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2203 {'i',0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2205 { /* 33 */
2206 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2207 {'I',0}, {0x130,0}, CSTR_LESS_THAN, -1, FALSE
2209 { /* 34 */
2210 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2211 {'I',0}, {0x131,0}, CSTR_EQUAL, CSTR_LESS_THAN, TRUE
2213 { /* 35 */
2214 "tr-TR", NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE,
2215 {0x130,0}, {0x131,0}, CSTR_GREATER_THAN, CSTR_LESS_THAN, TRUE
2219 static void test_CompareStringEx(void)
2221 const char *op[] = {"ERROR", "CSTR_LESS_THAN", "CSTR_EQUAL", "CSTR_GREATER_THAN"};
2222 WCHAR locale[6];
2223 INT ret, i;
2225 /* CompareStringEx is only available on Vista+ */
2226 if (!pCompareStringEx)
2228 win_skip("CompareStringEx not supported\n");
2229 return;
2232 for (i = 0; i < ARRAY_SIZE(comparestringex_tests); i++)
2234 const struct comparestringex_test *e = &comparestringex_tests[i];
2236 MultiByteToWideChar(CP_ACP, 0, e->locale, -1, locale, ARRAY_SIZE(locale));
2237 ret = pCompareStringEx(locale, e->flags, e->first, -1, e->second, -1, NULL, NULL, 0);
2238 todo_wine_if (e->todo)
2239 ok(ret == e->ret || broken(ret == e->broken),
2240 "%d: got %s, expected %s\n", i, op[ret], op[e->ret]);
2245 static const DWORD lcmap_invalid_flags[] = {
2247 LCMAP_HIRAGANA | LCMAP_KATAKANA,
2248 LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
2249 LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
2250 LCMAP_LOWERCASE | SORT_STRINGSORT,
2251 LCMAP_UPPERCASE | NORM_IGNORESYMBOLS,
2252 LCMAP_LOWERCASE | NORM_IGNORESYMBOLS,
2253 LCMAP_UPPERCASE | NORM_IGNORENONSPACE,
2254 LCMAP_LOWERCASE | NORM_IGNORENONSPACE,
2255 LCMAP_HIRAGANA | NORM_IGNORENONSPACE,
2256 LCMAP_HIRAGANA | NORM_IGNORESYMBOLS,
2257 LCMAP_HIRAGANA | LCMAP_SIMPLIFIED_CHINESE,
2258 LCMAP_HIRAGANA | LCMAP_TRADITIONAL_CHINESE,
2259 LCMAP_KATAKANA | NORM_IGNORENONSPACE,
2260 LCMAP_KATAKANA | NORM_IGNORESYMBOLS,
2261 LCMAP_KATAKANA | LCMAP_SIMPLIFIED_CHINESE,
2262 LCMAP_KATAKANA | LCMAP_TRADITIONAL_CHINESE,
2263 LCMAP_FULLWIDTH | NORM_IGNORENONSPACE,
2264 LCMAP_FULLWIDTH | NORM_IGNORESYMBOLS,
2265 LCMAP_FULLWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2266 LCMAP_FULLWIDTH | LCMAP_TRADITIONAL_CHINESE,
2267 LCMAP_HALFWIDTH | NORM_IGNORENONSPACE,
2268 LCMAP_HALFWIDTH | NORM_IGNORESYMBOLS,
2269 LCMAP_HALFWIDTH | LCMAP_SIMPLIFIED_CHINESE,
2270 LCMAP_HALFWIDTH | LCMAP_TRADITIONAL_CHINESE,
2273 static void test_LCMapStringA(void)
2275 int ret, ret2, i;
2276 char buf[256], buf2[256];
2277 static const char upper_case[] = "\tJUST! A, TEST; STRING 1/*+-.\r\n";
2278 static const char lower_case[] = "\tjust! a, test; string 1/*+-.\r\n";
2279 static const char symbols_stripped[] = "justateststring1";
2281 SetLastError(0xdeadbeef);
2282 ret = LCMapStringA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP | LCMAP_LOWERCASE,
2283 lower_case, -1, buf, sizeof(buf));
2284 ok(ret == lstrlenA(lower_case) + 1,
2285 "ret %d, error %d, expected value %d\n",
2286 ret, GetLastError(), lstrlenA(lower_case) + 1);
2287 ok(!memcmp(buf, lower_case, ret), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2289 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
2290 upper_case, -1, buf, sizeof(buf));
2291 ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
2292 ok(GetLastError() == ERROR_INVALID_FLAGS,
2293 "unexpected error code %d\n", GetLastError());
2295 /* test invalid flag combinations */
2296 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2297 lstrcpyA(buf, "foo");
2298 SetLastError(0xdeadbeef);
2299 ret = LCMapStringA(LOCALE_USER_DEFAULT, lcmap_invalid_flags[i],
2300 lower_case, -1, buf, sizeof(buf));
2301 ok(GetLastError() == ERROR_INVALID_FLAGS,
2302 "LCMapStringA (flag %08x) unexpected error code %d\n",
2303 lcmap_invalid_flags[i], GetLastError());
2304 ok(!ret, "LCMapStringA (flag %08x) should return 0, got %d\n",
2305 lcmap_invalid_flags[i], ret);
2308 /* test LCMAP_LOWERCASE */
2309 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2310 upper_case, -1, buf, sizeof(buf));
2311 ok(ret == lstrlenA(upper_case) + 1,
2312 "ret %d, error %d, expected value %d\n",
2313 ret, GetLastError(), lstrlenA(upper_case) + 1);
2314 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2316 /* test LCMAP_UPPERCASE */
2317 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2318 lower_case, -1, buf, sizeof(buf));
2319 ok(ret == lstrlenA(lower_case) + 1,
2320 "ret %d, error %d, expected value %d\n",
2321 ret, GetLastError(), lstrlenA(lower_case) + 1);
2322 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2324 /* test buffer overflow */
2325 SetLastError(0xdeadbeef);
2326 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2327 lower_case, -1, buf, 4);
2328 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2329 "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
2331 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2332 lstrcpyA(buf, lower_case);
2333 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
2334 buf, -1, buf, sizeof(buf));
2335 if (!ret) /* Win9x */
2336 trace("Ignoring LCMapStringA(LCMAP_UPPERCASE, buf, buf) error on Win9x\n");
2337 else
2339 ok(ret == lstrlenA(lower_case) + 1,
2340 "ret %d, error %d, expected value %d\n",
2341 ret, GetLastError(), lstrlenA(lower_case) + 1);
2342 ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
2344 lstrcpyA(buf, upper_case);
2345 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
2346 buf, -1, buf, sizeof(buf));
2347 if (!ret) /* Win9x */
2348 trace("Ignoring LCMapStringA(LCMAP_LOWERCASE, buf, buf) error on Win9x\n");
2349 else
2351 ok(ret == lstrlenA(upper_case) + 1,
2352 "ret %d, error %d, expected value %d\n",
2353 ret, GetLastError(), lstrlenA(lower_case) + 1);
2354 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2357 /* otherwise src == dst should fail */
2358 SetLastError(0xdeadbeef);
2359 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
2360 buf, 10, buf, sizeof(buf));
2361 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2362 GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
2363 "unexpected error code %d\n", GetLastError());
2364 ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
2366 /* test whether '\0' is always appended */
2367 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2368 upper_case, -1, buf, sizeof(buf));
2369 ok(ret, "LCMapStringA must succeed\n");
2370 ok(buf[ret-1] == 0, "LCMapStringA not null-terminated\n");
2371 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2372 upper_case, lstrlenA(upper_case), buf2, sizeof(buf2));
2373 ok(ret2, "LCMapStringA must succeed\n");
2374 ok(buf2[ret2-1] == 0, "LCMapStringA not null-terminated\n" );
2375 ok(ret == ret2, "lengths of sort keys must be equal\n");
2376 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2378 /* test we get the same length when no dest buffer is provided */
2379 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2380 upper_case, lstrlenA(upper_case), NULL, 0);
2381 ok(ret2, "LCMapStringA must succeed\n");
2382 ok(ret == ret2, "lengths of sort keys must be equal (%d vs %d)\n", ret, ret2);
2384 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2385 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
2386 upper_case, -1, buf, sizeof(buf));
2387 ok(ret, "LCMapStringA must succeed\n");
2388 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2389 lower_case, -1, buf2, sizeof(buf2));
2390 ok(ret2, "LCMapStringA must succeed\n");
2391 ok(ret == ret2, "lengths of sort keys must be equal\n");
2392 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2394 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2395 results from plain LCMAP_SORTKEY on Vista */
2397 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2398 ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2399 lower_case, -1, buf, sizeof(buf));
2400 ok(ret, "LCMapStringA must succeed\n");
2401 ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
2402 symbols_stripped, -1, buf2, sizeof(buf2));
2403 ok(ret2, "LCMapStringA must succeed\n");
2404 ok(ret == ret2, "lengths of sort keys must be equal\n");
2405 ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
2407 /* test NORM_IGNORENONSPACE */
2408 lstrcpyA(buf, "foo");
2409 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
2410 lower_case, -1, buf, sizeof(buf));
2411 ok(ret == lstrlenA(lower_case) + 1, "LCMapStringA should return %d, ret = %d\n",
2412 lstrlenA(lower_case) + 1, ret);
2413 ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2415 /* test NORM_IGNORESYMBOLS */
2416 lstrcpyA(buf, "foo");
2417 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
2418 lower_case, -1, buf, sizeof(buf));
2419 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2420 lstrlenA(symbols_stripped) + 1, ret);
2421 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2423 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2424 lstrcpyA(buf, "foo");
2425 ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE,
2426 lower_case, -1, buf, sizeof(buf));
2427 ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
2428 lstrlenA(symbols_stripped) + 1, ret);
2429 ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
2431 /* test srclen = 0 */
2432 SetLastError(0xdeadbeef);
2433 ret = LCMapStringA(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
2434 ok(!ret, "LCMapStringA should fail with srclen = 0\n");
2435 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2436 "unexpected error code %d\n", GetLastError());
2439 typedef INT (*lcmapstring_wrapper)(DWORD, LPCWSTR, INT, LPWSTR, INT);
2441 static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *func_name)
2443 const static WCHAR japanese_text[] = {
2444 0x3044, 0x309d, 0x3084, 0x3001, 0x30a4, 0x30fc, 0x30cf, 0x30c8,
2445 0x30fc, 0x30f4, 0x30a9, 0x306e, 0x91ce, 0x539f, 0x306f, 0x5e83,
2446 0x3044, 0x3093, 0x3060, 0x3088, 0x3002, 0
2448 const static WCHAR hiragana_text[] = {
2449 0x3044, 0x309d, 0x3084, 0x3001, 0x3044, 0x30fc, 0x306f, 0x3068,
2450 0x30fc, 0x3094, 0x3049, 0x306e, 0x91ce, 0x539f, 0x306f, 0x5e83,
2451 0x3044, 0x3093, 0x3060, 0x3088, 0x3002, 0
2453 const static WCHAR katakana_text[] = {
2454 0x30a4, 0x30fd, 0x30e4, 0x3001, 0x30a4, 0x30fc, 0x30cf, 0x30c8,
2455 0x30fc, 0x30f4, 0x30a9, 0x30ce, 0x91ce, 0x539f, 0x30cf, 0x5e83,
2456 0x30a4, 0x30f3, 0x30c0, 0x30e8, 0x3002, 0
2458 const static WCHAR halfwidth_text[] = {
2459 0x3044, 0x309d, 0x3084, 0xff64, 0xff72, 0xff70, 0xff8a, 0xff84,
2460 0xff70, 0xff73, 0xff9e, 0xff6b, 0x306e, 0x91ce, 0x539f, 0x306f,
2461 0x5e83, 0x3044, 0x3093, 0x3060, 0x3088, 0xff61, 0
2463 const static WCHAR halfwidth_text2[] = {
2464 0xff72, 0x30fd, 0xff94, 0xff64, 0xff72, 0xff70, 0xff8a, 0xff84,
2465 0xff70, 0xff73, 0xff9e, 0xff6b, 0xff89, 0x91ce, 0x539f, 0xff8a,
2466 0x5e83, 0xff72, 0xff9d, 0xff80, 0xff9e, 0xff96, 0xff61, 0
2468 int ret, ret2, i;
2469 WCHAR buf[256], buf2[256];
2470 char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
2472 /* LCMAP_LOWERCASE | LCMAP_UPPERCASE makes LCMAP_TITLECASE, so it's valid now. */
2473 ret = func_ptr(LCMAP_LOWERCASE | LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2474 todo_wine ok(ret == lstrlenW(title_case) + 1 || broken(!ret),
2475 "%s ret %d, error %d, expected value %d\n", func_name,
2476 ret, GetLastError(), lstrlenW(title_case) + 1);
2477 todo_wine ok(lstrcmpW(buf, title_case) == 0 || broken(!ret),
2478 "Expected title case string\n");
2480 /* test invalid flag combinations */
2481 for (i = 0; i < ARRAY_SIZE(lcmap_invalid_flags); i++) {
2482 lstrcpyW(buf, fooW);
2483 SetLastError(0xdeadbeef);
2484 ret = func_ptr(lcmap_invalid_flags[i],
2485 lower_case, -1, buf, sizeof(buf));
2486 ok(GetLastError() == ERROR_INVALID_FLAGS,
2487 "%s (flag %08x) unexpected error code %d\n",
2488 func_name, lcmap_invalid_flags[i], GetLastError());
2489 ok(!ret, "%s (flag %08x) should return 0, got %d\n",
2490 func_name, lcmap_invalid_flags[i], ret);
2493 /* test LCMAP_LOWERCASE */
2494 ret = func_ptr(LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2495 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2496 ret, GetLastError(), lstrlenW(upper_case) + 1);
2497 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2499 /* test LCMAP_UPPERCASE */
2500 ret = func_ptr(LCMAP_UPPERCASE, lower_case, -1, buf, ARRAY_SIZE(buf));
2501 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2502 ret, GetLastError(), lstrlenW(lower_case) + 1);
2503 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2505 /* test LCMAP_HIRAGANA */
2506 ret = func_ptr(LCMAP_HIRAGANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2507 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2508 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2509 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2511 buf[0] = 0x30f5; /* KATAKANA LETTER SMALL KA */
2512 ret = func_ptr(LCMAP_HIRAGANA, buf, 1, buf2, 1);
2513 ok(ret == 1, "%s ret %d, error %d, expected value 1\n", func_name,
2514 ret, GetLastError());
2515 /* U+3095: HIRAGANA LETTER SMALL KA was added in Unicode 3.2 */
2516 ok(buf2[0] == 0x3095 || broken(buf2[0] == 0x30f5 /* Vista and earlier versions */),
2517 "%s expected %04x, got %04x\n", func_name, 0x3095, buf2[0]);
2519 /* test LCMAP_KATAKANA | LCMAP_LOWERCASE */
2520 ret = func_ptr(LCMAP_KATAKANA | LCMAP_LOWERCASE, japanese_text, -1, buf, ARRAY_SIZE(buf));
2521 ok(ret == lstrlenW(katakana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2522 ret, GetLastError(), lstrlenW(katakana_text) + 1);
2523 ok(!lstrcmpW(buf, katakana_text), "%s string compare mismatch\n", func_name);
2525 /* test LCMAP_FULLWIDTH */
2526 ret = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2527 ok(ret == lstrlenW(japanese_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2528 ret, GetLastError(), lstrlenW(japanese_text) + 1);
2529 ok(!lstrcmpW(buf, japanese_text), "%s string compare mismatch\n", func_name);
2531 ret2 = func_ptr(LCMAP_FULLWIDTH, halfwidth_text, -1, NULL, 0);
2532 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret2, ret);
2534 /* test LCMAP_FULLWIDTH | LCMAP_HIRAGANA
2535 (half-width katakana is converted into full-width hiragana) */
2536 ret = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, buf, ARRAY_SIZE(buf));
2537 ok(ret == lstrlenW(hiragana_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2538 ret, GetLastError(), lstrlenW(hiragana_text) + 1);
2539 ok(!lstrcmpW(buf, hiragana_text), "%s string compare mismatch\n", func_name);
2541 ret2 = func_ptr(LCMAP_FULLWIDTH | LCMAP_HIRAGANA, halfwidth_text, -1, NULL, 0);
2542 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2544 /* test LCMAP_HALFWIDTH */
2545 ret = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, buf, ARRAY_SIZE(buf));
2546 ok(ret == lstrlenW(halfwidth_text) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2547 ret, GetLastError(), lstrlenW(halfwidth_text) + 1);
2548 ok(!lstrcmpW(buf, halfwidth_text), "%s string compare mismatch\n", func_name);
2550 ret2 = func_ptr(LCMAP_HALFWIDTH, japanese_text, -1, NULL, 0);
2551 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2553 /* test LCMAP_HALFWIDTH | LCMAP_KATAKANA
2554 (hiragana character is converted into half-width katakana) */
2555 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_KATAKANA, japanese_text, -1, buf, ARRAY_SIZE(buf));
2556 ok(ret == lstrlenW(halfwidth_text2) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2557 ret, GetLastError(), lstrlenW(halfwidth_text2) + 1);
2558 ok(!lstrcmpW(buf, halfwidth_text2), "%s string compare mismatch\n", func_name);
2560 ret2 = func_ptr(LCMAP_HALFWIDTH | LCMAP_KATAKANA, japanese_text, -1, NULL, 0);
2561 ok(ret == ret2, "%s ret %d, expected value %d\n", func_name, ret, ret2);
2563 /* test buffer overflow */
2564 SetLastError(0xdeadbeef);
2565 ret = func_ptr(LCMAP_UPPERCASE,
2566 lower_case, -1, buf, 4);
2567 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2568 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2570 /* KATAKANA LETTER GA (U+30AC) is converted into two half-width characters.
2571 Thus, it requires two WCHARs. */
2572 buf[0] = 0x30ac;
2573 SetLastError(0xdeadbeef);
2574 ret = func_ptr(LCMAP_HALFWIDTH, buf, 1, buf2, 1);
2575 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2576 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2578 buf[0] = 'a';
2579 buf[1] = 0x30ac;
2580 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 0);
2581 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2583 SetLastError(0xdeadbeef);
2584 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 1);
2585 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2586 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2588 SetLastError(0xdeadbeef);
2589 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 2);
2590 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
2591 "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
2593 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 3);
2594 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2596 ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_UPPERCASE, buf, 2, buf2, 4);
2597 ok(ret == 3, "%s ret %d, expected value 3\n", func_name, ret);
2599 /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
2600 lstrcpyW(buf, lower_case);
2601 ret = func_ptr(LCMAP_UPPERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2602 ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2603 ret, GetLastError(), lstrlenW(lower_case) + 1);
2604 ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
2606 lstrcpyW(buf, upper_case);
2607 ret = func_ptr(LCMAP_LOWERCASE, buf, -1, buf, ARRAY_SIZE(buf));
2608 ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
2609 ret, GetLastError(), lstrlenW(lower_case) + 1);
2610 ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
2612 /* otherwise src == dst should fail */
2613 SetLastError(0xdeadbeef);
2614 ret = func_ptr(LCMAP_SORTKEY | LCMAP_UPPERCASE,
2615 buf, 10, buf, sizeof(buf));
2616 ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
2617 GetLastError() == ERROR_INVALID_PARAMETER /* Win7+ */,
2618 "%s unexpected error code %d\n", func_name, GetLastError());
2619 ok(!ret, "%s src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n", func_name);
2621 /* test whether '\0' is always appended */
2622 ret = func_ptr(LCMAP_SORTKEY,
2623 upper_case, -1, buf, sizeof(buf));
2624 ok(ret, "%s func_ptr must succeed\n", func_name);
2625 ret2 = func_ptr(LCMAP_SORTKEY,
2626 upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
2627 ok(ret, "%s func_ptr must succeed\n", func_name);
2628 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2629 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2631 /* test LCMAP_SORTKEY | NORM_IGNORECASE */
2632 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORECASE,
2633 upper_case, -1, buf, sizeof(buf));
2634 ok(ret, "%s func_ptr must succeed\n", func_name);
2635 ret2 = func_ptr(LCMAP_SORTKEY,
2636 lower_case, -1, buf2, sizeof(buf2));
2637 ok(ret2, "%s func_ptr must succeed\n", func_name);
2638 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2639 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2641 /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
2642 results from plain LCMAP_SORTKEY on Vista */
2644 /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
2645 ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
2646 lower_case, -1, buf, sizeof(buf));
2647 ok(ret, "%s func_ptr must succeed\n", func_name);
2648 ret2 = func_ptr(LCMAP_SORTKEY,
2649 symbols_stripped, -1, buf2, sizeof(buf2));
2650 ok(ret2, "%s func_ptr must succeed\n", func_name);
2651 ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
2652 ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
2654 /* test NORM_IGNORENONSPACE */
2655 lstrcpyW(buf, fooW);
2656 ret = func_ptr(NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2657 ok(ret == lstrlenW(lower_case) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2658 lstrlenW(lower_case) + 1, ret);
2659 ok(!lstrcmpW(buf, lower_case), "%s string comparison mismatch\n", func_name);
2661 /* test NORM_IGNORESYMBOLS */
2662 lstrcpyW(buf, fooW);
2663 ret = func_ptr(NORM_IGNORESYMBOLS, lower_case, -1, buf, ARRAY_SIZE(buf));
2664 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2665 lstrlenW(symbols_stripped) + 1, ret);
2666 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2668 /* test NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE */
2669 lstrcpyW(buf, fooW);
2670 ret = func_ptr(NORM_IGNORESYMBOLS | NORM_IGNORENONSPACE, lower_case, -1, buf, ARRAY_SIZE(buf));
2671 ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
2672 lstrlenW(symbols_stripped) + 1, ret);
2673 ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
2675 /* test srclen = 0 */
2676 SetLastError(0xdeadbeef);
2677 ret = func_ptr(0, upper_case, 0, buf, ARRAY_SIZE(buf));
2678 ok(!ret, "%s func_ptr should fail with srclen = 0\n", func_name);
2679 ok(GetLastError() == ERROR_INVALID_PARAMETER,
2680 "%s unexpected error code %d\n", func_name, GetLastError());
2683 static INT LCMapStringW_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2685 return LCMapStringW(LOCALE_USER_DEFAULT, flags, src, srclen, dst, dstlen);
2688 static void test_LCMapStringW(void)
2690 int ret;
2691 WCHAR buf[256];
2693 trace("testing LCMapStringW\n");
2695 SetLastError(0xdeadbeef);
2696 ret = LCMapStringW((LCID)-1, LCMAP_LOWERCASE, upper_case, -1, buf, ARRAY_SIZE(buf));
2697 todo_wine {
2698 ok(!ret, "LCMapStringW should fail with bad lcid\n");
2699 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
2702 test_lcmapstring_unicode(LCMapStringW_wrapper, "LCMapStringW:");
2705 static INT LCMapStringEx_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
2707 return pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2710 static void test_LCMapStringEx(void)
2712 int ret;
2713 WCHAR buf[256];
2715 if (!pLCMapStringEx)
2717 win_skip( "LCMapStringEx not available\n" );
2718 return;
2721 trace("testing LCMapStringEx\n");
2723 SetLastError(0xdeadbeef);
2724 ret = pLCMapStringEx(invalidW, LCMAP_LOWERCASE,
2725 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 0);
2726 todo_wine {
2727 ok(!ret, "LCMapStringEx should fail with bad locale name\n");
2728 ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
2731 /* test reserved parameters */
2732 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2733 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, NULL, 1);
2734 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
2735 ret, GetLastError(), lstrlenW(upper_case) + 1);
2736 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2738 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2739 upper_case, -1, buf, ARRAY_SIZE(buf), NULL, (void*)1, 0);
2740 ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
2741 ret, GetLastError(), lstrlenW(upper_case) + 1);
2742 ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
2744 /* crashes on native */
2745 if(0)
2746 ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
2747 upper_case, -1, buf, ARRAY_SIZE(buf), (void*)1, NULL, 0);
2749 test_lcmapstring_unicode(LCMapStringEx_wrapper, "LCMapStringEx:");
2752 struct neutralsublang_name_t {
2753 WCHAR name[3];
2754 WCHAR sname[16];
2755 LCID lcid;
2756 int todo;
2759 static const struct neutralsublang_name_t neutralsublang_names[] = {
2760 { {'a','r',0}, {'a','r','-','S','A',0}, MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
2761 { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0}, MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
2762 { {'d','e',0}, {'d','e','-','D','E',0}, MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
2763 { {'e','n',0}, {'e','n','-','U','S',0}, MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
2764 { {'e','s',0}, {'e','s','-','E','S',0}, MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT) },
2765 { {'g','a',0}, {'g','a','-','I','E',0}, MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT) },
2766 { {'i','t',0}, {'i','t','-','I','T',0}, MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
2767 { {'m','s',0}, {'m','s','-','M','Y',0}, MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
2768 { {'n','l',0}, {'n','l','-','N','L',0}, MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
2769 { {'p','t',0}, {'p','t','-','B','R',0}, MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
2770 { {'s','r',0}, {'s','r','-','L','a','t','n','-','R','S',0}, MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN), SORT_DEFAULT), 1 },
2771 { {'s','v',0}, {'s','v','-','S','E',0}, MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
2772 { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0}, MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
2773 { {'z','h',0}, {'z','h','-','C','N',0}, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT) },
2774 { {0} }
2777 static void test_LocaleNameToLCID(void)
2779 LCID lcid, expect;
2780 NTSTATUS status;
2781 INT ret;
2782 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
2783 const struct neutralsublang_name_t *ptr;
2785 static const WCHAR enW[] = {'e','n',0};
2786 static const WCHAR esesW[] = {'e','s','-','e','s',0};
2787 static const WCHAR zhHansW[] = {'z','h','-','H','a','n','s',0};
2788 static const WCHAR zhhansW[] = {'z','h','-','h','a','n','s',0};
2789 static const WCHAR zhHantW[] = {'z','h','-','H','a','n','t',0};
2790 static const WCHAR zhhantW[] = {'z','h','-','h','a','n','t',0};
2791 static const WCHAR zhcnW[] = {'z','h','-','C','N',0};
2792 static const WCHAR zhhkW[] = {'z','h','-','H','K',0};
2794 if (!pLocaleNameToLCID)
2796 win_skip( "LocaleNameToLCID not available\n" );
2797 return;
2800 /* special cases */
2801 buffer[0] = 0;
2802 SetLastError(0xdeadbeef);
2803 lcid = pLocaleNameToLCID(LOCALE_NAME_USER_DEFAULT, 0);
2804 ok(lcid == GetUserDefaultLCID() || broken(GetLastError() == ERROR_INVALID_PARAMETER /* Vista */),
2805 "Expected lcid == %08x, got %08x, error %d\n", GetUserDefaultLCID(), lcid, GetLastError());
2806 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2807 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2808 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2810 buffer[0] = 0;
2811 SetLastError(0xdeadbeef);
2812 lcid = pLocaleNameToLCID(LOCALE_NAME_SYSTEM_DEFAULT, 0);
2813 ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
2814 "Expected lcid == 0, got %08x, error %d\n", lcid, GetLastError());
2815 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2816 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2817 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2819 buffer[0] = 0;
2820 SetLastError(0xdeadbeef);
2821 lcid = pLocaleNameToLCID(LOCALE_NAME_INVARIANT, 0);
2822 ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08x, error %d\n", lcid, GetLastError());
2823 ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
2824 ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
2825 trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
2827 /* bad name */
2828 SetLastError(0xdeadbeef);
2829 lcid = pLocaleNameToLCID(invalidW, 0);
2830 ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
2831 "Expected lcid == 0, got %08x, error %d\n", lcid, GetLastError());
2833 /* lower-case */
2834 lcid = pLocaleNameToLCID(esesW, 0);
2835 ok(lcid == MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT), "Got wrong lcid for es-es: 0x%x\n", lcid);
2837 /* english neutral name */
2838 lcid = pLocaleNameToLCID(enW, LOCALE_ALLOW_NEUTRAL_NAMES);
2839 ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT) ||
2840 broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid);
2841 lcid = pLocaleNameToLCID(enW, 0);
2842 ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) ||
2843 broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid);
2844 if (lcid)
2846 for (ptr = neutralsublang_names; *ptr->name; ptr++)
2848 lcid = pLocaleNameToLCID(ptr->name, 0);
2849 todo_wine_if (ptr->todo)
2850 ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
2851 wine_dbgstr_w(ptr->name), lcid, ptr->lcid);
2853 *buffer = 0;
2854 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2855 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(ptr->name), ret);
2856 todo_wine_if (ptr->todo)
2857 ok(!lstrcmpW(ptr->sname, buffer), "%s: got wrong locale name %s\n",
2858 wine_dbgstr_w(ptr->name), wine_dbgstr_w(buffer));
2862 /* zh-Hant has LCID 0x7c04, but LocaleNameToLCID actually returns 0x0c04, which is the LCID of zh-HK */
2863 lcid = pLocaleNameToLCID(zhHantW, 0);
2864 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
2865 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhHantW), lcid);
2866 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2867 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
2868 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2869 wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
2870 /* check that 0x7c04 also works and is mapped to zh-HK */
2871 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE_TRADITIONAL, SUBLANG_CHINESE_TRADITIONAL),
2872 buffer, ARRAY_SIZE(buffer), 0);
2873 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHantW), ret);
2874 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2875 wine_dbgstr_w(zhHantW), wine_dbgstr_w(buffer));
2877 /* zh-hant */
2878 lcid = pLocaleNameToLCID(zhhantW, 0);
2879 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), SORT_DEFAULT),
2880 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhhantW), lcid);
2881 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2882 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhantW), ret);
2883 ok(!lstrcmpW(zhhkW, buffer), "%s: got wrong locale name %s\n",
2884 wine_dbgstr_w(zhhantW), wine_dbgstr_w(buffer));
2886 /* zh-Hans has LCID 0x0004, but LocaleNameToLCID actually returns 0x0804, which is the LCID of zh-CN */
2887 lcid = pLocaleNameToLCID(zhHansW, 0);
2888 /* check that LocaleNameToLCID actually returns 0x0804 */
2889 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
2890 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhHansW), lcid);
2891 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2892 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
2893 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2894 wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
2895 /* check that 0x0004 also works and is mapped to zh-CN */
2896 ret = pLCIDToLocaleName(MAKELANGID(LANG_CHINESE, SUBLANG_NEUTRAL), buffer, ARRAY_SIZE(buffer), 0);
2897 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhHansW), ret);
2898 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2899 wine_dbgstr_w(zhHansW), wine_dbgstr_w(buffer));
2901 /* zh-hans */
2902 lcid = pLocaleNameToLCID(zhhansW, 0);
2903 ok(lcid == MAKELCID(MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT),
2904 "%s: got wrong lcid 0x%04x\n", wine_dbgstr_w(zhhansW), lcid);
2905 ret = pLCIDToLocaleName(lcid, buffer, ARRAY_SIZE(buffer), 0);
2906 ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(zhhansW), ret);
2907 ok(!lstrcmpW(zhcnW, buffer), "%s: got wrong locale name %s\n",
2908 wine_dbgstr_w(zhhansW), wine_dbgstr_w(buffer));
2911 if (pRtlLocaleNameToLcid)
2913 status = pRtlLocaleNameToLcid( LOCALE_NAME_USER_DEFAULT, &lcid, 0 );
2914 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2915 status = pRtlLocaleNameToLcid( LOCALE_NAME_SYSTEM_DEFAULT, &lcid, 0 );
2916 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2917 status = pRtlLocaleNameToLcid( invalidW, &lcid, 0 );
2918 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2920 lcid = 0;
2921 status = pRtlLocaleNameToLcid( LOCALE_NAME_INVARIANT, &lcid, 0 );
2922 ok( !status, "failed error %x\n", status );
2923 ok( lcid == LANG_INVARIANT, "got %08x\n", lcid );
2925 lcid = 0;
2926 status = pRtlLocaleNameToLcid( localeW, &lcid, 0 );
2927 ok( !status, "failed error %x\n", status );
2928 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), "got %08x\n", lcid );
2930 lcid = 0;
2931 status = pRtlLocaleNameToLcid( esesW, &lcid, 0 );
2932 ok( !status, "failed error %x\n", status );
2933 ok( lcid == MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), "got %08x\n", lcid );
2935 lcid = 0;
2936 status = pRtlLocaleNameToLcid( enW, &lcid, 0 );
2937 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2938 status = pRtlLocaleNameToLcid( enW, &lcid, 1 );
2939 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2940 status = pRtlLocaleNameToLcid( enW, &lcid, 2 );
2941 ok( !status, "failed error %x\n", status );
2942 ok( lcid == MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), "got %08x\n", lcid );
2943 status = pRtlLocaleNameToLcid( L"en-RR", &lcid, 2 );
2944 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2945 status = pRtlLocaleNameToLcid( L"en-Latn-RR", &lcid, 2 );
2946 ok( status == STATUS_INVALID_PARAMETER_1, "wrong error %x\n", status );
2948 for (ptr = neutralsublang_names; *ptr->name; ptr++)
2950 switch (LANGIDFROMLCID(ptr->lcid))
2952 case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN): expect = LANG_SERBIAN_NEUTRAL; break;
2953 case MAKELANGID( LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_CYRILLIC): expect = 0x6c1a; break;
2954 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ): expect = 0x7804; break;
2955 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ): expect = LANG_CHINESE_TRADITIONAL; break;
2956 default: expect = MAKELANGID( PRIMARYLANGID(ptr->lcid), SUBLANG_NEUTRAL ); break;
2959 status = pRtlLocaleNameToLcid( ptr->name, &lcid, 2 );
2960 ok( !status || broken(ptr->lcid == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)), /* vista */
2961 "%s failed error %x\n", wine_dbgstr_w(ptr->name), status );
2962 todo_wine_if(ptr->todo)
2963 if (!status) ok( lcid == expect, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
2964 wine_dbgstr_w(ptr->name), lcid, expect );
2965 status = pRtlLocaleNameToLcid( ptr->sname, &lcid, 0 );
2966 ok( !status || broken(ptr->lcid == MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN)), /* vista */
2967 "%s failed error %x\n", wine_dbgstr_w(ptr->name), status );
2968 todo_wine_if(ptr->todo)
2969 if (!status) ok( lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
2970 wine_dbgstr_w(ptr->name), lcid, ptr->lcid );
2973 else win_skip( "RtlLocaleNameToLcid not available\n" );
2976 /* this requires collation table patch to make it MS compatible */
2977 static const char * const strings_sorted[] =
2979 "'",
2980 "-",
2981 "!",
2982 "\"",
2983 ".",
2984 ":",
2985 "\\",
2986 "_",
2987 "`",
2988 "{",
2989 "}",
2990 "+",
2991 "0",
2992 "1",
2993 "2",
2994 "3",
2995 "4",
2996 "5",
2997 "6",
2998 "7",
2999 "8",
3000 "9",
3001 "a",
3002 "A",
3003 "b",
3004 "B",
3005 "c",
3009 static const char * const strings[] =
3011 "C",
3012 "\"",
3013 "9",
3014 "'",
3015 "}",
3016 "-",
3017 "7",
3018 "+",
3019 "`",
3020 "1",
3021 "a",
3022 "5",
3023 "\\",
3024 "8",
3025 "B",
3026 "3",
3027 "_",
3028 "6",
3029 "{",
3030 "2",
3031 "c",
3032 "4",
3033 "!",
3034 "0",
3035 "A",
3036 ":",
3037 "b",
3041 static int compare_string1(const void *e1, const void *e2)
3043 const char *s1 = *(const char *const *)e1;
3044 const char *s2 = *(const char *const *)e2;
3046 return lstrcmpA(s1, s2);
3049 static int compare_string2(const void *e1, const void *e2)
3051 const char *s1 = *(const char *const *)e1;
3052 const char *s2 = *(const char *const *)e2;
3054 return CompareStringA(0, 0, s1, -1, s2, -1) - 2;
3057 static int compare_string3(const void *e1, const void *e2)
3059 const char *s1 = *(const char *const *)e1;
3060 const char *s2 = *(const char *const *)e2;
3061 char key1[256], key2[256];
3063 LCMapStringA(0, LCMAP_SORTKEY, s1, -1, key1, sizeof(key1));
3064 LCMapStringA(0, LCMAP_SORTKEY, s2, -1, key2, sizeof(key2));
3065 return strcmp(key1, key2);
3068 static void test_sorting(void)
3070 char buf[256];
3071 char **str_buf = (char **)buf;
3072 int i;
3074 assert(sizeof(buf) >= sizeof(strings));
3076 /* 1. sort using lstrcmpA */
3077 memcpy(buf, strings, sizeof(strings));
3078 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string1);
3079 for (i = 0; i < ARRAY_SIZE(strings); i++)
3081 ok(!strcmp(strings_sorted[i], str_buf[i]),
3082 "qsort using lstrcmpA failed for element %d\n", i);
3084 /* 2. sort using CompareStringA */
3085 memcpy(buf, strings, sizeof(strings));
3086 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string2);
3087 for (i = 0; i < ARRAY_SIZE(strings); i++)
3089 ok(!strcmp(strings_sorted[i], str_buf[i]),
3090 "qsort using CompareStringA failed for element %d\n", i);
3092 /* 3. sort using sort keys */
3093 memcpy(buf, strings, sizeof(strings));
3094 qsort(buf, ARRAY_SIZE(strings), sizeof(strings[0]), compare_string3);
3095 for (i = 0; i < ARRAY_SIZE(strings); i++)
3097 ok(!strcmp(strings_sorted[i], str_buf[i]),
3098 "qsort using sort keys failed for element %d\n", i);
3102 static void test_FoldStringA(void)
3104 int ret, i, j;
3105 BOOL is_special;
3106 char src[256], dst[256];
3107 static const char digits_src[] = { 0xB9,0xB2,0xB3,'\0' };
3108 static const char digits_dst[] = { '1','2','3','\0' };
3109 static const char composite_src[] =
3111 0x8a,0x8e,0x9a,0x9e,0x9f,0xc0,0xc1,0xc2,
3112 0xc3,0xc4,0xc5,0xc7,0xc8,0xc9,0xca,0xcb,
3113 0xcc,0xcd,0xce,0xcf,0xd1,0xd2,0xd3,0xd4,
3114 0xd5,0xd6,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,
3115 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe7,0xe8,
3116 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf1,
3117 0xf2,0xf3,0xf4,0xf5,0xf6,0xf8,0xf9,0xfa,
3118 0xfb,0xfc,0xfd,0xff,'\0'
3120 static const char composite_dst[] =
3122 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
3123 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
3124 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
3125 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
3126 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
3127 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
3128 0x4f,0x7e,0x4f,0xa8,0x4f,0x3f,0x55,0x60,
3129 0x55,0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,
3130 0x61,0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,
3131 0x61,0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,
3132 0x65,0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,
3133 0x69,0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,
3134 0x6f,0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,
3135 0x6f,0xa8,0x6f,0x3f,0x75,0x60,0x75,0xb4,
3136 0x75,0x5e,0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
3138 static const char composite_dst_alt[] =
3140 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
3141 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
3142 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
3143 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
3144 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
3145 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
3146 0x4f,0x7e,0x4f,0xa8,0xd8,0x55,0x60,0x55,
3147 0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,0x61,
3148 0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,0x61,
3149 0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,0x65,
3150 0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,0x69,
3151 0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,0x6f,
3152 0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,0x6f,
3153 0xa8,0xf8,0x75,0x60,0x75,0xb4,0x75,0x5e,
3154 0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
3156 static const char ligatures_src[] =
3158 0x8C,0x9C,0xC6,0xDE,0xDF,0xE6,0xFE,'\0'
3160 static const char ligatures_dst[] =
3162 'O','E','o','e','A','E','T','H','s','s','a','e','t','h','\0'
3164 static const struct special
3166 char src;
3167 char dst[4];
3168 } foldczone_special[] =
3170 /* src dst */
3171 { 0x85, { 0x2e, 0x2e, 0x2e, 0x00 } },
3172 { 0x98, { 0x20, 0x7e, 0x00 } },
3173 { 0x99, { 0x54, 0x4d, 0x00 } },
3174 { 0xa0, { 0x20, 0x00 } },
3175 { 0xa8, { 0x20, 0xa8, 0x00 } },
3176 { 0xaa, { 0x61, 0x00 } },
3177 { 0xaf, { 0x20, 0xaf, 0x00 } },
3178 { 0xb2, { 0x32, 0x00 } },
3179 { 0xb3, { 0x33, 0x00 } },
3180 { 0xb4, { 0x20, 0xb4, 0x00 } },
3181 { 0xb8, { 0x20, 0xb8, 0x00 } },
3182 { 0xb9, { 0x31, 0x00 } },
3183 { 0xba, { 0x6f, 0x00 } },
3184 { 0xbc, { 0x31, 0x2f, 0x34, 0x00 } },
3185 { 0xbd, { 0x31, 0x2f, 0x32, 0x00 } },
3186 { 0xbe, { 0x33, 0x2f, 0x34, 0x00 } },
3187 { 0x00 }
3190 /* these tests are locale specific */
3191 if (GetACP() != 1252)
3193 trace("Skipping FoldStringA tests for a not Latin 1 locale\n");
3194 return;
3197 /* MAP_FOLDDIGITS */
3198 SetLastError(0xdeadbeef);
3199 ret = FoldStringA(MAP_FOLDDIGITS, digits_src, -1, dst, 256);
3200 ok(ret == 4, "Expected ret == 4, got %d, error %d\n", ret, GetLastError());
3201 ok(strcmp(dst, digits_dst) == 0,
3202 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", digits_dst, dst);
3203 for (i = 1; i < 256; i++)
3205 if (!strchr(digits_src, i))
3207 src[0] = i;
3208 src[1] = '\0';
3209 ret = FoldStringA(MAP_FOLDDIGITS, src, -1, dst, 256);
3210 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3211 ok(dst[0] == src[0],
3212 "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", src, dst);
3216 /* MAP_EXPAND_LIGATURES */
3217 SetLastError(0xdeadbeef);
3218 ret = FoldStringA(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
3219 ok(ret == sizeof(ligatures_dst), "Got %d, error %d\n", ret, GetLastError());
3220 ok(strcmp(dst, ligatures_dst) == 0,
3221 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", ligatures_dst, dst);
3222 for (i = 1; i < 256; i++)
3224 if (!strchr(ligatures_src, i))
3226 src[0] = i;
3227 src[1] = '\0';
3228 ret = FoldStringA(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
3229 if (ret == 3)
3231 /* Vista */
3232 ok((i == 0xDC && lstrcmpA(dst, "UE") == 0) ||
3233 (i == 0xFC && lstrcmpA(dst, "ue") == 0),
3234 "Got %s for %d\n", dst, i);
3236 else
3238 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3239 ok(dst[0] == src[0],
3240 "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", src, dst);
3245 /* MAP_COMPOSITE */
3246 SetLastError(0xdeadbeef);
3247 ret = FoldStringA(MAP_COMPOSITE, composite_src, -1, dst, 256);
3248 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3249 ok( GetLastError() == 0xdeadbeef || broken(!GetLastError()), /* vista */
3250 "wrong error %u\n", GetLastError());
3251 ok(ret == 121 || ret == 119, "Expected 121 or 119, got %d\n", ret);
3252 ok(strcmp(dst, composite_dst) == 0 || strcmp(dst, composite_dst_alt) == 0,
3253 "MAP_COMPOSITE: Mismatch, got '%s'\n", dst);
3255 for (i = 1; i < 256; i++)
3257 if (!strchr(composite_src, i))
3259 src[0] = i;
3260 src[1] = '\0';
3261 ret = FoldStringA(MAP_COMPOSITE, src, -1, dst, 256);
3262 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3263 ok(dst[0] == src[0],
3264 "0x%02x, 0x%02x,0x%02x,0x%02x,\n", (unsigned char)src[0],
3265 (unsigned char)dst[0],(unsigned char)dst[1],(unsigned char)dst[2]);
3269 /* MAP_FOLDCZONE */
3270 for (i = 1; i < 256; i++)
3272 src[0] = i;
3273 src[1] = '\0';
3274 SetLastError(0xdeadbeef);
3275 ret = FoldStringA(MAP_FOLDCZONE, src, -1, dst, 256);
3276 is_special = FALSE;
3277 for (j = 0; foldczone_special[j].src != 0 && ! is_special; j++)
3279 if (foldczone_special[j].src == src[0])
3281 ok(ret == 2 || ret == lstrlenA(foldczone_special[j].dst) + 1,
3282 "Expected ret == 2 or %d, got %d, error %d\n",
3283 lstrlenA(foldczone_special[j].dst) + 1, ret, GetLastError());
3284 ok(src[0] == dst[0] || lstrcmpA(foldczone_special[j].dst, dst) == 0,
3285 "MAP_FOLDCZONE: string mismatch for 0x%02x\n",
3286 (unsigned char)src[0]);
3287 is_special = TRUE;
3290 if (! is_special)
3292 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3293 ok(src[0] == dst[0],
3294 "MAP_FOLDCZONE: Expected 0x%02x, got 0x%02x\n",
3295 (unsigned char)src[0], (unsigned char)dst[0]);
3299 /* MAP_PRECOMPOSED */
3300 for (i = 1; i < 256; i++)
3302 src[0] = i;
3303 src[1] = '\0';
3304 ret = FoldStringA(MAP_PRECOMPOSED, src, -1, dst, 256);
3305 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3306 ok(src[0] == dst[0],
3307 "MAP_PRECOMPOSED: Expected 0x%02x, got 0x%02x\n",
3308 (unsigned char)src[0], (unsigned char)dst[0]);
3312 static void test_FoldStringW(void)
3314 int ret;
3315 WORD type;
3316 unsigned int i, j;
3317 WCHAR src[256], dst[256], ch, prev_ch = 1;
3318 static const DWORD badFlags[] =
3321 MAP_PRECOMPOSED|MAP_COMPOSITE,
3322 MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES,
3323 MAP_COMPOSITE|MAP_EXPAND_LIGATURES
3325 /* Ranges of digits 0-9 : Must be sorted! */
3326 static const WCHAR digitRanges[] =
3328 0x0030, /* '0'-'9' */
3329 0x0660, /* Eastern Arabic */
3330 0x06F0, /* Arabic - Hindu */
3331 0x07C0, /* Nko */
3332 0x0966, /* Devengari */
3333 0x09E6, /* Bengalii */
3334 0x0A66, /* Gurmukhi */
3335 0x0AE6, /* Gujarati */
3336 0x0B66, /* Oriya */
3337 0x0BE6, /* Tamil - No 0 */
3338 0x0C66, /* Telugu */
3339 0x0CE6, /* Kannada */
3340 0x0D66, /* Maylayalam */
3341 0x0DE6, /* Sinhala Lith */
3342 0x0E50, /* Thai */
3343 0x0ED0, /* Laos */
3344 0x0F20, /* Tibet */
3345 0x0F29, /* Tibet half - 0 is out of sequence */
3346 0x1040, /* Myanmar */
3347 0x1090, /* Myanmar Shan */
3348 0x1368, /* Ethiopic - no 0 */
3349 0x17E0, /* Khmer */
3350 0x1810, /* Mongolian */
3351 0x1946, /* Limbu */
3352 0x19D0, /* New Tai Lue */
3353 0x1A80, /* Tai Tham Hora */
3354 0x1A90, /* Tai Tham Tham */
3355 0x1B50, /* Balinese */
3356 0x1BB0, /* Sundanese */
3357 0x1C40, /* Lepcha */
3358 0x1C50, /* Ol Chiki */
3359 0x2070, /* Superscript - 1, 2, 3 are out of sequence */
3360 0x2080, /* Subscript */
3361 0x245F, /* Circled - 0 is out of sequence */
3362 0x2473, /* Bracketed */
3363 0x2487, /* Full stop */
3364 0x24F4, /* Double Circled */
3365 0x2775, /* Inverted circled - No 0 */
3366 0x277F, /* Patterned circled - No 0 */
3367 0x2789, /* Inverted Patterned circled - No 0 */
3368 0x3020, /* Hangzhou */
3369 0xA620, /* Vai */
3370 0xA8D0, /* Saurashtra */
3371 0xA900, /* Kayah Li */
3372 0xA9D0, /* Javanese */
3373 0xA9F0, /* Myanmar Tai Laing */
3374 0xAA50, /* Cham */
3375 0xABF0, /* Meetei Mayek */
3376 0xff10, /* Pliene chasse (?) */
3377 0xffff /* Terminator */
3379 /* Digits which are represented, but out of sequence */
3380 static const WCHAR outOfSequenceDigits[] =
3382 0xB9, /* Superscript 1 */
3383 0xB2, /* Superscript 2 */
3384 0xB3, /* Superscript 3 */
3385 0x0C78, /* Telugu Fraction 0 */
3386 0x0C79, /* Telugu Fraction 1 */
3387 0x0C7A, /* Telugu Fraction 2 */
3388 0x0C7B, /* Telugu Fraction 3 */
3389 0x0C7C, /* Telugu Fraction 1 */
3390 0x0C7D, /* Telugu Fraction 2 */
3391 0x0C7E, /* Telugu Fraction 3 */
3392 0x0F33, /* Tibetan half zero */
3393 0x19DA, /* New Tai Lue Tham 1 */
3394 0x24EA, /* Circled 0 */
3395 0x24FF, /* Negative Circled 0 */
3396 0x3007, /* Ideographic number zero */
3397 '\0' /* Terminator */
3399 /* Digits in digitRanges for which no representation is available */
3400 static const WCHAR noDigitAvailable[] =
3402 0x0BE6, /* No Tamil 0 */
3403 0x0F29, /* No Tibetan half zero (out of sequence) */
3404 0x1368, /* No Ethiopic 0 */
3405 0x2473, /* No Bracketed 0 */
3406 0x2487, /* No 0 Full stop */
3407 0x24F4, /* No double circled 0 */
3408 0x2775, /* No inverted circled 0 */
3409 0x277F, /* No patterned circled */
3410 0x2789, /* No inverted Patterned circled */
3411 0x3020, /* No Hangzhou 0 */
3412 '\0' /* Terminator */
3414 static const WCHAR foldczone_src[] =
3416 'W', 'i', 'n', 'e', 0x0348, 0x0551, 0x1323, 0x280d,
3417 0xff37, 0xff49, 0xff4e, 0xff45, 0x3c5, 0x308, 0x6a, 0x30c, 0xa0, 0xaa, 0
3419 static const WCHAR foldczone_dst[] =
3421 'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x3cb,0x1f0,' ','a',0
3423 static const WCHAR foldczone_broken_dst[] =
3425 'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e',0x03c5,0x0308,'j',0x030c,0x00a0,0x00aa,0
3427 static const WCHAR ligatures_src[] =
3429 'W', 'i', 'n', 'e', 0x03a6, 0x03b9, 0x03bd, 0x03b5,
3430 0x00c6, 0x00de, 0x00df, 0x00e6, 0x00fe, 0x0132, 0x0133, 0x0152,
3431 0x0153, 0x01c4, 0x01c5, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01ca,
3432 0x01cb, 0x01cc, 0x01e2, 0x01e3, 0x01f1, 0x01f2, 0x01f3, 0x01fc,
3433 0x01fd, 0x05f0, 0x05f1, 0x05f2, 0xfb00, 0xfb01, 0xfb02, 0xfb03,
3434 0xfb04, 0xfb05, 0xfb06, '\0'
3436 static const WCHAR ligatures_dst[] =
3438 'W','i','n','e',0x03a6,0x03b9,0x03bd,0x03b5,
3439 'A','E','T','H','s','s','a','e','t','h','I','J','i','j','O','E','o','e',
3440 'D',0x017d,'D',0x017e,'d',0x017e,'L','J','L','j','l','j','N','J','N','j',
3441 'n','j',0x0100,0x0112,0x0101,0x0113,'D','Z','D','z','d','z',0x00c1,0x00c9,
3442 0x00e1,0x00e9,0x05d5,0x05d5,0x05d5,0x05d9,0x05d9,0x05d9,'f','f','f','i',
3443 'f','l','f','f','i','f','f','l',0x017f,'t','s','t','\0'
3446 /* Invalid flag combinations */
3447 for (i = 0; i < ARRAY_SIZE(badFlags); i++)
3449 src[0] = dst[0] = '\0';
3450 SetLastError(0xdeadbeef);
3451 ret = FoldStringW(badFlags[i], src, 256, dst, 256);
3452 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS,
3453 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3456 /* src & dst cannot be the same */
3457 SetLastError(0xdeadbeef);
3458 ret = FoldStringW(MAP_FOLDCZONE, src, -1, src, 256);
3459 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3460 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3462 /* src can't be NULL */
3463 SetLastError(0xdeadbeef);
3464 ret = FoldStringW(MAP_FOLDCZONE, NULL, -1, dst, 256);
3465 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3466 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3468 /* srclen can't be 0 */
3469 SetLastError(0xdeadbeef);
3470 ret = FoldStringW(MAP_FOLDCZONE, src, 0, dst, 256);
3471 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3472 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3474 /* dstlen can't be < 0 */
3475 SetLastError(0xdeadbeef);
3476 ret = FoldStringW(MAP_FOLDCZONE, src, -1, dst, -1);
3477 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3478 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3480 /* Ret includes terminating NUL which is appended if srclen = -1 */
3481 SetLastError(0xdeadbeef);
3482 src[0] = 'A';
3483 src[1] = '\0';
3484 dst[0] = '\0';
3485 ret = FoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
3486 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3487 ok(dst[0] == 'A' && dst[1] == '\0',
3488 "srclen=-1: Expected ret=2 [%d,%d], got ret=%d [%d,%d], err=%d\n",
3489 'A', '\0', ret, dst[0], dst[1], GetLastError());
3491 /* If size is given, result is not NUL terminated */
3492 SetLastError(0xdeadbeef);
3493 src[0] = 'A';
3494 src[1] = 'A';
3495 dst[0] = 'X';
3496 dst[1] = 'X';
3497 ret = FoldStringW(MAP_FOLDCZONE, src, 1, dst, 256);
3498 ok(ret == 1, "Expected ret == 1, got %d, error %d\n", ret, GetLastError());
3499 ok(dst[0] == 'A' && dst[1] == 'X',
3500 "srclen=1: Expected ret=1, [%d,%d], got ret=%d,[%d,%d], err=%d\n",
3501 'A','X', ret, dst[0], dst[1], GetLastError());
3503 /* MAP_FOLDDIGITS */
3504 for (j = 0; j < ARRAY_SIZE(digitRanges); j++)
3506 /* Check everything before this range */
3507 for (ch = prev_ch; ch < digitRanges[j]; ch++)
3509 SetLastError(0xdeadbeef);
3510 src[0] = ch;
3511 src[1] = dst[0] = '\0';
3512 ret = FoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
3513 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3515 ok(dst[0] == ch || wcschr(outOfSequenceDigits, ch) ||
3516 (ch >= 0xa8e0 && ch <= 0xa8e9), /* combining Devanagari on Win8 */
3517 "MAP_FOLDDIGITS: ch 0x%04x Expected unchanged got %04x\n", ch, dst[0]);
3518 GetStringTypeW( CT_CTYPE1, &ch, 1, &type );
3519 ok(!(type & C1_DIGIT) || wcschr(outOfSequenceDigits, ch) ||
3520 broken( ch >= 0xbf0 && ch <= 0xbf2 ), /* win2k */
3521 "char %04x should not be a digit\n", ch );
3524 if (digitRanges[j] == 0xffff)
3525 break; /* Finished the whole code point space */
3527 for (ch = digitRanges[j]; ch < digitRanges[j] + 10; ch++)
3529 WCHAR c;
3531 /* Map out of sequence characters */
3532 if (ch == 0x2071) c = 0x00B9; /* Superscript 1 */
3533 else if (ch == 0x2072) c = 0x00B2; /* Superscript 2 */
3534 else if (ch == 0x2073) c = 0x00B3; /* Superscript 3 */
3535 else if (ch == 0x245F) c = 0x24EA; /* Circled 0 */
3536 else c = ch;
3537 SetLastError(0xdeadbeef);
3538 src[0] = c;
3539 src[1] = dst[0] = '\0';
3540 ret = FoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
3541 ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError());
3543 ok((dst[0] == '0' + ch - digitRanges[j] && dst[1] == '\0') ||
3544 broken( dst[0] == ch ) || /* old Windows versions don't have all mappings */
3545 (digitRanges[j] == 0x3020 && dst[0] == ch) || /* Hangzhou not present in all Windows versions */
3546 (digitRanges[j] == 0x0F29 && dst[0] == ch) || /* Tibetan not present in all Windows versions */
3547 wcschr(noDigitAvailable, c),
3548 "MAP_FOLDDIGITS: ch %04x Expected %04x got %04x\n",
3549 ch, '0' + digitRanges[j] - ch, dst[0]);
3551 prev_ch = ch;
3554 /* MAP_FOLDCZONE */
3555 SetLastError(0xdeadbeef);
3556 ret = FoldStringW(MAP_FOLDCZONE, foldczone_src, -1, dst, 256);
3557 ok(ret == ARRAY_SIZE(foldczone_dst)
3558 || broken(ret == ARRAY_SIZE(foldczone_broken_dst)), /* winxp, win2003 */
3559 "Got %d, error %d.\n", ret, GetLastError());
3560 ok(!memcmp(dst, foldczone_dst, sizeof(foldczone_dst))
3561 || broken(!memcmp(dst, foldczone_broken_dst, sizeof(foldczone_broken_dst))), /* winxp, win2003 */
3562 "Got unexpected string %s.\n", wine_dbgstr_w(dst));
3564 /* MAP_EXPAND_LIGATURES */
3565 SetLastError(0xdeadbeef);
3566 ret = FoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
3567 ok(ret == ARRAY_SIZE(ligatures_dst), "Got %d, error %d\n", ret, GetLastError());
3568 ok(!memcmp(dst, ligatures_dst, sizeof(ligatures_dst)),
3569 "Got unexpected string %s.\n", wine_dbgstr_w(dst));
3571 /* FIXME: MAP_PRECOMPOSED : MAP_COMPOSITE */
3576 #define LCID_OK(l) \
3577 ok(lcid == l, "Expected lcid = %08x, got %08x\n", l, lcid)
3578 #define MKLCID(x,y,z) MAKELCID(MAKELANGID(x, y), z)
3579 #define LCID_RES(src, res) do { lcid = ConvertDefaultLocale(src); LCID_OK(res); } while (0)
3580 #define TEST_LCIDLANG(a,b) LCID_RES(MAKELCID(a,b), MAKELCID(a,b))
3581 #define TEST_LCID(a,b,c) LCID_RES(MKLCID(a,b,c), MKLCID(a,b,c))
3583 static void test_ConvertDefaultLocale(void)
3585 /* some languages use a different default than SUBLANG_DEFAULT */
3586 static const struct { WORD lang, sublang; } nondefault_langs[] =
3588 { LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED },
3589 { LANG_SPANISH, SUBLANG_SPANISH_MODERN },
3590 { LANG_IRISH, SUBLANG_IRISH_IRELAND },
3591 { LANG_BENGALI, SUBLANG_BENGALI_BANGLADESH },
3592 { LANG_SINDHI, SUBLANG_SINDHI_AFGHANISTAN },
3593 { LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA_LATIN },
3594 { LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN },
3595 { LANG_FULAH, SUBLANG_FULAH_SENEGAL },
3596 { LANG_TIGRINYA, SUBLANG_TIGRINYA_ERITREA }
3598 LCID lcid;
3599 unsigned int i;
3601 /* Doesn't change lcid, even if non default sublang/sort used */
3602 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_US, SORT_DEFAULT);
3603 TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_UK, SORT_DEFAULT);
3604 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT);
3605 TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE);
3606 lcid = ConvertDefaultLocale( MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ));
3607 ok( lcid == MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ) ||
3608 broken( lcid == MKLCID( LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE )), /* <= vista */
3609 "Expected lcid = %08x got %08x\n",
3610 MKLCID( LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ), lcid );
3611 lcid = ConvertDefaultLocale( MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ));
3612 ok( lcid == MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ) ||
3613 broken( lcid == MKLCID( LANG_IRISH, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE )), /* <= vista */
3614 "Expected lcid = %08x got %08x\n",
3615 MKLCID( LANG_IRISH, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE ), lcid );
3617 /* SUBLANG_NEUTRAL -> SUBLANG_DEFAULT */
3618 LCID_RES(MKLCID(LANG_ENGLISH, SUBLANG_NEUTRAL, SORT_DEFAULT),
3619 MKLCID(LANG_ENGLISH, SUBLANG_DEFAULT, SORT_DEFAULT));
3620 LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_DEFAULT),
3621 MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT));
3622 for (i = 0; i < ARRAY_SIZE(nondefault_langs); i++)
3624 lcid = ConvertDefaultLocale( MAKELANGID( nondefault_langs[i].lang, SUBLANG_NEUTRAL ));
3625 ok( lcid == MAKELANGID( nondefault_langs[i].lang, nondefault_langs[i].sublang ) ||
3626 broken( lcid == MAKELANGID( nondefault_langs[i].lang, SUBLANG_DEFAULT )) || /* <= vista */
3627 broken( lcid == MAKELANGID( nondefault_langs[i].lang, SUBLANG_NEUTRAL )), /* w7 */
3628 "Expected lcid = %08x got %08x\n",
3629 MAKELANGID( nondefault_langs[i].lang, nondefault_langs[i].sublang ), lcid );
3631 lcid = ConvertDefaultLocale( 0x7804 );
3632 ok( lcid == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ) ||
3633 broken( lcid == 0x7804 ), /* <= vista */
3634 "Expected lcid = %08x got %08x\n", MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ), lcid );
3635 lcid = ConvertDefaultLocale( 0x7c04 );
3636 ok( lcid == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ) ||
3637 broken( lcid == 0x7c04 ) || /* winxp */
3638 broken( lcid == 0x0404 ), /* vista */
3639 "Expected lcid = %08x got %08x\n", MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG ), lcid );
3641 /* Invariant language is not treated specially */
3642 TEST_LCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT);
3644 /* User/system default languages alone are not mapped */
3645 TEST_LCIDLANG(LANG_SYSTEM_DEFAULT, SORT_JAPANESE_UNICODE);
3646 TEST_LCIDLANG(LANG_USER_DEFAULT, SORT_JAPANESE_UNICODE);
3648 /* Default lcids */
3649 LCID_RES(LOCALE_SYSTEM_DEFAULT, GetSystemDefaultLCID());
3650 LCID_RES(LOCALE_USER_DEFAULT, GetUserDefaultLCID());
3651 LCID_RES(LOCALE_NEUTRAL, GetUserDefaultLCID());
3652 lcid = ConvertDefaultLocale(LOCALE_INVARIANT);
3653 ok(lcid == LOCALE_INVARIANT || broken(lcid == 0x47f) /* win2k[3]/winxp */,
3654 "Expected lcid = %08x, got %08x\n", LOCALE_INVARIANT, lcid);
3657 static BOOL CALLBACK langgrp_procA(LGRPID lgrpid, LPSTR lpszNum, LPSTR lpszName,
3658 DWORD dwFlags, LONG_PTR lParam)
3660 if (winetest_debug > 1)
3661 trace("%08x, %s, %s, %08x, %08lx\n",
3662 lgrpid, lpszNum, lpszName, dwFlags, lParam);
3664 ok(pIsValidLanguageGroup(lgrpid, dwFlags) == TRUE,
3665 "Enumerated grp %d not valid (flags %d)\n", lgrpid, dwFlags);
3667 /* If lParam is one, we are calling with flags defaulted from 0 */
3668 ok(!lParam || (dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED),
3669 "Expected dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED, got %d\n", dwFlags);
3671 return TRUE;
3674 static void test_EnumSystemLanguageGroupsA(void)
3676 BOOL ret;
3678 if (!pEnumSystemLanguageGroupsA || !pIsValidLanguageGroup)
3680 win_skip("EnumSystemLanguageGroupsA and/or IsValidLanguageGroup are not available\n");
3681 return;
3684 /* No enumeration proc */
3685 SetLastError(0);
3686 ret = pEnumSystemLanguageGroupsA(0, LGRPID_INSTALLED, 0);
3687 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3689 win_skip("EnumSystemLanguageGroupsA is not implemented\n");
3690 return;
3692 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3693 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3695 /* Invalid flags */
3696 SetLastError(0);
3697 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED|LGRPID_SUPPORTED, 0);
3698 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3700 /* No flags - defaults to LGRPID_INSTALLED */
3701 SetLastError(0xdeadbeef);
3702 pEnumSystemLanguageGroupsA(langgrp_procA, 0, 1);
3703 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
3705 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED, 0);
3706 pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
3709 static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam )
3711 if (winetest_debug > 1)
3712 trace( "%s %x\n", wine_dbgstr_w(name), flags );
3713 return TRUE;
3716 static void test_EnumSystemLocalesEx(void)
3718 BOOL ret;
3720 if (!pEnumSystemLocalesEx)
3722 win_skip( "EnumSystemLocalesEx not available\n" );
3723 return;
3725 SetLastError( 0xdeadbeef );
3726 ret = pEnumSystemLocalesEx( enum_func, LOCALE_ALL, 0, (void *)1 );
3727 ok( !ret, "should have failed\n" );
3728 ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
3729 SetLastError( 0xdeadbeef );
3730 ret = pEnumSystemLocalesEx( enum_func, 0, 0, NULL );
3731 ok( ret, "failed err %u\n", GetLastError() );
3734 static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
3735 LONG_PTR lParam)
3737 if (winetest_debug > 1)
3738 trace("%08x, %08x, %s, %08lx\n", lgrpid, lcid, lpszNum, lParam);
3740 /* invalid locale enumerated on some platforms */
3741 if (lcid == 0)
3742 return TRUE;
3744 ok(pIsValidLanguageGroup(lgrpid, LGRPID_SUPPORTED) == TRUE,
3745 "Enumerated grp %d not valid\n", lgrpid);
3746 ok(IsValidLocale(lcid, LCID_SUPPORTED) == TRUE,
3747 "Enumerated grp locale %04x not valid\n", lcid);
3748 return TRUE;
3751 static void test_EnumLanguageGroupLocalesA(void)
3753 BOOL ret;
3755 if (!pEnumLanguageGroupLocalesA || !pIsValidLanguageGroup)
3757 win_skip("EnumLanguageGroupLocalesA and/or IsValidLanguageGroup are not available\n");
3758 return;
3761 /* No enumeration proc */
3762 SetLastError(0);
3763 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0, 0);
3764 if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3766 win_skip("EnumLanguageGroupLocalesA is not implemented\n");
3767 return;
3769 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3770 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3772 /* lgrpid too small */
3773 SetLastError(0);
3774 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, 0, 0, 0);
3775 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3776 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3778 /* lgrpid too big */
3779 SetLastError(0);
3780 ret = pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_ARMENIAN + 1, 0, 0);
3781 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3782 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3784 /* dwFlags is reserved */
3785 SetLastError(0);
3786 ret = pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0x1, 0);
3787 ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
3788 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3790 pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_WESTERN_EUROPE, 0, 0);
3793 static void test_SetLocaleInfoA(void)
3795 BOOL bRet;
3796 LCID lcid = GetUserDefaultLCID();
3798 /* Null data */
3799 SetLastError(0);
3800 bRet = SetLocaleInfoA(lcid, LOCALE_SDATE, 0);
3801 ok( !bRet && GetLastError() == ERROR_INVALID_PARAMETER,
3802 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3804 /* IDATE */
3805 SetLastError(0);
3806 bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, "test_SetLocaleInfoA");
3807 ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
3808 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3810 /* ILDATE */
3811 SetLastError(0);
3812 bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, "test_SetLocaleInfoA");
3813 ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
3814 "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3817 static BOOL CALLBACK luilocale_proc1A(LPSTR value, LONG_PTR lParam)
3819 if (winetest_debug > 1)
3820 trace("%s %08lx\n", value, lParam);
3821 return(TRUE);
3824 static BOOL CALLBACK luilocale_proc2A(LPSTR value, LONG_PTR lParam)
3826 ok(!enumCount, "callback called again unexpected\n");
3827 enumCount++;
3828 return(FALSE);
3831 static BOOL CALLBACK luilocale_proc3A(LPSTR value, LONG_PTR lParam)
3833 ok(0,"callback called unexpected\n");
3834 return(FALSE);
3837 static void test_EnumUILanguageA(void)
3839 BOOL ret;
3840 if (!pEnumUILanguagesA) {
3841 win_skip("EnumUILanguagesA is not available on Win9x or NT4\n");
3842 return;
3845 SetLastError(ERROR_SUCCESS);
3846 ret = pEnumUILanguagesA(luilocale_proc1A, 0, 0);
3847 if (ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3849 win_skip("EnumUILanguagesA is not implemented\n");
3850 return;
3852 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3854 enumCount = 0;
3855 SetLastError(ERROR_SUCCESS);
3856 ret = pEnumUILanguagesA(luilocale_proc2A, 0, 0);
3857 ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3858 ok(enumCount == 1, "enumCount = %u\n", enumCount);
3860 enumCount = 0;
3861 SetLastError(ERROR_SUCCESS);
3862 ret = pEnumUILanguagesA(luilocale_proc2A, MUI_LANGUAGE_ID, 0);
3863 ok(ret || broken(!ret && GetLastError() == ERROR_INVALID_FLAGS), /* winxp */
3864 "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
3865 if (ret) ok(enumCount == 1, "enumCount = %u\n", enumCount);
3867 SetLastError(ERROR_SUCCESS);
3868 ret = pEnumUILanguagesA(NULL, 0, 0);
3869 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3870 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3871 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3873 SetLastError(ERROR_SUCCESS);
3874 ret = pEnumUILanguagesA(luilocale_proc3A, 0x5a5a5a5a, 0);
3875 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3876 ok(GetLastError() == ERROR_INVALID_FLAGS, "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
3878 SetLastError(ERROR_SUCCESS);
3879 ret = pEnumUILanguagesA(NULL, 0x5a5a5a5a, 0);
3880 ok(!ret, "Expected return value FALSE, got %u\n", ret);
3881 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3882 "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
3885 static char date_fmt_buf[1024];
3886 static WCHAR date_fmt_bufW[1024];
3888 static BOOL CALLBACK enum_datetime_procA(LPSTR fmt)
3890 lstrcatA(date_fmt_buf, fmt);
3891 lstrcatA(date_fmt_buf, "\n");
3892 return TRUE;
3895 static BOOL CALLBACK enum_datetime_procW(WCHAR *fmt)
3897 lstrcatW(date_fmt_bufW, fmt);
3898 return FALSE;
3901 static void test_EnumDateFormatsA(void)
3903 char *p, buf[256];
3904 BOOL ret;
3905 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
3907 date_fmt_buf[0] = 0;
3908 SetLastError(0xdeadbeef);
3909 ret = EnumDateFormatsA(enum_datetime_procA, lcid, 0);
3910 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3912 win_skip("0 for dwFlags is not supported\n");
3914 else
3916 ok(ret, "EnumDateFormatsA(0) error %d\n", GetLastError());
3917 trace("EnumDateFormatsA(0): %s\n", date_fmt_buf);
3918 /* test the 1st enumerated format */
3919 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3920 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3921 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3922 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3925 date_fmt_buf[0] = 0;
3926 SetLastError(0xdeadbeef);
3927 ret = EnumDateFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
3928 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3930 win_skip("LOCALE_USE_CP_ACP is not supported\n");
3932 else
3934 ok(ret, "EnumDateFormatsA(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
3935 trace("EnumDateFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
3936 /* test the 1st enumerated format */
3937 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3938 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3939 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3940 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3943 date_fmt_buf[0] = 0;
3944 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_SHORTDATE);
3945 ok(ret, "EnumDateFormatsA(DATE_SHORTDATE) error %d\n", GetLastError());
3946 trace("EnumDateFormatsA(DATE_SHORTDATE): %s\n", date_fmt_buf);
3947 /* test the 1st enumerated format */
3948 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3949 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
3950 ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %d\n", GetLastError());
3951 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3953 date_fmt_buf[0] = 0;
3954 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_LONGDATE);
3955 ok(ret, "EnumDateFormatsA(DATE_LONGDATE) error %d\n", GetLastError());
3956 trace("EnumDateFormatsA(DATE_LONGDATE): %s\n", date_fmt_buf);
3957 /* test the 1st enumerated format */
3958 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3959 ret = GetLocaleInfoA(lcid, LOCALE_SLONGDATE, buf, sizeof(buf));
3960 ok(ret, "GetLocaleInfoA(LOCALE_SLONGDATE) error %d\n", GetLastError());
3961 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3963 date_fmt_buf[0] = 0;
3964 SetLastError(0xdeadbeef);
3965 ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_YEARMONTH);
3966 if (!ret && (GetLastError() == ERROR_INVALID_FLAGS))
3968 win_skip("DATE_YEARMONTH is only present on W2K and later\n");
3969 return;
3971 ok(ret, "EnumDateFormatsA(DATE_YEARMONTH) error %d\n", GetLastError());
3972 trace("EnumDateFormatsA(DATE_YEARMONTH): %s\n", date_fmt_buf);
3973 /* test the 1st enumerated format */
3974 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3975 ret = GetLocaleInfoA(lcid, LOCALE_SYEARMONTH, buf, sizeof(buf));
3976 ok(ret, "GetLocaleInfoA(LOCALE_SYEARMONTH) error %d\n", GetLastError());
3977 ok(!lstrcmpA(date_fmt_buf, buf) || broken(!buf[0]) /* win9x */,
3978 "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3981 static void test_EnumTimeFormatsA(void)
3983 char *p, buf[256];
3984 BOOL ret;
3985 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
3987 date_fmt_buf[0] = 0;
3988 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, 0);
3989 ok(ret, "EnumTimeFormatsA(0) error %d\n", GetLastError());
3990 trace("EnumTimeFormatsA(0): %s\n", date_fmt_buf);
3991 /* test the 1st enumerated format */
3992 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
3993 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
3994 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
3995 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
3997 date_fmt_buf[0] = 0;
3998 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
3999 ok(ret, "EnumTimeFormatsA(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
4000 trace("EnumTimeFormatsA(LOCALE_USE_CP_ACP): %s\n", date_fmt_buf);
4001 /* test the 1st enumerated format */
4002 if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
4003 ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
4004 ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
4005 ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
4008 static void test_EnumTimeFormatsW(void)
4010 LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
4011 WCHAR bufW[256];
4012 BOOL ret;
4014 date_fmt_bufW[0] = 0;
4015 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, 0);
4016 ok(ret, "EnumTimeFormatsW(0) error %d\n", GetLastError());
4017 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
4018 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
4019 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4020 wine_dbgstr_w(bufW));
4022 date_fmt_bufW[0] = 0;
4023 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, LOCALE_USE_CP_ACP);
4024 ok(ret, "EnumTimeFormatsW(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
4025 ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, ARRAY_SIZE(bufW));
4026 ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
4027 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4028 wine_dbgstr_w(bufW));
4030 /* TIME_NOSECONDS is Win7+ feature */
4031 date_fmt_bufW[0] = 0;
4032 ret = EnumTimeFormatsW(enum_datetime_procW, lcid, TIME_NOSECONDS);
4033 if (!ret && GetLastError() == ERROR_INVALID_FLAGS)
4034 win_skip("EnumTimeFormatsW doesn't support TIME_NOSECONDS\n");
4035 else {
4036 char buf[256];
4038 ok(ret, "EnumTimeFormatsW(TIME_NOSECONDS) error %d\n", GetLastError());
4039 ret = GetLocaleInfoW(lcid, LOCALE_SSHORTTIME, bufW, ARRAY_SIZE(bufW));
4040 ok(ret, "GetLocaleInfoW(LOCALE_SSHORTTIME) error %d\n", GetLastError());
4041 ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
4042 wine_dbgstr_w(bufW));
4044 /* EnumTimeFormatsA doesn't support this flag */
4045 ret = EnumTimeFormatsA(enum_datetime_procA, lcid, TIME_NOSECONDS);
4046 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
4047 GetLastError());
4049 ret = EnumTimeFormatsA(NULL, lcid, TIME_NOSECONDS);
4050 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
4051 GetLastError());
4053 /* And it's not supported by GetLocaleInfoA either */
4054 ret = GetLocaleInfoA(lcid, LOCALE_SSHORTTIME, buf, ARRAY_SIZE(buf));
4055 ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "GetLocaleInfoA(LOCALE_SSHORTTIME) ret %d, error %d\n", ret,
4056 GetLastError());
4060 static void test_GetCPInfo(void)
4062 BOOL ret;
4063 CPINFO cpinfo;
4065 SetLastError(0xdeadbeef);
4066 ret = GetCPInfo(CP_SYMBOL, &cpinfo);
4067 ok(!ret, "GetCPInfo(CP_SYMBOL) should fail\n");
4068 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4069 "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
4071 SetLastError(0xdeadbeef);
4072 ret = GetCPInfo(CP_UTF7, &cpinfo);
4073 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
4075 win_skip("Codepage CP_UTF7 is not installed/available\n");
4077 else
4079 ok(ret, "GetCPInfo(CP_UTF7) error %u\n", GetLastError());
4080 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
4081 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
4082 ok(cpinfo.LeadByte[0] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[0]);
4083 ok(cpinfo.LeadByte[1] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[1]);
4084 ok(cpinfo.MaxCharSize == 5, "expected 5, got 0x%x\n", cpinfo.MaxCharSize);
4087 SetLastError(0xdeadbeef);
4088 ret = GetCPInfo(CP_UTF8, &cpinfo);
4089 if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
4091 win_skip("Codepage CP_UTF8 is not installed/available\n");
4093 else
4095 ok(ret, "GetCPInfo(CP_UTF8) error %u\n", GetLastError());
4096 ok(cpinfo.DefaultChar[0] == 0x3f, "expected 0x3f, got 0x%x\n", cpinfo.DefaultChar[0]);
4097 ok(cpinfo.DefaultChar[1] == 0, "expected 0, got 0x%x\n", cpinfo.DefaultChar[1]);
4098 ok(cpinfo.LeadByte[0] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[0]);
4099 ok(cpinfo.LeadByte[1] == 0, "expected 0, got 0x%x\n", cpinfo.LeadByte[1]);
4100 ok(cpinfo.MaxCharSize == 4 || broken(cpinfo.MaxCharSize == 3) /* win9x */,
4101 "expected 4, got %u\n", cpinfo.MaxCharSize);
4104 if (pNtGetNlsSectionPtr)
4106 CPTABLEINFO table;
4107 NTSTATUS status;
4108 void *ptr, *ptr2;
4109 SIZE_T size;
4110 int i;
4112 for (i = 0; i < 100; i++)
4114 ptr = NULL;
4115 size = 0;
4116 status = pNtGetNlsSectionPtr( i, 9999, NULL, &ptr, &size );
4117 switch (i)
4119 case 9: /* unknown */
4120 case 13: /* unknown */
4121 ok( status == STATUS_INVALID_PARAMETER_1 || status == STATUS_INVALID_PARAMETER_3, /* vista */
4122 "%u: failed %x\n", i, status );
4123 break;
4124 case 10: /* casemap */
4125 ok( status == STATUS_INVALID_PARAMETER_1 || status == STATUS_UNSUCCESSFUL,
4126 "%u: failed %x\n", i, status );
4127 break;
4128 case 11: /* codepage */
4129 case 12: /* normalization */
4130 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%u: failed %x\n", i, status );
4131 break;
4132 default:
4133 ok( status == STATUS_INVALID_PARAMETER_1, "%u: failed %x\n", i, status );
4134 break;
4138 /* casemap table */
4140 status = pNtGetNlsSectionPtr( 10, 0, NULL, &ptr, &size );
4141 if (status != STATUS_INVALID_PARAMETER_1)
4143 ok( !status, "failed %x\n", status );
4144 ok( size > 0x1000 && size <= 0x8000 , "wrong size %lx\n", size );
4145 status = pNtGetNlsSectionPtr( 10, 0, NULL, &ptr2, &size );
4146 ok( ptr != ptr2, "got same pointer\n" );
4147 ret = UnmapViewOfFile( ptr );
4148 todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4149 ret = UnmapViewOfFile( ptr2 );
4150 todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4153 /* codepage tables */
4155 ptr = (void *)0xdeadbeef;
4156 size = 0xdeadbeef;
4157 status = pNtGetNlsSectionPtr( 11, 437, NULL, &ptr, &size );
4158 ok( !status, "failed %x\n", status );
4159 ok( size > 0x10000 && size <= 0x20000, "wrong size %lx\n", size );
4160 memset( &table, 0xcc, sizeof(table) );
4161 if (pRtlInitCodePageTable)
4163 pRtlInitCodePageTable( ptr, &table );
4164 ok( table.CodePage == 437, "wrong codepage %u\n", table.CodePage );
4165 ok( table.MaximumCharacterSize == 1, "wrong char size %u\n", table.MaximumCharacterSize );
4166 ok( table.DefaultChar == '?', "wrong default char %x\n", table.DefaultChar );
4167 ok( !table.DBCSCodePage, "wrong dbcs %u\n", table.DBCSCodePage );
4169 ret = UnmapViewOfFile( ptr );
4170 todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4172 status = pNtGetNlsSectionPtr( 11, 936, NULL, &ptr, &size );
4173 ok( !status, "failed %x\n", status );
4174 ok( size > 0x30000 && size <= 0x40000, "wrong size %lx\n", size );
4175 memset( &table, 0xcc, sizeof(table) );
4176 if (pRtlInitCodePageTable)
4178 pRtlInitCodePageTable( ptr, &table );
4179 ok( table.CodePage == 936, "wrong codepage %u\n", table.CodePage );
4180 ok( table.MaximumCharacterSize == 2, "wrong char size %u\n", table.MaximumCharacterSize );
4181 ok( table.DefaultChar == '?', "wrong default char %x\n", table.DefaultChar );
4182 ok( table.DBCSCodePage == TRUE, "wrong dbcs %u\n", table.DBCSCodePage );
4184 if (pRtlCustomCPToUnicodeN)
4186 static const unsigned char buf[] = { 0xbf, 0xb4, 0xc7, 0, 0x78 };
4187 static const WCHAR expect[][4] = { { 0xcccc, 0xcccc, 0xcccc, 0xcccc },
4188 { 0x0000, 0xcccc, 0xcccc, 0xcccc },
4189 { 0x770b, 0xcccc, 0xcccc, 0xcccc },
4190 { 0x770b, 0x0000, 0xcccc, 0xcccc },
4191 { 0x770b, 0x003f, 0xcccc, 0xcccc },
4192 { 0x770b, 0x003f, 0x0078, 0xcccc } };
4193 WCHAR wbuf[5];
4194 DWORD i, j, reslen;
4196 for (i = 0; i <= sizeof(buf); i++)
4198 memset( wbuf, 0xcc, sizeof(wbuf) );
4199 RtlCustomCPToUnicodeN( &table, wbuf, sizeof(wbuf), &reslen, (char *)buf, i );
4200 for (j = 0; j < 4; j++) if (expect[i][j] == 0xcccc) break;
4201 ok( reslen == j * sizeof(WCHAR), "%u: wrong len %u\n", i, reslen );
4202 for (j = 0; j < 4; j++)
4203 ok( wbuf[j] == expect[i][j], "%u: char %u got %04x\n", i, j, wbuf[j] );
4207 ret = UnmapViewOfFile( ptr );
4208 todo_wine ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4210 /* normalization tables */
4212 for (i = 0; i < 100; i++)
4214 status = pNtGetNlsSectionPtr( 12, i, NULL, &ptr, &size );
4215 switch (i)
4217 case NormalizationC:
4218 case NormalizationD:
4219 case NormalizationKC:
4220 case NormalizationKD:
4221 case 13: /* IDN */
4222 todo_wine ok( !status, "%u: failed %x\n", i, status );
4223 if (status) break;
4224 ok( size > 0x8000 && size <= 0x30000 , "wrong size %lx\n", size );
4225 ret = UnmapViewOfFile( ptr );
4226 ok( ret, "UnmapViewOfFile failed err %u\n", GetLastError() );
4227 break;
4228 default:
4229 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%u: failed %x\n", i, status );
4230 break;
4234 else win_skip( "NtGetNlsSectionPtr not supported\n" );
4238 * The CT_TYPE1 has varied over windows version.
4239 * The current target for correct behavior is windows 7.
4240 * There was a big shift between windows 2000 (first introduced) and windows Xp
4241 * Most of the old values below are from windows 2000.
4242 * A smaller subset of changes happened between windows Xp and Window vista/7
4244 static void test_GetStringTypeW(void)
4246 static const WCHAR blanks[] = {0x9, 0x20, 0xa0, 0x3000, 0xfeff};
4247 static const WORD blanks_new[] = {C1_SPACE | C1_CNTRL | C1_BLANK | C1_DEFINED,
4248 C1_SPACE | C1_BLANK | C1_DEFINED,
4249 C1_SPACE | C1_BLANK | C1_DEFINED,
4250 C1_SPACE | C1_BLANK | C1_DEFINED,
4251 C1_CNTRL | C1_BLANK | C1_DEFINED};
4252 static const WORD blanks_old[] ={C1_SPACE | C1_CNTRL | C1_BLANK,
4253 C1_SPACE | C1_BLANK,
4254 C1_SPACE | C1_BLANK,
4255 C1_SPACE | C1_BLANK,
4256 C1_SPACE | C1_BLANK};
4258 static const WCHAR undefined[] = {0x378, 0x379, 0x5ff, 0xfff8, 0xfffe};
4260 /* Lu, Ll, Lt */
4261 static const WCHAR alpha[] = {0x47, 0x67, 0x1c5};
4262 static const WORD alpha_old[] = {C1_UPPER | C1_ALPHA,
4263 C1_LOWER | C1_ALPHA,
4264 C1_UPPER | C1_LOWER | C1_ALPHA,
4265 C1_ALPHA};
4267 /* Sk, Sk, Mn, So, Me */
4268 static const WCHAR oldpunc[] = { 0x2c2, 0x2e5, 0x322, 0x482, 0x6de,
4269 /* Sc, Sm, No,*/
4270 0xffe0, 0xffe9, 0x2153};
4272 /* Lm, Nl, Cf, 0xad(Cf), 0x1f88 (Lt), Lo, Mc */
4273 static const WCHAR changed[] = {0x2b0, 0x2160, 0x600, 0xad, 0x1f88, 0x294, 0x903};
4274 static const WORD changed_old[] = { C1_PUNCT, C1_PUNCT, 0, C1_PUNCT, C1_UPPER | C1_ALPHA, C1_ALPHA, C1_PUNCT };
4275 static const WORD changed_xp[] = {C1_ALPHA | C1_DEFINED,
4276 C1_ALPHA | C1_DEFINED,
4277 C1_CNTRL | C1_DEFINED,
4278 C1_PUNCT | C1_DEFINED,
4279 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
4280 C1_ALPHA | C1_LOWER | C1_DEFINED,
4281 C1_ALPHA | C1_DEFINED };
4282 static const WORD changed_new[] = { C1_ALPHA | C1_DEFINED,
4283 C1_ALPHA | C1_DEFINED,
4284 C1_CNTRL | C1_DEFINED,
4285 C1_PUNCT | C1_CNTRL | C1_DEFINED,
4286 C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
4287 C1_ALPHA | C1_DEFINED,
4288 C1_DEFINED
4290 /* Pc, Pd, Ps, Pe, Pi, Pf, Po*/
4291 static const WCHAR punct[] = { 0x5f, 0x2d, 0x28, 0x29, 0xab, 0xbb, 0x21 };
4293 static const WCHAR punct_special[] = {0x24, 0x2b, 0x3c, 0x3e, 0x5e, 0x60,
4294 0x7c, 0x7e, 0xa2, 0xbe, 0xd7, 0xf7};
4295 static const WCHAR digit_special[] = {0xb2, 0xb3, 0xb9};
4296 static const WCHAR lower_special[] = {0x2071, 0x207f};
4297 static const WCHAR cntrl_special[] = {0x070f, 0x200c, 0x200d,
4298 0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
4299 0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
4300 0xfff9, 0xfffa, 0xfffb};
4301 static const WCHAR space_special[] = {0x09, 0x0d, 0x85};
4303 WORD types[20];
4304 WCHAR ch[2];
4305 BOOL ret;
4306 int i;
4308 /* NULL src */
4309 SetLastError(0xdeadbeef);
4310 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, NULL);
4311 ok(!ret, "got %d\n", ret);
4312 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4314 SetLastError(0xdeadbeef);
4315 ret = GetStringTypeW(CT_CTYPE1, NULL, 0, types);
4316 ok(!ret, "got %d\n", ret);
4317 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4319 SetLastError(0xdeadbeef);
4320 ret = GetStringTypeW(CT_CTYPE1, NULL, 5, types);
4321 ok(!ret, "got %d\n", ret);
4322 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
4324 memset(types,0,sizeof(types));
4325 GetStringTypeW(CT_CTYPE1, blanks, 5, types);
4326 for (i = 0; i < 5; i++)
4327 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]);
4329 memset(types,0,sizeof(types));
4330 GetStringTypeW(CT_CTYPE1, alpha, 3, types);
4331 for (i = 0; i < 3; i++)
4332 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]));
4333 memset(types,0,sizeof(types));
4334 GetStringTypeW(CT_CTYPE1, undefined, 5, types);
4335 for (i = 0; i < 5; i++)
4336 ok(types[i] == 0, "incorrect types returned for %x -> (%x != 0)\n",undefined[i], types[i]);
4338 memset(types,0,sizeof(types));
4339 GetStringTypeW(CT_CTYPE1, oldpunc, 8, types);
4340 for (i = 0; i < 8; i++)
4341 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);
4343 memset(types,0,sizeof(types));
4344 GetStringTypeW(CT_CTYPE1, changed, 7, types);
4345 for (i = 0; i < 7; i++)
4346 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]);
4348 memset(types,0,sizeof(types));
4349 GetStringTypeW(CT_CTYPE1, punct, 7, types);
4350 for (i = 0; i < 7; i++)
4351 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));
4354 memset(types,0,sizeof(types));
4355 GetStringTypeW(CT_CTYPE1, punct_special, 12, types);
4356 for (i = 0; i < 12; i++)
4357 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);
4359 memset(types,0,sizeof(types));
4360 GetStringTypeW(CT_CTYPE1, digit_special, 3, types);
4361 for (i = 0; i < 3; i++)
4362 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);
4364 memset(types,0,sizeof(types));
4365 GetStringTypeW(CT_CTYPE1, lower_special, 2, types);
4366 for (i = 0; i < 2; i++)
4367 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);
4369 memset(types,0,sizeof(types));
4370 GetStringTypeW(CT_CTYPE1, cntrl_special, 20, types);
4371 for (i = 0; i < 20; i++)
4372 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);
4374 memset(types,0,sizeof(types));
4375 GetStringTypeW(CT_CTYPE1, space_special, 3, types);
4376 for (i = 0; i < 3; i++)
4377 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 );
4379 /* surrogate pairs */
4380 ch[0] = 0xd800;
4381 memset(types, 0, sizeof(types));
4382 GetStringTypeW(CT_CTYPE3, ch, 1, types);
4383 if (types[0] == C3_NOTAPPLICABLE)
4384 win_skip("C3_HIGHSURROGATE/C3_LOWSURROGATE are not supported.\n");
4385 else {
4386 ok(types[0] == C3_HIGHSURROGATE, "got %x\n", types[0]);
4388 ch[0] = 0xdc00;
4389 memset(types, 0, sizeof(types));
4390 GetStringTypeW(CT_CTYPE3, ch, 1, types);
4391 ok(types[0] == C3_LOWSURROGATE, "got %x\n", types[0]);
4394 /* Zl, Zp categories */
4395 ch[0] = 0x2028;
4396 ch[1] = 0x2029;
4397 memset(types, 0, sizeof(types));
4398 GetStringTypeW(CT_CTYPE1, ch, 2, types);
4399 ok(types[0] == (C1_DEFINED|C1_SPACE), "got %x\n", types[0]);
4400 ok(types[1] == (C1_DEFINED|C1_SPACE), "got %x\n", types[1]);
4402 /* check Arabic range for kashida flag */
4403 for (ch[0] = 0x600; ch[0] <= 0x6ff; ch[0] += 1)
4405 types[0] = 0;
4406 ret = GetStringTypeW(CT_CTYPE3, ch, 1, types);
4407 ok(ret, "%#x: failed %d\n", ch[0], ret);
4408 if (ch[0] == 0x640) /* ARABIC TATWEEL (Kashida) */
4409 ok(types[0] & C3_KASHIDA, "%#x: type %#x\n", ch[0], types[0]);
4410 else
4411 ok(!(types[0] & C3_KASHIDA), "%#x: type %#x\n", ch[0], types[0]);
4415 static void test_IdnToNameprepUnicode(void)
4417 struct {
4418 DWORD in_len;
4419 const WCHAR in[64];
4420 DWORD ret;
4421 DWORD broken_ret;
4422 const WCHAR out[64];
4423 DWORD flags;
4424 DWORD err;
4425 } test_data[] = {
4427 5, {'t','e','s','t',0},
4428 5, 5, {'t','e','s','t',0},
4429 0, 0xdeadbeef
4432 3, {'a',0xe111,'b'},
4433 0, 0, {0},
4434 0, ERROR_INVALID_NAME
4437 4, {'t',0,'e',0},
4438 0, 0, {0},
4439 0, ERROR_INVALID_NAME
4442 1, {'T',0},
4443 1, 1, {'T',0},
4444 0, 0xdeadbeef
4447 1, {0},
4448 0, 0, {0},
4449 0, ERROR_INVALID_NAME
4452 6, {' ','-','/','[',']',0},
4453 6, 6, {' ','-','/','[',']',0},
4454 0, 0xdeadbeef
4457 3, {'a','-','a'},
4458 3, 3, {'a','-','a'},
4459 IDN_USE_STD3_ASCII_RULES, 0xdeadbeef
4462 3, {'a','a','-'},
4463 0, 0, {0},
4464 IDN_USE_STD3_ASCII_RULES, ERROR_INVALID_NAME
4467 10, {'T',0xdf,0x130,0x143,0x37a,0x6a,0x30c,' ',0xaa,0},
4468 12, 12, {'t','s','s','i',0x307,0x144,' ',0x3b9,0x1f0,' ','a',0},
4469 0, 0xdeadbeef
4472 11, {'t',0xad,0x34f,0x1806,0x180b,0x180c,0x180d,0x200b,0x200c,0x200d,0},
4473 2, 0, {'t',0},
4474 0, 0xdeadbeef
4477 2, {0x3b0, 0},
4478 2, 2, {0x3b0, 0},
4479 0, 0xdeadbeef,
4482 2, {0x221, 0},
4483 0, 2, {0},
4484 0, ERROR_NO_UNICODE_TRANSLATION
4487 2, {0x221, 0},
4488 2, 2, {0x221, 0},
4489 IDN_ALLOW_UNASSIGNED, 0xdeadbeef
4492 5, {'a','.','.','a',0},
4493 0, 0, {0},
4494 0, ERROR_INVALID_NAME
4497 3, {'a','.',0},
4498 3, 3, {'a','.',0},
4499 0, 0xdeadbeef
4503 WCHAR buf[1024];
4504 DWORD i, ret, err;
4506 if (!pIdnToNameprepUnicode)
4508 win_skip("IdnToNameprepUnicode is not available\n");
4509 return;
4512 ret = pIdnToNameprepUnicode(0, test_data[0].in,
4513 test_data[0].in_len, NULL, 0);
4514 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4516 SetLastError(0xdeadbeef);
4517 ret = pIdnToNameprepUnicode(0, test_data[1].in,
4518 test_data[1].in_len, NULL, 0);
4519 err = GetLastError();
4520 ok(ret == test_data[1].ret, "ret = %d\n", ret);
4521 ok(err == test_data[1].err, "err = %d\n", err);
4523 SetLastError(0xdeadbeef);
4524 ret = pIdnToNameprepUnicode(0, test_data[0].in, -1, buf, ARRAY_SIZE(buf));
4525 err = GetLastError();
4526 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4527 ok(err == 0xdeadbeef, "err = %d\n", err);
4529 SetLastError(0xdeadbeef);
4530 ret = pIdnToNameprepUnicode(0, test_data[0].in, -2, buf, ARRAY_SIZE(buf));
4531 err = GetLastError();
4532 ok(ret == 0, "ret = %d\n", ret);
4533 ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
4535 SetLastError(0xdeadbeef);
4536 ret = pIdnToNameprepUnicode(0, test_data[0].in, 0, buf, ARRAY_SIZE(buf));
4537 err = GetLastError();
4538 ok(ret == 0, "ret = %d\n", ret);
4539 ok(err == ERROR_INVALID_NAME, "err = %d\n", err);
4541 ret = pIdnToNameprepUnicode(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES,
4542 test_data[0].in, -1, buf, ARRAY_SIZE(buf));
4543 ok(ret == test_data[0].ret, "ret = %d\n", ret);
4545 SetLastError(0xdeadbeef);
4546 ret = pIdnToNameprepUnicode(0, NULL, 0, NULL, 0);
4547 err = GetLastError();
4548 ok(ret == 0, "ret = %d\n", ret);
4549 ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
4551 SetLastError(0xdeadbeef);
4552 ret = pIdnToNameprepUnicode(4, NULL, 0, NULL, 0);
4553 err = GetLastError();
4554 ok(ret == 0, "ret = %d\n", ret);
4555 ok(err == ERROR_INVALID_FLAGS || err == ERROR_INVALID_PARAMETER /* Win8 */,
4556 "err = %d\n", err);
4558 for (i=0; i<ARRAY_SIZE(test_data); i++)
4560 SetLastError(0xdeadbeef);
4561 ret = pIdnToNameprepUnicode(test_data[i].flags, test_data[i].in, test_data[i].in_len,
4562 buf, ARRAY_SIZE(buf));
4563 err = GetLastError();
4565 ok(ret == test_data[i].ret || broken(ret == test_data[i].broken_ret), "%d: ret = %d\n", i, ret);
4567 if(ret != test_data[i].ret)
4568 continue;
4570 ok(err == test_data[i].err, "%d) err = %d\n", i, err);
4571 ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
4572 "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4576 static void test_IdnToAscii(void)
4578 struct {
4579 DWORD in_len;
4580 const WCHAR in[64];
4581 DWORD ret;
4582 const WCHAR out[64];
4583 DWORD flags;
4584 DWORD err;
4585 } test_data[] = {
4587 5, {'T','e','s','t',0},
4588 5, {'T','e','s','t',0},
4589 0, 0xdeadbeef
4592 5, {'T','e',0x017c,'s','t',0},
4593 12, {'x','n','-','-','t','e','s','t','-','c','b','b',0},
4594 0, 0xdeadbeef
4597 12, {'t','e',0x0105,'s','t','.','t','e',0x017c,'s','t',0},
4598 26, {'x','n','-','-','t','e','s','t','-','c','t','a','.','x','n','-','-','t','e','s','t','-','c','b','b',0},
4599 0, 0xdeadbeef
4602 3, {0x0105,'.',0},
4603 9, {'x','n','-','-','2','d','a','.',0},
4604 0, 0xdeadbeef
4607 10, {'h','t','t','p',':','/','/','t',0x0106,0},
4608 17, {'x','n','-','-','h','t','t','p',':','/','/','t','-','7','8','a',0},
4609 0, 0xdeadbeef
4612 10, {0x4e3a,0x8bf4,0x4e0d,0x4ed6,0x5011,0x10d,0x11b,0x305c,0x306a,0},
4613 35, {'x','n','-','-','b','e','a','2','a','1','6','3','1','a','v','b','a',
4614 'v','4','4','t','y','h','a','3','2','b','9','1','e','g','s','2','t',0},
4615 0, 0xdeadbeef
4618 2, {0x221,0},
4619 8, {'x','n','-','-','6','l','a',0},
4620 IDN_ALLOW_UNASSIGNED, 0xdeadbeef
4624 WCHAR buf[1024];
4625 DWORD i, ret, err;
4627 if (!pIdnToAscii)
4629 win_skip("IdnToAscii is not available\n");
4630 return;
4633 for (i=0; i<ARRAY_SIZE(test_data); i++)
4635 SetLastError(0xdeadbeef);
4636 ret = pIdnToAscii(test_data[i].flags, test_data[i].in,
4637 test_data[i].in_len, buf, sizeof(buf));
4638 err = GetLastError();
4639 ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
4640 ok(err == test_data[i].err, "%d) err = %d\n", i, err);
4641 ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
4642 "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4646 static void test_IdnToUnicode(void)
4648 struct {
4649 DWORD in_len;
4650 const WCHAR in[64];
4651 DWORD ret;
4652 const WCHAR out[64];
4653 DWORD flags;
4654 DWORD err;
4655 } test_data[] = {
4657 5, {'T','e','s','.',0},
4658 5, {'T','e','s','.',0},
4659 0, 0xdeadbeef
4662 2, {0x105,0},
4663 0, {0},
4664 0, ERROR_INVALID_NAME
4667 33, {'x','n','-','-','4','d','b','c','a','g','d','a','h','y','m','b',
4668 'x','e','k','h','e','h','6','e','0','a','7','f','e','i','0','b',0},
4669 23, {0x05dc,0x05de,0x05d4,0x05d4,0x05dd,0x05e4,0x05e9,0x05d5,0x05d8,
4670 0x05dc,0x05d0,0x05de,0x05d3,0x05d1,0x05e8,0x05d9,0x05dd,0x05e2,
4671 0x05d1,0x05e8,0x05d9,0x05ea,0},
4672 0, 0xdeadbeef
4675 34, {'t','e','s','t','.','x','n','-','-','k','d','a','9','a','g','5','e',
4676 '9','j','n','f','s','j','.','x','n','-','-','p','d','-','f','n','a'},
4677 16, {'t','e','s','t','.',0x0105,0x0119,0x015b,0x0107,
4678 0x0142,0x00f3,0x017c,'.','p',0x0119,'d'},
4679 0, 0xdeadbeef
4682 64, {'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
4683 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
4684 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
4685 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a'},
4686 0, {0},
4687 0, ERROR_INVALID_NAME
4690 8, {'x','n','-','-','6','l','a',0},
4691 2, {0x221,0},
4692 IDN_ALLOW_UNASSIGNED, 0xdeadbeef
4696 WCHAR buf[1024];
4697 DWORD i, ret, err;
4699 if (!pIdnToUnicode)
4701 win_skip("IdnToUnicode is not available\n");
4702 return;
4705 for (i=0; i<ARRAY_SIZE(test_data); i++)
4707 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in,
4708 test_data[i].in_len, NULL, 0);
4709 ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
4711 SetLastError(0xdeadbeef);
4712 ret = pIdnToUnicode(test_data[i].flags, test_data[i].in,
4713 test_data[i].in_len, buf, sizeof(buf));
4714 err = GetLastError();
4715 ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
4716 ok(err == test_data[i].err, "%d) err = %d\n", i, err);
4717 ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
4718 "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
4722 static void test_GetLocaleInfoEx(void)
4724 static const WCHAR enW[] = {'e','n',0};
4725 WCHAR bufferW[80], buffer2[80];
4726 INT ret;
4728 if (!pGetLocaleInfoEx)
4730 win_skip("GetLocaleInfoEx not supported\n");
4731 return;
4734 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4735 ok(ret || broken(ret == 0) /* Vista */, "got %d\n", ret);
4736 if (ret)
4738 static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
4739 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
4740 static const WCHAR enusW[] = {'e','n','-','U','S',0};
4741 static const WCHAR usaW[] = {'U','S','A',0};
4742 static const WCHAR enuW[] = {'E','N','U',0};
4743 const struct neutralsublang_name_t *ptr = neutralsublang_names;
4744 DWORD val;
4746 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4747 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
4749 SetLastError(0xdeadbeef);
4750 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, 2);
4751 ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, %d\n", ret, GetLastError());
4753 SetLastError(0xdeadbeef);
4754 ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, NULL, 0);
4755 ok(ret == 3 && GetLastError() == 0xdeadbeef, "got %d, %d\n", ret, GetLastError());
4757 ret = pGetLocaleInfoEx(enusW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4758 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4759 ok(!lstrcmpW(bufferW, enusW), "got %s\n", wine_dbgstr_w(bufferW));
4761 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVCTRYNAME, bufferW, ARRAY_SIZE(bufferW));
4762 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4763 ok(!lstrcmpW(bufferW, usaW), "got %s\n", wine_dbgstr_w(bufferW));
4765 ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVLANGNAME, bufferW, ARRAY_SIZE(bufferW));
4766 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4767 ok(!lstrcmpW(bufferW, enuW), "got %s\n", wine_dbgstr_w(bufferW));
4769 ret = pGetLocaleInfoEx(enusW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
4770 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4771 ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
4773 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT, bufferW, ARRAY_SIZE(bufferW));
4774 ok(ret == 1, "got %d\n", ret);
4775 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
4777 ret = pGetLocaleInfoEx(enW, LOCALE_SPARENT | LOCALE_NOUSEROVERRIDE, bufferW, ARRAY_SIZE(bufferW));
4778 ok(ret == 1, "got %d\n", ret);
4779 ok(!bufferW[0], "got %s\n", wine_dbgstr_w(bufferW));
4781 ret = pGetLocaleInfoEx(enW, LOCALE_SCOUNTRY, bufferW, ARRAY_SIZE(bufferW));
4782 ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
4783 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
4784 (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
4786 skip("Non-English locale\n");
4788 else
4789 ok(!lstrcmpW(bufferW, statesW), "got %s\n", wine_dbgstr_w(bufferW));
4791 bufferW[0] = 0;
4792 SetLastError(0xdeadbeef);
4793 ret = pGetLocaleInfoEx(dummyW, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4794 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
4796 while (*ptr->name)
4798 val = 0;
4799 pGetLocaleInfoEx(ptr->name, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
4800 todo_wine_if (ptr->todo)
4801 ok(val == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", wine_dbgstr_w(ptr->name), val, ptr->lcid);
4802 bufferW[0] = 0;
4803 ret = pGetLocaleInfoEx(ptr->name, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4804 ok(ret == lstrlenW(bufferW)+1, "%s: got ret value %d\n", wine_dbgstr_w(ptr->name), ret);
4805 ok(!lstrcmpW(bufferW, ptr->name), "%s: got wrong LOCALE_SNAME %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
4806 ptr++;
4809 ret = pGetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, bufferW, ARRAY_SIZE(bufferW));
4810 ok(ret && ret == lstrlenW(bufferW)+1, "got ret value %d\n", ret);
4811 ret = GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SNAME, buffer2, ARRAY_SIZE(buffer2));
4812 ok(ret && ret == lstrlenW(buffer2)+1, "got ret value %d\n", ret);
4813 ok(!lstrcmpW(bufferW, buffer2), "LOCALE_SNAMEs don't match %s %s\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(buffer2));
4817 static void test_IsValidLocaleName(void)
4819 static const WCHAR enusW[] = {'e','n','-','U','S',0};
4820 static const WCHAR enW[] = {'e','n',0};
4821 static const WCHAR zzW[] = {'z','z',0};
4822 static const WCHAR zz_zzW[] = {'z','z','-','Z','Z',0};
4823 static const WCHAR zzzzW[] = {'z','z','z','z',0};
4824 BOOL ret;
4826 if (!pIsValidLocaleName)
4828 win_skip("IsValidLocaleName not supported\n");
4829 return;
4832 ret = pIsValidLocaleName(enusW);
4833 ok(ret, "IsValidLocaleName failed\n");
4834 ret = pIsValidLocaleName(enW);
4835 ok(ret || broken(!ret), "IsValidLocaleName failed\n");
4836 ret = pIsValidLocaleName(zzW);
4837 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
4838 ret = pIsValidLocaleName(zz_zzW);
4839 ok(!ret || broken(ret), "IsValidLocaleName should have failed\n");
4840 ret = pIsValidLocaleName(zzzzW);
4841 ok(!ret, "IsValidLocaleName should have failed\n");
4842 ret = pIsValidLocaleName(LOCALE_NAME_INVARIANT);
4843 ok(ret, "IsValidLocaleName failed\n");
4844 ret = pIsValidLocaleName(NULL);
4845 ok(!ret, "IsValidLocaleName should have failed\n");
4848 static void test_CompareStringOrdinal(void)
4850 INT ret;
4851 WCHAR test1[] = { 't','e','s','t',0 };
4852 WCHAR test2[] = { 'T','e','S','t',0 };
4853 WCHAR test3[] = { 't','e','s','t','3',0 };
4854 WCHAR null1[] = { 'a',0,'a',0 };
4855 WCHAR null2[] = { 'a',0,'b',0 };
4856 WCHAR bills1[] = { 'b','i','l','l','\'','s',0 };
4857 WCHAR bills2[] = { 'b','i','l','l','s',0 };
4858 WCHAR coop1[] = { 'c','o','-','o','p',0 };
4859 WCHAR coop2[] = { 'c','o','o','p',0 };
4860 WCHAR nonascii1[] = { 0x0102,0 };
4861 WCHAR nonascii2[] = { 0x0201,0 };
4862 WCHAR ch1, ch2;
4864 if (!pCompareStringOrdinal)
4866 win_skip("CompareStringOrdinal not supported\n");
4867 return;
4870 /* Check errors */
4871 SetLastError(0xdeadbeef);
4872 ret = pCompareStringOrdinal(NULL, 0, NULL, 0, FALSE);
4873 ok(!ret, "Got %u, expected 0\n", ret);
4874 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4875 SetLastError(0xdeadbeef);
4876 ret = pCompareStringOrdinal(test1, -1, NULL, 0, FALSE);
4877 ok(!ret, "Got %u, expected 0\n", ret);
4878 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4879 SetLastError(0xdeadbeef);
4880 ret = pCompareStringOrdinal(NULL, 0, test1, -1, FALSE);
4881 ok(!ret, "Got %u, expected 0\n", ret);
4882 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
4884 /* Check case */
4885 ret = pCompareStringOrdinal(test1, -1, test1, -1, FALSE);
4886 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
4887 ret = pCompareStringOrdinal(test1, -1, test2, -1, FALSE);
4888 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4889 ret = pCompareStringOrdinal(test2, -1, test1, -1, FALSE);
4890 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4891 ret = pCompareStringOrdinal(test1, -1, test2, -1, TRUE);
4892 ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
4894 /* Check different sizes */
4895 ret = pCompareStringOrdinal(test1, 3, test2, -1, TRUE);
4896 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4897 ret = pCompareStringOrdinal(test1, -1, test2, 3, TRUE);
4898 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4900 /* Check null character */
4901 ret = pCompareStringOrdinal(null1, 3, null2, 3, FALSE);
4902 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4903 ret = pCompareStringOrdinal(null1, 3, null2, 3, TRUE);
4904 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4905 ret = pCompareStringOrdinal(test1, 5, test3, 5, FALSE);
4906 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4907 ret = pCompareStringOrdinal(test1, 4, test1, 5, FALSE);
4908 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4910 /* Check ordinal behaviour */
4911 ret = pCompareStringOrdinal(bills1, -1, bills2, -1, FALSE);
4912 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4913 ret = pCompareStringOrdinal(coop2, -1, coop1, -1, FALSE);
4914 ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
4915 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, FALSE);
4916 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4917 ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, TRUE);
4918 ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
4920 for (ch1 = 0; ch1 < 512; ch1++)
4922 for (ch2 = 0; ch2 < 1024; ch2++)
4924 int diff = ch1 - ch2;
4925 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, FALSE );
4926 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
4927 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
4928 diff = pRtlUpcaseUnicodeChar( ch1 ) - pRtlUpcaseUnicodeChar( ch2 );
4929 ret = pCompareStringOrdinal( &ch1, 1, &ch2, 1, TRUE );
4930 ok( ret == (diff > 0 ? CSTR_GREATER_THAN : diff < 0 ? CSTR_LESS_THAN : CSTR_EQUAL),
4931 "wrong result %d %04x %04x\n", ret, ch1, ch2 );
4936 static void test_GetGeoInfo(void)
4938 char buffA[20];
4939 INT ret;
4941 if (!pGetGeoInfoA)
4943 win_skip("GetGeoInfo is not available.\n");
4944 return;
4947 /* unassigned id */
4948 SetLastError(0xdeadbeef);
4949 ret = pGetGeoInfoA(344, GEO_ISO2, NULL, 0, 0);
4950 ok(ret == 0, "got %d\n", ret);
4951 ok(GetLastError() == ERROR_INVALID_PARAMETER ||
4952 broken(GetLastError() == 0xdeadbeef /* Win10 */), "got %d\n", GetLastError());
4954 ret = pGetGeoInfoA(203, GEO_ISO2, NULL, 0, 0);
4955 ok(ret == 3, "got %d\n", ret);
4957 ret = pGetGeoInfoA(203, GEO_ISO3, NULL, 0, 0);
4958 ok(ret == 4, "got %d\n", ret);
4960 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 3, 0);
4961 ok(ret == 3, "got %d\n", ret);
4962 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
4964 /* buffer pointer not NULL, length is 0 - return required length */
4965 buffA[0] = 'a';
4966 SetLastError(0xdeadbeef);
4967 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 0, 0);
4968 ok(ret == 3, "got %d\n", ret);
4969 ok(buffA[0] == 'a', "got %c\n", buffA[0]);
4971 ret = pGetGeoInfoA(203, GEO_ISO3, buffA, 4, 0);
4972 ok(ret == 4, "got %d\n", ret);
4973 ok(!strcmp(buffA, "RUS"), "got %s\n", buffA);
4975 /* shorter buffer */
4976 SetLastError(0xdeadbeef);
4977 buffA[1] = buffA[2] = 0;
4978 ret = pGetGeoInfoA(203, GEO_ISO2, buffA, 2, 0);
4979 ok(ret == 0, "got %d\n", ret);
4980 ok(!strcmp(buffA, "RU"), "got %s\n", buffA);
4981 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d\n", GetLastError());
4983 /* GEO_NATION returns GEOID in a string form, but only for GEOCLASS_NATION-type IDs */
4984 ret = pGetGeoInfoA(203, GEO_NATION, buffA, 20, 0); /* GEOCLASS_NATION */
4985 ok(ret == 4, "GEO_NATION of nation: expected 4, got %d\n", ret);
4986 ok(!strcmp(buffA, "203"), "GEO_NATION of nation: expected 203, got %s\n", buffA);
4988 buffA[0] = 0;
4989 ret = pGetGeoInfoA(39070, GEO_NATION, buffA, 20, 0); /* GEOCLASS_REGION */
4990 ok(ret == 0, "GEO_NATION of region: expected 0, got %d\n", ret);
4991 ok(*buffA == 0, "GEO_NATION of region: expected empty string, got %s\n", buffA);
4993 buffA[0] = 0;
4994 ret = pGetGeoInfoA(333, GEO_NATION, buffA, 20, 0); /* LOCATION_BOTH internal Wine type */
4995 ok(ret == 0 ||
4996 broken(ret == 4) /* Win7 and older */,
4997 "GEO_NATION of LOCATION_BOTH: expected 0, got %d\n", ret);
4998 ok(*buffA == 0 ||
4999 broken(!strcmp(buffA, "333")) /* Win7 and older */,
5000 "GEO_NATION of LOCATION_BOTH: expected empty string, got %s\n", buffA);
5002 /* GEO_ID is like GEO_NATION but works for any ID */
5003 buffA[0] = 0;
5004 ret = pGetGeoInfoA(203, GEO_ID, buffA, 20, 0); /* GEOCLASS_NATION */
5005 if (ret == 0)
5006 win_skip("GEO_ID not supported.\n");
5007 else
5009 ok(ret == 4, "GEO_ID: expected 4, got %d\n", ret);
5010 ok(!strcmp(buffA, "203"), "GEO_ID: expected 203, got %s\n", buffA);
5012 ret = pGetGeoInfoA(47610, GEO_ID, buffA, 20, 0); /* GEOCLASS_REGION */
5013 ok(ret == 6, "got %d\n", ret);
5014 ok(!strcmp(buffA, "47610"), "got %s\n", buffA);
5016 ret = pGetGeoInfoA(333, GEO_ID, buffA, 20, 0); /* LOCATION_BOTH internal Wine type */
5017 ok(ret == 4, "got %d\n", ret);
5018 ok(!strcmp(buffA, "333"), "got %s\n", buffA);
5021 /* GEO_PARENT */
5022 buffA[0] = 0;
5023 ret = pGetGeoInfoA(203, GEO_PARENT, buffA, 20, 0);
5024 if (ret == 0)
5025 win_skip("GEO_PARENT not supported.\n");
5026 else
5028 ok(ret == 6, "got %d\n", ret);
5029 ok(!strcmp(buffA, "47609"), "got %s\n", buffA);
5032 buffA[0] = 0;
5033 ret = pGetGeoInfoA(203, GEO_ISO_UN_NUMBER, buffA, 20, 0);
5034 if (ret == 0)
5035 win_skip("GEO_ISO_UN_NUMBER not supported.\n");
5036 else
5038 ok(ret == 4, "got %d\n", ret);
5039 ok(!strcmp(buffA, "643"), "got %s\n", buffA);
5042 /* try invalid type value */
5043 SetLastError(0xdeadbeef);
5044 ret = pGetGeoInfoA(203, GEO_ID + 1, NULL, 0, 0);
5045 ok(ret == 0, "got %d\n", ret);
5046 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError());
5049 static int geoidenum_count;
5050 static BOOL CALLBACK test_geoid_enumproc(GEOID geoid)
5052 INT ret = pGetGeoInfoA(geoid, GEO_ISO2, NULL, 0, 0);
5053 ok(ret == 3, "got %d for %d\n", ret, geoid);
5054 /* valid geoid starts at 2 */
5055 ok(geoid >= 2, "got geoid %d\n", geoid);
5057 return geoidenum_count++ < 5;
5060 static BOOL CALLBACK test_geoid_enumproc2(GEOID geoid)
5062 geoidenum_count++;
5063 return TRUE;
5066 static void test_EnumSystemGeoID(void)
5068 BOOL ret;
5070 if (!pEnumSystemGeoID)
5072 win_skip("EnumSystemGeoID is not available.\n");
5073 return;
5076 SetLastError(0xdeadbeef);
5077 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, NULL);
5078 ok(!ret, "got %d\n", ret);
5079 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %d\n", GetLastError());
5081 SetLastError(0xdeadbeef);
5082 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, test_geoid_enumproc);
5083 ok(!ret, "got %d\n", ret);
5084 ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError());
5086 SetLastError(0xdeadbeef);
5087 ret = pEnumSystemGeoID(GEOCLASS_NATION+1, 0, NULL);
5088 ok(!ret, "got %d\n", ret);
5089 ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %d\n", GetLastError());
5091 ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, test_geoid_enumproc);
5092 ok(ret, "got %d\n", ret);
5094 /* only the first level is enumerated, not the whole hierarchy */
5095 geoidenum_count = 0;
5096 ret = pEnumSystemGeoID(GEOCLASS_NATION, 39070, test_geoid_enumproc2);
5097 if (ret == 0)
5098 win_skip("Parent GEOID is not supported in EnumSystemGeoID.\n");
5099 else
5100 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5102 geoidenum_count = 0;
5103 ret = pEnumSystemGeoID(GEOCLASS_REGION, 39070, test_geoid_enumproc2);
5104 if (ret == 0)
5105 win_skip("GEOCLASS_REGION is not supported in EnumSystemGeoID.\n");
5106 else
5108 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5110 geoidenum_count = 0;
5111 ret = pEnumSystemGeoID(GEOCLASS_REGION, 0, test_geoid_enumproc2);
5112 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5115 geoidenum_count = 0;
5116 ret = pEnumSystemGeoID(GEOCLASS_ALL, 39070, test_geoid_enumproc2);
5117 if (ret == 0)
5118 win_skip("GEOCLASS_ALL is not supported in EnumSystemGeoID.\n");
5119 else
5121 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5123 geoidenum_count = 0;
5124 ret = pEnumSystemGeoID(GEOCLASS_ALL, 0, test_geoid_enumproc2);
5125 ok(ret && geoidenum_count > 0, "got %d, count %d\n", ret, geoidenum_count);
5129 struct invariant_entry {
5130 const char *name;
5131 int id;
5132 const char *expect, *expect2;
5135 #define X(x) #x, x
5136 static const struct invariant_entry invariant_list[] = {
5137 { X(LOCALE_ILANGUAGE), "007f" },
5138 { X(LOCALE_SENGLANGUAGE), "Invariant Language" },
5139 { X(LOCALE_SABBREVLANGNAME), "IVL" },
5140 { X(LOCALE_SNATIVELANGNAME), "Invariant Language" },
5141 { X(LOCALE_ICOUNTRY), "1" },
5142 { X(LOCALE_SENGCOUNTRY), "Invariant Country" },
5143 { X(LOCALE_SABBREVCTRYNAME), "IVC", "" },
5144 { X(LOCALE_SNATIVECTRYNAME), "Invariant Country" },
5145 { X(LOCALE_IDEFAULTLANGUAGE), "0409" },
5146 { X(LOCALE_IDEFAULTCOUNTRY), "1" },
5147 { X(LOCALE_IDEFAULTCODEPAGE), "437" },
5148 { X(LOCALE_IDEFAULTANSICODEPAGE), "1252" },
5149 { X(LOCALE_IDEFAULTMACCODEPAGE), "10000" },
5150 { X(LOCALE_SLIST), "," },
5151 { X(LOCALE_IMEASURE), "0" },
5152 { X(LOCALE_SDECIMAL), "." },
5153 { X(LOCALE_STHOUSAND), "," },
5154 { X(LOCALE_SGROUPING), "3;0" },
5155 { X(LOCALE_IDIGITS), "2" },
5156 { X(LOCALE_ILZERO), "1" },
5157 { X(LOCALE_INEGNUMBER), "1" },
5158 { X(LOCALE_SNATIVEDIGITS), "0123456789" },
5159 { X(LOCALE_SCURRENCY), "\x00a4" },
5160 { X(LOCALE_SINTLSYMBOL), "XDR" },
5161 { X(LOCALE_SMONDECIMALSEP), "." },
5162 { X(LOCALE_SMONTHOUSANDSEP), "," },
5163 { X(LOCALE_SMONGROUPING), "3;0" },
5164 { X(LOCALE_ICURRDIGITS), "2" },
5165 { X(LOCALE_IINTLCURRDIGITS), "2" },
5166 { X(LOCALE_ICURRENCY), "0" },
5167 { X(LOCALE_INEGCURR), "0" },
5168 { X(LOCALE_SDATE), "/" },
5169 { X(LOCALE_STIME), ":" },
5170 { X(LOCALE_SSHORTDATE), "MM/dd/yyyy" },
5171 { X(LOCALE_SLONGDATE), "dddd, dd MMMM yyyy" },
5172 { X(LOCALE_STIMEFORMAT), "HH:mm:ss" },
5173 { X(LOCALE_IDATE), "0" },
5174 { X(LOCALE_ILDATE), "1" },
5175 { X(LOCALE_ITIME), "1" },
5176 { X(LOCALE_ITIMEMARKPOSN), "0" },
5177 { X(LOCALE_ICENTURY), "1" },
5178 { X(LOCALE_ITLZERO), "1" },
5179 { X(LOCALE_IDAYLZERO), "1" },
5180 { X(LOCALE_IMONLZERO), "1" },
5181 { X(LOCALE_S1159), "AM" },
5182 { X(LOCALE_S2359), "PM" },
5183 { X(LOCALE_ICALENDARTYPE), "1" },
5184 { X(LOCALE_IOPTIONALCALENDAR), "0" },
5185 { X(LOCALE_IFIRSTDAYOFWEEK), "6" },
5186 { X(LOCALE_IFIRSTWEEKOFYEAR), "0" },
5187 { X(LOCALE_SDAYNAME1), "Monday" },
5188 { X(LOCALE_SDAYNAME2), "Tuesday" },
5189 { X(LOCALE_SDAYNAME3), "Wednesday" },
5190 { X(LOCALE_SDAYNAME4), "Thursday" },
5191 { X(LOCALE_SDAYNAME5), "Friday" },
5192 { X(LOCALE_SDAYNAME6), "Saturday" },
5193 { X(LOCALE_SDAYNAME7), "Sunday" },
5194 { X(LOCALE_SABBREVDAYNAME1), "Mon" },
5195 { X(LOCALE_SABBREVDAYNAME2), "Tue" },
5196 { X(LOCALE_SABBREVDAYNAME3), "Wed" },
5197 { X(LOCALE_SABBREVDAYNAME4), "Thu" },
5198 { X(LOCALE_SABBREVDAYNAME5), "Fri" },
5199 { X(LOCALE_SABBREVDAYNAME6), "Sat" },
5200 { X(LOCALE_SABBREVDAYNAME7), "Sun" },
5201 { X(LOCALE_SMONTHNAME1), "January" },
5202 { X(LOCALE_SMONTHNAME2), "February" },
5203 { X(LOCALE_SMONTHNAME3), "March" },
5204 { X(LOCALE_SMONTHNAME4), "April" },
5205 { X(LOCALE_SMONTHNAME5), "May" },
5206 { X(LOCALE_SMONTHNAME6), "June" },
5207 { X(LOCALE_SMONTHNAME7), "July" },
5208 { X(LOCALE_SMONTHNAME8), "August" },
5209 { X(LOCALE_SMONTHNAME9), "September" },
5210 { X(LOCALE_SMONTHNAME10), "October" },
5211 { X(LOCALE_SMONTHNAME11), "November" },
5212 { X(LOCALE_SMONTHNAME12), "December" },
5213 { X(LOCALE_SMONTHNAME13), "" },
5214 { X(LOCALE_SABBREVMONTHNAME1), "Jan" },
5215 { X(LOCALE_SABBREVMONTHNAME2), "Feb" },
5216 { X(LOCALE_SABBREVMONTHNAME3), "Mar" },
5217 { X(LOCALE_SABBREVMONTHNAME4), "Apr" },
5218 { X(LOCALE_SABBREVMONTHNAME5), "May" },
5219 { X(LOCALE_SABBREVMONTHNAME6), "Jun" },
5220 { X(LOCALE_SABBREVMONTHNAME7), "Jul" },
5221 { X(LOCALE_SABBREVMONTHNAME8), "Aug" },
5222 { X(LOCALE_SABBREVMONTHNAME9), "Sep" },
5223 { X(LOCALE_SABBREVMONTHNAME10), "Oct" },
5224 { X(LOCALE_SABBREVMONTHNAME11), "Nov" },
5225 { X(LOCALE_SABBREVMONTHNAME12), "Dec" },
5226 { X(LOCALE_SABBREVMONTHNAME13), "" },
5227 { X(LOCALE_SPOSITIVESIGN), "+" },
5228 { X(LOCALE_SNEGATIVESIGN), "-" },
5229 { X(LOCALE_IPOSSIGNPOSN), "3" },
5230 { X(LOCALE_INEGSIGNPOSN), "0" },
5231 { X(LOCALE_IPOSSYMPRECEDES), "1" },
5232 { X(LOCALE_IPOSSEPBYSPACE), "0" },
5233 { X(LOCALE_INEGSYMPRECEDES), "1" },
5234 { X(LOCALE_INEGSEPBYSPACE), "0" },
5235 { X(LOCALE_SISO639LANGNAME), "iv" },
5236 { X(LOCALE_SISO3166CTRYNAME), "IV" },
5237 { X(LOCALE_IDEFAULTEBCDICCODEPAGE), "037" },
5238 { X(LOCALE_IPAPERSIZE), "9" },
5239 { X(LOCALE_SENGCURRNAME), "International Monetary Fund" },
5240 { X(LOCALE_SNATIVECURRNAME), "International Monetary Fund" },
5241 { X(LOCALE_SYEARMONTH), "yyyy MMMM" },
5242 { X(LOCALE_IDIGITSUBSTITUTION), "1" },
5243 { X(LOCALE_SNAME), "" },
5244 { X(LOCALE_SSCRIPTS), "Latn;" },
5245 { 0 }
5247 #undef X
5249 static void test_invariant(void)
5251 int ret;
5252 int len;
5253 char buffer[BUFFER_SIZE];
5254 const struct invariant_entry *ptr = invariant_list;
5256 if (!GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer)))
5258 win_skip("GetLocaleInfoA(LOCALE_INVARIANT) not supported\n"); /* win2k */
5259 return;
5262 while (ptr->name)
5264 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|ptr->id, buffer, sizeof(buffer));
5265 if (!ret && (ptr->id == LOCALE_SNAME || ptr->id == LOCALE_SSCRIPTS))
5266 win_skip("not supported\n"); /* winxp/win2k3 */
5267 else
5269 len = strlen(ptr->expect)+1; /* include \0 */
5270 ok(ret == len || (ptr->expect2 && ret == strlen(ptr->expect2)+1),
5271 "For id %d, expected ret == %d, got %d, error %d\n",
5272 ptr->id, len, ret, GetLastError());
5273 ok(!strcmp(buffer, ptr->expect) || (ptr->expect2 && !strcmp(buffer, ptr->expect2)),
5274 "For id %d, Expected %s, got '%s'\n",
5275 ptr->id, ptr->expect, buffer);
5278 ptr++;
5281 if ((LANGIDFROMLCID(GetSystemDefaultLCID()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)) ||
5282 (LANGIDFROMLCID(GetThreadLocale()) != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)))
5284 skip("Non US-English locale\n");
5286 else
5288 /* some locales translate these */
5289 static const char lang[] = "Invariant Language (Invariant Country)";
5290 static const char cntry[] = "Invariant Country";
5291 static const char sortm[] = "Math Alphanumerics";
5292 static const char sortms[] = "Maths Alphanumerics";
5293 static const char sortd[] = "Default"; /* win2k3 */
5295 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer));
5296 len = lstrlenA(lang) + 1;
5297 ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
5298 ok(!strcmp(buffer, lang), "Expected %s, got '%s'\n", lang, buffer);
5300 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SCOUNTRY, buffer, sizeof(buffer));
5301 len = lstrlenA(cntry) + 1;
5302 ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
5303 ok(!strcmp(buffer, cntry), "Expected %s, got '%s'\n", cntry, buffer);
5305 ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SSORTNAME, buffer, sizeof(buffer));
5306 ok(ret, "Failed err %d\n", GetLastError());
5307 ok(!strcmp(buffer, sortm) || !strcmp(buffer, sortd) || !strcmp(buffer, sortms), "Got '%s'\n", buffer);
5311 static void test_GetSystemPreferredUILanguages(void)
5313 BOOL ret;
5314 ULONG count, size, size_id, size_name, size_buffer;
5315 WCHAR *buffer;
5318 if (!pGetSystemPreferredUILanguages)
5320 win_skip("GetSystemPreferredUILanguages is not available.\n");
5321 return;
5324 /* (in)valid first parameter */
5325 count = 0xdeadbeef;
5326 size = 0;
5327 SetLastError(0xdeadbeef);
5328 ret = pGetSystemPreferredUILanguages(0, &count, NULL, &size);
5329 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5330 ok(count, "Expected count > 0\n");
5331 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5333 count = 0xdeadbeef;
5334 size = 0;
5335 SetLastError(0xdeadbeef);
5336 ret = pGetSystemPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
5337 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5338 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5339 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5341 count = 0xdeadbeef;
5342 size = 0;
5343 SetLastError(0xdeadbeef);
5344 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
5345 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5346 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5347 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5349 count = 0xdeadbeef;
5350 size = 0;
5351 SetLastError(0xdeadbeef);
5352 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME, &count, NULL, &size);
5353 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5354 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5355 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5357 count = 0xdeadbeef;
5358 size = 0;
5359 SetLastError(0xdeadbeef);
5360 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5361 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5362 ok(count, "Expected count > 0\n");
5363 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5365 count = 0xdeadbeef;
5366 size = 0;
5367 SetLastError(0xdeadbeef);
5368 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5369 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5370 ok(count, "Expected count > 0\n");
5371 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5373 /* second parameter
5374 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, NULL, NULL, &size);
5375 * -> unhandled exception c0000005
5378 /* invalid third parameter */
5379 count = 0xdeadbeef;
5380 size = 1;
5381 SetLastError(0xdeadbeef);
5382 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
5383 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5384 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5385 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5387 /* fourth parameter
5388 * ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, NULL);
5389 * -> unhandled exception c0000005
5392 count = 0xdeadbeef;
5393 size_id = 0;
5394 SetLastError(0xdeadbeef);
5395 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5396 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5397 ok(count, "Expected count > 0\n");
5398 ok(size_id % 5 == 1, "Expected size (%d) %% 5 == 1\n", size_id);
5400 count = 0xdeadbeef;
5401 size_name = 0;
5402 SetLastError(0xdeadbeef);
5403 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
5404 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5405 ok(count, "Expected count > 0\n");
5406 ok(size_name % 6 == 1, "Expected size (%d) %% 6 == 1\n", size_name);
5408 size_buffer = max(size_id, size_name);
5409 if(!size_buffer)
5411 skip("No valid buffer size\n");
5412 return;
5415 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
5416 if (!buffer)
5418 skip("Failed to allocate memory for %d chars\n", size_buffer);
5419 return;
5422 count = 0xdeadbeef;
5423 size = size_buffer;
5424 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5425 SetLastError(0xdeadbeef);
5426 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
5427 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5428 ok(count, "Expected count > 0\n");
5429 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5430 if (ret && size % 6 == 1)
5431 ok(!buffer[size -2] && !buffer[size -1],
5432 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5433 buffer[size -2], buffer[size -1]);
5435 count = 0xdeadbeef;
5436 size = size_buffer;
5437 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5438 SetLastError(0xdeadbeef);
5439 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5440 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5441 ok(count, "Expected count > 0\n");
5442 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5443 if (ret && size % 5 == 1)
5444 ok(!buffer[size -2] && !buffer[size -1],
5445 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5446 buffer[size -2], buffer[size -1]);
5448 count = 0xdeadbeef;
5449 size = size_buffer;
5450 SetLastError(0xdeadbeef);
5451 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
5452 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5453 ok(count, "Expected count > 0\n");
5454 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5455 if (ret && size % 5 == 1)
5456 ok(!buffer[size -2] && !buffer[size -1],
5457 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5458 buffer[size -2], buffer[size -1]);
5460 count = 0xdeadbeef;
5461 size = 0;
5462 SetLastError(0xdeadbeef);
5463 ret = pGetSystemPreferredUILanguages(MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5464 ok(ret, "Expected GetSystemPreferredUILanguages to succeed\n");
5465 ok(count, "Expected count > 0\n");
5466 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5467 if (ret && size % 6 == 1)
5468 ok(!buffer[size -2] && !buffer[size -1],
5469 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5470 buffer[size -2], buffer[size -1]);
5472 count = 0xdeadbeef;
5473 size = 1;
5474 SetLastError(0xdeadbeef);
5475 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5476 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5477 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5478 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5480 count = 0xdeadbeef;
5481 size = size_id -1;
5482 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5483 SetLastError(0xdeadbeef);
5484 ret = pGetSystemPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5485 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5486 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5487 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5489 count = 0xdeadbeef;
5490 size = size_id -2;
5491 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5492 SetLastError(0xdeadbeef);
5493 ret = pGetSystemPreferredUILanguages(0, &count, buffer, &size);
5494 ok(!ret, "Expected GetSystemPreferredUILanguages to fail\n");
5495 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5496 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5498 HeapFree(GetProcessHeap(), 0, buffer);
5501 static void test_GetThreadPreferredUILanguages(void)
5503 BOOL ret;
5504 ULONG count, size;
5505 WCHAR *buf;
5507 if (!pGetThreadPreferredUILanguages)
5509 win_skip("GetThreadPreferredUILanguages is not available.\n");
5510 return;
5513 size = count = 0;
5514 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, NULL, &size);
5515 ok(ret, "got %u\n", GetLastError());
5516 ok(count, "expected count > 0\n");
5517 ok(size, "expected size > 0\n");
5519 count = 0;
5520 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size * sizeof(WCHAR));
5521 ret = pGetThreadPreferredUILanguages(MUI_LANGUAGE_ID|MUI_UI_FALLBACK, &count, buf, &size);
5522 ok(ret, "got %u\n", GetLastError());
5523 ok(count, "expected count > 0\n");
5524 HeapFree(GetProcessHeap(), 0, buf);
5527 static void test_GetUserPreferredUILanguages(void)
5529 BOOL ret;
5530 ULONG count, size, size_id, size_name, size_buffer;
5531 WCHAR *buffer;
5534 if (!pGetUserPreferredUILanguages)
5536 win_skip("GetUserPreferredUILanguages is not available.\n");
5537 return;
5540 count = 0xdeadbeef;
5541 size = 0;
5542 SetLastError(0xdeadbeef);
5543 ret = pGetUserPreferredUILanguages(MUI_FULL_LANGUAGE, &count, NULL, &size);
5544 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5545 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5546 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5548 count = 0xdeadbeef;
5549 size = 0;
5550 SetLastError(0xdeadbeef);
5551 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_FULL_LANGUAGE, &count, NULL, &size);
5552 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5553 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5554 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5556 count = 0xdeadbeef;
5557 size = 0;
5558 SetLastError(0xdeadbeef);
5559 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS, &count, NULL, &size);
5560 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5561 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5562 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5564 count = 0xdeadbeef;
5565 size = 1;
5566 SetLastError(0xdeadbeef);
5567 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size);
5568 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5569 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5570 "Expected error ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
5572 count = 0xdeadbeef;
5573 size_id = 0;
5574 SetLastError(0xdeadbeef);
5575 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, NULL, &size_id);
5576 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5577 ok(count, "Expected count > 0\n");
5578 ok(size_id % 5 == 1, "Expected size (%d) %% 5 == 1\n", size_id);
5580 count = 0xdeadbeef;
5581 size_name = 0;
5582 SetLastError(0xdeadbeef);
5583 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, NULL, &size_name);
5584 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5585 ok(count, "Expected count > 0\n");
5586 ok(size_name % 6 == 1, "Expected size (%d) %% 6 == 1\n", size_name);
5588 size_buffer = max(size_id, size_name);
5589 if(!size_buffer)
5591 skip("No valid buffer size\n");
5592 return;
5595 buffer = HeapAlloc(GetProcessHeap(), 0, size_buffer * sizeof(WCHAR));
5597 count = 0xdeadbeef;
5598 size = size_buffer;
5599 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5600 SetLastError(0xdeadbeef);
5601 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
5602 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5603 ok(count, "Expected count > 0\n");
5604 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5605 if (ret && size % 6 == 1)
5606 ok(!buffer[size -2] && !buffer[size -1],
5607 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5608 buffer[size -2], buffer[size -1]);
5610 count = 0xdeadbeef;
5611 size = size_buffer;
5612 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5613 SetLastError(0xdeadbeef);
5614 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5615 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5616 ok(count, "Expected count > 0\n");
5617 ok(size % 5 == 1, "Expected size (%d) %% 5 == 1\n", size);
5618 if (ret && size % 5 == 1)
5619 ok(!buffer[size -2] && !buffer[size -1],
5620 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5621 buffer[size -2], buffer[size -1]);
5623 count = 0xdeadbeef;
5624 size = size_buffer;
5625 SetLastError(0xdeadbeef);
5626 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer, &size);
5627 ok(ret, "Expected GetUserPreferredUILanguages to succeed\n");
5628 ok(count, "Expected count > 0\n");
5629 ok(size % 6 == 1, "Expected size (%d) %% 6 == 1\n", size);
5630 if (ret && size % 5 == 1)
5631 ok(!buffer[size -2] && !buffer[size -1],
5632 "Expected last two WCHARs being empty, got 0x%x 0x%x\n",
5633 buffer[size -2], buffer[size -1]);
5635 count = 0xdeadbeef;
5636 size = 1;
5637 SetLastError(0xdeadbeef);
5638 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5639 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5640 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5641 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5643 count = 0xdeadbeef;
5644 size = size_id -1;
5645 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5646 SetLastError(0xdeadbeef);
5647 ret = pGetUserPreferredUILanguages(MUI_LANGUAGE_ID, &count, buffer, &size);
5648 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5649 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5650 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5652 count = 0xdeadbeef;
5653 size = size_id -2;
5654 memset(buffer, 0x5a, size_buffer * sizeof(WCHAR));
5655 SetLastError(0xdeadbeef);
5656 ret = pGetUserPreferredUILanguages(0, &count, buffer, &size);
5657 ok(!ret, "Expected GetUserPreferredUILanguages to fail\n");
5658 ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
5659 "Expected error ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
5661 HeapFree(GetProcessHeap(), 0, buffer);
5664 static void test_FindNLSStringEx(void)
5666 INT res;
5667 static WCHAR en_simpsimpW[] = {'S','i','m','p','l','e','S','i','m','p','l','e',0};
5668 static WCHAR en_simpW[] = {'S','i','m','p','l','e',0};
5669 static WCHAR comb_s_accent1W[] = {0x1e69, 'o','u','r','c','e',0};
5670 static WCHAR comb_s_accent2W[] = {0x0073,0x323,0x307,'o','u','r','c','e',0};
5671 static WCHAR comb_q_accent1W[] = {0x0071,0x0307,0x323,'u','o','t','e',0};
5672 static WCHAR comb_q_accent2W[] = {0x0071,0x0323,0x307,'u','o','t','e',0};
5673 struct test_data {
5674 const WCHAR *locale;
5675 DWORD flags;
5676 WCHAR *src;
5677 INT src_size;
5678 WCHAR *value;
5679 INT val_size;
5680 INT found;
5681 INT expected_ret;
5682 INT expected_found;
5683 int todo;
5684 BOOL broken_vista_servers;
5687 static struct test_data test_arr[] =
5689 { localeW, FIND_FROMSTART, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5690 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 0, 6, 0, FALSE},
5691 { localeW, FIND_FROMEND, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5692 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 6, 6, 0, FALSE},
5693 { localeW, FIND_STARTSWITH, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5694 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 0, 6, 0, FALSE},
5695 { localeW, FIND_ENDSWITH, en_simpsimpW, ARRAY_SIZE(en_simpsimpW)-1,
5696 en_simpW, ARRAY_SIZE(en_simpW)-1, 0, 6, 6, 0, FALSE},
5697 { localeW, FIND_FROMSTART, comb_s_accent1W, ARRAY_SIZE(comb_s_accent1W)-1,
5698 comb_s_accent2W, ARRAY_SIZE(comb_s_accent2W)-1, 0, 0, 6, 1, TRUE },
5699 { localeW, FIND_FROMSTART, comb_q_accent1W, ARRAY_SIZE(comb_q_accent1W)-1,
5700 comb_q_accent2W, ARRAY_SIZE(comb_q_accent2W)-1, 0, 0, 7, 1, FALSE },
5701 { 0 }
5703 struct test_data *ptest;
5705 if (!pFindNLSStringEx)
5707 win_skip("FindNLSStringEx is not available.\n");
5708 return;
5711 SetLastError( 0xdeadbeef );
5712 res = pFindNLSStringEx(invalidW, FIND_FROMSTART, fooW, 3, fooW,
5713 3, NULL, NULL, NULL, 0);
5714 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5715 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5716 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5718 SetLastError( 0xdeadbeef );
5719 res = pFindNLSStringEx(localeW, FIND_FROMSTART, NULL, 3, fooW, 3,
5720 NULL, NULL, NULL, 0);
5721 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5722 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5723 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5725 SetLastError( 0xdeadbeef );
5726 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, -5, fooW, 3,
5727 NULL, NULL, NULL, 0);
5728 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5729 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5730 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5732 SetLastError( 0xdeadbeef );
5733 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, NULL, 3,
5734 NULL, NULL, NULL, 0);
5735 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5736 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5737 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5739 SetLastError( 0xdeadbeef );
5740 res = pFindNLSStringEx(localeW, FIND_FROMSTART, fooW, 3, fooW, -5,
5741 NULL, NULL, NULL, 0);
5742 ok(res, "Expected failure of FindNLSStringEx. Return value was %d\n", res);
5743 ok(ERROR_INVALID_PARAMETER == GetLastError(),
5744 "Expected ERROR_INVALID_PARAMETER as last error; got %d\n", GetLastError());
5746 for (ptest = test_arr; ptest->src != NULL; ptest++)
5748 todo_wine_if(ptest->todo)
5750 res = pFindNLSStringEx(ptest->locale, ptest->flags, ptest->src, ptest->src_size,
5751 ptest->value, ptest->val_size, &ptest->found, NULL, NULL, 0);
5752 if (ptest->broken_vista_servers)
5754 ok(res == ptest->expected_ret || /* Win 7 onwards */
5755 broken(res == -1), /* Win Vista, Server 2003 and 2008 */
5756 "Expected FindNLSStringEx to return %d. Returned value was %d\n",
5757 ptest->expected_ret, res);
5758 ok(ptest->found == ptest->expected_found || /* Win 7 onwards */
5759 broken(ptest->found == 0), /* Win Vista, Server 2003 and 2008 */
5760 "Expected FindNLSStringEx to output %d. Value was %d\n",
5761 ptest->expected_found, ptest->found);
5763 else
5765 ok(res == ptest->expected_ret,
5766 "Expected FindNLSStringEx to return %d. Returned value was %d\n",
5767 ptest->expected_ret, res);
5768 ok(ptest->found == ptest->expected_found,
5769 "Expected FindNLSStringEx to output %d. Value was %d\n",
5770 ptest->expected_found, ptest->found);
5776 static void test_FindStringOrdinal(void)
5778 static const WCHAR abc123aBcW[] = {'a', 'b', 'c', '1', '2', '3', 'a', 'B', 'c', 0};
5779 static const WCHAR abcW[] = {'a', 'b', 'c', 0};
5780 static const WCHAR aBcW[] = {'a', 'B', 'c', 0};
5781 static const WCHAR aaaW[] = {'a', 'a', 'a', 0};
5782 static const struct
5784 DWORD flag;
5785 const WCHAR *src;
5786 INT src_size;
5787 const WCHAR *val;
5788 INT val_size;
5789 BOOL ignore_case;
5790 INT ret;
5791 DWORD err;
5793 tests[] =
5795 /* Invalid */
5796 {1, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, ERROR_INVALID_FLAGS},
5797 {FIND_FROMSTART, NULL, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1,
5798 ERROR_INVALID_PARAMETER},
5799 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, NULL, ARRAY_SIZE(abcW) - 1, FALSE, -1,
5800 ERROR_INVALID_PARAMETER},
5801 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, NULL, 0, FALSE, -1, ERROR_INVALID_PARAMETER},
5802 {FIND_FROMSTART, NULL, 0, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, ERROR_INVALID_PARAMETER},
5803 {FIND_FROMSTART, NULL, 0, NULL, 0, FALSE, -1, ERROR_INVALID_PARAMETER},
5804 /* Case-insensitive */
5805 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5806 {FIND_FROMEND, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5807 {FIND_STARTSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5808 {FIND_ENDSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, NO_ERROR},
5809 /* Case-sensitive */
5810 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 0, NO_ERROR},
5811 {FIND_FROMEND, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 6, NO_ERROR},
5812 {FIND_STARTSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 0, NO_ERROR},
5813 {FIND_ENDSWITH, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aBcW, ARRAY_SIZE(aBcW) - 1, TRUE, 6, NO_ERROR},
5814 /* Other */
5815 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, aaaW, ARRAY_SIZE(aaaW) - 1, FALSE, -1, NO_ERROR},
5816 {FIND_FROMSTART, abc123aBcW, -1, abcW, ARRAY_SIZE(abcW) - 1, FALSE, 0, NO_ERROR},
5817 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, -1, FALSE, 0, NO_ERROR},
5818 {FIND_FROMSTART, abc123aBcW, 0, abcW, ARRAY_SIZE(abcW) - 1, FALSE, -1, NO_ERROR},
5819 {FIND_FROMSTART, abc123aBcW, ARRAY_SIZE(abc123aBcW) - 1, abcW, 0, FALSE, 0, NO_ERROR},
5820 {FIND_FROMSTART, abc123aBcW, 0, abcW, 0, FALSE, 0, NO_ERROR},
5822 INT ret;
5823 DWORD err;
5824 INT i;
5826 if (!pFindStringOrdinal)
5828 win_skip("FindStringOrdinal is not available.\n");
5829 return;
5832 for (i = 0; i < ARRAY_SIZE(tests); i++)
5834 SetLastError(0xdeadbeef);
5835 ret = pFindStringOrdinal(tests[i].flag, tests[i].src, tests[i].src_size, tests[i].val, tests[i].val_size,
5836 tests[i].ignore_case);
5837 err = GetLastError();
5838 ok(ret == tests[i].ret, "Item %d expected %d, got %d\n", i, tests[i].ret, ret);
5839 ok(err == tests[i].err, "Item %d expected %#x, got %#x\n", i, tests[i].err, err);
5843 static void test_SetThreadUILanguage(void)
5845 LANGID res;
5847 if (!pGetThreadUILanguage)
5849 win_skip("GetThreadUILanguage isn't implemented, skipping SetThreadUILanguage tests for version < Vista\n");
5850 return; /* BTW SetThreadUILanguage is present on winxp/2003 but doesn`t set the LANGID anyway when tested */
5853 res = pSetThreadUILanguage(0);
5854 ok(res == pGetThreadUILanguage(), "expected %d got %d\n", pGetThreadUILanguage(), res);
5856 res = pSetThreadUILanguage(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN));
5857 ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
5858 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
5860 res = pSetThreadUILanguage(0);
5861 todo_wine ok(res == MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN),
5862 "expected %d got %d\n", MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), res);
5865 static void test_NormalizeString(void)
5867 /* part 0: specific cases */
5868 /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
5869 static const WCHAR part0_str1[] = {0x1e0a,0};
5870 static const WCHAR part0_nfd1[] = {0x0044,0x0307,0};
5872 /* LATIN CAPITAL LETTER D, COMBINING DOT BELOW, COMBINING DOT ABOVE */
5873 static const WCHAR part0_str2[] = {0x0044,0x0323,0x0307,0};
5874 static const WCHAR part0_nfc2[] = {0x1e0c,0x0307,0};
5876 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
5877 static const WCHAR part0_str3[] = {0x0044,0x031b,0x0323,0x0307,0};
5878 static const WCHAR part0_nfc3[] = {0x1e0c,0x031b,0x0307,0};
5880 /* LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE */
5881 static const WCHAR part0_str4[] = {0x0044,0x031b,0x0323,0x0307,0};
5882 static const WCHAR part0_nfc4[] = {0x1e0c,0x031b,0x0307,0};
5885 * HEBREW ACCENT SEGOL, HEBREW POINT PATAH, HEBREW POINT DAGESH OR MAPIQ,
5886 * HEBREW ACCENT MERKHA, HEBREW POINT SHEVA, HEBREW PUNCTUATION PASEQ,
5887 * HEBREW MARK UPPER DOT, HEBREW ACCENT DEHI
5889 static const WCHAR part0_str5[] = {0x0592,0x05B7,0x05BC,0x05A5,0x05B0,0x05C0,0x05C4,0x05AD,0};
5890 static const WCHAR part0_nfc5[] = {0x05B0,0x05B7,0x05BC,0x05A5,0x0592,0x05C0,0x05AD,0x05C4,0};
5893 * HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT HATAF SEGOL,
5894 * HEBREW ACCENT ETNAHTA, HEBREW PUNCTUATION SOF PASUQ, HEBREW POINT SHEVA,
5895 * HEBREW ACCENT ILUY, HEBREW ACCENT QARNEY PARA
5897 static const WCHAR part0_str6[] = {0x05B8,0x05B9,0x05B1,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
5898 static const WCHAR part0_nfc6[] = {0x05B1,0x05B8,0x05B9,0x0591,0x05C3,0x05B0,0x05AC,0x059F,0};
5900 /* LATIN CAPITAL LETTER D WITH DOT BELOW */
5901 static const WCHAR part0_str8[] = {0x1E0C,0};
5902 static const WCHAR part0_nfd8[] = {0x0044,0x0323,0};
5904 /* LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING DOT BELOW */
5905 static const WCHAR part0_str9[] = {0x1E0A,0x0323,0};
5906 static const WCHAR part0_nfc9[] = {0x1E0C,0x0307,0};
5907 static const WCHAR part0_nfd9[] = {0x0044,0x0323,0x0307,0};
5909 /* LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING DOT ABOVE */
5910 static const WCHAR part0_str10[] = {0x1E0C,0x0307,0};
5911 static const WCHAR part0_nfd10[] = {0x0044,0x0323,0x0307,0};
5913 /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE, COMBINING MACRON */
5914 static const WCHAR part0_str11[] = {0x1E14,0x0304,0};
5915 static const WCHAR part0_nfd11[] = {0x0045,0x0304,0x0300,0x0304,0};
5917 /* LATIN CAPITAL LETTER E WITH MACRON, COMBINING GRAVE ACCENT */
5918 static const WCHAR part0_str12[] = {0x0112,0x0300,0};
5919 static const WCHAR part0_nfc12[] = {0x1E14,0};
5920 static const WCHAR part0_nfd12[] = {0x0045,0x0304,0x0300,0};
5922 /* part 1: character by character */
5923 /* DIAERESIS */
5924 static const WCHAR part1_str1[] = {0x00a8,0};
5925 static const WCHAR part1_nfkc1[] = {0x0020,0x0308,0};
5927 /* VULGAR FRACTION ONE QUARTER */
5928 static const WCHAR part1_str2[] = {0x00bc,0};
5929 static const WCHAR part1_nfkc2[] = {0x0031,0x2044,0x0034,0};
5931 /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
5932 static const WCHAR part1_str3[] = {0x00ca,0};
5933 static const WCHAR part1_nfd3[] = {0x0045,0x0302,0};
5935 /* MODIFIER LETTER SMALL GAMMA */
5936 static const WCHAR part1_str4[] = {0x02e0,0};
5937 static const WCHAR part1_nfkc4[] = {0x0263,0};
5939 /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */
5940 static const WCHAR part1_str5[] = {0x0400,0};
5941 static const WCHAR part1_nfd5[] = {0x0415,0x0300,0};
5943 /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */
5944 static const WCHAR part1_str6[] = {0x0476,0};
5945 static const WCHAR part1_nfd6[] = {0x0474,0x030F,0};
5947 /* ARABIC LIGATURE HAH WITH JEEM INITIAL FORM */
5948 static const WCHAR part1_str7[] = {0xFCA9,0};
5949 static const WCHAR part1_nfkc7[] = {0x062D,0x062C,0};
5951 /* GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA */
5952 static const WCHAR part1_str8[] = {0x1F42,0};
5953 static const WCHAR part1_nfd8[] = {0x03BF,0x0313,0x0300,0};
5955 /* QUADRUPLE PRIME */
5956 static const WCHAR part1_str9[] = {0x2057,0};
5957 static const WCHAR part1_nfkc9[] = {0x2032,0x2032,0x2032,0x2032,0};
5959 /* KATAKANA-HIRAGANA VOICED SOUND MARK */
5960 static const WCHAR part1_str10[] = {0x309B,0};
5961 static const WCHAR part1_nfkc10[] = {0x20,0x3099,0};
5963 /* ANGSTROM SIGN */
5964 static const WCHAR part1_str11[] = {0x212B,0};
5965 static const WCHAR part1_nfc11[] = {0xC5,0};
5966 static const WCHAR part1_nfd11[] = {'A',0x030A,0};
5968 static const WCHAR composite_src[] =
5970 0x008a, 0x008e, 0x009a, 0x009e, 0x009f, 0x00c0, 0x00c1, 0x00c2,
5971 0x00c3, 0x00c4, 0x00c5, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb,
5972 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d1, 0x00d2, 0x00d3, 0x00d4,
5973 0x00d5, 0x00d6, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd,
5974 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e7, 0x00e8,
5975 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f1,
5976 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f8, 0x00f9, 0x00fa,
5977 0x00fb, 0x00fc, 0x00fd, 0x00ff, 0x212b
5980 struct test_data_normal {
5981 const WCHAR *str;
5982 const WCHAR *expected[4];
5984 static const struct test_data_normal test_arr[] =
5986 { part0_str1, { part0_str1, part0_nfd1, part0_str1, part0_nfd1 } },
5987 { part0_str2, { part0_nfc2, part0_str2, part0_nfc2, part0_str2 } },
5988 { part0_str3, { part0_nfc3, part0_str3, part0_nfc3, part0_str3 } },
5989 { part0_str4, { part0_nfc4, part0_str4, part0_nfc4, part0_str4 } },
5990 { part0_str5, { part0_nfc5, part0_nfc5, part0_nfc5, part0_nfc5 } },
5991 { part0_str6, { part0_nfc6, part0_nfc6, part0_nfc6, part0_nfc6 } },
5992 { part0_str8, { part0_str8, part0_nfd8, part0_str8, part0_nfd8 } },
5993 { part0_str9, { part0_nfc9, part0_nfd9, part0_nfc9, part0_nfd9 } },
5994 { part0_str10, { part0_str10, part0_nfd10, part0_str10, part0_nfd10 } },
5995 { part0_str11, { part0_str11, part0_nfd11, part0_str11, part0_nfd11 } },
5996 { part0_str12, { part0_nfc12, part0_nfd12, part0_nfc12, part0_nfd12 } },
5997 { part1_str1, { part1_str1, part1_str1, part1_nfkc1, part1_nfkc1 } },
5998 { part1_str2, { part1_str2, part1_str2, part1_nfkc2, part1_nfkc2 } },
5999 { part1_str3, { part1_str3, part1_nfd3, part1_str3, part1_nfd3 } },
6000 { part1_str4, { part1_str4, part1_str4, part1_nfkc4, part1_nfkc4 } },
6001 { part1_str5, { part1_str5, part1_nfd5, part1_str5, part1_nfd5 } },
6002 { part1_str6, { part1_str6, part1_nfd6, part1_str6, part1_nfd6 } },
6003 { part1_str7, { part1_str7, part1_str7, part1_nfkc7, part1_nfkc7 } },
6004 { part1_str8, { part1_str8, part1_nfd8, part1_str8, part1_nfd8 } },
6005 { part1_str9, { part1_str9, part1_str9, part1_nfkc9, part1_nfkc9 } },
6006 { part1_str10, { part1_str10, part1_str10, part1_nfkc10, part1_nfkc10 } },
6007 { part1_str11, { part1_nfc11, part1_nfd11, part1_nfc11, part1_nfd11 } },
6008 { 0 }
6010 const struct test_data_normal *ptest = test_arr;
6011 const int norm_forms[] = { NormalizationC, NormalizationD, NormalizationKC, NormalizationKD };
6012 WCHAR dst[256];
6013 NTSTATUS status;
6014 int dstlen, str_cmp, i, j;
6016 if (!pNormalizeString)
6018 win_skip("NormalizeString is not available.\n");
6019 return;
6021 if (!pRtlNormalizeString) win_skip("RtlNormalizeString is not available.\n");
6024 * For each string, first test passing -1 as srclen to NormalizeString,
6025 * thereby assuming a null-terminating string in src, and then test passing
6026 * explicitly the string length.
6027 * Do that for all 4 normalization forms.
6029 while (ptest->str)
6031 for (i = 0; i < 4; i++)
6033 SetLastError(0xdeadbeef);
6034 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, NULL, 0 );
6035 ok( dstlen > lstrlenW(ptest->str), "%s:%d: wrong len %d / %d\n",
6036 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW(ptest->str) );
6037 ok(GetLastError() == ERROR_SUCCESS, "%s:%d: got error %u\n",
6038 wine_dbgstr_w(ptest->str), i, GetLastError());
6039 SetLastError(0xdeadbeef);
6040 dstlen = pNormalizeString( norm_forms[i], ptest->str, -1, dst, dstlen );
6041 ok(GetLastError() == ERROR_SUCCESS, "%s:%d: got error %u\n",
6042 wine_dbgstr_w(ptest->str), i, GetLastError());
6043 ok(dstlen == lstrlenW( dst )+1, "%s:%d: Copied length differed: was %d, should be %d\n",
6044 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst )+1);
6045 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen+1 );
6046 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
6047 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
6049 dstlen = pNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), NULL, 0 );
6050 memset(dst, 0xcc, sizeof(dst));
6051 dstlen = pNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), dst, dstlen );
6052 ok(dstlen == lstrlenW( ptest->expected[i] ), "%s:%d: Copied length differed: was %d, should be %d\n",
6053 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst ));
6054 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen );
6055 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
6056 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
6058 if (pRtlNormalizeString)
6060 BOOLEAN ret = FALSE;
6062 dstlen = 0;
6063 status = pRtlNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), NULL, &dstlen );
6064 ok( !status, "%s:%d: failed %x\n", wine_dbgstr_w(ptest->str), i, status );
6065 ok( dstlen > lstrlenW(ptest->str), "%s:%d: wrong len %d / %d\n",
6066 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW(ptest->str) );
6067 memset(dst, 0, sizeof(dst));
6068 status = pRtlNormalizeString( norm_forms[i], ptest->str, lstrlenW(ptest->str), dst, &dstlen );
6069 ok( !status, "%s:%d: failed %x\n", wine_dbgstr_w(ptest->str), i, status );
6070 ok(dstlen == lstrlenW( dst ), "%s:%d: Copied length differed: was %d, should be %d\n",
6071 wine_dbgstr_w(ptest->str), i, dstlen, lstrlenW( dst ));
6072 str_cmp = wcsncmp( ptest->expected[i], dst, dstlen );
6073 ok( str_cmp == 0, "%s:%d: string incorrect got %s expect %s\n", wine_dbgstr_w(ptest->str), i,
6074 wine_dbgstr_w(dst), wine_dbgstr_w(ptest->expected[i]) );
6075 status = pRtlIsNormalizedString( norm_forms[i], dst, dstlen, &ret );
6076 todo_wine ok( !status, "%s:%d: failed %x\n", wine_dbgstr_w(ptest->str), i, status );
6077 todo_wine ok( ret, "%s:%d: not normalized\n", wine_dbgstr_w(ptest->str), i );
6080 ptest++;
6083 /* buffer overflows */
6085 SetLastError(0xdeadbeef);
6086 dstlen = pNormalizeString( NormalizationD, part0_str1, -1, dst, 1 );
6087 ok( dstlen <= 0, "wrong len %d\n", dstlen );
6088 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %u\n", GetLastError());
6090 SetLastError(0xdeadbeef);
6091 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, dst, 1 );
6092 ok( dstlen <= 0, "wrong len %d\n", dstlen );
6093 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %u\n", GetLastError());
6095 SetLastError(0xdeadbeef);
6096 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, NULL, 0 );
6097 ok( dstlen == 12, "wrong len %d\n", dstlen );
6098 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6100 SetLastError(0xdeadbeef);
6101 dstlen = pNormalizeString( NormalizationC, part0_str2, -1, dst, 3 );
6102 ok( dstlen == 3, "wrong len %d\n", dstlen );
6103 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6105 SetLastError(0xdeadbeef);
6106 dstlen = pNormalizeString( NormalizationC, part0_str2, 0, NULL, 0 );
6107 ok( dstlen == 0, "wrong len %d\n", dstlen );
6108 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6110 SetLastError(0xdeadbeef);
6111 dstlen = pNormalizeString( NormalizationC, part0_str2, 0, dst, 3 );
6112 ok( dstlen == 0, "wrong len %d\n", dstlen );
6113 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6115 /* size estimations */
6117 memset( dst, 'A', sizeof(dst) );
6118 for (j = 1; j < ARRAY_SIZE(dst); j++)
6120 for (i = 0; i < 4; i++)
6122 int expect = (i < 2) ? j * 3 : j * 18;
6123 if (expect > 64) expect = max( 64, j + j / 8 );
6124 dstlen = pNormalizeString( norm_forms[i], dst, j, NULL, 0 );
6125 ok( dstlen == expect, "%d: %d -> wrong len %d\n", i, j, dstlen );
6126 if (pRtlNormalizeString)
6128 dstlen = 0;
6129 status = pRtlNormalizeString( norm_forms[i], dst, j, NULL, &dstlen );
6130 ok( !status, "%d: failed %x\n", i, status );
6131 ok( dstlen == expect, "%d: %d -> wrong len %d\n", i, j, dstlen );
6135 for (i = 0; i < 4; i++)
6137 int srclen = ARRAY_SIZE( composite_src );
6138 int expect = max( 64, srclen + srclen / 8 );
6139 dstlen = pNormalizeString( norm_forms[i], composite_src, srclen, NULL, 0 );
6140 ok( dstlen == expect, "%d: wrong len %d\n", i, dstlen );
6141 dstlen = pNormalizeString( norm_forms[i], composite_src, srclen, dst, dstlen );
6142 if (i == 0 || i == 2)
6144 ok( dstlen == srclen, "%d: wrong len %d\n", i, dstlen );
6145 ok(GetLastError() == ERROR_SUCCESS, "got error %u\n", GetLastError());
6147 else
6149 ok( dstlen < -expect, "%d: wrong len %d\n", i, dstlen );
6150 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got error %u\n", GetLastError());
6152 if (pRtlNormalizeString)
6154 dstlen = 0;
6155 status = pRtlNormalizeString( norm_forms[i], composite_src, srclen, NULL, &dstlen );
6156 ok( !status, "%d: failed %x\n", i, status );
6157 ok( dstlen == expect, "%d: wrong len %d\n", i, dstlen );
6158 status = pRtlNormalizeString( norm_forms[i], composite_src, srclen, dst, &dstlen );
6159 if (i == 0 || i == 2)
6161 ok( !status, "%d: failed %x\n", i, status );
6162 ok( dstlen == srclen, "%d: wrong len %d\n", i, dstlen );
6164 else
6166 ok( status == STATUS_BUFFER_TOO_SMALL, "%d: failed %x\n", i, status );
6167 ok( dstlen > expect, "%d: wrong len %d\n", i, dstlen );
6172 /* invalid parameters */
6174 for (i = 0; i < 32; i++)
6176 SetLastError(0xdeadbeef);
6177 dstlen = pNormalizeString( i, L"ABC", -1, NULL, 0 );
6178 switch (i)
6180 case NormalizationC:
6181 case NormalizationD:
6182 case NormalizationKC:
6183 case NormalizationKD:
6184 case 13: /* Idn */
6185 todo_wine_if (i == 13)
6187 ok( dstlen > 0, "%d: wrong len %d\n", i, dstlen );
6188 ok( GetLastError() == ERROR_SUCCESS, "%d: got error %u\n", i, GetLastError());
6190 break;
6191 default:
6192 ok( dstlen <= 0, "%d: wrong len %d\n", i, dstlen );
6193 ok( GetLastError() == ERROR_INVALID_PARAMETER, "%d: got error %u\n", i, GetLastError());
6194 break;
6196 if (pRtlNormalizeString)
6198 dstlen = 0;
6199 status = pRtlNormalizeString( i, L"ABC", -1, NULL, &dstlen );
6200 switch (i)
6202 case 0:
6203 ok( status == STATUS_INVALID_PARAMETER, "%d: failed %x\n", i, status );
6204 break;
6205 case NormalizationC:
6206 case NormalizationD:
6207 case NormalizationKC:
6208 case NormalizationKD:
6209 case 13: /* Idn */
6210 todo_wine_if (i == 13)
6211 ok( status == STATUS_SUCCESS, "%d: failed %x\n", i, status );
6212 break;
6213 default:
6214 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "%d: failed %x\n", i, status );
6215 break;
6220 /* invalid sequences */
6222 for (i = 0; i < 4; i++)
6224 dstlen = pNormalizeString( norm_forms[i], L"AB\xd800Z", -1, NULL, 0 );
6225 ok( dstlen == (i < 2 ? 15 : 64), "%d: wrong len %d\n", i, dstlen );
6226 SetLastError( 0xdeadbeef );
6227 dstlen = pNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, ARRAY_SIZE(dst) );
6228 todo_wine ok( dstlen == -3, "%d: wrong len %d\n", i, dstlen );
6229 todo_wine ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%d: wrong error %d\n", i, GetLastError() );
6230 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, NULL, 0 );
6231 ok( dstlen == (i < 2 ? 21 : 64), "%d: wrong len %d\n", i, dstlen );
6232 SetLastError( 0xdeadbeef );
6233 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, dst, ARRAY_SIZE(dst) );
6234 todo_wine ok( dstlen == -4, "%d: wrong len %d\n", i, dstlen );
6235 todo_wine ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%d: wrong error %d\n", i, GetLastError() );
6236 SetLastError( 0xdeadbeef );
6237 dstlen = pNormalizeString( norm_forms[i], L"ABCD\xdc12Z", -1, dst, 2 );
6238 todo_wine ok( dstlen == (i < 2 ? -18 : -74), "%d: wrong len %d\n", i, dstlen );
6239 ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "%d: wrong error %d\n", i, GetLastError() );
6240 if (pRtlNormalizeString)
6242 dstlen = 0;
6243 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, NULL, &dstlen );
6244 ok( !status, "%d: failed %x\n", i, status );
6245 ok( dstlen == (i < 2 ? 15 : 64), "%d: wrong len %d\n", i, dstlen );
6246 dstlen = ARRAY_SIZE(dst);
6247 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
6248 todo_wine ok( status == STATUS_NO_UNICODE_TRANSLATION, "%d: failed %x\n", i, status );
6249 todo_wine ok( dstlen == 3, "%d: wrong len %d\n", i, dstlen );
6250 dstlen = 1;
6251 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
6252 ok( status == STATUS_BUFFER_TOO_SMALL, "%d: failed %x\n", i, status );
6253 todo_wine_if (i != 3)
6254 ok( dstlen == (i < 2 ? 14 : 73), "%d: wrong len %d\n", i, dstlen );
6255 dstlen = 2;
6256 status = pRtlNormalizeString( norm_forms[i], L"AB\xd800Z", -1, dst, &dstlen );
6257 todo_wine ok( status == STATUS_NO_UNICODE_TRANSLATION, "%d: failed %x\n", i, status );
6258 todo_wine ok( dstlen == 3, "%d: wrong len %d\n", i, dstlen );
6263 static void test_SpecialCasing(void)
6265 int ret, i;
6266 WCHAR exp, buffer[8];
6267 static const WCHAR azCyrlazW[] = {'a','z','-','C','y','r','l','-','a','z',0};
6268 static const WCHAR azLatnazW[] = {'a','z','-','L','a','t','n','-','a','z',0};
6269 static const WCHAR deDEW[] = {'d','e','-','D','E',0};
6270 static const WCHAR elGRW[] = {'e','l','-','G','R',0};
6271 static const WCHAR enUSW[] = {'e','n','-','U','S',0};
6272 static const WCHAR hyAMW[] = {'h','y','-','A','M',0};
6273 static const WCHAR ltLTW[] = {'l','t','-','L','T',0};
6274 static const WCHAR trTRW[] = {'t','r','-','T','R',0};
6275 static const WCHAR TRTRW[] = {'T','R','-','T','R',0};
6276 static const struct test {
6277 const WCHAR *lang;
6278 DWORD flags;
6279 WCHAR ch;
6280 WCHAR exp; /* 0 if self */
6281 WCHAR exp_ling; /* 0 if exp */
6282 BOOL todo;
6283 } tests[] = {
6284 {deDEW, LCMAP_UPPERCASE, 0x00DF}, /* LATIN SMALL LETTER SHARP S */
6286 {enUSW, LCMAP_UPPERCASE, 0xFB00}, /* LATIN SMALL LIGATURE FF */
6287 {enUSW, LCMAP_UPPERCASE, 0xFB01}, /* LATIN SMALL LIGATURE FI */
6288 {enUSW, LCMAP_UPPERCASE, 0xFB02}, /* LATIN SMALL LIGATURE FL */
6289 {enUSW, LCMAP_UPPERCASE, 0xFB03}, /* LATIN SMALL LIGATURE FFI */
6290 {enUSW, LCMAP_UPPERCASE, 0xFB04}, /* LATIN SMALL LIGATURE FFL */
6291 {enUSW, LCMAP_UPPERCASE, 0xFB05}, /* LATIN SMALL LIGATURE LONG S T */
6292 {enUSW, LCMAP_UPPERCASE, 0xFB06}, /* LATIN SMALL LIGATURE ST */
6294 {hyAMW, LCMAP_UPPERCASE, 0x0587}, /* ARMENIAN SMALL LIGATURE ECH YIWN */
6295 {hyAMW, LCMAP_UPPERCASE, 0xFB13}, /* ARMENIAN SMALL LIGATURE MEN NOW */
6296 {hyAMW, LCMAP_UPPERCASE, 0xFB14}, /* ARMENIAN SMALL LIGATURE MEN ECH */
6297 {hyAMW, LCMAP_UPPERCASE, 0xFB15}, /* ARMENIAN SMALL LIGATURE MEN INI */
6298 {hyAMW, LCMAP_UPPERCASE, 0xFB16}, /* ARMENIAN SMALL LIGATURE VEW NOW */
6299 {hyAMW, LCMAP_UPPERCASE, 0xFB17}, /* ARMENIAN SMALL LIGATURE MEN XEH */
6301 {enUSW, LCMAP_UPPERCASE, 0x0149}, /* LATIN SMALL LETTER N PRECEDED BY APOSTROPHE */
6302 {elGRW, LCMAP_UPPERCASE, 0x0390}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
6303 {elGRW, LCMAP_UPPERCASE, 0x03B0}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
6304 {enUSW, LCMAP_UPPERCASE, 0x01F0}, /* LATIN SMALL LETTER J WITH CARON */
6305 {enUSW, LCMAP_UPPERCASE, 0x1E96}, /* LATIN SMALL LETTER H WITH LINE BELOW */
6306 {enUSW, LCMAP_UPPERCASE, 0x1E97}, /* LATIN SMALL LETTER T WITH DIAERESIS */
6307 {enUSW, LCMAP_UPPERCASE, 0x1E98}, /* LATIN SMALL LETTER W WITH RING ABOVE */
6308 {enUSW, LCMAP_UPPERCASE, 0x1E99}, /* LATIN SMALL LETTER Y WITH RING ABOVE */
6309 {enUSW, LCMAP_UPPERCASE, 0x1E9A}, /* LATIN SMALL LETTER A WITH RIGHT HALF RING */
6310 {elGRW, LCMAP_UPPERCASE, 0x1F50}, /* GREEK SMALL LETTER UPSILON WITH PSILI */
6311 {elGRW, LCMAP_UPPERCASE, 0x1F52}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA */
6312 {elGRW, LCMAP_UPPERCASE, 0x1F54}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA */
6313 {elGRW, LCMAP_UPPERCASE, 0x1F56}, /* GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI */
6314 {elGRW, LCMAP_UPPERCASE, 0x1FB6}, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI */
6315 {elGRW, LCMAP_UPPERCASE, 0x1FC6}, /* GREEK SMALL LETTER ETA WITH PERISPOMENI */
6316 {elGRW, LCMAP_UPPERCASE, 0x1FD2}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA */
6317 {elGRW, LCMAP_UPPERCASE, 0x1FD3}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA */
6318 {elGRW, LCMAP_UPPERCASE, 0x1FD6}, /* GREEK SMALL LETTER IOTA WITH PERISPOMENI */
6319 {elGRW, LCMAP_UPPERCASE, 0x1FD7}, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI */
6320 {elGRW, LCMAP_UPPERCASE, 0x1FE2}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA */
6321 {elGRW, LCMAP_UPPERCASE, 0x1FE3}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA */
6322 {elGRW, LCMAP_UPPERCASE, 0x1FE4}, /* GREEK SMALL LETTER RHO WITH PSILI */
6323 {elGRW, LCMAP_UPPERCASE, 0x1FE6}, /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI */
6324 {elGRW, LCMAP_UPPERCASE, 0x1FE7}, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI */
6325 {elGRW, LCMAP_UPPERCASE, 0x1FF6}, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI */
6327 {elGRW, LCMAP_UPPERCASE, 0x1F80,0x1F88}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI */
6328 {elGRW, LCMAP_UPPERCASE, 0x1F81,0x1F89}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI */
6329 {elGRW, LCMAP_UPPERCASE, 0x1F82,0x1F8A}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
6330 {elGRW, LCMAP_UPPERCASE, 0x1F83,0x1F8B}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
6331 {elGRW, LCMAP_UPPERCASE, 0x1F84,0x1F8C}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
6332 {elGRW, LCMAP_UPPERCASE, 0x1F85,0x1F8D}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
6333 {elGRW, LCMAP_UPPERCASE, 0x1F86,0x1F8E}, /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
6334 {elGRW, LCMAP_UPPERCASE, 0x1F87,0x1F8F}, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
6336 {elGRW, LCMAP_LOWERCASE, 0x1F88,0x1F80}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */
6337 {elGRW, LCMAP_LOWERCASE, 0x1F89,0x1F81}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */
6338 {elGRW, LCMAP_LOWERCASE, 0x1F8A,0x1F82}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
6339 {elGRW, LCMAP_LOWERCASE, 0x1F8B,0x1F83}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
6340 {elGRW, LCMAP_LOWERCASE, 0x1F8C,0x1F84}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
6341 {elGRW, LCMAP_LOWERCASE, 0x1F8D,0x1F85}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
6342 {elGRW, LCMAP_LOWERCASE, 0x1F8E,0x1F86}, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
6343 {elGRW, LCMAP_LOWERCASE, 0x1F8F,0x1F87}, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
6345 {elGRW, LCMAP_UPPERCASE, 0x1F90,0x1F98}, /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI */
6346 {elGRW, LCMAP_UPPERCASE, 0x1F91,0x1F99}, /* GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI */
6347 {elGRW, LCMAP_UPPERCASE, 0x1F92,0x1F9A}, /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
6348 {elGRW, LCMAP_UPPERCASE, 0x1F93,0x1F9B}, /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
6349 {elGRW, LCMAP_UPPERCASE, 0x1F94,0x1F9C}, /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
6350 {elGRW, LCMAP_UPPERCASE, 0x1F95,0x1F9D}, /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
6351 {elGRW, LCMAP_UPPERCASE, 0x1F96,0x1F9E}, /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
6352 {elGRW, LCMAP_UPPERCASE, 0x1F97,0x1F9F}, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
6354 {elGRW, LCMAP_LOWERCASE, 0x1FA8,0x1FA0}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */
6355 {elGRW, LCMAP_LOWERCASE, 0x1FA9,0x1FA1}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */
6356 {elGRW, LCMAP_LOWERCASE, 0x1FAA,0x1FA2}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
6357 {elGRW, LCMAP_LOWERCASE, 0x1FAB,0x1FA3}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
6358 {elGRW, LCMAP_LOWERCASE, 0x1FAC,0x1FA4}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
6359 {elGRW, LCMAP_LOWERCASE, 0x1FAD,0x1FA5}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
6360 {elGRW, LCMAP_LOWERCASE, 0x1FAE,0x1FA6}, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
6361 {elGRW, LCMAP_LOWERCASE, 0x1FAF,0x1FA7}, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
6363 {elGRW, LCMAP_UPPERCASE, 0x1FB3,0x1FBC}, /* GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI */
6364 {elGRW, LCMAP_LOWERCASE, 0x1FBC,0x1FB3}, /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */
6365 {elGRW, LCMAP_UPPERCASE, 0x1FC3,0x1FCC}, /* GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI */
6366 {elGRW, LCMAP_LOWERCASE, 0x1FCC,0x1FC3}, /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */
6367 {elGRW, LCMAP_UPPERCASE, 0x1FF3,0x1FFC}, /* GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI */
6368 {elGRW, LCMAP_LOWERCASE, 0x1FFC,0x1FF3}, /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */
6370 {elGRW, LCMAP_UPPERCASE, 0x1FB2}, /* GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI */
6371 {elGRW, LCMAP_UPPERCASE, 0x1FB4}, /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI */
6372 {elGRW, LCMAP_UPPERCASE, 0x1FC2}, /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI */
6373 {elGRW, LCMAP_UPPERCASE, 0x1FC4}, /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI */
6374 {elGRW, LCMAP_UPPERCASE, 0x1FF2}, /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI */
6375 {elGRW, LCMAP_UPPERCASE, 0x1FF4}, /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI */
6377 {elGRW, LCMAP_UPPERCASE, 0x1FB7}, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI */
6378 {elGRW, LCMAP_UPPERCASE, 0x1FC7}, /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI */
6379 {elGRW, LCMAP_UPPERCASE, 0x1FF7}, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI */
6381 {elGRW, LCMAP_LOWERCASE, 0x03A3,0x03C3}, /* GREEK CAPITAL LETTER SIGMA */
6383 {ltLTW, LCMAP_LOWERCASE, 'J','j'}, /* LATIN CAPITAL LETTER J */
6384 {ltLTW, LCMAP_LOWERCASE, 0x012E,0x012F}, /* LATIN CAPITAL LETTER I WITH OGONEK */
6385 {ltLTW, LCMAP_LOWERCASE, 0x00CC,0x00EC}, /* LATIN CAPITAL LETTER I WITH GRAVE */
6386 {ltLTW, LCMAP_LOWERCASE, 0x00CD,0x00ED}, /* LATIN CAPITAL LETTER I WITH ACUTE */
6387 {ltLTW, LCMAP_LOWERCASE, 0x0128,0x0129}, /* LATIN CAPITAL LETTER I WITH TILDE */
6389 {enUSW, LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
6390 {ltLTW, LCMAP_UPPERCASE, 'i', 'I'}, /* LATIN SMALL LETTER I */
6391 {trTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
6392 {TRTRW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
6393 {azCyrlazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
6394 {azLatnazW, LCMAP_UPPERCASE, 'i', 'I', 0x0130, TRUE}, /* LATIN SMALL LETTER I */
6396 {enUSW, LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
6397 {ltLTW, LCMAP_LOWERCASE, 'I', 'i'}, /* LATIN CAPITAL LETTER I */
6398 {trTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
6399 {TRTRW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
6400 {azCyrlazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
6401 {azLatnazW, LCMAP_LOWERCASE, 'I', 'i', 0x0131, TRUE}, /* LATIN CAPITAL LETTER I */
6403 {enUSW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6404 {trTRW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6405 {TRTRW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6406 {azCyrlazW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6407 {azLatnazW, LCMAP_LOWERCASE, 0x0130,0,'i', TRUE}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
6409 {enUSW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
6410 {trTRW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
6411 {TRTRW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
6412 {azCyrlazW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
6413 {azLatnazW, LCMAP_UPPERCASE, 0x0131,0,'I', TRUE}, /* LATIN SMALL LETTER DOTLESS I */
6416 if (!pLCMapStringEx)
6418 win_skip("LCMapStringEx not available\n");
6419 return;
6422 for (i = 0; i < ARRAY_SIZE(tests); i++) {
6423 memset(buffer, 0, sizeof(buffer));
6424 ret = pLCMapStringEx(tests[i].lang, tests[i].flags,
6425 &tests[i].ch, 1, buffer, ARRAY_SIZE(buffer), NULL, NULL, 0);
6426 ok(ret == 1, "expected 1, got %d for %04x for %s\n", ret, tests[i].ch,
6427 wine_dbgstr_w(tests[i].lang));
6428 exp = tests[i].exp ? tests[i].exp : tests[i].ch;
6429 ok(buffer[0] == exp || broken(buffer[0] != exp),
6430 "expected %04x, got %04x for %04x for %s\n",
6431 exp, buffer[0], tests[i].ch, wine_dbgstr_w(tests[i].lang));
6433 memset(buffer, 0, sizeof(buffer));
6434 ret = pLCMapStringEx(tests[i].lang, tests[i].flags|LCMAP_LINGUISTIC_CASING,
6435 &tests[i].ch, 1, buffer, ARRAY_SIZE(buffer), NULL, NULL, 0);
6436 ok(ret == 1, "expected 1, got %d for %04x for %s\n", ret, tests[i].ch,
6437 wine_dbgstr_w(tests[i].lang));
6438 exp = tests[i].exp_ling ? tests[i].exp_ling : exp;
6439 todo_wine_if(tests[i].todo)
6440 ok(buffer[0] == exp || broken(buffer[0] != exp),
6441 "expected %04x, got %04x for %04x for %s\n",
6442 exp, buffer[0], tests[i].ch, wine_dbgstr_w(tests[i].lang));
6446 START_TEST(locale)
6448 InitFunctionPointers();
6450 test_EnumTimeFormatsA();
6451 test_EnumTimeFormatsW();
6452 test_EnumDateFormatsA();
6453 test_GetLocaleInfoA();
6454 test_GetLocaleInfoW();
6455 test_GetLocaleInfoEx();
6456 test_GetTimeFormatA();
6457 test_GetTimeFormatEx();
6458 test_GetDateFormatA();
6459 test_GetDateFormatEx();
6460 test_GetDateFormatW();
6461 test_GetCurrencyFormatA(); /* Also tests the W version */
6462 test_GetNumberFormatA(); /* Also tests the W version */
6463 test_GetNumberFormatEx();
6464 test_CompareStringA();
6465 test_CompareStringW();
6466 test_CompareStringEx();
6467 test_LCMapStringA();
6468 test_LCMapStringW();
6469 test_LCMapStringEx();
6470 test_LocaleNameToLCID();
6471 test_FoldStringA();
6472 test_FoldStringW();
6473 test_ConvertDefaultLocale();
6474 test_EnumSystemLanguageGroupsA();
6475 test_EnumSystemLocalesEx();
6476 test_EnumLanguageGroupLocalesA();
6477 test_SetLocaleInfoA();
6478 test_EnumUILanguageA();
6479 test_GetCPInfo();
6480 test_GetStringTypeW();
6481 test_IdnToNameprepUnicode();
6482 test_IdnToAscii();
6483 test_IdnToUnicode();
6484 test_IsValidLocaleName();
6485 test_CompareStringOrdinal();
6486 test_GetGeoInfo();
6487 test_EnumSystemGeoID();
6488 test_invariant();
6489 test_GetSystemPreferredUILanguages();
6490 test_GetThreadPreferredUILanguages();
6491 test_GetUserPreferredUILanguages();
6492 test_FindNLSStringEx();
6493 test_FindStringOrdinal();
6494 test_SetThreadUILanguage();
6495 test_NormalizeString();
6496 test_SpecialCasing();
6497 /* this requires collation table patch to make it MS compatible */
6498 if (0) test_sorting();