diff --git a/modules/rostests/apitests/user32/CMakeLists.txt b/modules/rostests/apitests/user32/CMakeLists.txt index ef2e7c1715b..1eaf276a825 100644 --- a/modules/rostests/apitests/user32/CMakeLists.txt +++ b/modules/rostests/apitests/user32/CMakeLists.txt @@ -2,6 +2,7 @@ list(APPEND SOURCE AttachThreadInput.c ../include/msgtrace.c + CharFuncs.c CloseWindow.c CreateDialog.c CreateIconFromResourceEx.c diff --git a/modules/rostests/apitests/user32/CharFuncs.c b/modules/rostests/apitests/user32/CharFuncs.c new file mode 100644 index 00000000000..8e0b49aafbb --- /dev/null +++ b/modules/rostests/apitests/user32/CharFuncs.c @@ -0,0 +1,817 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for Char* functions + * COPYRIGHT: Copyright 2022 Stanislav Motylkov + */ + +#include "precomp.h" + +#include +#include +#include +#include +#include + +#define INVALID_PTR_OFF(x) ((PVOID)(ULONG_PTR)(0xdeadbeefdeadbeefULL + x)) +#define INVALID_PTR INVALID_PTR_OFF(0) + +/* Default code page to be tested */ +#define TEST_ACP 1252 + +typedef enum +{ + testLen, + testOffs, + testBoth, +} TEST_TYPE; + +/* Dynamic allocation tests */ +typedef struct +{ + TEST_TYPE testType; + LPWSTR lpszStart; /* Specified string for szStart */ + LPWSTR lpszCurrent; /* Specified string for szCurrent (only when testType == testBoth) */ + INT iOffset; /* Specified offset to test (only when testType == testOffs) */ + INT iResOffset; /* Expected offset when szCurrent >= szStart */ + INT iResOffsetNeg; /* Expected offset when szCurrent < szStart */ + BOOL bWithinStart; /* TRUE for pointer expected to be within szStart, FALSE for within szCurrent */ + BOOL bWideOnly; /* Perform test only for Unicode case */ +} TESTS_CHARPREV; + +TESTS_CHARPREV TestCharPrev[] = +{ + {testLen, L"C:\\ReactOS", NULL, 0, 9, 9, TRUE, FALSE}, + {testOffs, L"abcdefghijk", NULL, 11, 10, 10, TRUE, FALSE}, + {testOffs, L"test a´^~¯", NULL, 10, 9, 9, TRUE, FALSE}, + {testOffs, L"test å", NULL, 6, 5, 5, TRUE, FALSE}, + {testBoth, L"C:\\ReactOS", L"", 0, -1, 0, FALSE, FALSE}, + {testBoth, L"C:\\ReactOS\\", L"C:\\ReactOS", 0, -1, 0, FALSE, FALSE}, + {testBoth, L"C:\\ReactOS\\", L"ReactOS", 0, -1, 0, FALSE, FALSE}, +}; + +TESTS_CHARPREV TestCharPrev_XP[] = +{ + /* XP/2003 treat diacritics as normal characters */ + {testOffs, L"test a\x030a", NULL, 7, 6, 6, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 10, 9, 9, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 9, 8, 8, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 8, 7, 7, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 7, 6, 6, TRUE, TRUE}, +}; + +TESTS_CHARPREV TestCharPrev_Vista[] = +{ + /* Vista+ does respect diacritics and skip them */ + {testOffs, L"test a\x030a", NULL, 7, 5, 5, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 10, 5, 5, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 9, 5, 5, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 8, 5, 5, TRUE, TRUE}, + {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 7, 5, 5, TRUE, TRUE}, +}; + +/* Static tests */ +static const WCHAR wszReactOS[] = L"C:\\ReactOS"; +static const CHAR szReactOS[] = "C:\\ReactOS"; +static const WCHAR wszSpecial[] = L"test\0\0\0\0\0\0aa\t\t\t\r\n\r\n"; +static const CHAR szSpecial[] = "test\0\0\0\0\0\0aa\t\t\t\r\n\r\n"; +static const WCHAR wszMagic1[] = L"test a\x030a"; +static const WCHAR wszMagic2[] = L"test a\x0301\x0302\x0303\x0304"; + +static const CHAR szUTF8Cyril[] = "test \xD1\x82\xD0\xB5\xD1\x81\xD1\x82"; /* UTF8(L"test тест") */ +static const CHAR szUTF8Greek[] = "test \xCF\x84\xCE\xB5\xCF\x83\xCF\x84"; /* UTF8(L"test τεστ") */ +static const CHAR szUTF8Japan[] = "test \xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88"; /* UTF8(L"test テスト") */ +static const CHAR szCP932Japan[] = "test \x83\x65\x83\x58\x83\x67"; /* CP932(L"test テスト") */ + +typedef struct +{ + LPCWSTR wszStart; + LPCWSTR wszCurrent; + LPCWSTR wszResult; + LPCSTR szStart; + LPCSTR szCurrent; + LPCSTR szResult; +} ST_TESTS_CHARPREV; + +ST_TESTS_CHARPREV TestStaticCharPrev[] = +{ + {wszReactOS, wszReactOS, wszReactOS, + szReactOS, szReactOS, szReactOS}, + {wszReactOS, wszReactOS + 1, wszReactOS, + szReactOS, szReactOS + 1, szReactOS}, + {wszReactOS, wszReactOS + 2, wszReactOS + 1, + szReactOS, szReactOS + 2, szReactOS + 1}, + {wszReactOS, wszReactOS + 3, wszReactOS + 2, + szReactOS, szReactOS + 3, szReactOS + 2}, + {wszReactOS, wszReactOS + 10, wszReactOS + 9, + szReactOS, szReactOS + 10, szReactOS + 9}, + + {wszReactOS + 2, wszReactOS, wszReactOS, + szReactOS + 2, szReactOS, szReactOS}, + {wszReactOS + 2, wszReactOS + 1, wszReactOS + 1, + szReactOS + 2, szReactOS + 1, szReactOS + 1}, + {wszReactOS + 2, wszReactOS + 2, wszReactOS + 2, + szReactOS + 2, szReactOS + 2, szReactOS + 2}, + {wszReactOS + 2, wszReactOS + 3, wszReactOS + 2, + szReactOS + 2, szReactOS + 3, szReactOS + 2}, + {wszReactOS + 2, wszReactOS + 4, wszReactOS + 3, + szReactOS + 2, szReactOS + 4, szReactOS + 3}, + + /* Test null-terminators */ + {wszSpecial, wszSpecial + 8, wszSpecial + 7, + szSpecial, szSpecial + 8, szSpecial + 7}, + + /* Test tabulation */ + {wszSpecial, wszSpecial + 13, wszSpecial + 12, + szSpecial, szSpecial + 13, szSpecial + 12}, + + /* Test linebreak */ + {wszSpecial, wszSpecial + 17, wszSpecial + 16, + szSpecial, szSpecial + 17, szSpecial + 16}, + {wszSpecial, wszSpecial + 18, wszSpecial + 17, + szSpecial, szSpecial + 18, szSpecial + 17}, +}; + +ST_TESTS_CHARPREV TestStaticCharPrev_XP[] = +{ + /* XP/2003 treat diacritics as normal characters */ + {wszMagic1, wszMagic1 + 7, wszMagic1 + 6, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 10, wszMagic2 + 9, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 9, wszMagic2 + 8, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 8, wszMagic2 + 7, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 7, wszMagic2 + 6, + NULL, NULL, NULL}, +}; + +ST_TESTS_CHARPREV TestStaticCharPrev_Vista[] = +{ + /* Vista+ does respect diacritics and skip them */ + {wszMagic1, wszMagic1 + 7, wszMagic1 + 5, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 10, wszMagic2 + 5, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 9, wszMagic2 + 5, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 8, wszMagic2 + 5, + NULL, NULL, NULL}, + {wszMagic2, wszMagic2 + 7, wszMagic2 + 5, + NULL, NULL, NULL}, +}; + +typedef struct +{ + UINT uCodePage; + LPCSTR szStart; + LPCSTR szCurrent; + LPCSTR szResult; +} ST_CODEPAGE_TESTS_CHARPREV; + +ST_CODEPAGE_TESTS_CHARPREV TestStaticCodePageCharPrev[] = +{ + /* UTF-8 characters are not properly counted */ + {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 2, szUTF8Cyril + 1}, + {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 7, szUTF8Cyril + 6}, + {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 9, szUTF8Cyril + 8}, + + {CP_UTF8, szUTF8Greek, szUTF8Greek + 7, szUTF8Greek + 6}, + {CP_UTF8, szUTF8Greek, szUTF8Greek + 9, szUTF8Greek + 8}, + + {CP_UTF8, szUTF8Japan, szUTF8Japan + 8, szUTF8Japan + 7}, + {CP_UTF8, szUTF8Japan, szUTF8Japan + 11, szUTF8Japan + 10}, + + /* Code Page 932 / Shift-JIS characters are properly counted */ + {932, szCP932Japan, szCP932Japan + 2, szCP932Japan + 1}, + {932, szCP932Japan, szCP932Japan + 7, szCP932Japan + 5}, + {932, szCP932Japan, szCP932Japan + 9, szCP932Japan + 7}, +}; + +typedef struct +{ + LPCWSTR wszString; + LPCWSTR wszResult; + LPCSTR szString; + LPCSTR szResult; +} ST_TESTS_CHARNEXT; + +ST_TESTS_CHARNEXT TestStaticCharNext[] = +{ + {wszReactOS, wszReactOS + 1, + szReactOS, szReactOS + 1}, + {wszReactOS + 1, wszReactOS + 2, + szReactOS + 1, szReactOS + 2}, + {wszReactOS + 2, wszReactOS + 3, + szReactOS + 2, szReactOS + 3}, + {wszReactOS + 9, wszReactOS + 10, + szReactOS + 9, szReactOS + 10}, + {wszReactOS + 10, wszReactOS + 10, + szReactOS + 10, szReactOS + 10}, + + /* Test null-terminators */ + {wszSpecial + 3, wszSpecial + 4, + szSpecial + 3, szSpecial + 4}, + {wszSpecial + 4, wszSpecial + 4, + szSpecial + 4, szSpecial + 4}, + {wszSpecial + 5, wszSpecial + 5, + szSpecial + 5, szSpecial + 5}, + + /* Test tabulation */ + {wszSpecial + 12, wszSpecial + 13, + szSpecial + 12, szSpecial + 13}, + + /* Test linebreak */ + {wszSpecial + 15, wszSpecial + 16, + szSpecial + 15, szSpecial + 16}, + {wszSpecial + 16, wszSpecial + 17, + szSpecial + 16, szSpecial + 17}, +}; + +ST_TESTS_CHARNEXT TestStaticCharNext_XP[] = +{ + /* XP/2003 treat diacritics as normal characters */ + {wszMagic1 + 5, wszMagic1 + 6, + NULL, NULL}, + {wszMagic2 + 5, wszMagic2 + 6, + NULL, NULL}, + {wszMagic2 + 6, wszMagic2 + 7, + NULL, NULL}, + {wszMagic2 + 7, wszMagic2 + 8, + NULL, NULL}, + {wszMagic2 + 8, wszMagic2 + 9, + NULL, NULL}, +}; + +ST_TESTS_CHARNEXT TestStaticCharNext_Vista[] = +{ + /* Vista+ does respect diacritics and skip them */ + {wszMagic1 + 5, wszMagic1 + 7, + NULL, NULL}, + {wszMagic2 + 5, wszMagic2 + 10, + NULL, NULL}, + {wszMagic2 + 6, wszMagic2 + 10, + NULL, NULL}, + {wszMagic2 + 7, wszMagic2 + 10, + NULL, NULL}, + {wszMagic2 + 8, wszMagic2 + 10, + NULL, NULL}, +}; + +typedef struct +{ + UINT uCodePage; + LPCSTR szString; + LPCSTR szResult; +} ST_CODEPAGE_TESTS_CHARNEXT; + +ST_CODEPAGE_TESTS_CHARNEXT TestStaticCodePageCharNext[] = +{ + /* UTF-8 characters are not properly counted */ + {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 1}, + {CP_UTF8, szUTF8Cyril + 4, szUTF8Cyril + 5}, + {CP_UTF8, szUTF8Cyril + 5, szUTF8Cyril + 6}, + {CP_UTF8, szUTF8Cyril + 7, szUTF8Cyril + 8}, + + {CP_UTF8, szUTF8Greek + 5, szUTF8Greek + 6}, + {CP_UTF8, szUTF8Greek + 7, szUTF8Greek + 8}, + + {CP_UTF8, szUTF8Japan + 5, szUTF8Japan + 6}, + {CP_UTF8, szUTF8Japan + 8, szUTF8Japan + 9}, + + /* Code Page 932 / Shift-JIS characters are properly counted */ + {932, szCP932Japan, szCP932Japan + 1}, + {932, szCP932Japan + 5, szCP932Japan + 7}, + {932, szCP932Japan + 7, szCP932Japan + 9}, +}; + +/* Exception tests (corner cases) */ +typedef struct +{ + LPCWSTR wszStart; + LPCWSTR wszCurrent; + LPCWSTR wszResult; + LPCSTR szStart; + LPCSTR szCurrent; + LPCSTR szResult; + LPCSTR szExResult; + NTSTATUS resStatus; +} EX_TESTS_CHARPREV; + +EX_TESTS_CHARPREV TestExceptionCharPrev[] = +{ + {wszReactOS, NULL, NULL, + szReactOS, NULL, NULL, NULL, + STATUS_SUCCESS}, + {NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + STATUS_SUCCESS}, + {NULL, wszReactOS, wszReactOS - 1, + NULL, szReactOS, szReactOS - 1, szReactOS - 1, + STATUS_SUCCESS}, + + {INVALID_PTR, NULL, NULL, + INVALID_PTR, NULL, NULL, NULL, + STATUS_SUCCESS}, + {NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + STATUS_SUCCESS}, + {NULL, INVALID_PTR, NULL, + NULL, INVALID_PTR, INVALID_PTR_OFF(-1) /* NULL on Win7 with updates */, NULL, + STATUS_ACCESS_VIOLATION}, + + {wszReactOS, INVALID_PTR, NULL, + szReactOS, INVALID_PTR, INVALID_PTR_OFF(-1) /* NULL on Win7 with updates */, NULL, + STATUS_ACCESS_VIOLATION}, + {INVALID_PTR, INVALID_PTR, INVALID_PTR, + INVALID_PTR, INVALID_PTR, INVALID_PTR, INVALID_PTR, + STATUS_SUCCESS}, + {INVALID_PTR, wszReactOS, wszReactOS, + INVALID_PTR, szReactOS, szReactOS, szReactOS, + STATUS_SUCCESS}, + + {INVALID_PTR_OFF(-2), INVALID_PTR, NULL, + INVALID_PTR_OFF(-2), INVALID_PTR, INVALID_PTR_OFF(-1) /* NULL on Win7 with updates */, NULL, + STATUS_ACCESS_VIOLATION}, + {INVALID_PTR, INVALID_PTR_OFF(2), NULL, + INVALID_PTR, INVALID_PTR_OFF(2), INVALID_PTR_OFF(1) /* NULL on Win7 with updates */, NULL, + STATUS_ACCESS_VIOLATION}, + {INVALID_PTR, INVALID_PTR_OFF(-2), INVALID_PTR_OFF(-2), + INVALID_PTR, INVALID_PTR_OFF(-2), INVALID_PTR_OFF(-2), INVALID_PTR_OFF(-2), + STATUS_SUCCESS}, + {INVALID_PTR_OFF(2), INVALID_PTR, INVALID_PTR, + INVALID_PTR_OFF(2), INVALID_PTR, INVALID_PTR, INVALID_PTR, + STATUS_SUCCESS}, +}; + +typedef struct +{ + LPCWSTR wszString; + LPCWSTR wszResult; + LPCSTR szString; + LPCSTR szResult; + NTSTATUS resStatus; +} EX_TESTS_CHARNEXT; + +EX_TESTS_CHARNEXT TestExceptionCharNext[] = +{ + {wszReactOS, wszReactOS + 1, + szReactOS, szReactOS + 1, + STATUS_SUCCESS}, + {NULL, NULL, + NULL, NULL, + STATUS_ACCESS_VIOLATION}, + {INVALID_PTR, NULL, + INVALID_PTR, NULL, + STATUS_ACCESS_VIOLATION}, + + {INVALID_PTR_OFF(-2), NULL, + INVALID_PTR_OFF(-2), NULL, + STATUS_ACCESS_VIOLATION}, + {INVALID_PTR_OFF(2), NULL, + INVALID_PTR_OFF(2), NULL, + STATUS_ACCESS_VIOLATION}, +}; + +static LPWSTR AllocStringW(LPWSTR lpszStr, SIZE_T len) +{ + LPWSTR str; + SIZE_T sz; + + if (!lpszStr) + return NULL; + + sz = (len + 1) * sizeof(lpszStr[0]); + str = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz); + if (!str) + { + trace("HeapAlloc failed (error %ld)\n", GetLastError()); + goto Skip; + } + StringCbCopyW(str, sz, lpszStr); +Skip: + return str; +} + +static LPSTR AllocStringA(LPWSTR lpszStr, SIZE_T len) +{ + LPSTR str; + SIZE_T sz, mbs; + + if (!lpszStr) + return NULL; + + sz = len + 1; + str = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz); + if (!str) + { + trace("HeapAlloc failed (error %ld)\n", GetLastError()); + goto Skip; + } + + mbs = WideCharToMultiByte(TEST_ACP, 0, lpszStr, -1, NULL, 0, NULL, NULL); + if (!mbs || mbs > sz) + { + HeapFree(GetProcessHeap(), 0, str); + str = NULL; + trace("WideCharToMultiByte returned %lu (error %ld)\n", mbs, GetLastError()); + goto Skip; + } + + WideCharToMultiByte(TEST_ACP, 0, lpszStr, -1, str, mbs, NULL, NULL); +Skip: + return str; +} + +static void testCharPrevW(const TESTS_CHARPREV *pEntry, SIZE_T len, UINT i) +{ + LPWSTR wszStart, wszCurrent; + LPWSTR pchW; + INT iRealOffset; + BOOL b; + + wszStart = AllocStringW(pEntry->lpszStart, len); + if (!wszStart && pEntry->lpszStart) + { + skip("[%u] AllocStringW for wszStart failed\n", i); + goto Cleanup; + } + if (pEntry->testType == testLen) + wszCurrent = wszStart + len; + else if (pEntry->testType == testOffs) + wszCurrent = wszStart + pEntry->iOffset; + else + { + wszCurrent = AllocStringW(pEntry->lpszCurrent, wcslen(pEntry->lpszCurrent)); + if (!wszCurrent && pEntry->lpszCurrent) + { + skip("[%u] AllocStringW for wszCurrent failed\n", i); + goto Cleanup; + } + } + pchW = CharPrevW(wszStart, wszCurrent); + if (wszCurrent - wszStart >= 0) + iRealOffset = pEntry->iResOffset; + else + iRealOffset = pEntry->iResOffsetNeg; + if (pEntry->bWithinStart) + { + b = pchW >= wszStart && pchW <= wszStart + len; + if (iRealOffset >= 0) + ok(b, "[%u] CharPrevW: pchW (0x%p) is expected to be within wszStart (0x%p)\n", i, pchW, wszStart); + else + ok(!b, "[%u] CharPrevW: pchW (0x%p) is expected to be outside wszStart (0x%p)\n", i, pchW, wszStart); + ok(pchW == wszStart + iRealOffset, "[%u] CharPrevW: pchW is 0x%p (offset %d)\n", i, pchW, pchW - wszStart); + } + else + { + b = pchW >= wszCurrent && pchW <= wszCurrent + wcslen(pEntry->lpszCurrent); + if (iRealOffset >= 0) + ok(b, "[%u] CharPrevW: pchW (0x%p) is expected to be within wszCurrent (0x%p)\n", i, pchW, wszCurrent); + else + ok(!b, "[%u] CharPrevW: pchW (0x%p) is expected to be outside wszCurrent (0x%p)\n", i, pchW, wszCurrent); + ok(pchW == wszCurrent + iRealOffset, "[%u] CharPrevW: pchW is 0x%p (offset %d)\n", i, pchW, pchW - wszCurrent); + } + +Cleanup: + if (pEntry->testType != testBoth) + wszCurrent = NULL; + HeapFree(GetProcessHeap(), 0, wszStart); + HeapFree(GetProcessHeap(), 0, wszCurrent); +} + +static void testCharPrevA(const TESTS_CHARPREV *pEntry, SIZE_T len, UINT i) +{ + LPSTR szStart, szCurrent; + LPSTR pchA, pchEx; + INT iRealOffset; + BOOL b; + + szStart = AllocStringA(pEntry->lpszStart, len); + if (!szStart && pEntry->lpszStart) + { + skip("[%u] AllocStringA for szStart failed\n", i); + goto Cleanup; + } + if (pEntry->testType == testLen) + szCurrent = szStart + len; + else if (pEntry->testType == testOffs) + szCurrent = szStart + pEntry->iOffset; + else + { + szCurrent = AllocStringA(pEntry->lpszCurrent, wcslen(pEntry->lpszCurrent)); + if (!szCurrent && pEntry->lpszCurrent) + { + skip("[%u] AllocStringA for szCurrent failed\n", i); + goto Cleanup; + } + } + pchA = CharPrevA(szStart, szCurrent); + pchEx = CharPrevExA(TEST_ACP, szStart, szCurrent, 0); + if (szCurrent - szStart >= 0) + iRealOffset = pEntry->iResOffset; + else + iRealOffset = pEntry->iResOffsetNeg; + if (pEntry->bWithinStart) + { + b = pchA >= szStart && pchA <= szStart + len; + if (iRealOffset >= 0) + ok(b, "[%u] CharPrevA: pchA (0x%p) is expected to be within szStart (0x%p)\n", i, pchA, szStart); + else + ok(!b, "[%u] CharPrevA: pchA (0x%p) is expected to be outside szStart (0x%p)\n", i, pchA, szStart); + ok(pchA == szStart + iRealOffset, "[%u] CharPrevA: pchA is 0x%p (offset %d)\n", i, pchA, pchA - szStart); + } + else + { + b = pchA >= szCurrent && pchA <= szCurrent + wcslen(pEntry->lpszCurrent); + if (iRealOffset >= 0) + ok(b, "[%u] CharPrevA: pchA (0x%p) is expected to be within szCurrent (0x%p)\n", i, pchA, szCurrent); + else + ok(!b, "[%u] CharPrevA: pchA (0x%p) is expected to be outside szCurrent (0x%p)\n", i, pchA, szCurrent); + ok(pchA == szCurrent + iRealOffset, "[%u] CharPrevA: pchA is 0x%p (offset %d)\n", i, pchA, pchA - szCurrent); + } + ok(pchA == pchEx, "[%u] CharPrevExA: pchA (0x%p) is not equal to pchEx (0x%p)\n", i, pchA, pchEx); + +Cleanup: + if (pEntry->testType != testBoth) + szCurrent = NULL; + HeapFree(GetProcessHeap(), 0, szStart); + HeapFree(GetProcessHeap(), 0, szCurrent); +} + +static void testDynCharPrev(const TESTS_CHARPREV *pEntry, UINT i) +{ + SIZE_T len; + + len = wcslen(pEntry->lpszStart); + testCharPrevW(pEntry, len, i); + + if (pEntry->bWideOnly) + return; + + testCharPrevA(pEntry, len, i); +} + +static void testStatCharPrev(const ST_TESTS_CHARPREV *pEntry, UINT i) +{ + LPWSTR pchW; + LPSTR pchA; + + pchW = CharPrevW(pEntry->wszStart, pEntry->wszCurrent); + ok(pchW == pEntry->wszResult, "[%u] CharPrevW: pchW is 0x%p (expected 0x%p)\n", i, pchW, pEntry->wszResult); + + if (!pEntry->szStart) + return; + + pchA = CharPrevA(pEntry->szStart, pEntry->szCurrent); + ok(pchA == pEntry->szResult, "[%u] CharPrevA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult); + + pchA = CharPrevExA(TEST_ACP, pEntry->szStart, pEntry->szCurrent, 0); + ok(pchA == pEntry->szResult, "[%u] CharPrevExA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult); +} + +static void testStatCodePageCharPrev(const ST_CODEPAGE_TESTS_CHARPREV *pEntry, UINT i) +{ + LPSTR pchA; + + pchA = CharPrevExA(pEntry->uCodePage, pEntry->szStart, pEntry->szCurrent, 0); + ok(pchA == pEntry->szResult, "[%u] CharPrevExA(%u): pchA is 0x%p (expected 0x%p)\n", i, pEntry->uCodePage, pchA, pEntry->szResult); +} + +static void testStatCharNext(const ST_TESTS_CHARNEXT *pEntry, UINT i) +{ + LPWSTR pchW; + LPSTR pchA; + + pchW = CharNextW(pEntry->wszString); + ok(pchW == pEntry->wszResult, "[%u] CharNextW: pchW is 0x%p (expected 0x%p)\n", i, pchW, pEntry->wszResult); + + if (!pEntry->szString) + return; + + pchA = CharNextA(pEntry->szString); + ok(pchA == pEntry->szResult, "[%u] CharNextA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult); + + pchA = CharNextExA(TEST_ACP, pEntry->szString, 0); + ok(pchA == pEntry->szResult, "[%u] CharNextExA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult); +} + +static void testStatCodePageCharNext(const ST_CODEPAGE_TESTS_CHARNEXT *pEntry, UINT i) +{ + LPSTR pchA; + + pchA = CharNextExA(pEntry->uCodePage, pEntry->szString, 0); + ok(pchA == pEntry->szResult, "[%u] CharNextExA(%u): pchA is 0x%p (expected 0x%p)\n", i, pEntry->uCodePage, pchA, pEntry->szResult); +} + +static void testCharPrev(void) +{ + UINT i; + + /* Perform dynamic allocation tests */ + for (i = 0; i < _countof(TestCharPrev); i++) + { + testDynCharPrev(&TestCharPrev[i], i); + } + + if (!IsWindowsVistaOrGreater()) + { + for (i = 0; i < _countof(TestCharPrev_XP); i++) + { + testDynCharPrev(&TestCharPrev_XP[i], i); + } + } + else + { + for (i = 0; i < _countof(TestCharPrev_Vista); i++) + { + testDynCharPrev(&TestCharPrev_Vista[i], i); + } + } + + /* Perform static tests */ + for (i = 0; i < _countof(TestStaticCharPrev); i++) + { + testStatCharPrev(&TestStaticCharPrev[i], i); + } + + if (!IsWindowsVistaOrGreater()) + { + for (i = 0; i < _countof(TestStaticCharPrev_XP); i++) + { + testStatCharPrev(&TestStaticCharPrev_XP[i], i); + } + } + else + { + for (i = 0; i < _countof(TestStaticCharPrev_Vista); i++) + { + testStatCharPrev(&TestStaticCharPrev_Vista[i], i); + } + } + + for (i = 0; i < _countof(TestStaticCodePageCharPrev); i++) + { + testStatCodePageCharPrev(&TestStaticCodePageCharPrev[i], i); + } + + /* Perform exception tests (check corner cases) */ + if (INVALID_PTR < (PVOID)wszReactOS) + { + ok(FALSE, "testCharPrev: unexpected INVALID PTR < wszReactOS\n"); + return; + } + if (INVALID_PTR < (PVOID)szReactOS) + { + ok(FALSE, "testCharPrev: unexpected INVALID PTR < szReactOS\n"); + return; + } + + for (i = 0; i < _countof(TestExceptionCharPrev); i++) + { + LPWSTR pchW; + LPSTR pchA; + const EX_TESTS_CHARPREV *pEntry = &TestExceptionCharPrev[i]; + NTSTATUS Status = STATUS_SUCCESS; + + //trace("0x%p 0x%p\n", pEntry->wszStart, pEntry->wszCurrent); + pchW = NULL; + _SEH2_TRY + { + pchW = CharPrevW(pEntry->wszStart, pEntry->wszCurrent); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok(Status == pEntry->resStatus, "[%u] CharPrevW: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus); + ok(pchW == pEntry->wszResult, "[%u] CharPrevW: pchW is 0x%p, expected 0x%p\n", i, pchW, pEntry->wszResult); + + //trace("0x%p 0x%p\n", pEntry->szStart, pEntry->szCurrent); + pchA = NULL; + _SEH2_TRY + { + pchA = CharPrevA(pEntry->szStart, pEntry->szCurrent); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok(Status == pEntry->resStatus, "[%u] CharPrevA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus); + ok(pchA == pEntry->szResult, "[%u] CharPrevA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szResult); + + pchA = NULL; + _SEH2_TRY + { + pchA = CharPrevExA(TEST_ACP, pEntry->szStart, pEntry->szCurrent, 0); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok(Status == pEntry->resStatus, "[%u] CharPrevExA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus); + ok(pchA == pEntry->szExResult, "[%u] CharPrevExA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szExResult); + } +} + +static void testCharNext(void) +{ + UINT i; + + /* Perform static tests */ + for (i = 0; i < _countof(TestStaticCharNext); i++) + { + testStatCharNext(&TestStaticCharNext[i], i); + } + + if (!IsWindowsVistaOrGreater()) + { + for (i = 0; i < _countof(TestStaticCharNext_XP); i++) + { + testStatCharNext(&TestStaticCharNext_XP[i], i); + } + } + else + { + for (i = 0; i < _countof(TestStaticCharNext_Vista); i++) + { + testStatCharNext(&TestStaticCharNext_Vista[i], i); + } + } + + for (i = 0; i < _countof(TestStaticCodePageCharNext); i++) + { + testStatCodePageCharNext(&TestStaticCodePageCharNext[i], i); + } + + /* Perform exception tests (check corner cases) */ + if (INVALID_PTR < (PVOID)wszReactOS) + { + ok(FALSE, "testCharNext: unexpected INVALID PTR < wszReactOS\n"); + return; + } + if (INVALID_PTR < (PVOID)szReactOS) + { + ok(FALSE, "testCharNext: unexpected INVALID PTR < szReactOS\n"); + return; + } + + for (i = 0; i < _countof(TestExceptionCharNext); i++) + { + LPWSTR pchW; + LPSTR pchA; + const EX_TESTS_CHARNEXT *pEntry = &TestExceptionCharNext[i]; + NTSTATUS Status = STATUS_SUCCESS; + + //trace("0x%p\n", pEntry->wszString); + pchW = NULL; + _SEH2_TRY + { + pchW = CharNextW(pEntry->wszString); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok(Status == pEntry->resStatus, "[%u] CharNextW: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus); + ok(pchW == pEntry->wszResult, "[%u] CharNextW: pchW is 0x%p, expected 0x%p\n", i, pchW, pEntry->wszResult); + + //trace("0x%p 0x%p\n", pEntry->szString); + pchA = NULL; + _SEH2_TRY + { + pchA = CharNextA(pEntry->szString); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok(Status == pEntry->resStatus, "[%u] CharNextA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus); + ok(pchA == pEntry->szResult, "[%u] CharNextA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szResult); + + pchA = NULL; + _SEH2_TRY + { + pchA = CharNextExA(TEST_ACP, pEntry->szString, 0); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + ok(Status == pEntry->resStatus, "[%u] CharNextExA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus); + ok(pchA == pEntry->szResult, "[%u] CharNextExA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szResult); + } +} + +START_TEST(CharFuncs) +{ + testCharPrev(); + testCharNext(); +} diff --git a/modules/rostests/apitests/user32/testlist.c b/modules/rostests/apitests/user32/testlist.c index 53d1c8b243d..34c34259eda 100644 --- a/modules/rostests/apitests/user32/testlist.c +++ b/modules/rostests/apitests/user32/testlist.c @@ -4,6 +4,7 @@ #include extern void func_AttachThreadInput(void); +extern void func_CharFuncs(void); extern void func_CloseWindow(void); extern void func_CreateDialog(void); extern void func_CreateIconFromResourceEx(void); @@ -58,6 +59,7 @@ extern void func_wsprintf(void); const struct test winetest_testlist[] = { { "AttachThreadInput", func_AttachThreadInput }, + { "CharFuncs", func_CharFuncs }, { "CloseWindow", func_CloseWindow }, { "CreateDialog", func_CreateDialog }, { "CreateIconFromResourceEx", func_CreateIconFromResourceEx },