1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Test for IAutoComplete objects
5 * COPYRIGHT: Copyright 2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7 #define _UNICODE
8 #define UNICODE
9 #include <apitest.h>
10 #include <shlobj.h>
11 #include <atlbase.h>
12 #include <tchar.h>
13 #include <atlcom.h>
14 #include <atlwin.h>
15 #include <atlstr.h>
16 #include <atlsimpcoll.h>
17 #include <shlwapi.h>
18 #include <strsafe.h>
19 #include <shellutils.h>
20
21 //#define MANUAL_DEBUGGING
22
23 // compare wide strings
24 #define ok_wstri(x, y) \
25 ok(lstrcmpiW(x, y) == 0, "Wrong string. Expected '%S', got '%S'\n", y, x)
26
27 // create an EDIT control
MyCreateEditCtrl(INT x,INT y,INT cx,INT cy)28 static HWND MyCreateEditCtrl(INT x, INT y, INT cx, INT cy)
29 {
30 DWORD style = WS_POPUPWINDOW | WS_BORDER;
31 DWORD exstyle = WS_EX_CLIENTEDGE;
32 return CreateWindowExW(exstyle, L"EDIT", NULL, style, x, y, cx, cy,
33 NULL, NULL, GetModuleHandleW(NULL), NULL);
34 }
35
36 static BOOL s_bReset = FALSE;
37 static BOOL s_bExpand = FALSE;
38 static CStringW s_strExpand;
39
40 // CEnumString class for auto-completion test
41 class CEnumString : public IEnumString, public IACList2
42 {
43 public:
CEnumString()44 CEnumString() : m_cRefs(0), m_nIndex(0), m_nCount(0), m_pList(NULL)
45 {
46 trace("CEnumString::CEnumString(%p)\n", this);
47 }
48
~CEnumString()49 virtual ~CEnumString()
50 {
51 trace("CEnumString::~CEnumString(%p)\n", this);
52 for (UINT i = 0; i < m_nCount; ++i)
53 {
54 CoTaskMemFree(m_pList[i]);
55 m_pList[i] = NULL;
56 }
57 CoTaskMemFree(m_pList);
58 }
59
SetList(UINT nCount,LPWSTR * pList)60 VOID SetList(UINT nCount, LPWSTR *pList)
61 {
62 m_nCount = nCount;
63 m_pList = pList;
64 }
65
QueryInterface(REFIID iid,VOID ** ppv)66 STDMETHODIMP QueryInterface(REFIID iid, VOID** ppv) override
67 {
68 if (iid == IID_IUnknown || iid == IID_IEnumString)
69 {
70 trace("IID_IEnumString\n");
71 AddRef();
72 *ppv = static_cast<IEnumString *>(this);
73 return S_OK;
74 }
75 if (iid == IID_IACList || iid == IID_IACList2)
76 {
77 trace("IID_IACList2\n");
78 AddRef();
79 *ppv = static_cast<IACList2 *>(this);
80 return S_OK;
81 }
82 return E_NOINTERFACE;
83 }
AddRef()84 STDMETHODIMP_(ULONG) AddRef() override
85 {
86 //trace("CEnumString::AddRef\n");
87 ++m_cRefs;
88 return m_cRefs;
89 }
Release()90 STDMETHODIMP_(ULONG) Release() override
91 {
92 //trace("CEnumString::Release\n");
93 --m_cRefs;
94 if (m_cRefs == 0)
95 {
96 delete this;
97 return 0;
98 }
99 return m_cRefs;
100 }
101
Next(ULONG celt,LPWSTR * rgelt,ULONG * pceltFetched)102 STDMETHODIMP Next(ULONG celt, LPWSTR* rgelt, ULONG* pceltFetched) override
103 {
104 if (rgelt)
105 *rgelt = NULL;
106 if (pceltFetched)
107 *pceltFetched = 0;
108 if (celt != 1 || !rgelt || !pceltFetched)
109 return E_INVALIDARG;
110 if (m_nIndex >= m_nCount)
111 return S_FALSE;
112
113 SHStrDupW(m_pList[m_nIndex], rgelt);
114 ++m_nIndex;
115 if (!*rgelt)
116 return E_OUTOFMEMORY;
117 *pceltFetched = 1;
118 return S_OK;
119 }
Skip(ULONG celt)120 STDMETHODIMP Skip(ULONG celt) override
121 {
122 trace("CEnumString::Skip(%lu)\n", celt);
123 return E_NOTIMPL;
124 }
Reset()125 STDMETHODIMP Reset() override
126 {
127 trace("CEnumString::Reset\n");
128 m_nIndex = 0;
129 s_bReset = TRUE;
130 return S_OK;
131 }
Clone(IEnumString ** ppenum)132 STDMETHODIMP Clone(IEnumString** ppenum) override
133 {
134 trace("CEnumString::Clone()\n");
135 return E_NOTIMPL;
136 }
137
Expand(PCWSTR pszExpand)138 STDMETHODIMP Expand(PCWSTR pszExpand) override
139 {
140 trace("CEnumString::Expand(%S)\n", pszExpand);
141 s_bExpand = TRUE;
142 s_strExpand = pszExpand;
143 return S_OK;
144 }
GetOptions(DWORD * pdwFlag)145 STDMETHODIMP GetOptions(DWORD *pdwFlag) override
146 {
147 trace("CEnumString::GetOption(%p)\n", pdwFlag);
148 return S_OK;
149 }
SetOptions(DWORD dwFlag)150 STDMETHODIMP SetOptions(DWORD dwFlag) override
151 {
152 trace("CEnumString::SetOption(0x%lX)\n", dwFlag);
153 return S_OK;
154 }
155
156 protected:
157 ULONG m_cRefs;
158 UINT m_nIndex, m_nCount;
159 LPWSTR *m_pList;
160 };
161
162 // range of WCHAR (inclusive)
163 struct RANGE
164 {
165 WCHAR from, to;
166 };
167
168 //#define OUTPUT_TABLE // generate the table to analyze
169
170 #ifndef OUTPUT_TABLE
171 // comparison of two ranges
RangeCompare(const void * x,const void * y)172 static int __cdecl RangeCompare(const void *x, const void *y)
173 {
174 const RANGE *a = (const RANGE *)x;
175 const RANGE *b = (const RANGE *)y;
176 if (a->to < b->from)
177 return -1;
178 if (b->to < a->from)
179 return 1;
180 return 0;
181 }
182
183 // is the WCHAR a word break?
IsWordBreak(WCHAR ch)184 static __inline BOOL IsWordBreak(WCHAR ch)
185 {
186 static const RANGE s_ranges[] =
187 {
188 { 0x0009, 0x0009 }, { 0x0020, 0x002f }, { 0x003a, 0x0040 }, { 0x005b, 0x0060 },
189 { 0x007b, 0x007e }, { 0x00ab, 0x00ab }, { 0x00ad, 0x00ad }, { 0x00bb, 0x00bb },
190 { 0x02c7, 0x02c7 }, { 0x02c9, 0x02c9 }, { 0x055d, 0x055d }, { 0x060c, 0x060c },
191 { 0x2002, 0x200b }, { 0x2013, 0x2014 }, { 0x2016, 0x2016 }, { 0x2018, 0x2018 },
192 { 0x201c, 0x201d }, { 0x2022, 0x2022 }, { 0x2025, 0x2027 }, { 0x2039, 0x203a },
193 { 0x2045, 0x2046 }, { 0x207d, 0x207e }, { 0x208d, 0x208e }, { 0x226a, 0x226b },
194 { 0x2574, 0x2574 }, { 0x3001, 0x3003 }, { 0x3005, 0x3005 }, { 0x3008, 0x3011 },
195 { 0x3014, 0x301b }, { 0x301d, 0x301e }, { 0x3041, 0x3041 }, { 0x3043, 0x3043 },
196 { 0x3045, 0x3045 }, { 0x3047, 0x3047 }, { 0x3049, 0x3049 }, { 0x3063, 0x3063 },
197 { 0x3083, 0x3083 }, { 0x3085, 0x3085 }, { 0x3087, 0x3087 }, { 0x308e, 0x308e },
198 { 0x309b, 0x309e }, { 0x30a1, 0x30a1 }, { 0x30a3, 0x30a3 }, { 0x30a5, 0x30a5 },
199 { 0x30a7, 0x30a7 }, { 0x30a9, 0x30a9 }, { 0x30c3, 0x30c3 }, { 0x30e3, 0x30e3 },
200 { 0x30e5, 0x30e5 }, { 0x30e7, 0x30e7 }, { 0x30ee, 0x30ee }, { 0x30f5, 0x30f6 },
201 { 0x30fc, 0x30fe }, { 0xfd3e, 0xfd3f }, { 0xfe30, 0xfe31 }, { 0xfe33, 0xfe44 },
202 { 0xfe4f, 0xfe51 }, { 0xfe59, 0xfe5e }, { 0xff08, 0xff09 }, { 0xff0c, 0xff0c },
203 { 0xff0e, 0xff0e }, { 0xff1c, 0xff1c }, { 0xff1e, 0xff1e }, { 0xff3b, 0xff3b },
204 { 0xff3d, 0xff3d }, { 0xff40, 0xff40 }, { 0xff5b, 0xff5e }, { 0xff61, 0xff64 },
205 { 0xff67, 0xff70 }, { 0xff9e, 0xff9f }, { 0xffe9, 0xffe9 }, { 0xffeb, 0xffeb },
206 };
207 #ifndef NDEBUG
208 // check the table if first time
209 static BOOL s_bFirstTime = TRUE;
210 if (s_bFirstTime)
211 {
212 s_bFirstTime = FALSE;
213 for (UINT i = 0; i < _countof(s_ranges); ++i)
214 {
215 ATLASSERT(s_ranges[i].from <= s_ranges[i].to);
216 }
217 for (UINT i = 0; i + 1 < _countof(s_ranges); ++i)
218 {
219 ATLASSERT(s_ranges[i].to < s_ranges[i + 1].from);
220 }
221 }
222 #endif
223 RANGE range = { ch, ch };
224 return !!bsearch(&range, s_ranges, _countof(s_ranges), sizeof(RANGE), RangeCompare);
225 }
226 #endif
227
DoWordBreakProc(EDITWORDBREAKPROC fn)228 static VOID DoWordBreakProc(EDITWORDBREAKPROC fn)
229 {
230 #ifdef OUTPUT_TABLE
231 // generate the table text
232 WORD wType1, wType2, wType3;
233 for (DWORD i = 0; i <= 0xFFFF; ++i)
234 {
235 WCHAR ch = (WCHAR)i;
236 GetStringTypeW(CT_CTYPE1, &ch, 1, &wType1);
237 GetStringTypeW(CT_CTYPE2, &ch, 1, &wType2);
238 GetStringTypeW(CT_CTYPE3, &ch, 1, &wType3);
239 BOOL b = fn(&ch, 0, 1, WB_ISDELIMITER);
240 trace("%u\t0x%04x\t0x%04x\t0x%04x\t0x%04x\n", b, wType1, wType2, wType3, ch);
241 }
242 #else
243 // check the word break procedure
244 for (DWORD i = 0; i <= 0xFFFF; ++i)
245 {
246 WCHAR ch = (WCHAR)i;
247 BOOL b1 = fn(&ch, 0, 1, WB_ISDELIMITER);
248 BOOL b2 = IsWordBreak(ch);
249 ok(b1 == b2, "ch:0x%04x, b1:%d, b2:%d\n", ch, b1, b2);
250 }
251 #endif
252 }
253
254 // the testcase A
255 static VOID
DoTestCaseA(INT x,INT y,INT cx,INT cy,LPCWSTR pszInput,LPWSTR * pList,UINT nCount,BOOL bDowner,BOOL bLong)256 DoTestCaseA(INT x, INT y, INT cx, INT cy, LPCWSTR pszInput,
257 LPWSTR *pList, UINT nCount, BOOL bDowner, BOOL bLong)
258 {
259 MSG msg;
260 s_bExpand = s_bReset = FALSE;
261
262 // create EDIT control
263 HWND hwndEdit = MyCreateEditCtrl(x, y, cx, cy);
264 ok(hwndEdit != NULL, "hwndEdit was NULL\n");
265 ShowWindowAsync(hwndEdit, SW_SHOWNORMAL);
266
267 // get word break procedure
268 EDITWORDBREAKPROC fn1 =
269 (EDITWORDBREAKPROC)SendMessageW(hwndEdit, EM_GETWORDBREAKPROC, 0, 0);
270 ok(fn1 == NULL, "fn1 was %p\n", fn1);
271
272 // set the list data
273 CComPtr<CEnumString> pEnum(new CEnumString());
274 pEnum->SetList(nCount, pList);
275
276 // create auto-completion object
277 CComPtr<IAutoComplete2> pAC;
278 HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
279 IID_IAutoComplete2, (VOID **)&pAC);
280 ok_hr(hr, S_OK);
281
282 // enable auto-suggest
283 hr = pAC->SetOptions(ACO_AUTOSUGGEST);
284 ok_hr(hr, S_OK);
285
286 // initialize
287 IUnknown *punk = static_cast<IEnumString *>(pEnum);
288 hr = pAC->Init(hwndEdit, punk, NULL, NULL); // IAutoComplete::Init
289 ok_hr(hr, S_OK);
290
291 #ifdef MANUAL_DEBUGGING
292 trace("enter MANUAL_DEBUGGING...\n");
293 trace("NOTE: You can quit EDIT control by Alt+F4.\n");
294 while (GetMessageW(&msg, NULL, 0, 0))
295 {
296 TranslateMessage(&msg);
297 DispatchMessageW(&msg);
298 if (!IsWindow(hwndEdit))
299 break;
300 }
301 trace("leave MANUAL_DEBUGGING...\n");
302 return;
303 #endif
304
305 // check expansion
306 ok_int(s_bExpand, FALSE);
307 // check reset
308 ok_int(s_bReset, FALSE);
309
310 // input
311 SetFocus(hwndEdit);
312 WCHAR chLast = 0;
313 for (UINT i = 0; pszInput[i]; ++i)
314 {
315 PostMessageW(hwndEdit, WM_CHAR, pszInput[i], 0);
316 chLast = pszInput[i];
317 }
318
319 // wait for hwndDropDown
320 DWORD style, exstyle;
321 HWND hwndDropDown;
322 LONG_PTR id;
323 for (INT i = 0; i < 100; ++i)
324 {
325 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
326 {
327 TranslateMessage(&msg);
328 DispatchMessageW(&msg);
329 }
330 hwndDropDown = FindWindowW(L"Auto-Suggest Dropdown", L"");
331 if (IsWindowVisible(hwndDropDown))
332 break;
333 Sleep(100);
334 }
335 ok(hwndDropDown != NULL, "hwndDropDown was NULL\n");
336 ok_int(IsWindowVisible(hwndDropDown), TRUE);
337
338 // check word break procedure
339 static BOOL s_bFirstTime = TRUE;
340 if (s_bFirstTime)
341 {
342 s_bFirstTime = FALSE;
343 EDITWORDBREAKPROC fn2 =
344 (EDITWORDBREAKPROC)SendMessageW(hwndEdit, EM_GETWORDBREAKPROC, 0, 0);
345 ok(fn1 != fn2, "fn1 == fn2\n");
346 ok(fn2 != NULL, "fn2 was NULL\n");
347 if (fn2)
348 DoWordBreakProc(fn2);
349 else
350 skip("fn2 == NULL\n");
351 }
352
353 // take care of the message queue
354 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
355 {
356 TranslateMessage(&msg);
357 DispatchMessageW(&msg);
358 }
359
360 // check reset
361 ok_int(s_bReset, TRUE);
362
363 // get sizes and positions
364 RECT rcEdit, rcDropDown;
365 GetWindowRect(hwndEdit, &rcEdit);
366 GetWindowRect(hwndDropDown, &rcDropDown);
367 trace("rcEdit: (%ld, %ld, %ld, %ld)\n", rcEdit.left, rcEdit.top, rcEdit.right, rcEdit.bottom);
368 trace("rcDropDown: (%ld, %ld, %ld, %ld)\n", rcDropDown.left, rcDropDown.top, rcDropDown.right, rcDropDown.bottom);
369
370 // is it "downer"?
371 ok_int(bDowner, rcEdit.top < rcDropDown.top);
372
373 // check window style and id
374 style = (LONG)GetWindowLongPtrW(hwndDropDown, GWL_STYLE);
375 exstyle = (LONG)GetWindowLongPtrW(hwndDropDown, GWL_EXSTYLE);
376 id = GetWindowLongPtrW(hwndDropDown, GWLP_ID);
377 #define DROPDOWN_STYLE (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | \
378 WS_CLIPCHILDREN | WS_BORDER) // 0x96800000
379 ok(style == DROPDOWN_STYLE, "style was 0x%08lx\n", style);
380 #define DROPDOWN_EX_STYLE (WS_EX_TOOLWINDOW | WS_EX_TOPMOST | \
381 WS_EX_NOPARENTNOTIFY) // 0x8c
382 ok_long(exstyle, DROPDOWN_EX_STYLE);
383 ok_long((LONG)id, 0);
384
385 // check class style
386 style = (LONG)GetClassLongPtrW(hwndDropDown, GCL_STYLE);
387 #define DROPDOWN_CLASS_STYLE_1 (CS_DROPSHADOW | CS_SAVEBITS)
388 #define DROPDOWN_CLASS_STYLE_2 0
389 ok(style == DROPDOWN_CLASS_STYLE_1 /* Win10 */ ||
390 style == DROPDOWN_CLASS_STYLE_2 /* WinXP/Win2k3 */,
391 "style was 0x%08lx\n", style);
392
393 // get client rectangle
394 RECT rcClient;
395 GetClientRect(hwndDropDown, &rcClient);
396 trace("rcClient: (%ld, %ld, %ld, %ld)\n",
397 rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
398
399 HWND hwndScrollBar, hwndSizeBox, hwndList, hwndNone;
400 WCHAR szClass[64];
401
402 // scroll bar
403 hwndScrollBar = GetTopWindow(hwndDropDown);
404 ok(hwndScrollBar != NULL, "hwndScrollBar was NULL\n");
405 GetClassNameW(hwndScrollBar, szClass, _countof(szClass));
406 ok_wstri(szClass, L"ScrollBar");
407 style = (LONG)GetWindowLongPtrW(hwndScrollBar, GWL_STYLE);
408 exstyle = (LONG)GetWindowLongPtrW(hwndScrollBar, GWL_EXSTYLE);
409 id = GetWindowLongPtrW(hwndScrollBar, GWLP_ID);
410 #define SCROLLBAR_STYLE_1 (WS_CHILD | WS_VISIBLE | SBS_BOTTOMALIGN | SBS_VERT) // 0x50000005
411 #define SCROLLBAR_STYLE_2 (WS_CHILD | SBS_BOTTOMALIGN | SBS_VERT) // 0x40000005
412 if (bLong)
413 ok(style == SCROLLBAR_STYLE_1, "style was 0x%08lx\n", style);
414 else
415 ok(style == SCROLLBAR_STYLE_2, "style was 0x%08lx\n", style);
416 ok_long(exstyle, 0);
417 ok_long((LONG)id, 0);
418
419 // size-box
420 hwndSizeBox = GetNextWindow(hwndScrollBar, GW_HWNDNEXT);
421 ok(hwndSizeBox != NULL, "hwndSizeBox was NULL\n");
422 GetClassNameW(hwndSizeBox, szClass, _countof(szClass));
423 ok_wstri(szClass, L"ScrollBar");
424 style = (LONG)GetWindowLongPtrW(hwndSizeBox, GWL_STYLE);
425 exstyle = (LONG)GetWindowLongPtrW(hwndSizeBox, GWL_EXSTYLE);
426 id = GetWindowLongPtrW(hwndSizeBox, GWLP_ID);
427 #define SIZEBOX_STYLE_1 \
428 (WS_CHILD | WS_VISIBLE | SBS_SIZEBOX | SBS_SIZEBOXBOTTOMRIGHTALIGN) // 0x5000000c
429 #define SIZEBOX_STYLE_2 \
430 (WS_CHILD | WS_VISIBLE | SBS_SIZEBOX) // 0x50000008
431 ok(style == SIZEBOX_STYLE_1 /* Win10 */ ||
432 style == SIZEBOX_STYLE_2 /* Win2k3/WinXP */, "style was 0x%08lx\n", style);
433 ok_long(exstyle, 0);
434 ok_long((LONG)id, 0);
435
436 // the list
437 hwndList = GetNextWindow(hwndSizeBox, GW_HWNDNEXT);
438 ok(hwndList != NULL, "hwndList was NULL\n");
439 GetClassNameW(hwndList, szClass, _countof(szClass));
440 ok_wstri(szClass, WC_LISTVIEWW); // L"SysListView32"
441 style = (LONG)GetWindowLongPtrW(hwndList, GWL_STYLE);
442 exstyle = (LONG)GetWindowLongPtrW(hwndList, GWL_EXSTYLE);
443 id = GetWindowLongPtrW(hwndList, GWLP_ID);
444 #define LIST_STYLE_1 \
445 (WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_CLIPSIBLINGS | \
446 LVS_NOCOLUMNHEADER | LVS_OWNERDATA | LVS_OWNERDRAWFIXED | \
447 LVS_SINGLESEL | LVS_REPORT) // 0x54205405
448 #define LIST_STYLE_2 \
449 (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | LVS_NOCOLUMNHEADER | \
450 LVS_OWNERDATA | LVS_OWNERDRAWFIXED | LVS_SINGLESEL | LVS_REPORT) // 0x54005405
451 if (bLong)
452 (void)0; // ok(style == LIST_STYLE_1, "style was 0x%08lx\n", style); broken on Windows
453 else
454 ok(style == LIST_STYLE_2, "style was 0x%08lx\n", style);
455 ok_long(exstyle, 0);
456 ok_long((LONG)id, 0);
457 #define LIST_EXTENDED_LV_STYLE_1 \
458 (LVS_EX_DOUBLEBUFFER | LVS_EX_ONECLICKACTIVATE | \
459 LVS_EX_FULLROWSELECT | LVS_EX_TRACKSELECT) // 0x10068
460 #define LIST_EXTENDED_LV_STYLE_2 \
461 (LVS_EX_ONECLICKACTIVATE | LVS_EX_FULLROWSELECT | \
462 LVS_EX_TRACKSELECT) // 0x68
463 exstyle = ListView_GetExtendedListViewStyle(hwndList);
464 ok(exstyle == LIST_EXTENDED_LV_STYLE_1 /* Win10 */ ||
465 exstyle == LIST_EXTENDED_LV_STYLE_2 /* WinXP/Win2k3 */,
466 "exstyle was 0x%08lx\n", exstyle);
467
468 // no more controls
469 hwndNone = GetNextWindow(hwndList, GW_HWNDNEXT);
470 ok(hwndNone == NULL, "hwndNone was %p\n", hwndNone);
471
472 // get rectangles of controls
473 RECT rcScrollBar, rcSizeBox, rcList;
474 GetWindowRect(hwndScrollBar, &rcScrollBar);
475 MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcScrollBar, 2);
476 GetWindowRect(hwndSizeBox, &rcSizeBox);
477 MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcSizeBox, 2);
478 GetWindowRect(hwndList, &rcList);
479 MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcList, 2);
480 trace("rcScrollBar: (%ld, %ld, %ld, %ld)\n", rcScrollBar.left, rcScrollBar.top,
481 rcScrollBar.right, rcScrollBar.bottom);
482 trace("rcSizeBox: (%ld, %ld, %ld, %ld)\n", rcSizeBox.left, rcSizeBox.top,
483 rcSizeBox.right, rcSizeBox.bottom);
484 trace("rcList: (%ld, %ld, %ld, %ld)\n", rcList.left, rcList.top,
485 rcList.right, rcList.bottom);
486
487 // are they visible?
488 ok_int(IsWindowVisible(hwndDropDown), TRUE);
489 ok_int(IsWindowVisible(hwndEdit), TRUE);
490 ok_int(IsWindowVisible(hwndSizeBox), TRUE);
491 ok_int(IsWindowVisible(hwndList), TRUE);
492
493 // check item count
494 INT nListCount = ListView_GetItemCount(hwndList);
495 if (nListCount < 1000)
496 (void)0; // ok_int(nListCount, nCount); broken on Windows
497 else
498 ok_int(nListCount, 1000);
499
500 // check the positions
501 if (bDowner) // downer
502 {
503 ok_int(rcDropDown.left, rcEdit.left);
504 ok_int(rcDropDown.top, rcEdit.bottom);
505 ok_int(rcDropDown.right, rcEdit.right);
506 //ok_int(rcDropDown.bottom, ???);
507 ok_int(rcSizeBox.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
508 ok_int(rcSizeBox.top, rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL));
509 ok_int(rcSizeBox.right, rcClient.right);
510 ok_int(rcSizeBox.bottom, rcClient.bottom);
511 ok_int(rcScrollBar.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
512 ok_int(rcScrollBar.top, 0);
513 ok_int(rcScrollBar.right, rcClient.right);
514 ok_int(rcScrollBar.bottom, rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL));
515 ok_int(rcList.left, 0);
516 ok_int(rcList.top, 0);
517 //ok_int(rcList.right, 30160 or 30170???);
518 ok_int(rcList.bottom, rcClient.bottom);
519 }
520 else // upper
521 {
522 ok_int(rcDropDown.left, rcEdit.left);
523 //ok_int(rcDropDown.top, ???);
524 ok_int(rcDropDown.right, rcEdit.right);
525 ok_int(rcDropDown.bottom, rcEdit.top);
526 ok_int(rcSizeBox.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
527 ok_int(rcSizeBox.top, 0);
528 ok_int(rcSizeBox.right, rcClient.right);
529 ok_int(rcSizeBox.bottom, rcClient.top + GetSystemMetrics(SM_CYHSCROLL));
530 ok_int(rcScrollBar.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
531 ok_int(rcScrollBar.top, GetSystemMetrics(SM_CYHSCROLL));
532 ok_int(rcScrollBar.right, rcClient.right);
533 ok_int(rcScrollBar.bottom, rcClient.bottom);
534 ok_int(rcList.left, 0);
535 ok_int(rcList.top, 0);
536 //ok_int(rcList.right, 30160 or 30170???);
537 ok_int(rcList.bottom, rcClient.bottom);
538 }
539
540 // append WM_QUIT message into message queue
541 PostQuitMessage(0);
542
543 // do the messages
544 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
545 {
546 TranslateMessage(&msg);
547 DispatchMessageW(&msg);
548 Sleep(30); // another thread is working...
549 }
550
551 // destroy the EDIT control and drop-down window
552 DestroyWindow(hwndEdit);
553 DestroyWindow(hwndDropDown);
554
555 // do the messages
556 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
557 {
558 TranslateMessage(&msg);
559 DispatchMessageW(&msg);
560 }
561
562 // check the expansion
563 ok_int(s_bExpand, chLast == L'\\');
564 }
565
566 struct TEST_B_ENTRY
567 {
568 INT m_line;
569 UINT m_uMsg;
570 WPARAM m_wParam;
571 LPARAM m_lParam;
572 LPCWSTR m_text;
573 BOOL m_bVisible;
574 INT m_ich0, m_ich1;
575 INT m_iItem;
576 BOOL m_bReset, m_bExpand;
577 LPCWSTR m_expand;
578 };
579
580 static BOOL
DoesMatch(LPWSTR * pList,UINT nCount,LPCWSTR psz)581 DoesMatch(LPWSTR *pList, UINT nCount, LPCWSTR psz)
582 {
583 INT cch = lstrlenW(psz);
584 if (cch == 0)
585 return FALSE;
586 for (UINT i = 0; i < nCount; ++i)
587 {
588 if (::StrCmpNIW(pList[i], psz, cch) == 0)
589 return TRUE;
590 }
591 return FALSE;
592 }
593
594 // the testcase B
595 static VOID
DoTestCaseB(INT x,INT y,INT cx,INT cy,LPWSTR * pList,UINT nCount,const TEST_B_ENTRY * pEntries,UINT cEntries)596 DoTestCaseB(INT x, INT y, INT cx, INT cy, LPWSTR *pList, UINT nCount,
597 const TEST_B_ENTRY *pEntries, UINT cEntries)
598 {
599 MSG msg;
600 s_bExpand = s_bReset = FALSE;
601
602 // create EDIT control
603 HWND hwndEdit = MyCreateEditCtrl(x, y, cx, cy);
604 ok(hwndEdit != NULL, "hwndEdit was NULL\n");
605 ShowWindowAsync(hwndEdit, SW_SHOWNORMAL);
606
607 // do messages
608 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
609 {
610 TranslateMessage(&msg);
611 DispatchMessageW(&msg);
612 }
613 ok_int(IsWindowVisible(hwndEdit), TRUE);
614
615 // set the list data
616 CComPtr<CEnumString> pEnum(new CEnumString());
617 pEnum->SetList(nCount, pList);
618
619 // create auto-completion object
620 CComPtr<IAutoComplete2> pAC;
621 HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
622 IID_IAutoComplete2, (VOID **)&pAC);
623 ok_hr(hr, S_OK);
624
625 // enable auto-suggest
626 hr = pAC->SetOptions(ACO_AUTOSUGGEST);
627 ok_hr(hr, S_OK);
628
629 // initialize
630 IUnknown *punk = static_cast<IEnumString *>(pEnum);
631 hr = pAC->Init(hwndEdit, punk, NULL, NULL); // IAutoComplete::Init
632 ok_hr(hr, S_OK);
633
634 HWND hwndDropDown = NULL, hwndScrollBar = NULL, hwndSizeBox = NULL, hwndList = NULL;
635 // input
636 SetFocus(hwndEdit);
637 Sleep(100);
638
639 for (UINT i = 0; i < cEntries; ++i)
640 {
641 const TEST_B_ENTRY& entry = pEntries[i];
642 s_bReset = s_bExpand = FALSE;
643 s_strExpand.Empty();
644
645 UINT uMsg = entry.m_uMsg;
646 WPARAM wParam = entry.m_wParam;
647 LPARAM lParam = entry.m_lParam;
648 PostMessageW(hwndEdit, uMsg, wParam, lParam);
649
650 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
651 {
652 TranslateMessage(&msg);
653 DispatchMessageW(&msg);
654
655 if (msg.message == WM_CHAR || msg.message == WM_KEYDOWN)
656 Sleep(100); // another thread is working
657 }
658
659 if (!IsWindow(hwndDropDown))
660 {
661 hwndDropDown = FindWindowW(L"Auto-Suggest Dropdown", L"");
662 hwndScrollBar = hwndSizeBox = hwndList = NULL;
663 }
664 if (IsWindowVisible(hwndDropDown))
665 {
666 hwndScrollBar = GetTopWindow(hwndDropDown);
667 hwndSizeBox = GetNextWindow(hwndScrollBar, GW_HWNDNEXT);
668 hwndList = GetNextWindow(hwndSizeBox, GW_HWNDNEXT);
669 }
670
671 WCHAR szText[64];
672 GetWindowTextW(hwndEdit, szText, _countof(szText));
673 CStringW strText = szText;
674 INT ich0, ich1;
675 SendMessageW(hwndEdit, EM_GETSEL, (WPARAM)&ich0, (LPARAM)&ich1);
676
677 BOOL bVisible = IsWindowVisible(hwndDropDown);
678 ok(bVisible == entry.m_bVisible, "Line %d: bVisible was %d\n", entry.m_line, bVisible);
679 INT iItem = -1;
680 if (IsWindowVisible(hwndList))
681 {
682 iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED);
683 }
684 BOOL bDidMatch = DoesMatch(pList, nCount, strText);
685 ok(bVisible == bDidMatch, "Line %d: bVisible != bDidMatch\n", entry.m_line);
686
687 if (entry.m_text)
688 ok(strText == entry.m_text, "Line %d: szText was %S\n", entry.m_line, (LPCWSTR)strText);
689 else
690 ok(strText == L"", "Line %d: szText was %S\n", entry.m_line, (LPCWSTR)strText);
691 ok(ich0 == entry.m_ich0, "Line %d: ich0 was %d\n", entry.m_line, ich0);
692 ok(ich1 == entry.m_ich1, "Line %d: ich1 was %d\n", entry.m_line, ich1);
693 ok(iItem == entry.m_iItem, "Line %d: iItem was %d\n", entry.m_line, iItem);
694 ok(s_bReset == entry.m_bReset, "Line %d: s_bReset was %d\n", entry.m_line, s_bReset);
695 ok(s_bExpand == entry.m_bExpand, "Line %d: s_bExpand was %d\n", entry.m_line, s_bExpand);
696 if (entry.m_expand)
697 ok(s_strExpand == entry.m_expand, "Line %d: s_strExpand was %S\n", entry.m_line, (LPCWSTR)s_strExpand);
698 else
699 ok(s_strExpand == L"", "Line %d: s_strExpand was %S\n", entry.m_line, (LPCWSTR)s_strExpand);
700 }
701
702 DestroyWindow(hwndEdit);
703 }
704
START_TEST(IAutoComplete)705 START_TEST(IAutoComplete)
706 {
707 // initialize COM
708 CCoInit init;
709 ok_hr(init.hr, S_OK);
710 if (!SUCCEEDED(init.hr))
711 {
712 skip("CoInitialize failed\n");
713 return;
714 }
715
716 // get screen size
717 HMONITOR hMon = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTOPRIMARY);
718 MONITORINFO mi = { sizeof(mi) };
719 GetMonitorInfoW(hMon, &mi);
720 const RECT& rcWork = mi.rcWork;
721 trace("rcWork: (%ld, %ld, %ld, %ld)\n",
722 rcWork.left, rcWork.top, rcWork.right, rcWork.bottom);
723 trace("SM_CXVSCROLL: %d, SM_CYHSCROLL: %d\n",
724 GetSystemMetrics(SM_CXVSCROLL), GetSystemMetrics(SM_CYHSCROLL));
725
726 UINT nCount;
727 LPWSTR *pList;
728 WCHAR szText[64];
729
730 // Test case #1 (A)
731 trace("Testcase #1 (downer, short) ------------------------------\n");
732 nCount = 3;
733 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
734 SHStrDupW(L"test\\AA", &pList[0]);
735 SHStrDupW(L"test\\BBB", &pList[1]);
736 SHStrDupW(L"test\\CCC", &pList[2]);
737 DoTestCaseA(0, 0, 100, 30, L"test\\", pList, nCount, TRUE, FALSE);
738
739 // Test case #2 (A)
740 trace("Testcase #2 (downer, long) ------------------------------\n");
741 nCount = 300;
742 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
743 for (UINT i = 0; i < nCount; ++i)
744 {
745 StringCbPrintfW(szText, sizeof(szText), L"test\\%u", i);
746 SHStrDupW(szText, &pList[i]);
747 }
748 DoTestCaseA(100, 20, 100, 30, L"test\\", pList, nCount, TRUE, TRUE);
749
750 // Test case #3 (A)
751 trace("Testcase #3 (upper, short) ------------------------------\n");
752 nCount = 2;
753 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
754 SHStrDupW(L"test/AA", &pList[0]);
755 SHStrDupW(L"test/BBB", &pList[0]);
756 SHStrDupW(L"test/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", &pList[1]);
757 DoTestCaseA(rcWork.right - 100, rcWork.bottom - 30, 80, 40, L"test/",
758 pList, nCount, FALSE, FALSE);
759
760 // Test case #4 (A)
761 trace("Testcase #4 (upper, short) ------------------------------\n");
762 nCount = 2;
763 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
764 SHStrDupW(L"testtest\\AA", &pList[0]);
765 SHStrDupW(L"testtest\\BBB", &pList[0]);
766 SHStrDupW(L"testtest\\CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", &pList[1]);
767 DoTestCaseA(rcWork.right - 100, rcWork.bottom - 30, 80, 40, L"testtest\\",
768 pList, nCount, FALSE, FALSE);
769
770 #ifdef MANUAL_DEBUGGING
771 return;
772 #endif
773
774 // Test case #5 (A)
775 trace("Testcase #5 (upper, long) ------------------------------\n");
776 nCount = 300;
777 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
778 for (UINT i = 0; i < nCount; ++i)
779 {
780 StringCbPrintfW(szText, sizeof(szText), L"testtest/%u", i);
781 SHStrDupW(szText, &pList[i]);
782 }
783 DoTestCaseA(0, rcWork.bottom - 30, 80, 30, L"testtest/", pList, nCount, FALSE, TRUE);
784
785 // Test case #6 (A)
786 trace("Testcase #6 (upper, long) ------------------------------\n");
787 nCount = 2000;
788 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
789 for (UINT i = 0; i < nCount; ++i)
790 {
791 StringCbPrintfW(szText, sizeof(szText), L"testtest\\item-%u", i);
792 SHStrDupW(szText, &pList[i]);
793 }
794 DoTestCaseA(0, rcWork.bottom - 30, 80, 40, L"testtest\\", pList, nCount, FALSE, TRUE);
795
796 // Test case #7 (B)
797 trace("Testcase #7 ------------------------------\n");
798 nCount = 16;
799 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
800 for (UINT i = 0; i < nCount; ++i)
801 {
802 StringCbPrintfW(szText, sizeof(szText), L"test\\item-%u", i);
803 SHStrDupW(szText, &pList[i]);
804 }
805 static const TEST_B_ENTRY testcase7_entries[] =
806 {
807 { __LINE__, WM_CHAR, L't', 0, L"t", TRUE, 1, 1, -1, TRUE },
808 { __LINE__, WM_CHAR, L'e', 0, L"te", TRUE, 2, 2, -1 },
809 { __LINE__, WM_CHAR, L's', 0, L"tes", TRUE, 3, 3, -1 },
810 { __LINE__, WM_CHAR, L'T', 0, L"tesT", TRUE, 4, 4, -1 },
811 { __LINE__, WM_CHAR, L'\\', 0, L"tesT\\", TRUE, 5, 5, -1, TRUE, TRUE, L"tesT\\" },
812 { __LINE__, WM_CHAR, L't', 0, L"tesT\\t", FALSE, 6, 6, -1 },
813 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tesT\\", TRUE, 5, 5, -1 },
814 { __LINE__, WM_CHAR, L'i', 0, L"tesT\\i", TRUE, 6, 6, -1 },
815 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"test\\item-0", TRUE, 11, 11, 0 },
816 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"test\\item-1", TRUE, 11, 11, 1 },
817 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"test\\item-0", TRUE, 11, 11, 0 },
818 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"tesT\\i", TRUE, 6, 6, -1 },
819 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"test\\item-9", TRUE, 11, 11, 15 },
820 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"tesT\\i", TRUE, 6, 6, -1 },
821 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tesT\\", TRUE, 5, 5, -1 },
822 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tesT", TRUE, 4, 4, -1, TRUE },
823 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tes", TRUE, 3, 3, -1 },
824 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"te", TRUE, 2, 2, -1 },
825 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"t", TRUE, 1, 1, -1 },
826 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"", FALSE, 0, 0, -1 },
827 { __LINE__, WM_CHAR, L't', 0, L"t", TRUE, 1, 1, -1, TRUE },
828 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"t", TRUE, 0, 0, -1 },
829 { __LINE__, WM_KEYDOWN, VK_RIGHT, 0, L"t", TRUE, 1, 1, -1 },
830 };
831 DoTestCaseB(0, 0, 100, 30, pList, nCount, testcase7_entries, _countof(testcase7_entries));
832
833 // Test case #8 (B)
834 trace("Testcase #8 ------------------------------\n");
835 nCount = 10;
836 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
837 for (UINT i = 0; i < nCount; ++i)
838 {
839 StringCbPrintfW(szText, sizeof(szText), L"test\\item-%u", i);
840 SHStrDupW(szText, &pList[i]);
841 }
842 static const TEST_B_ENTRY testcase8_entries[] =
843 {
844 { __LINE__, WM_CHAR, L'a', 0, L"a", FALSE, 1, 1, -1, TRUE },
845 { __LINE__, WM_CHAR, L'b', 0, L"ab", FALSE, 2, 2, -1 },
846 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"ab", FALSE, 1, 1, -1 },
847 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"ab", FALSE, 0, 0, -1 },
848 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"b", FALSE, 0, 0, -1, TRUE },
849 { __LINE__, WM_CHAR, L't', 0, L"tb", FALSE, 1, 1, -1, TRUE },
850 { __LINE__, WM_CHAR, L'e', 0, L"teb", FALSE, 2, 2, -1 },
851 { __LINE__, WM_CHAR, L's', 0, L"tesb", FALSE, 3, 3, -1 },
852 { __LINE__, WM_CHAR, L't', 0, L"testb", FALSE, 4, 4, -1 },
853 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test", TRUE, 4, 4, -1 },
854 { __LINE__, WM_CHAR, L'\\', 0, L"test\\", TRUE, 5, 5, -1, TRUE, TRUE, L"test\\" },
855 { __LINE__, WM_CHAR, L'i', 0, L"test\\i", TRUE, 6, 6, -1 },
856 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test\\i", TRUE, 6, 6, -1 },
857 { __LINE__, WM_CHAR, L'z', 0, L"test\\iz", FALSE, 7, 7, -1 },
858 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\iz", FALSE, 6, 6, -1 },
859 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\iz", FALSE, 5, 5, -1 },
860 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test\\z", FALSE, 5, 5, -1 },
861 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test\\", TRUE, 5, 5, -1 },
862 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\", TRUE, 4, 4, -1 },
863 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\", TRUE, 3, 3, -1 },
864 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tet\\", FALSE, 2, 2, -1, TRUE, TRUE, L"tet\\" },
865 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"te\\", FALSE, 2, 2, -1, TRUE, TRUE, L"te\\" },
866 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"te", TRUE, 2, 2, -1, TRUE },
867 };
868 DoTestCaseB(rcWork.right - 100, rcWork.bottom - 30, 80, 40, pList, nCount, testcase8_entries, _countof(testcase8_entries));
869
870 // Test case #9 (B)
871 trace("Testcase #9 ------------------------------\n");
872 nCount = 32;
873 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
874 for (UINT i = 0; i < nCount; ++i)
875 {
876 StringCbPrintfW(szText, sizeof(szText), L"a%u\\b%u\\c%u", i % 2, (i / 2) % 2, i % 4);
877 SHStrDupW(szText, &pList[i]);
878 }
879 static const TEST_B_ENTRY testcase9_entries[] =
880 {
881 { __LINE__, WM_CHAR, L'a', 0, L"a", TRUE, 1, 1, -1, TRUE },
882 { __LINE__, WM_CHAR, L'0', 0, L"a0", TRUE, 2, 2, -1 },
883 { __LINE__, WM_CHAR, L'\\', 0, L"a0\\", TRUE, 3, 3, -1, TRUE, TRUE, L"a0\\" },
884 { __LINE__, WM_CHAR, L'b', 0, L"a0\\b", TRUE, 4, 4, -1 },
885 { __LINE__, WM_CHAR, L'1', 0, L"a0\\b1", TRUE, 5, 5, -1 },
886 { __LINE__, WM_CHAR, L'\\', 0, L"a0\\b1\\", TRUE, 6, 6, -1, TRUE, TRUE, L"a0\\b1\\" },
887 { __LINE__, WM_CHAR, L'c', 0, L"a0\\b1\\c", TRUE, 7, 7, -1 },
888 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\b1\\", TRUE, 6, 6, -1 },
889 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\b1", TRUE, 5, 5, -1, TRUE, TRUE, L"a0\\" },
890 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\b", TRUE, 4, 4, -1 },
891 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\", TRUE, 3, 3, -1 },
892 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0", TRUE, 2, 2, -1, TRUE },
893 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a", TRUE, 1, 1, -1 },
894 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"a0\\b0\\c0", TRUE, 8, 8, 0 },
895 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"a", TRUE, 1, 1, -1 },
896 };
897 DoTestCaseB(0, 0, 100, 30, pList, nCount, testcase9_entries, _countof(testcase9_entries));
898 }
899