From a3147ab6d9e736d748412e5bc80b03fd9d5c50b0 Mon Sep 17 00:00:00 2001 From: Stanislav Motylkov Date: Fri, 14 Nov 2025 19:08:22 +0300 Subject: [PATCH] [CRT_APITEST] Add _mbsn(b)cat tests based on Wine 10.0 (#8460) These tests examine msvcrt, crtdll, and our static CRT. - msvcrt/crtdll verified passing on Windows 2003, 7 and 10. References: - https://github.com/wine-mirror/wine/blob/9250ecc5a6a64c73aada0ea751815412f7f00410/dlls/msvcrt/tests/string.c#L2773 - https://github.com/reactos/reactos/blob/7f1075986f6484cbe1f1e33cb563bf2f908ba3a7/modules/rostests/winetests/msvcrt/string.c#L2789 CORE-19308 --- modules/rostests/apitests/crt/_mbsnbcat.c | 22 +++ modules/rostests/apitests/crt/_mbsncat.c | 156 ++++++++++++++++++ .../apitests/crt/static_crt_apitest.cmake | 2 + modules/rostests/apitests/crt/testlist.c | 4 + .../rostests/apitests/crtdll/CMakeLists.txt | 2 + modules/rostests/apitests/crtdll/testlist.c | 4 + .../rostests/apitests/msvcrt/CMakeLists.txt | 2 + modules/rostests/apitests/msvcrt/testlist.c | 4 + 8 files changed, 196 insertions(+) create mode 100644 modules/rostests/apitests/crt/_mbsnbcat.c create mode 100644 modules/rostests/apitests/crt/_mbsncat.c diff --git a/modules/rostests/apitests/crt/_mbsnbcat.c b/modules/rostests/apitests/crt/_mbsnbcat.c new file mode 100644 index 00000000000..29d80bfd7c4 --- /dev/null +++ b/modules/rostests/apitests/crt/_mbsnbcat.c @@ -0,0 +1,22 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Tests for _mbsnbcat + * COPYRIGHT: Copyright 2025 Stanislav Motylkov + */ + +#include +#include +#define WIN32_NO_STATUS + +typedef unsigned char *(__cdecl *PFN__mbsncat)(unsigned char*, const unsigned char*, size_t); + +extern VOID mbsncat_PerformTests(_In_ LPSTR fname, _In_opt_ PFN__mbsncat func); + +START_TEST(_mbsnbcat) +{ +#ifndef TEST_STATIC_CRT + #define _mbsnbcat NULL +#endif + mbsncat_PerformTests("_mbsnbcat", _mbsnbcat); +} diff --git a/modules/rostests/apitests/crt/_mbsncat.c b/modules/rostests/apitests/crt/_mbsncat.c new file mode 100644 index 00000000000..c8561dfc2df --- /dev/null +++ b/modules/rostests/apitests/crt/_mbsncat.c @@ -0,0 +1,156 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Tests for _mbsncat + * COPYRIGHT: Copyright 2025 Doug Lyons + * Copyright 2025 Stanislav Motylkov + */ + +#include +#include +#define WIN32_NO_STATUS +#include +#include + +typedef unsigned char *(__cdecl *PFN__mbsncat)(unsigned char*, const unsigned char*, size_t); + +#ifndef TEST_STATIC_CRT +static PFN__mbsncat Init(_In_ LPSTR fname) +{ + static PFN__mbsncat p__mbsncat; + HMODULE hdll = LoadLibraryA(TEST_DLL_NAME); + + p__mbsncat = (PFN__mbsncat)GetProcAddress(hdll, fname); + ok(p__mbsncat != NULL, "Failed to load %s from %s\n", fname, TEST_DLL_NAME); + return p__mbsncat; +} +#endif + +static USHORT GetWinVersion(VOID) +{ + return ((GetVersion() & 0xFF) << 8) | + ((GetVersion() >> 8) & 0xFF); +} + +VOID mbsncat_PerformTests(_In_ LPSTR fname, _In_opt_ PFN__mbsncat func) +{ + unsigned char dest[16]; + const unsigned char first[] = "dinosaur"; + const unsigned char second[] = "duck"; + unsigned char *s; + BOOL MsVcrt = FALSE, CrtDll = FALSE; + +#ifdef TEST_MSVCRT + MsVcrt = TRUE; +#endif +#ifdef TEST_CRTDLL + CrtDll = TRUE; +#endif + +#ifndef TEST_STATIC_CRT + if (!func) + func = Init(fname); +#endif + if (!func) + { + skip("Skipping tests, because %s is not available\n", fname); + return; + } + + /* Test invalid arguments */ + StartSeh() + s = func(NULL, NULL, 0); + EndSeh(STATUS_SUCCESS); + ok(s == NULL, "Expected %s to return NULL, got %p\n", fname, s); + + StartSeh() + s = func(NULL, NULL, 10); + EndSeh((CrtDll || (MsVcrt && GetWinVersion() <= 0x502)) ? STATUS_ACCESS_VIOLATION : STATUS_SUCCESS); + ok(s == NULL, "Expected %s to return NULL, got %p\n", fname, s); + + memset(dest, 'X', sizeof(dest)); + StartSeh() + s = func(dest, NULL, 0); + EndSeh(STATUS_SUCCESS); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(dest[0] == 'X', "Expected the output buffer to be untouched\n"); + + memset(dest, 'X', sizeof(dest)); + StartSeh() + s = func(dest, second, 0); + EndSeh(STATUS_SUCCESS); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(dest[0] == 'X', "Expected the output buffer to be untouched\n"); + + memset(dest, 'X', sizeof(dest)); + s = NULL; + StartSeh() + s = func(dest, NULL, 10); + EndSeh((CrtDll || (MsVcrt && GetWinVersion() <= 0x502)) ? STATUS_ACCESS_VIOLATION : STATUS_SUCCESS); + ok(s == NULL, "Expected %s to return NULL, got %p\n", fname, s); + ok(dest[0] == 'X', "Expected the output buffer to be untouched\n"); + + memset(dest, 'X', sizeof(dest)); + dest[0] = '\0'; + StartSeh() + s = func(dest, second, sizeof(second)); + EndSeh(STATUS_SUCCESS); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(!memcmp(dest, second, sizeof(second)), + "Expected the output buffer string to be \"duck\", got '%s'\n", dest); + + /* Test source truncation behavior */ + memset(dest, 'X', sizeof(dest)); + memcpy(dest, first, sizeof(first)); + s = func(dest, second, 0); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(!memcmp(dest, first, sizeof(first)), + "Expected the output buffer string to be \"dinosaur\", got '%s'\n", dest); + + memset(dest, 'X', sizeof(dest)); + memcpy(dest, first, sizeof(first)); + s = func(dest, second, sizeof(second)); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(!memcmp(dest, "dinosaurduck", sizeof("dinosaurduck")), + "Expected the output buffer string to be \"dinosaurduck\", got '%s'\n", dest); + + memset(dest, 'X', sizeof(dest)); + memcpy(dest, first, sizeof(first)); + s = func(dest, second, sizeof(second) + 1); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(!memcmp(dest, "dinosaurduck", sizeof("dinosaurduck")), + "Expected the output buffer string to be \"dinosaurduck\", got '%s'\n", dest); + + memset(dest, 'X', sizeof(dest)); + memcpy(dest, first, sizeof(first)); + s = func(dest, second, sizeof(second) - 1); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(!memcmp(dest, "dinosaurduck", sizeof("dinosaurduck")), + "Expected the output buffer string to be \"dinosaurduck\", got '%s'\n", dest); + + memset(dest, 'X', sizeof(dest)); + memcpy(dest, first, sizeof(first)); + s = func(dest, second, sizeof(second) - 2); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(!memcmp(dest, "dinosaurduc", sizeof("dinosaurduc")), + "Expected the output buffer string to be \"dinosaurduc\", got '%s'\n", dest); + + /* Test typical scenario */ + memset(dest, 'X', sizeof(dest)); + memcpy(dest, first, sizeof(first)); + s = func(dest, second, sizeof(second) - 1); + ok(s == dest, "Expected %s to return dest pointer, got %p\n", fname, s); + ok(!memcmp(dest, "dinosaurduck", sizeof("dinosaurduck")), + "Expected the output buffer string to be \"dinosaurduck\", got '%s'\n", dest); + + /* TODO: Add some distinguishing tests (_mbsncat vs. _mbsnbcat) for concatenating chars vs. bytes, + * should probably be done with actual multibyte-character strings. */ +} + +START_TEST(_mbsncat) +{ +#ifndef TEST_STATIC_CRT + #define _mbsncat NULL +#endif + mbsncat_PerformTests("_mbsncat", _mbsncat); +} diff --git a/modules/rostests/apitests/crt/static_crt_apitest.cmake b/modules/rostests/apitests/crt/static_crt_apitest.cmake index 6bd8bf2fe0a..ae8b9f4c112 100644 --- a/modules/rostests/apitests/crt/static_crt_apitest.cmake +++ b/modules/rostests/apitests/crt/static_crt_apitest.cmake @@ -1,5 +1,7 @@ list(APPEND SOURCE_STATIC + _mbsnbcat.c + _mbsncat.c _snprintf.c _snwprintf.c _vscprintf.c diff --git a/modules/rostests/apitests/crt/testlist.c b/modules/rostests/apitests/crt/testlist.c index 38953e9d8ab..d7ad583d5da 100644 --- a/modules/rostests/apitests/crt/testlist.c +++ b/modules/rostests/apitests/crt/testlist.c @@ -2,6 +2,8 @@ #define STANDALONE #include +extern void func__mbsnbcat(void); +extern void func__mbsncat(void); extern void func__mbsncmp(void); extern void func__mbsstr(void); #if defined(_M_ARM) @@ -44,6 +46,8 @@ const struct test winetest_testlist[] = { { "_vsnprintf", func__vsnprintf }, { "_vsnwprintf", func__vsnwprintf }, + { "_mbsnbcat", func__mbsnbcat }, + { "_mbsncat", func__mbsncat }, { "mbstowcs", func_mbstowcs }, { "mbtowc", func_mbtowc }, { "setjmp", func_setjmp }, diff --git a/modules/rostests/apitests/crtdll/CMakeLists.txt b/modules/rostests/apitests/crtdll/CMakeLists.txt index 4a4a012c374..d39ad400536 100644 --- a/modules/rostests/apitests/crtdll/CMakeLists.txt +++ b/modules/rostests/apitests/crtdll/CMakeLists.txt @@ -1,5 +1,7 @@ list(APPEND SOURCE_CRTDLL + ../crt/_mbsnbcat.c + ../crt/_mbsncat.c ../crt/_mbsncmp.c ../crt/_mbsstr.c ../crt/setjmp.c diff --git a/modules/rostests/apitests/crtdll/testlist.c b/modules/rostests/apitests/crtdll/testlist.c index 53eaab58e40..adda27c91f0 100644 --- a/modules/rostests/apitests/crtdll/testlist.c +++ b/modules/rostests/apitests/crtdll/testlist.c @@ -2,6 +2,8 @@ #define STANDALONE #include +extern void func__mbsnbcat(void); +extern void func__mbsncat(void); extern void func__mbsncmp(void); extern void func__mbsstr(void); extern void func__snprintf(void); @@ -22,6 +24,8 @@ extern void func_wctomb(void); const struct test winetest_testlist[] = { + { "_mbsnbcat", func__mbsnbcat }, + { "_mbsncat", func__mbsncat }, { "_mbsncmp", func__mbsncmp }, { "_mbsstr", func__mbsstr }, { "_snprintf", func__snprintf }, diff --git a/modules/rostests/apitests/msvcrt/CMakeLists.txt b/modules/rostests/apitests/msvcrt/CMakeLists.txt index e40bc6c6127..3beb9e250f2 100644 --- a/modules/rostests/apitests/msvcrt/CMakeLists.txt +++ b/modules/rostests/apitests/msvcrt/CMakeLists.txt @@ -10,6 +10,8 @@ list(APPEND SOURCE_CRT_TESTS ../crt/fpcontrol.c ../crt/_finite.c ../crt/_isnan.c + ../crt/_mbsnbcat.c + ../crt/_mbsncat.c ../crt/_mbsncmp.c ../crt/_mbsstr.c ../crt/_snprintf.c diff --git a/modules/rostests/apitests/msvcrt/testlist.c b/modules/rostests/apitests/msvcrt/testlist.c index 63fba2d54c3..c20176c6a20 100644 --- a/modules/rostests/apitests/msvcrt/testlist.c +++ b/modules/rostests/apitests/msvcrt/testlist.c @@ -4,6 +4,8 @@ extern void func__finite(void); extern void func__isnan(void); +extern void func__mbsnbcat(void); +extern void func__mbsncat(void); extern void func__mbsncmp(void); extern void func__mbsstr(void); extern void func__snprintf(void); @@ -60,6 +62,8 @@ const struct test winetest_testlist[] = { { "_finite", func__finite }, { "_isnan", func__isnan }, + { "_mbsnbcat", func__mbsnbcat }, + { "_mbsncat", func__mbsncat }, { "_mbsncmp", func__mbsncmp }, { "_mbsstr", func__mbsstr }, { "_snprintf", func__snprintf },