[KERNEL32_WINETEST]
[reactos.git] / rostests / winetests / kernel32 / locale.c
index f37d503..1cc6610 100755 (executable)
@@ -30,6 +30,9 @@
 #include <stdarg.h>
 #include <stdio.h>
 
+#undef WINVER
+#define WINVER 0x0600
+
 #include "wine/test.h"
 #include "windef.h"
 #include "winbase.h"
@@ -67,33 +70,34 @@ static inline int isdigitW( WCHAR wc )
 static HMODULE hKernel32;
 static WORD enumCount;
 
-typedef BOOL (WINAPI *EnumSystemLanguageGroupsAFn)(LANGUAGEGROUP_ENUMPROC,
-                                                   DWORD, LONG_PTR);
-static EnumSystemLanguageGroupsAFn pEnumSystemLanguageGroupsA;
-typedef BOOL (WINAPI *EnumLanguageGroupLocalesAFn)(LANGGROUPLOCALE_ENUMPROC,
-                                                   LGRPID, DWORD, LONG_PTR);
-static EnumLanguageGroupLocalesAFn pEnumLanguageGroupLocalesA;
-typedef BOOL (WINAPI *EnumUILanguagesAFn)(UILANGUAGE_ENUMPROC,
-                                                   DWORD, LONG_PTR);
-static EnumUILanguagesAFn pEnumUILanguagesA;
-
-typedef INT (WINAPI *FoldStringAFn)(DWORD, LPCSTR, INT, LPSTR, INT);
-static FoldStringAFn pFoldStringA;
-typedef INT (WINAPI *FoldStringWFn)(DWORD, LPCWSTR, INT, LPWSTR, INT);
-static FoldStringWFn pFoldStringW;
-
-typedef BOOL (WINAPI *IsValidLanguageGroupFn)(LGRPID, DWORD);
-static IsValidLanguageGroupFn pIsValidLanguageGroup;
+static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROC, DWORD, LONG_PTR);
+static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROC, LGRPID, DWORD, LONG_PTR);
+static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROC, DWORD, LONG_PTR);
+static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
+static LCID (WINAPI *pLocaleNameToLCID)(LPCWSTR, DWORD);
+static INT  (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD);
+static INT (WINAPI *pFoldStringA)(DWORD, LPCSTR, INT, LPSTR, INT);
+static INT (WINAPI *pFoldStringW)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
+static INT (WINAPI *pIdnToNameprepUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+static INT (WINAPI *pIdnToAscii)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+static INT (WINAPI *pIdnToUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
 
 static void InitFunctionPointers(void)
 {
   hKernel32 = GetModuleHandleA("kernel32");
   pEnumSystemLanguageGroupsA = (void*)GetProcAddress(hKernel32, "EnumSystemLanguageGroupsA");
   pEnumLanguageGroupLocalesA = (void*)GetProcAddress(hKernel32, "EnumLanguageGroupLocalesA");
+  pLocaleNameToLCID = (void*)GetProcAddress(hKernel32, "LocaleNameToLCID");
+  pLCIDToLocaleName = (void*)GetProcAddress(hKernel32, "LCIDToLocaleName");
   pFoldStringA = (void*)GetProcAddress(hKernel32, "FoldStringA");
   pFoldStringW = (void*)GetProcAddress(hKernel32, "FoldStringW");
   pIsValidLanguageGroup = (void*)GetProcAddress(hKernel32, "IsValidLanguageGroup");
   pEnumUILanguagesA = (void*)GetProcAddress(hKernel32, "EnumUILanguagesA");
+  pEnumSystemLocalesEx = (void*)GetProcAddress(hKernel32, "EnumSystemLocalesEx");
+  pIdnToNameprepUnicode = (void*)GetProcAddress(hKernel32, "IdnToNameprepUnicode");
+  pIdnToAscii = (void*)GetProcAddress(hKernel32, "IdnToAscii");
+  pIdnToUnicode = (void*)GetProcAddress(hKernel32, "IdnToUnicode");
 }
 
 #define eq(received, expected, label, type) \
@@ -1144,12 +1148,63 @@ static void test_GetNumberFormatA(void)
   }
 }
 
+struct comparestringa_entry {
+  LCID lcid;
+  DWORD flags;
+  const char *first;
+  int first_len;
+  const char *second;
+  int second_len;
+  int ret;
+};
+
+static const struct comparestringa_entry comparestringa_data[] = {
+  { LOCALE_SYSTEM_DEFAULT, 0, "EndDialog", -1, "_Property", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0070", -1, "_IEWWBrowserComp", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "r", -1, "\\", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "osp_vba.sreg0031", -1, "OriginalDatabase", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1, CSTR_LESS_THAN },
+  /* hyphen and apostrophe are treated differently depending on whether SORT_STRINGSORT specified or not */
+  { LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9, CSTR_EQUAL },
+  { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10, CSTR_LESS_THAN }
+};
 
 static void test_CompareStringA(void)
 {
-  int ret;
+  int ret, i;
   LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
 
+  for (i = 0; i < sizeof(comparestringa_data)/sizeof(struct comparestringa_entry); i++)
+  {
+      const struct comparestringa_entry *entry = &comparestringa_data[i];
+
+      ret = CompareStringA(entry->lcid, entry->flags, entry->first, entry->first_len,
+          entry->second, entry->second_len);
+      ok(ret == entry->ret, "%d: got %d, expected %d\n", i, ret, entry->ret);
+  }
+
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
   ok (ret== 1, "(Salut/Salute) Expected 1, got %d\n", ret);
 
@@ -1198,89 +1253,6 @@ static void test_CompareStringA(void)
     ret = lstrcmpA(NULL, "");
     ok (ret == -1 || broken(ret == -2) /* win9x */, "lstrcmpA(NULL, \"\") should return -1, got %d\n", ret);
  
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"EndDialog",-1,"_Property",-1);
-    ok( ret == 3, "EndDialog vs _Property ... expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"osp_vba.sreg0070",-1,"_IEWWBrowserComp",-1);
-    ok( ret == 3, "osp_vba.sreg0070 vs _IEWWBrowserComp ... expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"r",-1,"\\",-1); 
-    ok( ret == 3, "r vs \\ ... expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"osp_vba.sreg0031", -1, "OriginalDatabase", -1 );
-    ok( ret == 3, "osp_vba.sreg0031 vs OriginalDatabase ... expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1 );
-    ok( ret == 3, "AAA vs aaa expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1 );
-    ok( ret == 1, "AAA vs aab expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1 );
-    ok( ret == 1, "AAA vs Aab expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1 );
-    ok( ret == 1, ".AAA vs Aab expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1 );
-    ok( ret == 1, ".AAA vs A.ab expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1 );
-    ok( ret == 1, "aa vs AB expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1 );
-    ok( ret == 1, "aa vs Aab expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1 );
-    ok( ret == 3, "aB vs Aab expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1 );
-    ok( ret == 1, "Ba vs bab expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1 );
-    ok( ret == 1, "{100}{83}{71}{71}{71} vs Global_DataAccess_JRO expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1 );
-    ok( ret == 3, "a vs { expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1 );
-    ok( ret == 3, "A vs { expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1 );
-    ok(ret == 1, "3.5/0 vs 4.0/-1 expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1 );
-    ok(ret == 1, "3.5 vs 4.0 expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1 );
-    ok(ret == 1, "3.520.4403.2 vs 4.0.2927.10 expected 1, got %d\n", ret);
-
-   /* hyphen and apostrophe are treated differently depending on
-    * whether SORT_STRINGSORT specified or not
-    */
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1 );
-    ok(ret == 3, "-o vs /m expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1 );
-    ok(ret == 1, "/m vs -o expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1 );
-    ok(ret == 1, "-o vs /m expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1 );
-    ok(ret == 3, "/m vs -o expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1 );
-    ok(ret == 3, "'o vs /m expected 3, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1 );
-    ok(ret == 1, "/m vs 'o expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1 );
-    ok(ret == 1, "'o vs /m expected 1, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1 );
-    ok(ret == 3, "/m vs 'o expected 3, got %d\n", ret);
 
     if (0) { /* this requires collation table patch to make it MS compatible */
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
@@ -1320,11 +1292,6 @@ static void test_CompareStringA(void)
     ok(ret == 1, "-m vs `o expected 1, got %d\n", ret);
     }
 
-    ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9);
-    ok(ret == 2, "aLuZkUtZ vs aLuZkUtZ\\0 expected 2, got %d\n", ret);
-
-    ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10);
-    ok(ret == 1, "aLuZkUtZ vs aLuZkUtZ\\0A expected 1, got %d\n", ret);
 
     /* WinXP handles embedded NULLs differently than earlier versions */
     ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
@@ -1352,6 +1319,14 @@ static void test_CompareStringA(void)
 
     ret = lstrcmpi("#", ".");
     todo_wine ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret);
+
+    lcid = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT);
+
+    /* \xB9 character lies between a and b */
+    ret = CompareStringA(lcid, 0, "a", 1, "\xB9", 1);
+    todo_wine ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be greater than \'a\'\n");
+    ret = CompareStringA(lcid, 0, "\xB9", 1, "b", 1);
+    ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be smaller than \'b\'\n");
 }
 
 static void test_LCMapStringA(void)
@@ -1675,6 +1650,43 @@ static void test_LCMapStringW(void)
        "unexpected error code %d\n", GetLastError());
 }
 
+static void test_LocaleNames(void)
+{
+    LCID lcid;
+    INT ret;
+    WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
+
+    if (!pLocaleNameToLCID)
+    {
+        win_skip( "LocaleNameToLCID not available\n" );
+        return;
+    }
+
+    /* special cases */
+    buffer[0] = 0;
+    lcid = pLocaleNameToLCID(LOCALE_NAME_USER_DEFAULT, 0);
+    ok(lcid == GetUserDefaultLCID() || broken(GetLastError() == ERROR_INVALID_PARAMETER /* Vista */),
+       "Expected lcid == %08x, got %08x, error %d\n", lcid, GetUserDefaultLCID(), GetLastError());
+    ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
+    ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
+    trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
+
+    buffer[0] = 0;
+    lcid = pLocaleNameToLCID(LOCALE_NAME_SYSTEM_DEFAULT, 0);
+    todo_wine ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
+                 "Expected lcid != 0, got %08x, error %d\n", lcid, GetLastError());
+    ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
+    ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
+    trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
+
+    buffer[0] = 0;
+    lcid = pLocaleNameToLCID(LOCALE_NAME_INVARIANT, 0);
+    todo_wine ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08x, error %d\n", lcid, GetLastError());
+    ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
+    ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
+    trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
+}
+
 /* this requires collation table patch to make it MS compatible */
 static const char * const strings_sorted[] =
 {
@@ -2099,6 +2111,18 @@ static void test_FoldStringW(void)
   {
     'W','i','n','e',0x0348,0x0551,0x1323,0x280d,'W','i','n','e','\0'
   };
+  static const WCHAR foldczone_todo_src[] =
+  {
+      0x3c5,0x308,0x6a,0x30c,0xa0,0xaa,0
+  };
+  static const WCHAR foldczone_todo_dst[] =
+  {
+      0x3cb,0x1f0,' ','a',0
+  };
+  static const WCHAR foldczone_todo_broken_dst[] =
+  {
+      0x3cb,0x1f0,0xa0,0xaa,0
+  };
   static const WCHAR ligatures_src[] =
   {
     'W',    'i',    'n',    'e',    0x03a6, 0x03b9, 0x03bd, 0x03b5,
@@ -2200,7 +2224,7 @@ static void test_FoldStringW(void)
 
       ok(dst[0] == ch || strchrW(outOfSequenceDigits, ch) ||
          /* Wine (correctly) maps all Unicode 4.0+ digits */
-         isdigitW(ch) || (ch >= 0x24F5 && ch <= 0x24FD) || ch == 0x24FF ||
+         isdigitW(ch) || (ch >= 0x24F5 && ch <= 0x24FD) || ch == 0x24FF || ch == 0x19da ||
          (ch >= 0x1369 && ch <= 0x1371),
          "MAP_FOLDDIGITS: ch %d 0x%04x Expected unchanged got %d\n", ch, ch, dst[0]);
     }
@@ -2243,6 +2267,13 @@ static void test_FoldStringW(void)
   ok(!memcmp(dst, foldczone_dst, sizeof(foldczone_dst)),
      "MAP_FOLDCZONE: Expanded incorrectly\n");
 
+  ret = pFoldStringW(MAP_FOLDCZONE|MAP_PRECOMPOSED, foldczone_todo_src, -1, dst, 256);
+  todo_wine ok(ret == sizeof(foldczone_todo_dst)/sizeof(foldczone_todo_dst[0]),
+          "Got %d, error %d\n", ret, GetLastError());
+  todo_wine ok(!memcmp(dst, foldczone_todo_dst, sizeof(foldczone_todo_dst))
+          || broken(!memcmp(dst, foldczone_todo_broken_dst, sizeof(foldczone_todo_broken_dst))),
+          "MAP_FOLDCZONE: Expanded incorrectly (%s)\n", wine_dbgstr_w(dst));
+
   /* MAP_EXPAND_LIGATURES */
   SetLastError(0);
   ret = pFoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
@@ -2346,6 +2377,29 @@ static void test_EnumSystemLanguageGroupsA(void)
   pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
 }
 
+static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam )
+{
+    trace( "%s %x\n", wine_dbgstr_w(name), flags );
+    return TRUE;
+}
+
+static void test_EnumSystemLocalesEx(void)
+{
+    BOOL ret;
+
+    if (!pEnumSystemLocalesEx)
+    {
+        win_skip( "EnumSystemLocalesEx not available\n" );
+        return;
+    }
+    SetLastError( 0xdeadbeef );
+    ret = pEnumSystemLocalesEx( enum_func, LOCALE_ALL, 0, (void *)1 );
+    ok( !ret, "should have failed\n" );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+    SetLastError( 0xdeadbeef );
+    ret = pEnumSystemLocalesEx( enum_func, 0, 0, NULL );
+    ok( ret, "failed err %u\n", GetLastError() );
+}
 
 static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
                                       LONG_PTR lParam)
@@ -2418,13 +2472,13 @@ static void test_SetLocaleInfoA(void)
 
   /* IDATE */
   SetLastError(0);
-  bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, (LPSTR)test_SetLocaleInfoA);
+  bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, "test_SetLocaleInfoA");
   ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
      "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
 
   /* ILDATE */
   SetLastError(0);
-  bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, (LPSTR)test_SetLocaleInfoA);
+  bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, "test_SetLocaleInfoA");
   ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
      "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
 }
@@ -2656,6 +2710,436 @@ static void test_GetCPInfo(void)
     }
 }
 
+/*
+ * The CT_TYPE1 has varied over windows version.
+ * The current target for correct behavior is windows 7.
+ * There was a big shift between windows 2000 (first introduced) and windows Xp
+ * Most of the old values below are from windows 2000.
+ * A smaller subset of changes happened between windows Xp and Window vista/7
+ */
+static void test_GetStringTypeW(void)
+{
+    static const WCHAR blanks[] = {0x9, 0x20, 0xa0, 0x3000, 0xfeff};
+    static const WORD blanks_new[] = {C1_SPACE | C1_CNTRL | C1_BLANK | C1_DEFINED,
+                                 C1_SPACE | C1_BLANK | C1_DEFINED,
+                                 C1_SPACE | C1_BLANK | C1_DEFINED,
+                                 C1_SPACE | C1_BLANK | C1_DEFINED,
+                                 C1_CNTRL | C1_BLANK | C1_DEFINED};
+    static const WORD blanks_old[] ={C1_SPACE | C1_CNTRL | C1_BLANK,
+                                    C1_SPACE | C1_BLANK,
+                                    C1_SPACE | C1_BLANK,
+                                    C1_SPACE | C1_BLANK,
+                                    C1_SPACE | C1_BLANK};
+
+    static const WCHAR undefined[] = {0x378, 0x379, 0x604, 0xfff8, 0xfffe};
+
+                                  /* Lu, Ll, Lt */
+    static const WCHAR alpha[] = {0x47, 0x67, 0x1c5};
+    static const WORD alpha_old[] = {C1_UPPER | C1_ALPHA,
+                                     C1_LOWER | C1_ALPHA,
+                                     C1_UPPER | C1_LOWER | C1_ALPHA,
+                                     C1_ALPHA};
+
+                                  /* Sk, Sk, Mn, So, Me */
+    static const WCHAR oldpunc[] = { 0x2c2, 0x2e5, 0x322, 0x482, 0x6de,
+                                 /* Sc, Sm, No,*/
+                                     0xffe0, 0xffe9, 0x2153};
+
+                                /* Lm, Nl, Cf, 0xad(Cf), 0x1f88 (Lt), Lo, Mc */
+    static const WCHAR changed[] = {0x2b0, 0x2160, 0x600, 0xad, 0x1f88, 0x294, 0x903};
+    static const WORD changed_old[] = { C1_PUNCT, C1_PUNCT, 0, C1_PUNCT, C1_UPPER | C1_ALPHA, C1_ALPHA, C1_PUNCT };
+    static const WORD changed_xp[] = {C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED,
+                                      C1_CNTRL | C1_DEFINED,
+                                      C1_PUNCT | C1_DEFINED,
+                                      C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_LOWER | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED };
+    static const WORD changed_new[] = { C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED,
+                                      C1_CNTRL | C1_DEFINED,
+                                      C1_PUNCT | C1_CNTRL | C1_DEFINED,
+                                      C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED,
+                                      C1_DEFINED
+ };
+                                /* Pc,  Pd, Ps, Pe, Pi, Pf, Po*/
+    static const WCHAR punct[] = { 0x5f, 0x2d, 0x28, 0x29, 0xab, 0xbb, 0x21 };
+
+    static const WCHAR punct_special[] = {0x24, 0x2b, 0x3c, 0x3e, 0x5e, 0x60,
+                                          0x7c, 0x7e, 0xa2, 0xbe, 0xd7, 0xf7};
+    static const WCHAR digit_special[] = {0xb2, 0xb3, 0xb9};
+    static const WCHAR lower_special[] = {0x2071, 0x207f};
+    static const WCHAR cntrl_special[] = {0x070f, 0x200c, 0x200d,
+                  0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
+                  0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
+                  0xfff9, 0xfffa, 0xfffb};
+    static const WCHAR space_special[] = {0x09, 0x0d, 0x85};
+
+    WORD types[20];
+    int i;
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, blanks, 5, types);
+    for (i = 0; i < 5; i++)
+        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]);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, alpha, 3, types);
+    for (i = 0; i < 3; i++)
+        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]));
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, undefined, 5, types);
+    for (i = 0; i < 5; i++)
+        ok(types[i] == 0, "incorrect types returned for %x -> (%x != 0)\n",undefined[i], types[i]);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, oldpunc, 8, types);
+    for (i = 0; i < 8; i++)
+        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);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, changed, 7, types);
+    for (i = 0; i < 7; i++)
+        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]);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, punct, 7, types);
+    for (i = 0; i < 7; i++)
+        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));
+
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, punct_special, 12, types);
+    for (i = 0; i < 12; i++)
+        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);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, digit_special, 3, types);
+    for (i = 0; i < 3; i++)
+        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);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, lower_special, 2, types);
+    for (i = 0; i < 2; i++)
+        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);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, cntrl_special, 20, types);
+    for (i = 0; i < 20; i++)
+        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);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, space_special, 3, types);
+    for (i = 0; i < 3; i++)
+        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 );
+}
+
+static void test_IdnToNameprepUnicode(void)
+{
+    struct {
+        DWORD in_len;
+        const WCHAR in[64];
+        DWORD ret;
+        const WCHAR out[64];
+        DWORD flags;
+        DWORD err;
+        DWORD todo;
+    } test_data[] = {
+        {
+            5, {'t','e','s','t',0},
+            5, {'t','e','s','t',0},
+            0, 0xdeadbeef
+        },
+        {
+            3, {'a',0xe111,'b'},
+            0, {0},
+            0, ERROR_INVALID_NAME
+        },
+        {
+            4, {'t',0,'e',0},
+            0, {0},
+            0, ERROR_INVALID_NAME
+        },
+        {
+            1, {'T',0},
+            1, {'T',0},
+            0, 0xdeadbeef
+        },
+        {
+            1, {0},
+            0, {0},
+            0, ERROR_INVALID_NAME
+        },
+        {
+            6, {' ','-','/','[',']',0},
+            6, {' ','-','/','[',']',0},
+            0, 0xdeadbeef
+        },
+        {
+            3, {'a','-','a'},
+            3, {'a','-','a'},
+            IDN_USE_STD3_ASCII_RULES, 0xdeadbeef
+        },
+        {
+            3, {'a','a','-'},
+            0, {0},
+            IDN_USE_STD3_ASCII_RULES, ERROR_INVALID_NAME
+        },
+        { /* FoldString is not working as expected when MAP_FOLDCZONE is specified (composition+compatibility) */
+            10, {'T',0xdf,0x130,0x143,0x37a,0x6a,0x30c,' ',0xaa,0},
+            12, {'t','s','s','i',0x307,0x144,' ',0x3b9,0x1f0,' ','a',0},
+            0, 0xdeadbeef, TRUE
+        },
+        {
+            11, {'t',0xad,0x34f,0x1806,0x180b,0x180c,0x180d,0x200b,0x200c,0x200d,0},
+            2, {'t',0},
+            0, 0xdeadbeef
+        },
+        { /* Another example of incorrectly working FoldString (composition) */
+            2, {0x3b0, 0},
+            2, {0x3b0, 0},
+            0, 0xdeadbeef, TRUE
+        },
+        {
+            2, {0x221, 0},
+            0, {0},
+            0, ERROR_NO_UNICODE_TRANSLATION
+        },
+        {
+            2, {0x221, 0},
+            2, {0x221, 0},
+            IDN_ALLOW_UNASSIGNED, 0xdeadbeef
+        },
+        {
+            5, {'a','.','.','a',0},
+            0, {0},
+            0, ERROR_INVALID_NAME
+        },
+        {
+            3, {'a','.',0},
+            3, {'a','.',0},
+            0, 0xdeadbeef
+        },
+    };
+
+    WCHAR buf[1024];
+    DWORD i, ret, err;
+
+    if (!pIdnToNameprepUnicode)
+    {
+        win_skip("IdnToNameprepUnicode is not available\n");
+        return;
+    }
+
+    ret = pIdnToNameprepUnicode(0, test_data[0].in,
+            test_data[0].in_len, NULL, 0);
+    ok(ret == test_data[0].ret, "ret = %d\n", ret);
+
+    SetLastError(0xdeadbeef);
+    ret = pIdnToNameprepUnicode(0, test_data[1].in,
+            test_data[1].in_len, NULL, 0);
+    err = GetLastError();
+    ok(ret == test_data[1].ret, "ret = %d\n", ret);
+    ok(err == test_data[1].err, "err = %d\n", err);
+
+    SetLastError(0xdeadbeef);
+    ret = pIdnToNameprepUnicode(0, test_data[0].in, -1,
+            buf, sizeof(buf)/sizeof(WCHAR));
+    err = GetLastError();
+    ok(ret == test_data[0].ret, "ret = %d\n", ret);
+    ok(err == 0xdeadbeef, "err = %d\n", err);
+
+    SetLastError(0xdeadbeef);
+    ret = pIdnToNameprepUnicode(0, test_data[0].in, -2,
+            buf, sizeof(buf)/sizeof(WCHAR));
+    err = GetLastError();
+    ok(ret == 0, "ret = %d\n", ret);
+    ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
+
+    SetLastError(0xdeadbeef);
+    ret = pIdnToNameprepUnicode(0, test_data[0].in, 0,
+            buf, sizeof(buf)/sizeof(WCHAR));
+    err = GetLastError();
+    ok(ret == 0, "ret = %d\n", ret);
+    ok(err == ERROR_INVALID_NAME, "err = %d\n", err);
+
+    ret = pIdnToNameprepUnicode(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES,
+            test_data[0].in, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(ret == test_data[0].ret, "ret = %d\n", ret);
+
+    SetLastError(0xdeadbeef);
+    ret = pIdnToNameprepUnicode(0, NULL, 0, NULL, 0);
+    err = GetLastError();
+    ok(ret == 0, "ret = %d\n", ret);
+    ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
+
+    SetLastError(0xdeadbeef);
+    ret = pIdnToNameprepUnicode(4, NULL, 0, NULL, 0);
+    err = GetLastError();
+    ok(ret == 0, "ret = %d\n", ret);
+    ok(err == ERROR_INVALID_FLAGS, "err = %d\n", err);
+
+    for (i=0; i<sizeof(test_data)/sizeof(*test_data); i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = pIdnToNameprepUnicode(test_data[i].flags, test_data[i].in,
+                test_data[i].in_len, buf, sizeof(buf)/sizeof(WCHAR));
+        err = GetLastError();
+        if(!test_data[i].todo) {
+            ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
+            ok(err == test_data[i].err, "%d) err = %d\n", i, err);
+            ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
+                    "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
+        }else {
+            todo_wine ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
+                    "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
+        }
+    }
+}
+
+static void test_IdnToAscii(void)
+{
+    struct {
+        DWORD in_len;
+        const WCHAR in[64];
+        DWORD ret;
+        const WCHAR out[64];
+        DWORD flags;
+        DWORD err;
+    } test_data[] = {
+        {
+            5, {'T','e','s','t',0},
+            5, {'T','e','s','t',0},
+            0, 0xdeadbeef
+        },
+        {
+            5, {'T','e',0x017c,'s','t',0},
+            12, {'x','n','-','-','t','e','s','t','-','c','b','b',0},
+            0, 0xdeadbeef
+        },
+        {
+            12, {'t','e',0x0105,'s','t','.','t','e',0x017c,'s','t',0},
+            26, {'x','n','-','-','t','e','s','t','-','c','t','a','.','x','n','-','-','t','e','s','t','-','c','b','b',0},
+            0, 0xdeadbeef
+        },
+        {
+            3, {0x0105,'.',0},
+            9, {'x','n','-','-','2','d','a','.',0},
+            0, 0xdeadbeef
+        },
+        {
+            10, {'h','t','t','p',':','/','/','t',0x0106,0},
+            17, {'x','n','-','-','h','t','t','p',':','/','/','t','-','7','8','a',0},
+            0, 0xdeadbeef
+        },
+        {
+            10, {0x4e3a,0x8bf4,0x4e0d,0x4ed6,0x5011,0x10d,0x11b,0x305c,0x306a,0},
+            35, {'x','n','-','-','b','e','a','2','a','1','6','3','1','a','v','b','a',
+                'v','4','4','t','y','h','a','3','2','b','9','1','e','g','s','2','t',0},
+            0, 0xdeadbeef
+        },
+        {
+            2, {0x221,0},
+            8, {'x','n','-','-','6','l','a',0},
+            IDN_ALLOW_UNASSIGNED, 0xdeadbeef
+        },
+    };
+
+    WCHAR buf[1024];
+    DWORD i, ret, err;
+
+    if (!pIdnToAscii)
+    {
+        win_skip("IdnToAscii is not available\n");
+        return;
+    }
+
+    for (i=0; i<sizeof(test_data)/sizeof(*test_data); i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = pIdnToAscii(test_data[i].flags, test_data[i].in,
+                test_data[i].in_len, buf, sizeof(buf));
+        err = GetLastError();
+        ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
+        ok(err == test_data[i].err, "%d) err = %d\n", i, err);
+        ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
+                "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
+    }
+}
+
+static void test_IdnToUnicode(void)
+{
+    struct {
+        DWORD in_len;
+        const WCHAR in[64];
+        DWORD ret;
+        const WCHAR out[64];
+        DWORD flags;
+        DWORD err;
+    } test_data[] = {
+        {
+            5, {'T','e','s','.',0},
+            5, {'T','e','s','.',0},
+            0, 0xdeadbeef
+        },
+        {
+            2, {0x105,0},
+            0, {0},
+            0, ERROR_INVALID_NAME
+        },
+        {
+            33, {'x','n','-','-','4','d','b','c','a','g','d','a','h','y','m','b',
+                'x','e','k','h','e','h','6','e','0','a','7','f','e','i','0','b',0},
+            23, {0x05dc,0x05de,0x05d4,0x05d4,0x05dd,0x05e4,0x05e9,0x05d5,0x05d8,
+                0x05dc,0x05d0,0x05de,0x05d3,0x05d1,0x05e8,0x05d9,0x05dd,0x05e2,
+                0x05d1,0x05e8,0x05d9,0x05ea,0},
+            0, 0xdeadbeef
+        },
+        {
+            34, {'t','e','s','t','.','x','n','-','-','k','d','a','9','a','g','5','e',
+                '9','j','n','f','s','j','.','x','n','-','-','p','d','-','f','n','a'},
+            16, {'t','e','s','t','.',0x0105,0x0119,0x015b,0x0107,
+                0x0142,0x00f3,0x017c,'.','p',0x0119,'d'},
+            0, 0xdeadbeef
+        },
+        {
+            64, {'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+                'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+                'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+                'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a'},
+            0, {0},
+            0, ERROR_INVALID_NAME
+        },
+        {
+            8, {'x','n','-','-','6','l','a',0},
+            2, {0x221,0},
+            IDN_ALLOW_UNASSIGNED, 0xdeadbeef
+        },
+    };
+
+    WCHAR buf[1024];
+    DWORD i, ret, err;
+
+    if (!pIdnToUnicode)
+    {
+        win_skip("IdnToUnicode is not available\n");
+        return;
+    }
+
+    for (i=0; i<sizeof(test_data)/sizeof(*test_data); i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = pIdnToUnicode(test_data[i].flags, test_data[i].in,
+                test_data[i].in_len, buf, sizeof(buf));
+        err = GetLastError();
+        ok(ret == test_data[i].ret, "%d) ret = %d\n", i, ret);
+        ok(err == test_data[i].err, "%d) err = %d\n", i, err);
+        ok(!memcmp(test_data[i].out, buf, ret*sizeof(WCHAR)),
+                "%d) buf = %s\n", i, wine_dbgstr_wn(buf, ret));
+    }
+}
+
 START_TEST(locale)
 {
   InitFunctionPointers();
@@ -2672,14 +3156,20 @@ START_TEST(locale)
   test_CompareStringA();
   test_LCMapStringA();
   test_LCMapStringW();
+  test_LocaleNames();
   test_FoldStringA();
   test_FoldStringW();
   test_ConvertDefaultLocale();
   test_EnumSystemLanguageGroupsA();
+  test_EnumSystemLocalesEx();
   test_EnumLanguageGroupLocalesA();
   test_SetLocaleInfoA();
   test_EnumUILanguageA();
   test_GetCPInfo();
+  test_GetStringTypeW();
+  test_IdnToNameprepUnicode();
+  test_IdnToAscii();
+  test_IdnToUnicode();
   /* this requires collation table patch to make it MS compatible */
   if (0) test_sorting();
 }