[MSVCRT_WINETEST] Sync with Wine Staging 4.0. CORE-15682
authorAmine Khaldi <amine.khaldi@reactos.org>
Tue, 29 Jan 2019 12:08:13 +0000 (13:08 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Tue, 29 Jan 2019 12:08:13 +0000 (13:08 +0100)
modules/rostests/winetests/msvcrt/cpp.c
modules/rostests/winetests/msvcrt/dir.c
modules/rostests/winetests/msvcrt/file.c
modules/rostests/winetests/msvcrt/locale.c
modules/rostests/winetests/msvcrt/misc.c
modules/rostests/winetests/msvcrt/printf.c
modules/rostests/winetests/msvcrt/scanf.c
modules/rostests/winetests/msvcrt/string.c

index cf5e4c7..07b49ae 100644 (file)
@@ -1108,8 +1108,8 @@ static void test_demangle_datatype(void)
        { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$01@@@", "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$01@@@", FALSE},
 /*     { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$011@@@", "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$011@@@",FALSE}, */
     };
-    int i, num_test = (sizeof(demangle)/sizeof(struct _demangle));
-    
+    int i, num_test = ARRAY_SIZE(demangle);
+
     for (i = 0; i < num_test; i++)
     {
        name = p__unDName(0, demangle[i].mangled, 0, pmalloc, pfree, 0x2800);
@@ -1326,7 +1326,7 @@ static void test_demangle(void)
 /* 130 */ {"??_E?$TStrArray@$$BY0BAA@D$0BA@@@UAEPAXI@Z",
            "public: virtual void * __thiscall TStrArray<char [256],16>::`vector deleting destructor'(unsigned int)"},
     };
-    int i, num_test = (sizeof(test)/sizeof(test[0]));
+    int i, num_test = ARRAY_SIZE(test);
     char* name;
 
     for (i = 0; i < num_test; i++)
index a29d32b..210cf39 100644 (file)
@@ -97,7 +97,7 @@ static void test_makepath(void)
 
     unsigned int i, n;
 
-    for (i = 0; i < sizeof(makepath_cases)/sizeof(makepath_cases[0]); ++i)
+    for (i = 0; i < ARRAY_SIZE(makepath_cases); ++i)
     {
         const makepath_case* p = &makepath_cases[i];
 
@@ -253,7 +253,7 @@ static void test_makepath_s(void)
     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
 
     /* Test with the normal _makepath cases. */
-    for (i = 0; i < sizeof(makepath_cases)/sizeof(makepath_cases[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(makepath_cases); i++)
     {
         const makepath_case *p = makepath_cases + i;
 
@@ -296,7 +296,7 @@ static void test_makepath_s(void)
     }
 
     /* Try insufficient length cases. */
-    for (i = 0; i < sizeof(makepath_s_cases)/sizeof(makepath_s_cases[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(makepath_s_cases); i++)
     {
         const makepath_s_case *p = makepath_s_cases + i;
 
@@ -457,12 +457,12 @@ static void test_searchenv(void)
     ok(path_len, "GetTempPath failed\n");
     memcpy(path, tmppath, path_len);
 
-    for(i=0; i<sizeof(dirs)/sizeof(*dirs); i++) {
+    for (i=0; i<ARRAY_SIZE(dirs); i++) {
         strcpy(path+path_len, dirs[i]);
        ok(!mkdir(path), "mkdir failed (dir = %s)\n", path);
     }
 
-    for(i=0; i<sizeof(files)/sizeof(*files); i++) {
+    for (i=0; i<ARRAY_SIZE(files); i++) {
         strcpy(path+path_len, files[i]);
         tmp_file = fopen(path, "wb");
        ok(tmp_file != NULL, "fopen failed (file = %s)\n", path);
@@ -471,7 +471,7 @@ static void test_searchenv(void)
 
     strcpy(env1, "TEST_PATH=");
     strcpy(env2, "TEST_PATH=;");
-    for(i=1; i<sizeof(dirs)/sizeof(*dirs); i++) {
+    for (i=1; i<ARRAY_SIZE(dirs); i++) {
         strcat(env1, tmppath);
         strcat(env1, dirs[i]);
         strcat(env1, ";");
@@ -599,12 +599,12 @@ static void test_searchenv(void)
 
     putenv("TEST_PATH=");
 
-    for(i=sizeof(files)/sizeof(*files)-1; i>=0; i--) {
+    for (i=ARRAY_SIZE(files)-1; i>=0; i--) {
         strcpy(path+path_len, files[i]);
         ok(!remove(path), "remove failed (file = %s)\n", path);
     }
 
-    for(i=sizeof(dirs)/sizeof(*dirs)-1; i>=0; i--) {
+    for (i=ARRAY_SIZE(dirs)-1; i>=0; i--) {
         strcpy(path+path_len, dirs[i]);
         ok(!rmdir(path), "rmdir failed (dir = %s)\n", path);
     }
index 136ee15..fe62d5c 100644 (file)
@@ -144,7 +144,7 @@ static void test_fileops( void )
     write (fd, outbuffer, sizeof (outbuffer));
     close (fd);
 
-    for (bufmode=0; bufmode < sizeof(bufmodes)/sizeof(bufmodes[0]); bufmode++)
+    for (bufmode=0; bufmode < ARRAY_SIZE(bufmodes); bufmode++)
     {
         fd = open ("fdopen.tst", O_RDONLY | O_BINARY);
         file = fdopen (fd, "rb");
@@ -195,13 +195,13 @@ static void test_fileops( void )
     }
     fd = open ("fdopen.tst", O_RDONLY | O_TEXT);
     file = fdopen (fd, "rt"); /* open in TEXT mode */
-    ok(fgetws(wbuffer,sizeof(wbuffer)/sizeof(wbuffer[0]),file) !=0,"fgetws failed unexpected\n");
-    ok(fgetws(wbuffer,sizeof(wbuffer)/sizeof(wbuffer[0]),file) ==0,"fgetws didn't signal EOF\n");
+    ok(fgetws(wbuffer,ARRAY_SIZE(wbuffer),file) !=0,"fgetws failed unexpected\n");
+    ok(fgetws(wbuffer,ARRAY_SIZE(wbuffer),file) ==0,"fgetws didn't signal EOF\n");
     ok(feof(file) !=0,"feof doesn't signal EOF\n");
     rewind(file);
     ok(fgetws(wbuffer,strlen(outbuffer),file) !=0,"fgetws failed unexpected\n");
     ok(lstrlenW(wbuffer) == (lstrlenA(outbuffer) -1),"fgetws didn't read right size\n");
-    ok(fgetws(wbuffer,sizeof(outbuffer)/sizeof(outbuffer[0]),file) !=0,"fgets failed unexpected\n");
+    ok(fgetws(wbuffer,ARRAY_SIZE(outbuffer),file) !=0,"fgets failed unexpected\n");
     ok(lstrlenW(wbuffer) == 1,"fgets dropped chars\n");
     fclose (file);
 
@@ -624,7 +624,7 @@ static void test_flsbuf( void )
   static const int bufmodes[] = {_IOFBF,_IONBF};
 
   tempf=_tempnam(".","wne");
-  for (bufmode=0; bufmode < sizeof(bufmodes)/sizeof(bufmodes[0]); bufmode++)
+  for (bufmode=0; bufmode < ARRAY_SIZE(bufmodes); bufmode++)
   {
     tempfh = fopen(tempf,"wb");
     setvbuf(tempfh,NULL,bufmodes[bufmode],2048);
@@ -878,8 +878,7 @@ static void test_fgetwc_locale(const char* text, const char* locale, int codepag
     {
         /* mbstowcs rejects invalid multibyte sequence,
            so we use MultiByteToWideChar here. */
-        ret = MultiByteToWideChar(codepage, 0, text, -1,
-                                  wtextW, sizeof(wtextW)/sizeof(wtextW[0]));
+        ret = MultiByteToWideChar(codepage, 0, text, -1, wtextW, ARRAY_SIZE(wtextW));
         ok(ret > 0, "MultiByteToWideChar failed\n");
     }
     else
@@ -910,7 +909,7 @@ static void test_fgetwc_locale(const char* text, const char* locale, int codepag
 
     tempfh = fopen(tempfile, "rb");
     ok(tempfh != NULL, "can't open tempfile\n");
-    for (i = 0; i < sizeof(wchar_text)/sizeof(wchar_text[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(wchar_text); i++)
     {
         ch = fgetwc(tempfh);
         ok(ch == wchar_text[i], "got %04hx, expected %04x (cp%d[%d])\n", ch, wchar_text[i], codepage, i);
@@ -946,7 +945,7 @@ static void test_fgetwc_unicode(void)
 
     tempfh = fopen(tempfile, "rt,ccs=unicode");
     ok(tempfh != NULL, "can't open tempfile\n");
-    for (i = 1; i < sizeof(wchar_text)/sizeof(wchar_text[0]); i++)
+    for (i = 1; i < ARRAY_SIZE(wchar_text); i++)
     {
         ch = fgetwc(tempfh);
         ok(ch == wchar_text[i],
@@ -958,7 +957,7 @@ static void test_fgetwc_unicode(void)
 
     tempfh = fopen(tempfile, "wb");
     ok(tempfh != NULL, "can't open tempfile\n");
-    ret = WideCharToMultiByte(CP_UTF8, 0, wchar_text, sizeof(wchar_text)/sizeof(wchar_text[0]),
+    ret = WideCharToMultiByte(CP_UTF8, 0, wchar_text, ARRAY_SIZE(wchar_text),
                               utf8_text, sizeof(utf8_text), NULL, NULL);
     ok(ret > 0, "utf-8 conversion failed\n");
     fwrite(utf8_text, sizeof(char), ret, tempfh);
@@ -966,7 +965,7 @@ static void test_fgetwc_unicode(void)
 
     tempfh = fopen(tempfile, "rt, ccs=UTF-8");
     ok(tempfh != NULL, "can't open tempfile\n");
-    for (i = 1; i < sizeof(wchar_text)/sizeof(wchar_text[0]); i++)
+    for (i = 1; i < ARRAY_SIZE(wchar_text); i++)
     {
         ch = fgetwc(tempfh);
         ok(ch == wchar_text[i],
@@ -2659,5 +2658,5 @@ START_TEST(file)
     /* Wait for the (_P_NOWAIT) spawned processes to finish to make sure the report
      * file contains lines in the correct order
      */
-    WaitForMultipleObjects(sizeof(proc_handles)/sizeof(proc_handles[0]), proc_handles, TRUE, 5000);
+    WaitForMultipleObjects(ARRAY_SIZE(proc_handles), proc_handles, TRUE, 5000);
 }
index f87a791..ff77759 100644 (file)
@@ -144,12 +144,10 @@ static void test_setlocale(void)
         || broken(!strcmp(ret, "Chinese_Taiwan.950")), "ret = %s\n", ret);
 
     ret = setlocale(LC_ALL, "Chinese_China.936");
-todo_wine
     ok(ret != NULL || broken (ret == NULL), "ret == NULL\n");
     if(ret)
     {
         trace("Chinese_China.936=%s\n", ret);
-todo_wine
         ok(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936") /* Vista - Win7 */
         || !strcmp(ret, "Chinese (Simplified)_China.936") /* Win8 - Win10 */
         || broken(!strcmp(ret, "Chinese_People's Republic of China.936")), "ret = %s\n", ret);
@@ -158,12 +156,14 @@ todo_wine
     ret = setlocale(LC_ALL, "csy");
     ok(ret != NULL || broken (ret == NULL), "ret == NULL\n");
     if(ret)
-        ok(!strcmp(ret, "Czech_Czech Republic.1250"), "ret = %s\n", ret);
+        ok(!strcmp(ret, "Czech_Czech Republic.1250")
+        || !strcmp(ret, "Czech_Czechia.1250"), "ret = %s\n", ret);
 
     ret = setlocale(LC_ALL, "czech");
     ok(ret != NULL || broken (ret == NULL), "ret == NULL\n");
     if(ret)
-        ok(!strcmp(ret, "Czech_Czech Republic.1250"), "ret = %s\n", ret);
+        ok(!strcmp(ret, "Czech_Czech Republic.1250")
+        || !strcmp(ret, "Czech_Czechia.1250"), "ret = %s\n", ret);
 
     ret = setlocale(LC_ALL, "dan");
     ok(ret != NULL || broken (ret == NULL), "ret == NULL\n");
@@ -614,6 +614,9 @@ todo_wine
 
     ret = setlocale(LC_ALL, "English_United States.UTF8");
     ok(ret == NULL, "ret != NULL\n");
+
+    ret = setlocale(LC_ALL, "en-US");
+    ok(ret == NULL || broken (ret != NULL), "ret != NULL\n"); /* XP & 2003 */
 }
 
 static void test_crtGetStringTypeW(void)
@@ -640,7 +643,7 @@ static void test_crtGetStringTypeW(void)
         return;
     }
 
-    for(i=0; i<sizeof(str)/sizeof(*str); i++) {
+    for(i=0; i<ARRAY_SIZE(str); i++) {
         ret_crt = p__crtGetStringTypeW(0, CT_CTYPE1, str[i], 1, &out_crt);
         ret = GetStringTypeW(CT_CTYPE1, str[i], 1, &out);
         ok(ret == ret_crt, "%d) ret_crt = %d\n", i, (int)ret_crt);
@@ -705,7 +708,7 @@ static void test__Gettnames(void)
     else
         ok(size==0x164 || broken(size==0xb8), "structure size: %x\n", size);
 
-    for (i = 0; i < sizeof(time_data)/sizeof(time_data[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(time_data); i++)
     {
         size = GetLocaleInfoA(MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
                               time_data[i], buf, sizeof(buf));
@@ -719,7 +722,7 @@ static void test__Gettnames(void)
         return;
 
     ret = _Gettnames();
-    for (i = 0; i < sizeof(time_data)/sizeof(time_data[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(time_data); i++)
     {
         size = GetLocaleInfoA(MAKELCID(LANG_GERMAN, SORT_DEFAULT),
                               time_data[i], buf, sizeof(buf));
index 2025334..4a0330a 100644 (file)
@@ -170,7 +170,7 @@ static void test_I10_OUTPUT(void)
     if (j != 12)
         trace("sizeof(long double) = %d on this machine\n", j);
 
-    for(i=0; i<sizeof(I10_OUTPUT_tests)/sizeof(I10_OUTPUT_test); i++) {
+    for(i=0; i<ARRAY_SIZE(I10_OUTPUT_tests); i++) {
         memset(out.str, '#', sizeof(out.str));
 
         if (sizeof(long double) == 12)
@@ -627,7 +627,7 @@ static void test__lfind_s(void)
     }
 
     key = 1234;
-    num = sizeof(tests)/sizeof(tests[0]);
+    num = ARRAY_SIZE(tests);
 
     errno = 0xdeadbeef;
     found = p_lfind_s(NULL, tests, &num, sizeof(int), _lfind_s_comp, NULL);
index 64658ce..80180ff 100644 (file)
@@ -841,7 +841,7 @@ static void test_snprintf (void)
     const int bufsiz = sizeof buffer;
     unsigned int i;
 
-    for (i = 0; i < sizeof tests / sizeof tests[0]; i++) {
+    for (i = 0; i < ARRAY_SIZE(tests); i++) {
         const char *fmt  = tests[i].format;
         const int expect = tests[i].expected;
         const int n      = _snprintf (buffer, bufsiz, fmt);
@@ -1215,7 +1215,7 @@ static void test_vsnwprintf(void)
     wchar_t str[32];
     char buf[32];
 
-    ret = _vsnwprintf_wrapper( str, sizeof(str)/sizeof(str[0]), format, one, two, three );
+    ret = _vsnwprintf_wrapper( str, ARRAY_SIZE(str), format, one, two, three );
 
     ok( ret == 11, "got %d expected 11\n", ret );
     WideCharToMultiByte( CP_ACP, 0, str, -1, buf, sizeof(buf), NULL, NULL );
index 359b079..b724483 100644 (file)
@@ -282,10 +282,17 @@ static void test_sscanf_s(void)
     ok(ret == 0, "Wrong number of arguments read: %d\n", ret);
     ok(buf[0]=='\0', "buf = %s\n", buf);
 
-    buf[0] = 'a';
+    memset(buf, 'a', sizeof(buf));
     ret = psscanf_s("123", "%3c", buf, 2);
     ok(ret == 0, "Wrong number of arguments read: %d\n", ret);
     ok(buf[0]=='\0', "buf = %s\n", buf);
+    ok(buf[1]=='2', "buf[1] = %d\n", buf[1]);
+    ok(buf[2]=='a', "buf[2] = %d\n", buf[2]);
+
+    buf[3] = 'a';
+    buf[4] = 0;
+    ret = psscanf_s("123", "%3c", buf, 3);
+    ok(!strcmp("123a", buf), "buf = %s\n", buf);
 
     i = 1;
     ret = psscanf_s("123 123", "%s %d", buf, 2, &i);
index b270366..e80a518 100644 (file)
@@ -89,6 +89,12 @@ static errno_t (__cdecl *p_mbslwr_s)(unsigned char *str, size_t numberOfElements
 static int (__cdecl *p_wctob)(wint_t);
 static size_t (__cdecl *p_wcrtomb)(char*, wchar_t, mbstate_t*);
 static int (__cdecl *p_tolower)(int);
+static int (__cdecl *p_towlower)(wint_t);
+static int (__cdecl *p__towlower_l)(wint_t, _locale_t);
+static int (__cdecl *p_towupper)(wint_t);
+static int (__cdecl *p__towupper_l)(wint_t, _locale_t);
+static _locale_t(__cdecl *p__create_locale)(int, const char*);
+static void(__cdecl *p__free_locale)(_locale_t);
 static size_t (__cdecl *p_mbrlen)(const char*, size_t, mbstate_t*);
 static size_t (__cdecl *p_mbrtowc)(wchar_t*, const char*, size_t, mbstate_t*);
 static int (__cdecl *p__atodbl_l)(_CRT_DOUBLE*,char*,_locale_t);
@@ -100,6 +106,7 @@ static size_t (__cdecl *p__mbsnlen)(const unsigned char*, size_t);
 static int (__cdecl *p__mbccpy_s)(unsigned char*, size_t, int*, const unsigned char*);
 static int (__cdecl *p__memicmp)(const char*, const char*, size_t);
 static int (__cdecl *p__memicmp_l)(const char*, const char*, size_t, _locale_t);
+static size_t (__cdecl *p___strncnt)(const char*, size_t);
 
 #define SETNOFAIL(x,y) x = (void*)GetProcAddress(hMsvcrt,y)
 #define SET(x,y) SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y)
@@ -607,8 +614,6 @@ static void test_strcpy_s(void)
             dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
 }
 
-#define NUMELMS(array) (sizeof(array)/sizeof((array)[0]))
-
 #define okchars(dst, b0, b1, b2, b3, b4, b5, b6, b7) \
     ok(dst[0] == b0 && dst[1] == b1 && dst[2] == b2 && dst[3] == b3 && \
        dst[4] == b4 && dst[5] == b5 && dst[6] == b6 && dst[7] == b7, \
@@ -617,7 +622,7 @@ static void test_strcpy_s(void)
 
 static void test_memcpy_s(void)
 {
-    static char dest[8];
+    static char dest[8], buf[32];
     static const char tiny[] = {'T',0,'I','N','Y',0};
     static const char big[] = {'a','t','o','o','l','o','n','g','s','t','r','i','n','g',0};
     int ret;
@@ -628,14 +633,14 @@ static void test_memcpy_s(void)
 
     /* Normal */
     memset(dest, 'X', sizeof(dest));
-    ret = p_memcpy_s(dest, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ret = p_memcpy_s(dest, ARRAY_SIZE(dest), tiny, ARRAY_SIZE(tiny));
     ok(ret == 0, "Copying a buffer into a big enough destination returned %d, expected 0\n", ret);
     okchars(dest, tiny[0], tiny[1], tiny[2], tiny[3], tiny[4], tiny[5], 'X', 'X');
 
     /* Vary source size */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memcpy_s(dest, NUMELMS(dest), big, NUMELMS(big));
+    ret = p_memcpy_s(dest, ARRAY_SIZE(dest), big, ARRAY_SIZE(big));
     ok(ret == ERANGE, "Copying a big buffer to a small destination returned %d, expected ERANGE\n", ret);
     ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
     okchars(dest, 0, 0, 0, 0, 0, 0, 0, 0);
@@ -643,7 +648,7 @@ static void test_memcpy_s(void)
     /* Replace source with NULL */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memcpy_s(dest, NUMELMS(dest), NULL, NUMELMS(tiny));
+    ret = p_memcpy_s(dest, ARRAY_SIZE(dest), NULL, ARRAY_SIZE(tiny));
     ok(ret == EINVAL, "Copying a NULL source buffer returned %d, expected EINVAL\n", ret);
     ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
     okchars(dest, 0, 0, 0, 0, 0, 0, 0, 0);
@@ -651,24 +656,36 @@ static void test_memcpy_s(void)
     /* Vary dest size */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memcpy_s(dest, 0, tiny, NUMELMS(tiny));
+    ret = p_memcpy_s(dest, 0, tiny, ARRAY_SIZE(tiny));
     ok(ret == ERANGE, "Copying into a destination of size 0 returned %d, expected ERANGE\n", ret);
     ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
     okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
 
     /* Replace dest with NULL */
     errno = 0xdeadbeef;
-    ret = p_memcpy_s(NULL, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ret = p_memcpy_s(NULL, ARRAY_SIZE(dest), tiny, ARRAY_SIZE(tiny));
     ok(ret == EINVAL, "Copying a tiny buffer to a big NULL destination returned %d, expected EINVAL\n", ret);
     ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
 
     /* Combinations */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memcpy_s(dest, 0, NULL, NUMELMS(tiny));
+    ret = p_memcpy_s(dest, 0, NULL, ARRAY_SIZE(tiny));
     ok(ret == EINVAL, "Copying a NULL buffer into a destination of size 0 returned %d, expected EINVAL\n", ret);
     ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
     okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
+
+    ret = p_memcpy_s(buf, ARRAY_SIZE(buf), big, ARRAY_SIZE(big));
+    ok(!ret, "memcpy_s returned %d\n", ret);
+    ok(!memcmp(buf, big, sizeof(big)), "unexpected buf\n");
+
+    ret = p_memcpy_s(buf + 1, ARRAY_SIZE(buf) - 1, buf, ARRAY_SIZE(big));
+    ok(!ret, "memcpy_s returned %d\n", ret);
+    ok(!memcmp(buf + 1, big, sizeof(big)), "unexpected buf\n");
+
+    ret = p_memcpy_s(buf, ARRAY_SIZE(buf), buf + 1, ARRAY_SIZE(big));
+    ok(!ret, "memcpy_s returned %d\n", ret);
+    ok(!memcmp(buf, big, sizeof(big)), "unexpected buf\n");
 }
 
 static void test_memmove_s(void)
@@ -684,20 +701,20 @@ static void test_memmove_s(void)
 
     /* Normal */
     memset(dest, 'X', sizeof(dest));
-    ret = p_memmove_s(dest, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ret = p_memmove_s(dest, ARRAY_SIZE(dest), tiny, ARRAY_SIZE(tiny));
     ok(ret == 0, "Moving a buffer into a big enough destination returned %d, expected 0\n", ret);
     okchars(dest, tiny[0], tiny[1], tiny[2], tiny[3], tiny[4], tiny[5], 'X', 'X');
 
     /* Overlapping */
     memcpy(dest, big, sizeof(dest));
-    ret = p_memmove_s(dest+1, NUMELMS(dest)-1, dest, NUMELMS(dest)-1);
+    ret = p_memmove_s(dest+1, ARRAY_SIZE(dest)-1, dest, ARRAY_SIZE(dest)-1);
     ok(ret == 0, "Moving a buffer up one char returned %d, expected 0\n", ret);
     okchars(dest, big[0], big[0], big[1], big[2], big[3], big[4], big[5], big[6]);
 
     /* Vary source size */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memmove_s(dest, NUMELMS(dest), big, NUMELMS(big));
+    ret = p_memmove_s(dest, ARRAY_SIZE(dest), big, ARRAY_SIZE(big));
     ok(ret == ERANGE, "Moving a big buffer to a small destination returned %d, expected ERANGE\n", ret);
     ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
     okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
@@ -705,7 +722,7 @@ static void test_memmove_s(void)
     /* Replace source with NULL */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memmove_s(dest, NUMELMS(dest), NULL, NUMELMS(tiny));
+    ret = p_memmove_s(dest, ARRAY_SIZE(dest), NULL, ARRAY_SIZE(tiny));
     ok(ret == EINVAL, "Moving a NULL source buffer returned %d, expected EINVAL\n", ret);
     ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
     okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
@@ -713,21 +730,21 @@ static void test_memmove_s(void)
     /* Vary dest size */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memmove_s(dest, 0, tiny, NUMELMS(tiny));
+    ret = p_memmove_s(dest, 0, tiny, ARRAY_SIZE(tiny));
     ok(ret == ERANGE, "Moving into a destination of size 0 returned %d, expected ERANGE\n", ret);
     ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
     okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
 
     /* Replace dest with NULL */
     errno = 0xdeadbeef;
-    ret = p_memmove_s(NULL, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ret = p_memmove_s(NULL, ARRAY_SIZE(dest), tiny, ARRAY_SIZE(tiny));
     ok(ret == EINVAL, "Moving a tiny buffer to a big NULL destination returned %d, expected EINVAL\n", ret);
     ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
 
     /* Combinations */
     errno = 0xdeadbeef;
     memset(dest, 'X', sizeof(dest));
-    ret = p_memmove_s(dest, 0, NULL, NUMELMS(tiny));
+    ret = p_memmove_s(dest, 0, NULL, ARRAY_SIZE(tiny));
     ok(ret == EINVAL, "Moving a NULL buffer into a destination of size 0 returned %d, expected EINVAL\n", ret);
     ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
     okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
@@ -1016,7 +1033,7 @@ static void test_wcscpy_s(void)
         return;
     }
 
-    ret = p_wcsncpy_s(NULL, 18, szLongText, sizeof(szLongText)/sizeof(WCHAR));
+    ret = p_wcsncpy_s(NULL, 18, szLongText, ARRAY_SIZE(szLongText));
     ok(ret == EINVAL, "p_wcsncpy_s expect EINVAL got %d\n", ret);
 
     szDest[0] = 'A';
@@ -1030,16 +1047,16 @@ static void test_wcscpy_s(void)
     ok(szDest[0] == 0, "szDest[0] not 0\n");
 
     szDest[0] = 'A';
-    ret = p_wcsncpy_s(szDest, 0, szLongText, sizeof(szLongText)/sizeof(WCHAR));
+    ret = p_wcsncpy_s(szDest, 0, szLongText, ARRAY_SIZE(szLongText));
     ok(ret == ERANGE || ret == EINVAL, "expected ERANGE/EINVAL got %d\n", ret);
     ok(szDest[0] == 0 || ret == EINVAL, "szDest[0] not 0\n");
 
-    ret = p_wcsncpy_s(szDest, 18, szLongText, sizeof(szLongText)/sizeof(WCHAR));
+    ret = p_wcsncpy_s(szDest, 18, szLongText, ARRAY_SIZE(szLongText));
     ok(ret == 0, "expected 0 got %d\n", ret);
     ok(lstrcmpW(szDest, szLongText) == 0, "szDest != szLongText\n");
 
     szDest[0] = 'A';
-    ret = p_wcsncpy_s(szDestShort, 8, szLongText, sizeof(szLongText)/sizeof(WCHAR));
+    ret = p_wcsncpy_s(szDestShort, 8, szLongText, ARRAY_SIZE(szLongText));
     ok(ret == ERANGE || ret == EINVAL, "expected ERANGE/EINVAL got %d\n", ret);
     ok(szDestShort[0] == 0, "szDestShort[0] not 0\n");
 
@@ -1067,7 +1084,7 @@ static void test__wcsupr_s(void)
     static const WCHAR expectedString[] = {'M', 'I', 'X', 'E', 'D', 'L', 'O',
                                            'W', 'E', 'R', 'U', 'P', 'P', 'E',
                                            'R', 0};
-    WCHAR testBuffer[2*sizeof(mixedString)/sizeof(WCHAR)];
+    WCHAR testBuffer[2*ARRAY_SIZE(mixedString)];
     int ret;
 
     if (!p_wcsupr_s)
@@ -1084,7 +1101,7 @@ static void test__wcsupr_s(void)
 
     /* Test NULL input string and valid size. */
     errno = EBADF;
-    ret = p_wcsupr_s(NULL, sizeof(testBuffer)/sizeof(WCHAR));
+    ret = p_wcsupr_s(NULL, ARRAY_SIZE(testBuffer));
     ok(ret == EINVAL, "Expected _wcsupr_s to fail with EINVAL, got %d\n", ret);
     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
 
@@ -1128,21 +1145,21 @@ static void test__wcsupr_s(void)
 
     /* Test normal string uppercasing. */
     wcscpy(testBuffer, mixedString);
-    ret = p_wcsupr_s(testBuffer, sizeof(mixedString)/sizeof(WCHAR));
+    ret = p_wcsupr_s(testBuffer, ARRAY_SIZE(mixedString));
     ok(ret == 0, "Expected _wcsupr_s to succeed, got %d\n", ret);
     ok(!wcscmp(testBuffer, expectedString), "Expected the string to be fully upper-case\n");
 
     /* Test uppercasing with a shorter buffer size count. */
     wcscpy(testBuffer, mixedString);
     errno = EBADF;
-    ret = p_wcsupr_s(testBuffer, sizeof(mixedString)/sizeof(WCHAR) - 1);
+    ret = p_wcsupr_s(testBuffer, ARRAY_SIZE(mixedString) - 1);
     ok(ret == EINVAL, "Expected _wcsupr_s to fail with EINVAL, got %d\n", ret);
     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
     ok(testBuffer[0] == '\0', "Expected the first buffer character to be null\n");
 
     /* Test uppercasing with a longer buffer size count. */
     wcscpy(testBuffer, mixedString);
-    ret = p_wcsupr_s(testBuffer, sizeof(testBuffer)/sizeof(WCHAR));
+    ret = p_wcsupr_s(testBuffer, ARRAY_SIZE(testBuffer));
     ok(ret == 0, "Expected _wcsupr_s to succeed, got %d\n", ret);
     ok(!wcscmp(testBuffer, expectedString), "Expected the string to be fully upper-case\n");
 }
@@ -1154,7 +1171,7 @@ static void test__wcslwr_s(void)
     static const WCHAR expectedString[] = {'m', 'i', 'x', 'e', 'd', 'l', 'o',
                                            'w', 'e', 'r', 'u', 'p', 'p', 'e',
                                            'r', 0};
-    WCHAR buffer[2*sizeof(mixedString)/sizeof(WCHAR)];
+    WCHAR buffer[2*ARRAY_SIZE(mixedString)];
     int ret;
 
     if (!p_wcslwr_s)
@@ -1171,7 +1188,7 @@ static void test__wcslwr_s(void)
 
     /* Test NULL input string and valid size. */
     errno = EBADF;
-    ret = p_wcslwr_s(NULL, sizeof(buffer)/sizeof(buffer[0]));
+    ret = p_wcslwr_s(NULL, ARRAY_SIZE(buffer));
     ok(ret == EINVAL, "expected EINVAL, got %d\n", ret);
     ok(errno == EINVAL, "expected errno EINVAL, got %d\n", errno);
 
@@ -1215,21 +1232,21 @@ static void test__wcslwr_s(void)
 
     /* Test normal string uppercasing. */
     wcscpy(buffer, mixedString);
-    ret = p_wcslwr_s(buffer, sizeof(mixedString)/sizeof(WCHAR));
+    ret = p_wcslwr_s(buffer, ARRAY_SIZE(mixedString));
     ok(ret == 0, "expected 0, got %d\n", ret);
     ok(!wcscmp(buffer, expectedString), "expected lowercase\n");
 
     /* Test uppercasing with a shorter buffer size count. */
     wcscpy(buffer, mixedString);
     errno = EBADF;
-    ret = p_wcslwr_s(buffer, sizeof(mixedString)/sizeof(WCHAR) - 1);
+    ret = p_wcslwr_s(buffer, ARRAY_SIZE(mixedString) - 1);
     ok(ret == EINVAL, "expected EINVAL, got %d\n", ret);
     ok(errno == EINVAL, "expected errno to be EINVAL, got %d\n", errno);
     ok(buffer[0] == '\0', "expected empty string\n");
 
     /* Test uppercasing with a longer buffer size count. */
     wcscpy(buffer, mixedString);
-    ret = p_wcslwr_s(buffer, sizeof(buffer)/sizeof(WCHAR));
+    ret = p_wcslwr_s(buffer, ARRAY_SIZE(buffer));
     ok(ret == 0, "expected 0, got %d\n", ret);
     ok(!wcscmp(buffer, expectedString), "expected lowercase\n");
 }
@@ -1245,7 +1262,7 @@ static void test_mbcjisjms(void)
     unsigned int i, j;
     int prev_cp = _getmbcp();
 
-    for (i = 0; i < sizeof(cp)/sizeof(cp[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(cp); i++)
     {
         _setmbcp(cp[i]);
         for (j = 0; jisjms[j][0] != 0; j++)
@@ -1273,7 +1290,7 @@ static void test_mbcjmsjis(void)
     unsigned int i, j;
     int prev_cp = _getmbcp();
 
-    for (i = 0; i < sizeof(cp)/sizeof(cp[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(cp); i++)
     {
         _setmbcp(cp[i]);
         for (j = 0; jmsjis[j][0] != 0; j++)
@@ -1300,7 +1317,7 @@ static void test_mbctohira(void)
     unsigned int prev_cp = _getmbcp();
 
     _setmbcp(_MB_CP_SBCS);
-    for (i = 0; i < sizeof(mbchira_932)/sizeof(mbchira_932[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(mbchira_932); i++)
     {
         int ret, exp = mbchira_932[i][0];
         ret = _mbctohira(mbchira_932[i][0]);
@@ -1308,7 +1325,7 @@ static void test_mbctohira(void)
     }
 
     _setmbcp(932);
-    for (i = 0; i < sizeof(mbchira_932)/sizeof(mbchira_932[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(mbchira_932); i++)
     {
         unsigned int ret, exp;
         ret = _mbctohira(mbchira_932[i][0]);
@@ -1329,7 +1346,7 @@ static void test_mbctokata(void)
     unsigned int prev_cp = _getmbcp();
 
     _setmbcp(_MB_CP_SBCS);
-    for (i = 0; i < sizeof(mbckata_932)/sizeof(mbckata_932[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(mbckata_932); i++)
     {
         int ret, exp = mbckata_932[i][0];
         ret = _mbctokata(mbckata_932[i][0]);
@@ -1337,7 +1354,7 @@ static void test_mbctokata(void)
     }
 
     _setmbcp(932);
-    for (i = 0; i < sizeof(mbckata_932)/sizeof(mbckata_932[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(mbckata_932); i++)
     {
         unsigned int ret, exp;
         ret = _mbctokata(mbckata_932[i][0]);
@@ -1358,7 +1375,7 @@ static void test_mbbtombc(void)
     int i, j;
     int prev_cp = _getmbcp();
 
-    for (i = 0; i < sizeof(cp)/sizeof(cp[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(cp); i++)
     {
         _setmbcp(cp[i]);
         for (j = 0; mbbmbc[j][0] != 0; j++)
@@ -1411,13 +1428,13 @@ static void test_ismbckata(void) {
     unsigned int i;
 
     _setmbcp(_MB_CP_SBCS);
-    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
+    for (i = 0; i < ARRAY_SIZE(tests); i++) {
         ret = _ismbckata(tests[i].c);
         ok(!ret, "expected 0, got %d for %04x\n", ret, tests[i].c);
     }
 
     _setmbcp(932);
-    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
+    for (i = 0; i < ARRAY_SIZE(tests); i++) {
         ret = _ismbckata(tests[i].c);
         ok(!!ret == tests[i].exp, "expected %d, got %d for %04x\n",
            tests[i].exp, !!ret, tests[i].c);
@@ -2995,7 +3012,7 @@ static void test__wcstoi64(void)
     ok(ures == 071, "ures != 071\n");
 
     /* Test various unicode digits */
-    for (i = 0; i < sizeof(zeros) / sizeof(zeros[0]); ++i) {
+    for (i = 0; i < ARRAY_SIZE(zeros); ++i) {
         WCHAR tmp[] = {zeros[i] + 4, zeros[i], zeros[i] + 5, 0};
         res = p_wcstoi64(tmp, NULL, 0);
         ok(res == 405, "with zero = U+%04X: got %d, expected 405\n", zeros[i], (int)res);
@@ -3346,6 +3363,319 @@ static void test__memicmp_l(void)
     ok(errno == 0xdeadbeef, "errno is %d, expected 0xdeadbeef\n", errno);
 }
 
+static void test__strupr(void)
+{
+    const char str[] = "123";
+    char str2[4];
+    char *mem, *p;
+    DWORD prot;
+
+    mem = VirtualAlloc(NULL, sizeof(str), MEM_COMMIT, PAGE_READWRITE);
+    ok(mem != NULL, "VirtualAlloc failed\n");
+    memcpy(mem, str, sizeof(str));
+    ok(VirtualProtect(mem, sizeof(str), PAGE_READONLY, &prot), "VirtualProtect failed\n");
+
+    strcpy(str2, "aBc");
+    p = _strupr(str2);
+    ok(p == str2, "_strupr returned %p\n", p);
+    ok(!strcmp(str2, "ABC"), "str2 = %s\n", str2);
+
+    p = _strupr(mem);
+    ok(p == mem, "_strupr returned %p\n", p);
+    ok(!strcmp(mem, "123"), "mem = %s\n", mem);
+
+    if(!setlocale(LC_ALL, "english")) {
+        VirtualFree(mem, sizeof(str), MEM_RELEASE);
+        win_skip("English locale _strupr tests\n");
+        return;
+    }
+
+    strcpy(str2, "aBc");
+    p = _strupr(str2);
+    ok(p == str2, "_strupr returned %p\n", p);
+    ok(!strcmp(str2, "ABC"), "str2 = %s\n", str2);
+
+    if (0) /* crashes on Windows */
+    {
+        p = _strupr(mem);
+        ok(p == mem, "_strupr returned %p\n", p);
+        ok(!strcmp(mem, "123"), "mem = %s\n", mem);
+    }
+
+    setlocale(LC_ALL, "C");
+    VirtualFree(mem, sizeof(str), MEM_RELEASE);
+}
+
+static void test__tcsncoll(void)
+{
+    struct test {
+        const char *locale;
+        const char *str1;
+        const char *str2;
+        size_t count;
+        int exp;
+    };
+    static const struct test tests[] = {
+        { "English", "ABCD", "ABCD",  4,  0 },
+        { "English", "ABCD", "ABCD", 10,  0 },
+
+        { "English", "ABC",  "ABCD",  3,  0 },
+        { "English", "ABC",  "ABCD",  4, -1 },
+        { "English", "ABC",  "ABCD", 10, -1 },
+
+        { "English", "ABCD",  "ABC",  3,  0 },
+        { "English", "ABCD",  "ABC",  4,  1 },
+        { "English", "ABCD",  "ABC", 10,  1 },
+
+        { "English", "ABCe", "ABCf",  3,  0 },
+        { "English", "abcd", "ABCD", 10, -1 },
+
+        { "C",       "ABCD", "ABCD",  4,  0 },
+        { "C",       "ABCD", "ABCD", 10,  0 },
+
+        { "C",       "ABC",  "ABCD",  3,  0 },
+        { "C",       "ABC",  "ABCD", 10, -1 },
+
+        { "C",       "ABCD",  "ABC",  3,  0 },
+        { "C",       "ABCD",  "ABC", 10,  1 },
+
+        { "C",       "ABCe", "ABCf",  3,  0 },
+        { "C",       "abcd", "ABCD", 10,  1 },
+    };
+    WCHAR str1W[16];
+    WCHAR str2W[16];
+    char str1[16];
+    char str2[16];
+    size_t len;
+    int i, ret;
+
+    for (i = 0; i < ARRAY_SIZE(tests); i++)
+    {
+        if (!setlocale(LC_ALL, tests[i].locale))
+        {
+            win_skip("%s locale _tcsncoll tests\n", tests[i].locale);
+            for (; i+1 < ARRAY_SIZE(tests); i++)
+                if (strcmp(tests[i].locale, tests[i+1].locale)) break;
+            continue;
+        }
+
+        memset(str1, 0xee, sizeof(str1));
+        strcpy(str1, tests[i].str1);
+
+        memset(str2, 0xff, sizeof(str2));
+        strcpy(str2, tests[i].str2);
+
+        ret = _strncoll(str1, str2, tests[i].count);
+        if (!tests[i].exp)
+            ok(!ret, "expected 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else if (tests[i].exp < 0)
+            ok(ret < 0, "expected < 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else
+            ok(ret > 0, "expected > 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+
+        memset(str1W, 0xee, sizeof(str1W));
+        len = mbstowcs(str1W, str1, ARRAY_SIZE(str1W));
+        str1W[len] = 0;
+
+        memset(str2W, 0xff, sizeof(str2W));
+        len = mbstowcs(str2W, str2, ARRAY_SIZE(str2W));
+        str2W[len] = 0;
+
+        ret = _wcsncoll(str1W, str2W, tests[i].count);
+        if (!tests[i].exp)
+            ok(!ret, "expected 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else if (tests[i].exp < 0)
+            ok(ret < 0, "expected < 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else
+            ok(ret > 0, "expected > 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+    }
+}
+
+static void test__tcsnicoll(void)
+{
+    struct test {
+        const char *locale;
+        const char *str1;
+        const char *str2;
+        size_t count;
+        int exp;
+    };
+    static const struct test tests[] = {
+        { "English", "abcd", "ABCD",  4,  0 },
+        { "English", "abcd", "ABCD", 10,  0 },
+
+        { "English", "abc",  "ABCD",  3,  0 },
+        { "English", "abc",  "ABCD",  4, -1 },
+        { "English", "abc",  "ABCD", 10, -1 },
+
+        { "English", "abcd",  "ABC",  3,  0 },
+        { "English", "abcd",  "ABC",  4,  1 },
+        { "English", "abcd",  "ABC", 10,  1 },
+
+        { "English", "abcE", "ABCF",  3,  0 },
+
+        { "C",       "abcd", "ABCD",  4,  0 },
+        { "C",       "abcd", "ABCD", 10,  0 },
+
+        { "C",       "abc",  "ABCD",  3,  0 },
+        { "C",       "abc",  "ABCD", 10, -1 },
+
+        { "C",       "abcd",  "ABC",  3,  0 },
+        { "C",       "abcd",  "ABC", 10,  1 },
+
+        { "C",       "abce", "ABCf",  3,  0 },
+    };
+    WCHAR str1W[16];
+    WCHAR str2W[16];
+    char str1[16];
+    char str2[16];
+    size_t len;
+    int i, ret;
+
+    for (i = 0; i < ARRAY_SIZE(tests); i++)
+    {
+        if (!setlocale(LC_ALL, tests[i].locale))
+        {
+            win_skip("%s locale _tcsnicoll tests\n", tests[i].locale);
+            for (; i+1 < ARRAY_SIZE(tests); i++)
+                if (strcmp(tests[i].locale, tests[i+1].locale)) break;
+            continue;
+        }
+
+        memset(str1, 0xee, sizeof(str1));
+        strcpy(str1, tests[i].str1);
+
+        memset(str2, 0xff, sizeof(str2));
+        strcpy(str2, tests[i].str2);
+
+        ret = _strnicoll(str1, str2, tests[i].count);
+        if (!tests[i].exp)
+            ok(!ret, "expected 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else if (tests[i].exp < 0)
+            ok(ret < 0, "expected < 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else
+            ok(ret > 0, "expected > 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+
+        memset(str1W, 0xee, sizeof(str1W));
+        len = mbstowcs(str1W, str1, ARRAY_SIZE(str1W));
+        str1W[len] = 0;
+
+        memset(str2W, 0xff, sizeof(str2W));
+        len = mbstowcs(str2W, str2, ARRAY_SIZE(str2W));
+        str2W[len] = 0;
+
+        ret = _wcsnicoll(str1W, str2W, tests[i].count);
+        if (!tests[i].exp)
+            ok(!ret, "expected 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else if (tests[i].exp < 0)
+            ok(ret < 0, "expected < 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+        else
+            ok(ret > 0, "expected > 0, got %d for %s, %s, %d\n", ret, str1, str2, (int)tests[i].count);
+    }
+}
+
+static void test___strncnt(void)
+{
+    static const struct
+    {
+        const char *str;
+        size_t size;
+        size_t ret;
+    }
+    strncnt_tests[] =
+    {
+        { NULL, 0, 0 },
+        { "a", 0, 0 },
+        { "a", 1, 1 },
+        { "a", 10, 1 },
+        { "abc", 1, 1 },
+    };
+    unsigned int i;
+    size_t ret;
+
+    if (!p___strncnt)
+    {
+        win_skip("__strncnt() is not available.\n");
+        return;
+    }
+
+    if (0) /* crashes */
+        ret = p___strncnt(NULL, 1);
+
+    for (i = 0; i < ARRAY_SIZE(strncnt_tests); ++i)
+    {
+        ret = p___strncnt(strncnt_tests[i].str, strncnt_tests[i].size);
+        ok(ret == strncnt_tests[i].ret, "%u: unexpected return value %u.\n", i, (int)ret);
+    }
+}
+
+static void test_C_locale(void)
+{
+    int i, j;
+    wint_t ret, exp;
+    _locale_t locale;
+    static const char *locales[] = { NULL, "C" };
+
+    /* C locale only converts case for [a-zA-Z] */
+    setlocale(LC_ALL, "C");
+    for (i = 0; i <= 0xffff; i++)
+    {
+        ret = p_towlower(i);
+        if (i >= 'A' && i <= 'Z')
+        {
+            exp = i + 'a' - 'A';
+            ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
+        }
+        else
+        todo_wine_if(ret != i)
+            ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
+
+        ret = p_towupper(i);
+        if (i >= 'a' && i <= 'z')
+        {
+            exp = i + 'A' - 'a';
+            ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
+        }
+        else
+        todo_wine_if(ret != i)
+            ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
+    }
+
+    if (!p__towlower_l || !p__towupper_l || !p__create_locale)
+    {
+        win_skip("_towlower_l/_towupper_l/_create_locale not available\n");
+        return;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(locales); i++) {
+        locale = locales[i] ? p__create_locale(LC_ALL, locales[i]) : NULL;
+
+        for (j = 0; j <= 0xffff; j++) {
+            ret = p__towlower_l(j, locale);
+            if (j >= 'A' && j <= 'Z')
+            {
+                exp = j + 'a' - 'A';
+                ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
+            }
+            else
+            todo_wine_if(ret != j)
+                ok(ret == j, "expected self %x, got %x for C locale\n", j, ret);
+
+            ret = p__towupper_l(j, locale);
+            if (j >= 'a' && j <= 'z')
+            {
+                exp = j + 'A' - 'a';
+                ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
+            }
+            else
+            todo_wine_if(ret != j)
+                ok(ret == j, "expected self %x, got %x for C locale\n", j, ret);
+        }
+
+        p__free_locale(locale);
+    }
+}
+
 START_TEST(string)
 {
     char mem[100];
@@ -3391,6 +3721,12 @@ START_TEST(string)
     p_wctob = (void*)GetProcAddress(hMsvcrt, "wctob");
     p_wcrtomb = (void*)GetProcAddress(hMsvcrt, "wcrtomb");
     p_tolower = (void*)GetProcAddress(hMsvcrt, "tolower");
+    p_towlower = (void*)GetProcAddress(hMsvcrt, "towlower");
+    p__towlower_l = (void*)GetProcAddress(hMsvcrt, "_towlower_l");
+    p_towupper = (void*)GetProcAddress(hMsvcrt, "towupper");
+    p__towupper_l = (void*)GetProcAddress(hMsvcrt, "_towupper_l");
+    p__create_locale = (void*)GetProcAddress(hMsvcrt, "_create_locale");
+    p__free_locale = (void*)GetProcAddress(hMsvcrt, "_free_locale");
     p_mbrlen = (void*)GetProcAddress(hMsvcrt, "mbrlen");
     p_mbrtowc = (void*)GetProcAddress(hMsvcrt, "mbrtowc");
     p_mbsrtowcs = (void*)GetProcAddress(hMsvcrt, "mbsrtowcs");
@@ -3404,6 +3740,7 @@ START_TEST(string)
     p__mbccpy_s = (void*)GetProcAddress(hMsvcrt, "_mbccpy_s");
     p__memicmp = (void*)GetProcAddress(hMsvcrt, "_memicmp");
     p__memicmp_l = (void*)GetProcAddress(hMsvcrt, "_memicmp_l");
+    p___strncnt = (void*)GetProcAddress(hMsvcrt, "__strncnt");
 
     /* MSVCRT memcpy behaves like memmove for overlapping moves,
        MFC42 CString::Insert seems to rely on that behaviour */
@@ -3468,4 +3805,9 @@ START_TEST(string)
     test__ismbclx();
     test__memicmp();
     test__memicmp_l();
+    test__strupr();
+    test__tcsncoll();
+    test__tcsnicoll();
+    test___strncnt();
+    test_C_locale();
 }