https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4b10fdbce93cf06642f72cf0bf582abfba99d420

commit 4b10fdbce93cf06642f72cf0bf582abfba99d420
Author:     Stanislav Motylkov <[email protected]>
AuthorDate: Sun Nov 6 03:34:40 2022 +0300
Commit:     Stanislav Motylkov <[email protected]>
CommitDate: Mon Nov 7 19:02:09 2022 +0300

    [USER32_APITEST] Add tests for CharPrev/Next/Ex/A/W
    
    CORE-18415 CORE-18452
---
 modules/rostests/apitests/user32/CMakeLists.txt |   1 +
 modules/rostests/apitests/user32/CharFuncs.c    | 817 ++++++++++++++++++++++++
 modules/rostests/apitests/user32/testlist.c     |   2 +
 3 files changed, 820 insertions(+)

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 <[email protected]>
+ */
+
+#include "precomp.h"
+
+#include <winnls.h>
+#include <ndk/rtlfuncs.h>
+#include <pseh/pseh2.h>
+#include <strsafe.h>
+#include <versionhelpers.h>
+
+#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 <apitest.h>
 
 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 },

Reply via email to