1 /*
2  * PROJECT:     ReactOS API tests
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Tests for Char* functions
5  * COPYRIGHT:   Copyright 2022 Stanislav Motylkov <x86corez@gmail.com>
6  */
7 
8 #include "precomp.h"
9 
10 #include <winnls.h>
11 #include <ndk/rtlfuncs.h>
12 #include <pseh/pseh2.h>
13 #include <strsafe.h>
14 #include <versionhelpers.h>
15 
16 #define INVALID_PTR_OFF(x)  ((PVOID)(ULONG_PTR)(0xdeadbeefdeadbeefULL + x))
17 #define INVALID_PTR         INVALID_PTR_OFF(0)
18 
19 /* Default code page to be tested */
20 #define TEST_ACP            1252
21 
22 typedef enum
23 {
24     testLen,
25     testOffs,
26     testBoth,
27 } TEST_TYPE;
28 
29 /* Dynamic allocation tests */
30 typedef struct
31 {
32     TEST_TYPE testType;
33     LPWSTR lpszStart;   /* Specified string for szStart */
34     LPWSTR lpszCurrent; /* Specified string for szCurrent (only when testType == testBoth) */
35     INT iOffset;        /* Specified offset to test (only when testType == testOffs) */
36     INT iResOffset;     /* Expected offset when szCurrent >= szStart */
37     INT iResOffsetNeg;  /* Expected offset when szCurrent < szStart */
38     BOOL bWithinStart;  /* TRUE for pointer expected to be within szStart, FALSE for within szCurrent */
39     BOOL bWideOnly;     /* Perform test only for Unicode case */
40 } TESTS_CHARPREV;
41 
42 TESTS_CHARPREV TestCharPrev[] =
43 {
44     {testLen, L"C:\\ReactOS", NULL, 0, 9, 9, TRUE, FALSE},
45     {testOffs, L"abcdefghijk", NULL, 11, 10, 10, TRUE, FALSE},
46     {testOffs, L"test a´^~¯", NULL, 10, 9, 9, TRUE, FALSE},
47     {testOffs, L"test å", NULL, 6, 5, 5, TRUE, FALSE},
48     {testBoth, L"C:\\ReactOS", L"", 0, -1, 0, FALSE, FALSE},
49     {testBoth, L"C:\\ReactOS\\", L"C:\\ReactOS", 0, -1, 0, FALSE, FALSE},
50     {testBoth, L"C:\\ReactOS\\", L"ReactOS", 0, -1, 0, FALSE, FALSE},
51 };
52 
53 TESTS_CHARPREV TestCharPrev_XP[] =
54 {
55     /* XP/2003 treat diacritics as normal characters */
56     {testOffs, L"test a\x030a", NULL, 7, 6, 6, TRUE, TRUE},
57     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 10, 9, 9, TRUE, TRUE},
58     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 9, 8, 8, TRUE, TRUE},
59     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 8, 7, 7, TRUE, TRUE},
60     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 7, 6, 6, TRUE, TRUE},
61 };
62 
63 TESTS_CHARPREV TestCharPrev_Vista[] =
64 {
65     /* Vista+ does respect diacritics and skip them */
66     {testOffs, L"test a\x030a", NULL, 7, 5, 5, TRUE, TRUE},
67     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 10, 5, 5, TRUE, TRUE},
68     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 9, 5, 5, TRUE, TRUE},
69     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 8, 5, 5, TRUE, TRUE},
70     {testOffs, L"test a\x0301\x0302\x0303\x0304", NULL, 7, 5, 5, TRUE, TRUE},
71 };
72 
73 /* Static tests */
74 static const WCHAR wszReactOS[] = L"C:\\ReactOS";
75 static const CHAR szReactOS[] = "C:\\ReactOS";
76 static const WCHAR wszSpecial[] = L"test\0\0\0\0\0\0aa\t\t\t\r\n\r\n";
77 static const CHAR szSpecial[] = "test\0\0\0\0\0\0aa\t\t\t\r\n\r\n";
78 static const WCHAR wszMagic1[] = L"test a\x030a";
79 static const WCHAR wszMagic2[] = L"test a\x0301\x0302\x0303\x0304";
80 
81 static const CHAR szUTF8Cyril[] =  "test \xD1\x82\xD0\xB5\xD1\x81\xD1\x82";     /* UTF8(L"test тест") */
82 static const CHAR szUTF8Greek[] =  "test \xCF\x84\xCE\xB5\xCF\x83\xCF\x84";     /* UTF8(L"test τεστ") */
83 static const CHAR szUTF8Japan[] =  "test \xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88"; /* UTF8(L"test テスト") */
84 static const CHAR szCP932Japan[] = "test \x83\x65\x83\x58\x83\x67";             /* CP932(L"test テスト") */
85 
86 typedef struct
87 {
88     LPCWSTR wszStart;
89     LPCWSTR wszCurrent;
90     LPCWSTR wszResult;
91     LPCSTR szStart;
92     LPCSTR szCurrent;
93     LPCSTR szResult;
94 } ST_TESTS_CHARPREV;
95 
96 ST_TESTS_CHARPREV TestStaticCharPrev[] =
97 {
98     {wszReactOS, wszReactOS, wszReactOS,
99       szReactOS,  szReactOS,  szReactOS},
100     {wszReactOS, wszReactOS + 1, wszReactOS,
101       szReactOS,  szReactOS + 1,  szReactOS},
102     {wszReactOS, wszReactOS + 2, wszReactOS + 1,
103       szReactOS,  szReactOS + 2,  szReactOS + 1},
104     {wszReactOS, wszReactOS + 3, wszReactOS + 2,
105       szReactOS,  szReactOS + 3,  szReactOS + 2},
106     {wszReactOS, wszReactOS + 10, wszReactOS + 9,
107       szReactOS,  szReactOS + 10,  szReactOS + 9},
108 
109     {wszReactOS + 2, wszReactOS, wszReactOS,
110       szReactOS + 2,  szReactOS,  szReactOS},
111     {wszReactOS + 2, wszReactOS + 1, wszReactOS + 1,
112       szReactOS + 2,  szReactOS + 1,  szReactOS + 1},
113     {wszReactOS + 2, wszReactOS + 2, wszReactOS + 2,
114       szReactOS + 2,  szReactOS + 2,  szReactOS + 2},
115     {wszReactOS + 2, wszReactOS + 3, wszReactOS + 2,
116       szReactOS + 2,  szReactOS + 3,  szReactOS + 2},
117     {wszReactOS + 2, wszReactOS + 4, wszReactOS + 3,
118       szReactOS + 2,  szReactOS + 4,  szReactOS + 3},
119 
120     /* Test null-terminators */
121     {wszSpecial, wszSpecial + 8, wszSpecial + 7,
122       szSpecial,  szSpecial + 8,  szSpecial + 7},
123 
124     /* Test tabulation */
125     {wszSpecial, wszSpecial + 13, wszSpecial + 12,
126       szSpecial,  szSpecial + 13,  szSpecial + 12},
127 
128     /* Test linebreak */
129     {wszSpecial, wszSpecial + 17, wszSpecial + 16,
130       szSpecial,  szSpecial + 17,  szSpecial + 16},
131     {wszSpecial, wszSpecial + 18, wszSpecial + 17,
132       szSpecial,  szSpecial + 18,  szSpecial + 17},
133 };
134 
135 ST_TESTS_CHARPREV TestStaticCharPrev_XP[] =
136 {
137     /* XP/2003 treat diacritics as normal characters */
138     {wszMagic1, wszMagic1 + 7,  wszMagic1 + 6,
139      NULL, NULL, NULL},
140     {wszMagic2, wszMagic2 + 10, wszMagic2 + 9,
141      NULL, NULL, NULL},
142     {wszMagic2, wszMagic2 + 9,  wszMagic2 + 8,
143      NULL, NULL, NULL},
144     {wszMagic2, wszMagic2 + 8,  wszMagic2 + 7,
145      NULL, NULL, NULL},
146     {wszMagic2, wszMagic2 + 7,  wszMagic2 + 6,
147      NULL, NULL, NULL},
148 };
149 
150 ST_TESTS_CHARPREV TestStaticCharPrev_Vista[] =
151 {
152     /* Vista+ does respect diacritics and skip them */
153     {wszMagic1, wszMagic1 + 7,  wszMagic1 + 5,
154      NULL, NULL, NULL},
155     {wszMagic2, wszMagic2 + 10, wszMagic2 + 5,
156      NULL, NULL, NULL},
157     {wszMagic2, wszMagic2 + 9,  wszMagic2 + 5,
158      NULL, NULL, NULL},
159     {wszMagic2, wszMagic2 + 8,  wszMagic2 + 5,
160      NULL, NULL, NULL},
161     {wszMagic2, wszMagic2 + 7,  wszMagic2 + 5,
162      NULL, NULL, NULL},
163 };
164 
165 typedef struct
166 {
167     UINT uCodePage;
168     LPCSTR szStart;
169     LPCSTR szCurrent;
170     LPCSTR szResult;
171 } ST_CODEPAGE_TESTS_CHARPREV;
172 
173 ST_CODEPAGE_TESTS_CHARPREV TestStaticCodePageCharPrev[] =
174 {
175     /* UTF-8 characters are not properly counted */
176     {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 2, szUTF8Cyril + 1},
177     {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 7, szUTF8Cyril + 6},
178     {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 9, szUTF8Cyril + 8},
179 
180     {CP_UTF8, szUTF8Greek, szUTF8Greek + 7, szUTF8Greek + 6},
181     {CP_UTF8, szUTF8Greek, szUTF8Greek + 9, szUTF8Greek + 8},
182 
183     {CP_UTF8, szUTF8Japan, szUTF8Japan + 8, szUTF8Japan + 7},
184     {CP_UTF8, szUTF8Japan, szUTF8Japan + 11, szUTF8Japan + 10},
185 
186     /* Code Page 932 / Shift-JIS characters are properly counted */
187     {932,     szCP932Japan, szCP932Japan + 2, szCP932Japan + 1},
188     {932,     szCP932Japan, szCP932Japan + 7, szCP932Japan + 5},
189     {932,     szCP932Japan, szCP932Japan + 9, szCP932Japan + 7},
190 };
191 
192 typedef struct
193 {
194     LPCWSTR wszString;
195     LPCWSTR wszResult;
196     LPCSTR szString;
197     LPCSTR szResult;
198 } ST_TESTS_CHARNEXT;
199 
200 ST_TESTS_CHARNEXT TestStaticCharNext[] =
201 {
202     {wszReactOS, wszReactOS + 1,
203       szReactOS,  szReactOS + 1},
204     {wszReactOS + 1, wszReactOS + 2,
205       szReactOS + 1,  szReactOS + 2},
206     {wszReactOS + 2, wszReactOS + 3,
207       szReactOS + 2,  szReactOS + 3},
208     {wszReactOS + 9, wszReactOS + 10,
209       szReactOS + 9,  szReactOS + 10},
210     {wszReactOS + 10, wszReactOS + 10,
211       szReactOS + 10,  szReactOS + 10},
212 
213     /* Test null-terminators */
214     {wszSpecial + 3, wszSpecial + 4,
215       szSpecial + 3,  szSpecial + 4},
216     {wszSpecial + 4, wszSpecial + 4,
217       szSpecial + 4,  szSpecial + 4},
218     {wszSpecial + 5, wszSpecial + 5,
219       szSpecial + 5,  szSpecial + 5},
220 
221     /* Test tabulation */
222     {wszSpecial + 12, wszSpecial + 13,
223       szSpecial + 12,  szSpecial + 13},
224 
225     /* Test linebreak */
226     {wszSpecial + 15, wszSpecial + 16,
227       szSpecial + 15,  szSpecial + 16},
228     {wszSpecial + 16, wszSpecial + 17,
229       szSpecial + 16,  szSpecial + 17},
230 };
231 
232 ST_TESTS_CHARNEXT TestStaticCharNext_XP[] =
233 {
234     /* XP/2003 treat diacritics as normal characters */
235     {wszMagic1 + 5, wszMagic1 + 6,
236      NULL, NULL},
237     {wszMagic2 + 5, wszMagic2 + 6,
238      NULL, NULL},
239     {wszMagic2 + 6, wszMagic2 + 7,
240      NULL, NULL},
241     {wszMagic2 + 7, wszMagic2 + 8,
242      NULL, NULL},
243     {wszMagic2 + 8, wszMagic2 + 9,
244      NULL, NULL},
245 };
246 
247 ST_TESTS_CHARNEXT TestStaticCharNext_Vista[] =
248 {
249     /* Vista+ does respect diacritics and skip them */
250     {wszMagic1 + 5, wszMagic1 + 7,
251      NULL, NULL},
252     {wszMagic2 + 5, wszMagic2 + 10,
253      NULL, NULL},
254     {wszMagic2 + 6, wszMagic2 + 10,
255      NULL, NULL},
256     {wszMagic2 + 7, wszMagic2 + 10,
257      NULL, NULL},
258     {wszMagic2 + 8, wszMagic2 + 10,
259      NULL, NULL},
260 };
261 
262 typedef struct
263 {
264     UINT uCodePage;
265     LPCSTR szString;
266     LPCSTR szResult;
267 } ST_CODEPAGE_TESTS_CHARNEXT;
268 
269 ST_CODEPAGE_TESTS_CHARNEXT TestStaticCodePageCharNext[] =
270 {
271     /* UTF-8 characters are not properly counted */
272     {CP_UTF8, szUTF8Cyril, szUTF8Cyril + 1},
273     {CP_UTF8, szUTF8Cyril + 4, szUTF8Cyril + 5},
274     {CP_UTF8, szUTF8Cyril + 5, szUTF8Cyril + 6},
275     {CP_UTF8, szUTF8Cyril + 7, szUTF8Cyril + 8},
276 
277     {CP_UTF8, szUTF8Greek + 5, szUTF8Greek + 6},
278     {CP_UTF8, szUTF8Greek + 7, szUTF8Greek + 8},
279 
280     {CP_UTF8, szUTF8Japan + 5, szUTF8Japan + 6},
281     {CP_UTF8, szUTF8Japan + 8, szUTF8Japan + 9},
282 
283     /* Code Page 932 / Shift-JIS characters are properly counted */
284     {932,     szCP932Japan, szCP932Japan + 1},
285     {932,     szCP932Japan + 5, szCP932Japan + 7},
286     {932,     szCP932Japan + 7, szCP932Japan + 9},
287 };
288 
289 /* Exception tests (corner cases) */
290 typedef struct
291 {
292     LPCWSTR wszStart;
293     LPCWSTR wszCurrent;
294     LPCWSTR wszResult;
295     LPCSTR szStart;
296     LPCSTR szCurrent;
297     LPCSTR szResult;
298     LPCSTR szExResult;
299     NTSTATUS resStatus;
300 } EX_TESTS_CHARPREV;
301 
302 EX_TESTS_CHARPREV TestExceptionCharPrev[] =
303 {
304     {wszReactOS, NULL, NULL,
305       szReactOS, NULL, NULL, NULL,
306      STATUS_SUCCESS},
307     {NULL, NULL, NULL,
308      NULL, NULL, NULL, NULL,
309      STATUS_SUCCESS},
310     {NULL, wszReactOS, wszReactOS - 1,
311      NULL,  szReactOS,  szReactOS - 1, szReactOS - 1,
312      STATUS_SUCCESS},
313 
314     {INVALID_PTR, NULL, NULL,
315      INVALID_PTR, NULL, NULL, NULL,
316      STATUS_SUCCESS},
317     {NULL, NULL, NULL,
318      NULL, NULL, NULL, NULL,
319      STATUS_SUCCESS},
320     {NULL, INVALID_PTR, NULL,
321      NULL, INVALID_PTR, INVALID_PTR_OFF(-1) /* NULL on Win7 with updates */, NULL,
322      STATUS_ACCESS_VIOLATION},
323 
324     {wszReactOS, INVALID_PTR, NULL,
325       szReactOS, INVALID_PTR, INVALID_PTR_OFF(-1) /* NULL on Win7 with updates */, NULL,
326      STATUS_ACCESS_VIOLATION},
327     {INVALID_PTR, INVALID_PTR, INVALID_PTR,
328      INVALID_PTR, INVALID_PTR, INVALID_PTR, INVALID_PTR,
329      STATUS_SUCCESS},
330     {INVALID_PTR, wszReactOS, wszReactOS,
331      INVALID_PTR,  szReactOS,  szReactOS, szReactOS,
332      STATUS_SUCCESS},
333 
334     {INVALID_PTR_OFF(-2), INVALID_PTR, NULL,
335      INVALID_PTR_OFF(-2), INVALID_PTR, INVALID_PTR_OFF(-1) /* NULL on Win7 with updates */, NULL,
336      STATUS_ACCESS_VIOLATION},
337     {INVALID_PTR, INVALID_PTR_OFF(2), NULL,
338      INVALID_PTR, INVALID_PTR_OFF(2), INVALID_PTR_OFF(1) /* NULL on Win7 with updates */, NULL,
339      STATUS_ACCESS_VIOLATION},
340     {INVALID_PTR, INVALID_PTR_OFF(-2), INVALID_PTR_OFF(-2),
341      INVALID_PTR, INVALID_PTR_OFF(-2), INVALID_PTR_OFF(-2), INVALID_PTR_OFF(-2),
342      STATUS_SUCCESS},
343     {INVALID_PTR_OFF(2), INVALID_PTR, INVALID_PTR,
344      INVALID_PTR_OFF(2), INVALID_PTR, INVALID_PTR, INVALID_PTR,
345      STATUS_SUCCESS},
346 };
347 
348 typedef struct
349 {
350     LPCWSTR wszString;
351     LPCWSTR wszResult;
352     LPCSTR szString;
353     LPCSTR szResult;
354     NTSTATUS resStatus;
355 } EX_TESTS_CHARNEXT;
356 
357 EX_TESTS_CHARNEXT TestExceptionCharNext[] =
358 {
359     {wszReactOS, wszReactOS + 1,
360       szReactOS,  szReactOS + 1,
361      STATUS_SUCCESS},
362     {NULL, NULL,
363      NULL, NULL,
364      STATUS_ACCESS_VIOLATION},
365     {INVALID_PTR, NULL,
366      INVALID_PTR, NULL,
367      STATUS_ACCESS_VIOLATION},
368 
369     {INVALID_PTR_OFF(-2), NULL,
370      INVALID_PTR_OFF(-2), NULL,
371      STATUS_ACCESS_VIOLATION},
372     {INVALID_PTR_OFF(2), NULL,
373      INVALID_PTR_OFF(2), NULL,
374      STATUS_ACCESS_VIOLATION},
375 };
376 
377 static LPWSTR AllocStringW(LPWSTR lpszStr, SIZE_T len)
378 {
379     LPWSTR str;
380     SIZE_T sz;
381 
382     if (!lpszStr)
383         return NULL;
384 
385     sz = (len + 1) * sizeof(lpszStr[0]);
386     str = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz);
387     if (!str)
388     {
389         trace("HeapAlloc failed (error %ld)\n", GetLastError());
390         goto Skip;
391     }
392     StringCbCopyW(str, sz, lpszStr);
393 Skip:
394     return str;
395 }
396 
397 static LPSTR AllocStringA(LPWSTR lpszStr, SIZE_T len)
398 {
399     LPSTR str;
400     SIZE_T sz, mbs;
401 
402     if (!lpszStr)
403         return NULL;
404 
405     sz = len + 1;
406     str = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz);
407     if (!str)
408     {
409         trace("HeapAlloc failed (error %ld)\n", GetLastError());
410         goto Skip;
411     }
412 
413     mbs = WideCharToMultiByte(TEST_ACP, 0, lpszStr, -1, NULL, 0, NULL, NULL);
414     if (!mbs || mbs > sz)
415     {
416         HeapFree(GetProcessHeap(), 0, str);
417         str = NULL;
418         trace("WideCharToMultiByte returned %lu (error %ld)\n", mbs, GetLastError());
419         goto Skip;
420     }
421 
422     WideCharToMultiByte(TEST_ACP, 0, lpszStr, -1, str, mbs, NULL, NULL);
423 Skip:
424     return str;
425 }
426 
427 static void testCharPrevW(const TESTS_CHARPREV *pEntry, SIZE_T len, UINT i)
428 {
429     LPWSTR wszStart, wszCurrent;
430     LPWSTR pchW;
431     INT iRealOffset;
432     BOOL b;
433 
434     wszStart = AllocStringW(pEntry->lpszStart, len);
435     if (!wszStart && pEntry->lpszStart)
436     {
437         skip("[%u] AllocStringW for wszStart failed\n", i);
438         goto Cleanup;
439     }
440     if (pEntry->testType == testLen)
441         wszCurrent = wszStart + len;
442     else if (pEntry->testType == testOffs)
443         wszCurrent = wszStart + pEntry->iOffset;
444     else
445     {
446         wszCurrent = AllocStringW(pEntry->lpszCurrent, wcslen(pEntry->lpszCurrent));
447         if (!wszCurrent && pEntry->lpszCurrent)
448         {
449             skip("[%u] AllocStringW for wszCurrent failed\n", i);
450             goto Cleanup;
451         }
452     }
453     pchW = CharPrevW(wszStart, wszCurrent);
454     if (wszCurrent - wszStart >= 0)
455         iRealOffset = pEntry->iResOffset;
456     else
457         iRealOffset = pEntry->iResOffsetNeg;
458     if (pEntry->bWithinStart)
459     {
460         b = pchW >= wszStart && pchW <= wszStart + len;
461         if (iRealOffset >= 0)
462             ok(b, "[%u] CharPrevW: pchW (0x%p) is expected to be within wszStart (0x%p)\n", i, pchW, wszStart);
463         else
464             ok(!b, "[%u] CharPrevW: pchW (0x%p) is expected to be outside wszStart (0x%p)\n", i, pchW, wszStart);
465         ok(pchW == wszStart + iRealOffset, "[%u] CharPrevW: pchW is 0x%p (offset %d)\n", i, pchW, pchW - wszStart);
466     }
467     else
468     {
469         b = pchW >= wszCurrent && pchW <= wszCurrent + wcslen(pEntry->lpszCurrent);
470         if (iRealOffset >= 0)
471             ok(b, "[%u] CharPrevW: pchW (0x%p) is expected to be within wszCurrent (0x%p)\n", i, pchW, wszCurrent);
472         else
473             ok(!b, "[%u] CharPrevW: pchW (0x%p) is expected to be outside wszCurrent (0x%p)\n", i, pchW, wszCurrent);
474         ok(pchW == wszCurrent + iRealOffset, "[%u] CharPrevW: pchW is 0x%p (offset %d)\n", i, pchW, pchW - wszCurrent);
475     }
476 
477 Cleanup:
478     if (pEntry->testType != testBoth)
479         wszCurrent = NULL;
480     HeapFree(GetProcessHeap(), 0, wszStart);
481     HeapFree(GetProcessHeap(), 0, wszCurrent);
482 }
483 
484 static void testCharPrevA(const TESTS_CHARPREV *pEntry, SIZE_T len, UINT i)
485 {
486     LPSTR szStart, szCurrent;
487     LPSTR pchA, pchEx;
488     INT iRealOffset;
489     BOOL b;
490 
491     szStart = AllocStringA(pEntry->lpszStart, len);
492     if (!szStart && pEntry->lpszStart)
493     {
494         skip("[%u] AllocStringA for szStart failed\n", i);
495         goto Cleanup;
496     }
497     if (pEntry->testType == testLen)
498         szCurrent = szStart + len;
499     else if (pEntry->testType == testOffs)
500         szCurrent = szStart + pEntry->iOffset;
501     else
502     {
503         szCurrent = AllocStringA(pEntry->lpszCurrent, wcslen(pEntry->lpszCurrent));
504         if (!szCurrent && pEntry->lpszCurrent)
505         {
506             skip("[%u] AllocStringA for szCurrent failed\n", i);
507             goto Cleanup;
508         }
509     }
510     pchA = CharPrevA(szStart, szCurrent);
511     pchEx = CharPrevExA(TEST_ACP, szStart, szCurrent, 0);
512     if (szCurrent - szStart >= 0)
513         iRealOffset = pEntry->iResOffset;
514     else
515         iRealOffset = pEntry->iResOffsetNeg;
516     if (pEntry->bWithinStart)
517     {
518         b = pchA >= szStart && pchA <= szStart + len;
519         if (iRealOffset >= 0)
520             ok(b, "[%u] CharPrevA: pchA (0x%p) is expected to be within szStart (0x%p)\n", i, pchA, szStart);
521         else
522             ok(!b, "[%u] CharPrevA: pchA (0x%p) is expected to be outside szStart (0x%p)\n", i, pchA, szStart);
523         ok(pchA == szStart + iRealOffset, "[%u] CharPrevA: pchA is 0x%p (offset %d)\n", i, pchA, pchA - szStart);
524     }
525     else
526     {
527         b = pchA >= szCurrent && pchA <= szCurrent + wcslen(pEntry->lpszCurrent);
528         if (iRealOffset >= 0)
529             ok(b, "[%u] CharPrevA: pchA (0x%p) is expected to be within szCurrent (0x%p)\n", i, pchA, szCurrent);
530         else
531             ok(!b, "[%u] CharPrevA: pchA (0x%p) is expected to be outside szCurrent (0x%p)\n", i, pchA, szCurrent);
532         ok(pchA == szCurrent + iRealOffset, "[%u] CharPrevA: pchA is 0x%p (offset %d)\n", i, pchA, pchA - szCurrent);
533     }
534     ok(pchA == pchEx, "[%u] CharPrevExA: pchA (0x%p) is not equal to pchEx (0x%p)\n", i, pchA, pchEx);
535 
536 Cleanup:
537     if (pEntry->testType != testBoth)
538         szCurrent = NULL;
539     HeapFree(GetProcessHeap(), 0, szStart);
540     HeapFree(GetProcessHeap(), 0, szCurrent);
541 }
542 
543 static void testDynCharPrev(const TESTS_CHARPREV *pEntry, UINT i)
544 {
545     SIZE_T len;
546 
547     len = wcslen(pEntry->lpszStart);
548     testCharPrevW(pEntry, len, i);
549 
550     if (pEntry->bWideOnly)
551         return;
552 
553     testCharPrevA(pEntry, len, i);
554 }
555 
556 static void testStatCharPrev(const ST_TESTS_CHARPREV *pEntry, UINT i)
557 {
558     LPWSTR pchW;
559     LPSTR pchA;
560 
561     pchW = CharPrevW(pEntry->wszStart, pEntry->wszCurrent);
562     ok(pchW == pEntry->wszResult, "[%u] CharPrevW: pchW is 0x%p (expected 0x%p)\n", i, pchW, pEntry->wszResult);
563 
564     if (!pEntry->szStart)
565         return;
566 
567     pchA = CharPrevA(pEntry->szStart, pEntry->szCurrent);
568     ok(pchA == pEntry->szResult, "[%u] CharPrevA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult);
569 
570     pchA = CharPrevExA(TEST_ACP, pEntry->szStart, pEntry->szCurrent, 0);
571     ok(pchA == pEntry->szResult, "[%u] CharPrevExA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult);
572 }
573 
574 static void testStatCodePageCharPrev(const ST_CODEPAGE_TESTS_CHARPREV *pEntry, UINT i)
575 {
576     LPSTR pchA;
577 
578     pchA = CharPrevExA(pEntry->uCodePage, pEntry->szStart, pEntry->szCurrent, 0);
579     ok(pchA == pEntry->szResult, "[%u] CharPrevExA(%u): pchA is 0x%p (expected 0x%p)\n", i, pEntry->uCodePage, pchA, pEntry->szResult);
580 }
581 
582 static void testStatCharNext(const ST_TESTS_CHARNEXT *pEntry, UINT i)
583 {
584     LPWSTR pchW;
585     LPSTR pchA;
586 
587     pchW = CharNextW(pEntry->wszString);
588     ok(pchW == pEntry->wszResult, "[%u] CharNextW: pchW is 0x%p (expected 0x%p)\n", i, pchW, pEntry->wszResult);
589 
590     if (!pEntry->szString)
591         return;
592 
593     pchA = CharNextA(pEntry->szString);
594     ok(pchA == pEntry->szResult, "[%u] CharNextA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult);
595 
596     pchA = CharNextExA(TEST_ACP, pEntry->szString, 0);
597     ok(pchA == pEntry->szResult, "[%u] CharNextExA: pchA is 0x%p (expected 0x%p)\n", i, pchA, pEntry->szResult);
598 }
599 
600 static void testStatCodePageCharNext(const ST_CODEPAGE_TESTS_CHARNEXT *pEntry, UINT i)
601 {
602     LPSTR pchA;
603 
604     pchA = CharNextExA(pEntry->uCodePage, pEntry->szString, 0);
605     ok(pchA == pEntry->szResult, "[%u] CharNextExA(%u): pchA is 0x%p (expected 0x%p)\n", i, pEntry->uCodePage, pchA, pEntry->szResult);
606 }
607 
608 static void testCharPrev(void)
609 {
610     UINT i;
611 
612     /* Perform dynamic allocation tests */
613     for (i = 0; i < _countof(TestCharPrev); i++)
614     {
615         testDynCharPrev(&TestCharPrev[i], i);
616     }
617 
618     if (!IsWindowsVistaOrGreater())
619     {
620         for (i = 0; i < _countof(TestCharPrev_XP); i++)
621         {
622             testDynCharPrev(&TestCharPrev_XP[i], i);
623         }
624     }
625     else
626     {
627         for (i = 0; i < _countof(TestCharPrev_Vista); i++)
628         {
629             testDynCharPrev(&TestCharPrev_Vista[i], i);
630         }
631     }
632 
633     /* Perform static tests */
634     for (i = 0; i < _countof(TestStaticCharPrev); i++)
635     {
636         testStatCharPrev(&TestStaticCharPrev[i], i);
637     }
638 
639     if (!IsWindowsVistaOrGreater())
640     {
641         for (i = 0; i < _countof(TestStaticCharPrev_XP); i++)
642         {
643             testStatCharPrev(&TestStaticCharPrev_XP[i], i);
644         }
645     }
646     else
647     {
648         for (i = 0; i < _countof(TestStaticCharPrev_Vista); i++)
649         {
650             testStatCharPrev(&TestStaticCharPrev_Vista[i], i);
651         }
652     }
653 
654     for (i = 0; i < _countof(TestStaticCodePageCharPrev); i++)
655     {
656         testStatCodePageCharPrev(&TestStaticCodePageCharPrev[i], i);
657     }
658 
659     /* Perform exception tests (check corner cases) */
660     if (INVALID_PTR < (PVOID)wszReactOS)
661     {
662         ok(FALSE, "testCharPrev: unexpected INVALID PTR < wszReactOS\n");
663         return;
664     }
665     if (INVALID_PTR < (PVOID)szReactOS)
666     {
667         ok(FALSE, "testCharPrev: unexpected INVALID PTR < szReactOS\n");
668         return;
669     }
670 
671     for (i = 0; i < _countof(TestExceptionCharPrev); i++)
672     {
673         LPWSTR pchW;
674         LPSTR pchA;
675         const EX_TESTS_CHARPREV *pEntry = &TestExceptionCharPrev[i];
676         NTSTATUS Status = STATUS_SUCCESS;
677 
678         //trace("0x%p 0x%p\n", pEntry->wszStart, pEntry->wszCurrent);
679         pchW = NULL;
680         _SEH2_TRY
681         {
682             pchW = CharPrevW(pEntry->wszStart, pEntry->wszCurrent);
683         }
684         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
685         {
686             Status = _SEH2_GetExceptionCode();
687         }
688         _SEH2_END;
689         ok(Status == pEntry->resStatus, "[%u] CharPrevW: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus);
690         ok(pchW == pEntry->wszResult, "[%u] CharPrevW: pchW is 0x%p, expected 0x%p\n", i, pchW, pEntry->wszResult);
691 
692         //trace("0x%p 0x%p\n", pEntry->szStart, pEntry->szCurrent);
693         pchA = NULL;
694         _SEH2_TRY
695         {
696             pchA = CharPrevA(pEntry->szStart, pEntry->szCurrent);
697         }
698         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
699         {
700             Status = _SEH2_GetExceptionCode();
701         }
702         _SEH2_END;
703         ok(Status == pEntry->resStatus, "[%u] CharPrevA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus);
704         ok(pchA == pEntry->szResult, "[%u] CharPrevA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szResult);
705 
706         pchA = NULL;
707         _SEH2_TRY
708         {
709             pchA = CharPrevExA(TEST_ACP, pEntry->szStart, pEntry->szCurrent, 0);
710         }
711         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
712         {
713             Status = _SEH2_GetExceptionCode();
714         }
715         _SEH2_END;
716         ok(Status == pEntry->resStatus, "[%u] CharPrevExA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus);
717         ok(pchA == pEntry->szExResult, "[%u] CharPrevExA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szExResult);
718     }
719 }
720 
721 static void testCharNext(void)
722 {
723     UINT i;
724 
725     /* Perform static tests */
726     for (i = 0; i < _countof(TestStaticCharNext); i++)
727     {
728         testStatCharNext(&TestStaticCharNext[i], i);
729     }
730 
731     if (!IsWindowsVistaOrGreater())
732     {
733         for (i = 0; i < _countof(TestStaticCharNext_XP); i++)
734         {
735             testStatCharNext(&TestStaticCharNext_XP[i], i);
736         }
737     }
738     else
739     {
740         for (i = 0; i < _countof(TestStaticCharNext_Vista); i++)
741         {
742             testStatCharNext(&TestStaticCharNext_Vista[i], i);
743         }
744     }
745 
746     for (i = 0; i < _countof(TestStaticCodePageCharNext); i++)
747     {
748         testStatCodePageCharNext(&TestStaticCodePageCharNext[i], i);
749     }
750 
751     /* Perform exception tests (check corner cases) */
752     if (INVALID_PTR < (PVOID)wszReactOS)
753     {
754         ok(FALSE, "testCharNext: unexpected INVALID PTR < wszReactOS\n");
755         return;
756     }
757     if (INVALID_PTR < (PVOID)szReactOS)
758     {
759         ok(FALSE, "testCharNext: unexpected INVALID PTR < szReactOS\n");
760         return;
761     }
762 
763     for (i = 0; i < _countof(TestExceptionCharNext); i++)
764     {
765         LPWSTR pchW;
766         LPSTR pchA;
767         const EX_TESTS_CHARNEXT *pEntry = &TestExceptionCharNext[i];
768         NTSTATUS Status = STATUS_SUCCESS;
769 
770         //trace("0x%p\n", pEntry->wszString);
771         pchW = NULL;
772         _SEH2_TRY
773         {
774             pchW = CharNextW(pEntry->wszString);
775         }
776         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
777         {
778             Status = _SEH2_GetExceptionCode();
779         }
780         _SEH2_END;
781         ok(Status == pEntry->resStatus, "[%u] CharNextW: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus);
782         ok(pchW == pEntry->wszResult, "[%u] CharNextW: pchW is 0x%p, expected 0x%p\n", i, pchW, pEntry->wszResult);
783 
784         //trace("0x%p 0x%p\n", pEntry->szString);
785         pchA = NULL;
786         _SEH2_TRY
787         {
788             pchA = CharNextA(pEntry->szString);
789         }
790         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
791         {
792             Status = _SEH2_GetExceptionCode();
793         }
794         _SEH2_END;
795         ok(Status == pEntry->resStatus, "[%u] CharNextA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus);
796         ok(pchA == pEntry->szResult, "[%u] CharNextA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szResult);
797 
798         pchA = NULL;
799         _SEH2_TRY
800         {
801             pchA = CharNextExA(TEST_ACP, pEntry->szString, 0);
802         }
803         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
804         {
805             Status = _SEH2_GetExceptionCode();
806         }
807         _SEH2_END;
808         ok(Status == pEntry->resStatus, "[%u] CharNextExA: Status is 0x%lX, expected 0x%lX\n", i, Status, pEntry->resStatus);
809         ok(pchA == pEntry->szResult, "[%u] CharNextExA: pchA is 0x%p, expected 0x%p\n", i, pchA, pEntry->szResult);
810     }
811 }
812 
813 START_TEST(CharFuncs)
814 {
815     testCharPrev();
816     testCharNext();
817 }
818