https://git.reactos.org/?p=reactos.git;a=commitdiff;h=003b19dc3cf9c944f7419…
commit 003b19dc3cf9c944f74197c5ab88cf58b84a8d61
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Tue Mar 2 07:25:31 2021 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Tue Mar 2 07:25:31 2021 +0900
[BROWSEUI_APITEST] Add IAutoComplete testcase (#3493)
Add tests for IAutoComplete objects, especially on the drop-down window and related
controls.
CORE-9281
---
modules/rostests/apitests/browseui/CMakeLists.txt | 3 +-
.../rostests/apitests/browseui/IAutoComplete.cpp | 752 +++++++++++++++++++++
modules/rostests/apitests/browseui/testlist.c | 2 +
3 files changed, 756 insertions(+), 1 deletion(-)
diff --git a/modules/rostests/apitests/browseui/CMakeLists.txt
b/modules/rostests/apitests/browseui/CMakeLists.txt
index efae63cf8d9..a3448d69535 100644
--- a/modules/rostests/apitests/browseui/CMakeLists.txt
+++ b/modules/rostests/apitests/browseui/CMakeLists.txt
@@ -5,6 +5,7 @@ include_directories(
list(APPEND SOURCE
ACListISF.cpp
IACLCustomMRU.cpp
+ IAutoComplete.cpp
SHEnumClassesOfCategories.cpp
SHExplorerParseCmdLine.c
testlist.c)
@@ -14,5 +15,5 @@ add_executable(browseui_apitest ${SOURCE})
target_link_libraries(browseui_apitest uuid wine cpprt atl_classes)
set_target_cpp_properties(browseui_apitest WITH_EXCEPTIONS WITH_RTTI)
set_module_type(browseui_apitest win32cui)
-add_importlibs(browseui_apitest advapi32 shell32 ole32 shlwapi msvcrt kernel32 ntdll)
+add_importlibs(browseui_apitest advapi32 shell32 ole32 shlwapi msvcrt user32 kernel32
ntdll)
add_rostests_file(TARGET browseui_apitest)
diff --git a/modules/rostests/apitests/browseui/IAutoComplete.cpp
b/modules/rostests/apitests/browseui/IAutoComplete.cpp
new file mode 100644
index 00000000000..c438c873828
--- /dev/null
+++ b/modules/rostests/apitests/browseui/IAutoComplete.cpp
@@ -0,0 +1,752 @@
+/*
+ * PROJECT: ReactOS api tests
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Test for IAutoComplete objects
+ * COPYRIGHT: Copyright 2021 Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
+ */
+#define _UNICODE
+#define UNICODE
+#include <apitest.h>
+#include <shlobj.h>
+#include <atlbase.h>
+#include <tchar.h>
+#include <atlcom.h>
+#include <atlwin.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+//#define MANUAL_DEBUGGING
+
+// compare wide strings
+#define ok_wstri(x, y) \
+ ok(lstrcmpiW(x, y) == 0, "Wrong string. Expected '%S', got
'%S'\n", y, x)
+
+struct CCoInit
+{
+ CCoInit() { hr = CoInitialize(NULL); }
+ ~CCoInit() { if (SUCCEEDED(hr)) { CoUninitialize(); } }
+ HRESULT hr;
+};
+
+// create an EDIT control
+static HWND MyCreateEditCtrl(INT x, INT y, INT cx, INT cy)
+{
+ DWORD style = WS_POPUPWINDOW | WS_BORDER;
+ DWORD exstyle = WS_EX_CLIENTEDGE;
+ return CreateWindowExW(exstyle, L"EDIT", NULL, style, x, y, cx, cy,
+ NULL, NULL, GetModuleHandleW(NULL), NULL);
+}
+
+static BOOL s_bReset = FALSE;
+static BOOL s_bExpand = FALSE;
+
+// CEnumString class for auto-completion test
+class CEnumString : public IEnumString, public IACList2
+{
+public:
+ CEnumString() : m_cRefs(0), m_nIndex(0), m_nCount(0), m_pList(NULL)
+ {
+ trace("CEnumString::CEnumString(%p)\n", this);
+ }
+
+ virtual ~CEnumString()
+ {
+ trace("CEnumString::~CEnumString(%p)\n", this);
+ for (UINT i = 0; i < m_nCount; ++i)
+ {
+ CoTaskMemFree(m_pList[i]);
+ m_pList[i] = NULL;
+ }
+ CoTaskMemFree(m_pList);
+ }
+
+ VOID SetList(UINT nCount, LPWSTR *pList)
+ {
+ m_nCount = nCount;
+ m_pList = pList;
+ }
+
+ STDMETHODIMP QueryInterface(REFIID iid, VOID** ppv) override
+ {
+ if (iid == IID_IUnknown || iid == IID_IEnumString)
+ {
+ trace("IID_IEnumString\n");
+ AddRef();
+ *ppv = static_cast<IEnumString *>(this);
+ return S_OK;
+ }
+ if (iid == IID_IACList || iid == IID_IACList2)
+ {
+ trace("IID_IACList2\n");
+ AddRef();
+ *ppv = static_cast<IACList2 *>(this);
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+ STDMETHODIMP_(ULONG) AddRef() override
+ {
+ //trace("CEnumString::AddRef\n");
+ ++m_cRefs;
+ return m_cRefs;
+ }
+ STDMETHODIMP_(ULONG) Release() override
+ {
+ //trace("CEnumString::Release\n");
+ --m_cRefs;
+ if (m_cRefs == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return m_cRefs;
+ }
+
+ STDMETHODIMP Next(ULONG celt, LPWSTR* rgelt, ULONG* pceltFetched) override
+ {
+ if (rgelt)
+ *rgelt = NULL;
+ if (pceltFetched)
+ *pceltFetched = 0;
+ if (celt != 1 || !rgelt || !pceltFetched)
+ return E_INVALIDARG;
+ if (m_nIndex >= m_nCount)
+ return S_FALSE;
+
+ SHStrDupW(m_pList[m_nIndex], rgelt);
+ ++m_nIndex;
+ if (!*rgelt)
+ return E_OUTOFMEMORY;
+ *pceltFetched = 1;
+ return S_OK;
+ }
+ STDMETHODIMP Skip(ULONG celt) override
+ {
+ trace("CEnumString::Skip(%lu)\n", celt);
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP Reset() override
+ {
+ trace("CEnumString::Reset\n");
+ m_nIndex = 0;
+ s_bReset = TRUE;
+ return S_OK;
+ }
+ STDMETHODIMP Clone(IEnumString** ppenum) override
+ {
+ trace("CEnumString::Clone()\n");
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP Expand(PCWSTR pszExpand) override
+ {
+ trace("CEnumString::Expand(%S)\n", pszExpand);
+ s_bExpand = TRUE;
+ return S_OK;
+ }
+ STDMETHODIMP GetOptions(DWORD *pdwFlag) override
+ {
+ trace("CEnumString::GetOption(%p)\n", pdwFlag);
+ return S_OK;
+ }
+ STDMETHODIMP SetOptions(DWORD dwFlag) override
+ {
+ trace("CEnumString::SetOption(0x%lX)\n", dwFlag);
+ return S_OK;
+ }
+
+protected:
+ ULONG m_cRefs;
+ UINT m_nIndex, m_nCount;
+ LPWSTR *m_pList;
+};
+
+// range of WCHAR (inclusive)
+struct RANGE
+{
+ WCHAR from, to;
+};
+
+//#define OUTPUT_TABLE // generate the table to analyze
+
+#ifndef OUTPUT_TABLE
+// comparison of two ranges
+static int __cdecl RangeCompare(const void *x, const void *y)
+{
+ const RANGE *a = (const RANGE *)x;
+ const RANGE *b = (const RANGE *)y;
+ if (a->to < b->from)
+ return -1;
+ if (b->to < a->from)
+ return 1;
+ return 0;
+}
+
+// is the WCHAR a word break?
+static __inline BOOL IsWordBreak(WCHAR ch)
+{
+ static const RANGE s_ranges[] =
+ {
+ { 0x0009, 0x0009 }, { 0x0020, 0x002f }, { 0x003a, 0x0040 }, { 0x005b, 0x0060 },
+ { 0x007b, 0x007e }, { 0x00ab, 0x00ab }, { 0x00ad, 0x00ad }, { 0x00bb, 0x00bb },
+ { 0x02c7, 0x02c7 }, { 0x02c9, 0x02c9 }, { 0x055d, 0x055d }, { 0x060c, 0x060c },
+ { 0x2002, 0x200b }, { 0x2013, 0x2014 }, { 0x2016, 0x2016 }, { 0x2018, 0x2018 },
+ { 0x201c, 0x201d }, { 0x2022, 0x2022 }, { 0x2025, 0x2027 }, { 0x2039, 0x203a },
+ { 0x2045, 0x2046 }, { 0x207d, 0x207e }, { 0x208d, 0x208e }, { 0x226a, 0x226b },
+ { 0x2574, 0x2574 }, { 0x3001, 0x3003 }, { 0x3005, 0x3005 }, { 0x3008, 0x3011 },
+ { 0x3014, 0x301b }, { 0x301d, 0x301e }, { 0x3041, 0x3041 }, { 0x3043, 0x3043 },
+ { 0x3045, 0x3045 }, { 0x3047, 0x3047 }, { 0x3049, 0x3049 }, { 0x3063, 0x3063 },
+ { 0x3083, 0x3083 }, { 0x3085, 0x3085 }, { 0x3087, 0x3087 }, { 0x308e, 0x308e },
+ { 0x309b, 0x309e }, { 0x30a1, 0x30a1 }, { 0x30a3, 0x30a3 }, { 0x30a5, 0x30a5 },
+ { 0x30a7, 0x30a7 }, { 0x30a9, 0x30a9 }, { 0x30c3, 0x30c3 }, { 0x30e3, 0x30e3 },
+ { 0x30e5, 0x30e5 }, { 0x30e7, 0x30e7 }, { 0x30ee, 0x30ee }, { 0x30f5, 0x30f6 },
+ { 0x30fc, 0x30fe }, { 0xfd3e, 0xfd3f }, { 0xfe30, 0xfe31 }, { 0xfe33, 0xfe44 },
+ { 0xfe4f, 0xfe51 }, { 0xfe59, 0xfe5e }, { 0xff08, 0xff09 }, { 0xff0c, 0xff0c },
+ { 0xff0e, 0xff0e }, { 0xff1c, 0xff1c }, { 0xff1e, 0xff1e }, { 0xff3b, 0xff3b },
+ { 0xff3d, 0xff3d }, { 0xff40, 0xff40 }, { 0xff5b, 0xff5e }, { 0xff61, 0xff64 },
+ { 0xff67, 0xff70 }, { 0xff9e, 0xff9f }, { 0xffe9, 0xffe9 }, { 0xffeb, 0xffeb },
+ };
+#ifndef NDEBUG
+ // check the table if first time
+ static BOOL s_bFirstTime = TRUE;
+ if (s_bFirstTime)
+ {
+ s_bFirstTime = FALSE;
+ for (UINT i = 0; i < _countof(s_ranges); ++i)
+ {
+ ATLASSERT(s_ranges[i].from <= s_ranges[i].to);
+ }
+ for (UINT i = 0; i + 1 < _countof(s_ranges); ++i)
+ {
+ ATLASSERT(s_ranges[i].to < s_ranges[i + 1].from);
+ }
+ }
+#endif
+ RANGE range = { ch, ch };
+ return !!bsearch(&range, s_ranges, _countof(s_ranges), sizeof(RANGE),
RangeCompare);
+}
+#endif
+
+static VOID DoWordBreakProc(EDITWORDBREAKPROC fn)
+{
+#ifdef OUTPUT_TABLE
+ // generate the table text
+ WORD wType1, wType2, wType3;
+ for (DWORD i = 0; i <= 0xFFFF; ++i)
+ {
+ WCHAR ch = (WCHAR)i;
+ GetStringTypeW(CT_CTYPE1, &ch, 1, &wType1);
+ GetStringTypeW(CT_CTYPE2, &ch, 1, &wType2);
+ GetStringTypeW(CT_CTYPE3, &ch, 1, &wType3);
+ BOOL b = fn(&ch, 0, 1, WB_ISDELIMITER);
+ trace("%u\t0x%04x\t0x%04x\t0x%04x\t0x%04x\n", b, wType1, wType2,
wType3, ch);
+ }
+#else
+ // check the word break procedure
+ for (DWORD i = 0; i <= 0xFFFF; ++i)
+ {
+ WCHAR ch = (WCHAR)i;
+ BOOL b1 = fn(&ch, 0, 1, WB_ISDELIMITER);
+ BOOL b2 = IsWordBreak(ch);
+ ok(b1 == b2, "ch:0x%04x, b1:%d, b2:%d\n", ch, b1, b2);
+ }
+#endif
+}
+
+// the testcase A
+static VOID
+DoTestCaseA(INT x, INT y, INT cx, INT cy, LPCWSTR pszInput,
+ LPWSTR *pList, UINT nCount, BOOL bDowner, BOOL bLong)
+{
+ MSG msg;
+ s_bExpand = s_bReset = FALSE;
+
+ // create EDIT control
+ HWND hwndEdit = MyCreateEditCtrl(x, y, cx, cy);
+ ok(hwndEdit != NULL, "hwndEdit was NULL\n");
+ ShowWindowAsync(hwndEdit, SW_SHOWNORMAL);
+
+ // get word break procedure
+ EDITWORDBREAKPROC fn1 =
+ (EDITWORDBREAKPROC)SendMessageW(hwndEdit, EM_GETWORDBREAKPROC, 0, 0);
+ ok(fn1 == NULL, "fn1 was %p\n", fn1);
+
+ // set the list data
+ CComPtr<CEnumString> pEnum(new CEnumString());
+ pEnum->SetList(nCount, pList);
+
+ // create auto-completion object
+ CComPtr<IAutoComplete2> pAC;
+ HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
+ IID_IAutoComplete2, (VOID **)&pAC);
+ ok_hr(hr, S_OK);
+
+ // enable auto-suggest
+ hr = pAC->SetOptions(ACO_AUTOSUGGEST);
+ ok_hr(hr, S_OK);
+
+ // initialize
+ IUnknown *punk = static_cast<IEnumString *>(pEnum);
+ hr = pAC->Init(hwndEdit, punk, NULL, NULL); // IAutoComplete::Init
+ ok_hr(hr, S_OK);
+
+#ifdef MANUAL_DEBUGGING
+ trace("enter MANUAL_DEBUGGING...\n");
+ trace("NOTE: You can quit EDIT control by Alt+F4.\n");
+ while (GetMessageW(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ if (!IsWindow(hwndEdit))
+ break;
+ }
+ trace("leave MANUAL_DEBUGGING...\n");
+ return;
+#endif
+
+ // check expansion
+ ok_int(s_bExpand, FALSE);
+ // check reset
+ ok_int(s_bReset, FALSE);
+
+ // input
+ SetFocus(hwndEdit);
+ WCHAR chLast = 0;
+ for (UINT i = 0; pszInput[i]; ++i)
+ {
+ PostMessageW(hwndEdit, WM_CHAR, pszInput[i], 0);
+ chLast = pszInput[i];
+ }
+
+ // wait for hwndDropDown
+ DWORD style, exstyle;
+ HWND hwndDropDown;
+ LONG_PTR id;
+ for (INT i = 0; i < 100; ++i)
+ {
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ hwndDropDown = FindWindowW(L"Auto-Suggest Dropdown", L"");
+ if (IsWindowVisible(hwndDropDown))
+ break;
+ Sleep(100);
+ }
+ ok(hwndDropDown != NULL, "hwndDropDown was NULL\n");
+ ok_int(IsWindowVisible(hwndDropDown), TRUE);
+
+ // check word break procedure
+ static BOOL s_bFirstTime = TRUE;
+ if (s_bFirstTime)
+ {
+ s_bFirstTime = FALSE;
+ EDITWORDBREAKPROC fn2 =
+ (EDITWORDBREAKPROC)SendMessageW(hwndEdit, EM_GETWORDBREAKPROC, 0, 0);
+ ok(fn1 != fn2, "fn1 == fn2\n");
+ ok(fn2 != NULL, "fn2 was NULL\n");
+ DoWordBreakProc(fn2);
+ }
+
+ // take care of the message queue
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+
+ // check reset
+ ok_int(s_bReset, TRUE);
+
+ // get sizes and positions
+ RECT rcEdit, rcDropDown;
+ GetWindowRect(hwndEdit, &rcEdit);
+ GetWindowRect(hwndDropDown, &rcDropDown);
+ trace("rcEdit: (%ld, %ld, %ld, %ld)\n", rcEdit.left, rcEdit.top,
rcEdit.right, rcEdit.bottom);
+ trace("rcDropDown: (%ld, %ld, %ld, %ld)\n", rcDropDown.left,
rcDropDown.top, rcDropDown.right, rcDropDown.bottom);
+
+ // is it "downer"?
+ ok_int(bDowner, rcEdit.top < rcDropDown.top);
+
+ // check window style and id
+ style = (LONG)GetWindowLongPtrW(hwndDropDown, GWL_STYLE);
+ exstyle = (LONG)GetWindowLongPtrW(hwndDropDown, GWL_EXSTYLE);
+ id = GetWindowLongPtrW(hwndDropDown, GWLP_ID);
+#define DROPDOWN_STYLE (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | \
+ WS_CLIPCHILDREN | WS_BORDER) // 0x96800000
+ ok(style == DROPDOWN_STYLE, "style was 0x%08lx\n", style);
+ ok_long(exstyle, 0x8c);
+ ok_long((LONG)id, 0);
+
+ // check class style
+ style = (LONG)GetClassLongPtrW(hwndDropDown, GCL_STYLE);
+#define DROPDOWN_CLASS_STYLE_1 (CS_DROPSHADOW | CS_SAVEBITS)
+#define DROPDOWN_CLASS_STYLE_2 0
+ ok(style == DROPDOWN_CLASS_STYLE_1 /* Win10 */ ||
+ style == DROPDOWN_CLASS_STYLE_2 /* WinXP/Win2k3 */,
+ "style was 0x%08lx\n", style);
+
+ // get client rectangle
+ RECT rcClient;
+ GetClientRect(hwndDropDown, &rcClient);
+ trace("rcClient: (%ld, %ld, %ld, %ld)\n",
+ rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
+
+ HWND hwndScrollBar, hwndSizeBox, hwndList, hwndNone;
+ WCHAR szClass[64];
+
+ // scroll bar
+ hwndScrollBar = GetTopWindow(hwndDropDown);
+ ok(hwndScrollBar != NULL, "hwndScrollBar was NULL\n");
+ GetClassNameW(hwndScrollBar, szClass, _countof(szClass));
+ ok_wstri(szClass, L"ScrollBar");
+ style = (LONG)GetWindowLongPtrW(hwndScrollBar, GWL_STYLE);
+ exstyle = (LONG)GetWindowLongPtrW(hwndScrollBar, GWL_EXSTYLE);
+ id = GetWindowLongPtrW(hwndScrollBar, GWLP_ID);
+#define SCROLLBAR_STYLE_1 (WS_CHILD | WS_VISIBLE | SBS_BOTTOMALIGN | SBS_VERT) //
0x50000005
+#define SCROLLBAR_STYLE_2 (WS_CHILD | SBS_BOTTOMALIGN | SBS_VERT) // 0x40000005
+ if (bLong)
+ ok(style == SCROLLBAR_STYLE_1, "style was 0x%08lx\n", style);
+ else
+ ok(style == SCROLLBAR_STYLE_2, "style was 0x%08lx\n", style);
+ ok_long(exstyle, 0);
+ ok_long((LONG)id, 0);
+
+ // size-box
+ hwndSizeBox = GetNextWindow(hwndScrollBar, GW_HWNDNEXT);
+ ok(hwndSizeBox != NULL, "hwndSizeBox was NULL\n");
+ GetClassNameW(hwndSizeBox, szClass, _countof(szClass));
+ ok_wstri(szClass, L"ScrollBar");
+ style = (LONG)GetWindowLongPtrW(hwndSizeBox, GWL_STYLE);
+ exstyle = (LONG)GetWindowLongPtrW(hwndSizeBox, GWL_EXSTYLE);
+ id = GetWindowLongPtrW(hwndSizeBox, GWLP_ID);
+#define SIZEBOX_STYLE_1 \
+ (WS_CHILD | WS_VISIBLE | SBS_SIZEBOX | SBS_SIZEBOXBOTTOMRIGHTALIGN) // 0x5000000c
+#define SIZEBOX_STYLE_2 \
+ (WS_CHILD | WS_VISIBLE | SBS_SIZEBOX) // 0x50000008
+ ok(style == SIZEBOX_STYLE_1 /* Win10 */ ||
+ style == SIZEBOX_STYLE_2 /* Win2k3/WinXP */, "style was 0x%08lx\n",
style);
+ ok_long(exstyle, 0);
+ ok_long((LONG)id, 0);
+
+ // the list
+ hwndList = GetNextWindow(hwndSizeBox, GW_HWNDNEXT);
+ ok(hwndList != NULL, "hwndList was NULL\n");
+ GetClassNameW(hwndList, szClass, _countof(szClass));
+ ok_wstri(szClass, WC_LISTVIEWW); // L"SysListView32"
+ style = (LONG)GetWindowLongPtrW(hwndList, GWL_STYLE);
+ exstyle = (LONG)GetWindowLongPtrW(hwndList, GWL_EXSTYLE);
+ id = GetWindowLongPtrW(hwndList, GWLP_ID);
+#define LIST_STYLE_1 \
+ (WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_CLIPSIBLINGS | \
+ LVS_NOCOLUMNHEADER | LVS_OWNERDATA | LVS_OWNERDRAWFIXED | \
+ LVS_SINGLESEL | LVS_REPORT) // 0x54205405
+#define LIST_STYLE_2 \
+ (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | LVS_NOCOLUMNHEADER | \
+ LVS_OWNERDATA | LVS_OWNERDRAWFIXED | LVS_SINGLESEL | LVS_REPORT) // 0x54005405
+ if (bLong)
+ ok(style == LIST_STYLE_1, "style was 0x%08lx\n", style);
+ else
+ ok(style == LIST_STYLE_2, "style was 0x%08lx\n", style);
+ ok_long(exstyle, 0);
+ ok_long((LONG)id, 0);
+
+ // no more controls
+ hwndNone = GetNextWindow(hwndList, GW_HWNDNEXT);
+ ok(hwndNone == NULL, "hwndNone was %p\n", hwndNone);
+
+ // get rectangles of controls
+ RECT rcScrollBar, rcSizeBox, rcList;
+ GetWindowRect(hwndScrollBar, &rcScrollBar);
+ MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcScrollBar, 2);
+ GetWindowRect(hwndSizeBox, &rcSizeBox);
+ MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcSizeBox, 2);
+ GetWindowRect(hwndList, &rcList);
+ MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcList, 2);
+ trace("rcScrollBar: (%ld, %ld, %ld, %ld)\n", rcScrollBar.left,
rcScrollBar.top,
+ rcScrollBar.right, rcScrollBar.bottom);
+ trace("rcSizeBox: (%ld, %ld, %ld, %ld)\n", rcSizeBox.left, rcSizeBox.top,
+ rcSizeBox.right, rcSizeBox.bottom);
+ trace("rcList: (%ld, %ld, %ld, %ld)\n", rcList.left, rcList.top,
+ rcList.right, rcList.bottom);
+
+ // are they visible?
+ ok_int(IsWindowVisible(hwndDropDown), TRUE);
+ ok_int(IsWindowVisible(hwndEdit), TRUE);
+ ok_int(IsWindowVisible(hwndSizeBox), TRUE);
+ ok_int(IsWindowVisible(hwndList), TRUE);
+
+ // check item count
+ INT nListCount = ListView_GetItemCount(hwndList);
+ if (nListCount < 1000)
+ ok_int(nListCount, nCount);
+ else
+ ok_int(nListCount, 1000);
+
+ // check the positions
+ if (bDowner) // downer
+ {
+ ok_int(rcDropDown.left, rcEdit.left);
+ ok_int(rcDropDown.top, rcEdit.bottom);
+ ok_int(rcDropDown.right, rcEdit.right);
+ //ok_int(rcDropDown.bottom, ???);
+ ok_int(rcSizeBox.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
+ ok_int(rcSizeBox.top, rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL));
+ ok_int(rcSizeBox.right, rcClient.right);
+ ok_int(rcSizeBox.bottom, rcClient.bottom);
+ ok_int(rcScrollBar.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
+ ok_int(rcScrollBar.top, 0);
+ ok_int(rcScrollBar.right, rcClient.right);
+ ok_int(rcScrollBar.bottom, rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL));
+ ok_int(rcList.left, 0);
+ ok_int(rcList.top, 0);
+ //ok_int(rcList.right, 30160 or 30170???);
+ ok_int(rcList.bottom, rcClient.bottom);
+ }
+ else // upper
+ {
+ ok_int(rcDropDown.left, rcEdit.left);
+ //ok_int(rcDropDown.top, ???);
+ ok_int(rcDropDown.right, rcEdit.right);
+ ok_int(rcDropDown.bottom, rcEdit.top);
+ ok_int(rcSizeBox.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
+ ok_int(rcSizeBox.top, 0);
+ ok_int(rcSizeBox.right, rcClient.right);
+ ok_int(rcSizeBox.bottom, rcClient.top + GetSystemMetrics(SM_CYHSCROLL));
+ ok_int(rcScrollBar.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL));
+ ok_int(rcScrollBar.top, GetSystemMetrics(SM_CYHSCROLL));
+ ok_int(rcScrollBar.right, rcClient.right);
+ ok_int(rcScrollBar.bottom, rcClient.bottom);
+ ok_int(rcList.left, 0);
+ ok_int(rcList.top, 0);
+ //ok_int(rcList.right, 30160 or 30170???);
+ ok_int(rcList.bottom, rcClient.bottom);
+ }
+
+ // append WM_QUIT message into message queue
+ PostQuitMessage(0);
+
+ // do the messages
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ Sleep(30); // another thread is working...
+ }
+
+ // destroy the EDIT control and drop-down window
+ DestroyWindow(hwndEdit);
+ DestroyWindow(hwndDropDown);
+
+ // do the messages
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+
+ // check the expansion
+ ok_int(s_bExpand, chLast == L'\\');
+}
+
+// the testcase B
+static VOID
+DoTestCaseB(INT x, INT y, INT cx, INT cy, LPCWSTR pszInput,
+ LPWSTR *pList, UINT nCount)
+{
+ MSG msg;
+ s_bExpand = s_bReset = FALSE;
+
+ // create EDIT control
+ HWND hwndEdit = MyCreateEditCtrl(x, y, cx, cy);
+ ok(hwndEdit != NULL, "hwndEdit was NULL\n");
+ ShowWindowAsync(hwndEdit, SW_SHOWNORMAL);
+
+ // set the list data
+ CComPtr<CEnumString> pEnum(new CEnumString());
+ pEnum->SetList(nCount, pList);
+
+ // create auto-completion object
+ CComPtr<IAutoComplete2> pAC;
+ HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
+ IID_IAutoComplete2, (VOID **)&pAC);
+ ok_hr(hr, S_OK);
+
+ // enable auto-suggest
+ hr = pAC->SetOptions(ACO_AUTOSUGGEST);
+ ok_hr(hr, S_OK);
+
+ // initialize
+ IUnknown *punk = static_cast<IEnumString *>(pEnum);
+ hr = pAC->Init(hwndEdit, punk, NULL, NULL); // IAutoComplete::Init
+ ok_hr(hr, S_OK);
+
+ // input
+ SetFocus(hwndEdit);
+ for (UINT i = 0; pszInput[i]; ++i)
+ {
+ PostMessageW(hwndEdit, WM_COMMAND, (0xFFFF0000 + i), 0xDEADFACE);
+ PostMessageW(hwndEdit, WM_CHAR, pszInput[i], 0);
+ }
+ PostMessageW(hwndEdit, WM_CHAR, L'!', 0);
+
+ // observe the message responses
+ BOOL bFlag = FALSE;
+ INT i = 0;
+ WCHAR ch = 0;
+ s_bExpand = s_bReset = FALSE;
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ if (bFlag && msg.message == WM_CHAR)
+ {
+ bFlag = FALSE;
+ ch = (WCHAR)msg.wParam;
+ if (ch == L'!')
+ break;
+ //trace("i: %d, ch:%C, s_bReset:%d, s_bExpand:%d, ch:%C\n", i, ch,
s_bReset, s_bExpand, ch);
+ Sleep(100);
+ if (i == 0)
+ {
+ ok_int(s_bReset, TRUE);
+ s_bReset = FALSE;
+ ok_int(s_bExpand, FALSE);
+ }
+ else if (ch != L'\\')
+ {
+ ok_int(s_bReset, FALSE);
+ ok_int(s_bExpand, FALSE);
+ }
+ }
+ if (msg.message == WM_COMMAND && (DWORD)msg.lParam == 0xDEADFACE)
+ {
+ i = (msg.wParam & 0x0000FFFF);
+ //trace("i: %d, ch:%C, s_bReset:%d, s_bExpand:%d, ch:%C\n", i, ch,
s_bReset, s_bExpand, ch);
+ if (ch == L'\\')
+ {
+ ok_int(s_bReset, TRUE);
+ ok_int(s_bExpand, TRUE);
+ s_bReset = s_bExpand = FALSE;
+ }
+ else
+ {
+ ok_int(s_bReset, FALSE);
+ ok_int(s_bExpand, FALSE);
+ }
+ bFlag = TRUE;
+ Sleep(100);
+ }
+ }
+
+ // post quit
+ DestroyWindow(hwndEdit);
+ PostQuitMessage(0);
+
+ // take care of the message queue
+ while (GetMessageW(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+}
+
+START_TEST(IAutoComplete)
+{
+ // initialize COM
+ CCoInit init;
+ ok_hr(init.hr, S_OK);
+ if (!SUCCEEDED(init.hr))
+ {
+ skip("CoInitialize failed\n");
+ return;
+ }
+
+ // get screen size
+ HMONITOR hMon = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFO mi = { sizeof(mi) };
+ GetMonitorInfoW(hMon, &mi);
+ const RECT& rcWork = mi.rcWork;
+ trace("rcWork: (%ld, %ld, %ld, %ld)\n",
+ rcWork.left, rcWork.top, rcWork.right, rcWork.bottom);
+ trace("SM_CXVSCROLL: %d, SM_CYHSCROLL: %d\n",
+ GetSystemMetrics(SM_CXVSCROLL), GetSystemMetrics(SM_CYHSCROLL));
+
+ UINT nCount;
+ LPWSTR *pList;
+ WCHAR szText[64];
+
+ // Test case #1 (A)
+ trace("Testcase #1 (downer, short) ------------------------------\n");
+ nCount = 3;
+ pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
+ SHStrDupW(L"test\\AA", &pList[0]);
+ SHStrDupW(L"test\\BBB", &pList[1]);
+ SHStrDupW(L"test\\CCC", &pList[2]);
+ DoTestCaseA(0, 0, 100, 30, L"test\\", pList, nCount, TRUE, FALSE);
+
+ // Test case #2 (A)
+ trace("Testcase #2 (downer, long) ------------------------------\n");
+ nCount = 300;
+ pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
+ for (UINT i = 0; i < nCount; ++i)
+ {
+ StringCbPrintfW(szText, sizeof(szText), L"test\\%u", i);
+ SHStrDupW(szText, &pList[i]);
+ }
+ DoTestCaseA(100, 20, 100, 30, L"test\\", pList, nCount, TRUE, TRUE);
+
+ // Test case #3 (A)
+ trace("Testcase #3 (upper, short) ------------------------------\n");
+ nCount = 2;
+ pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
+ SHStrDupW(L"test/AA", &pList[0]);
+ SHStrDupW(L"test/BBB", &pList[0]);
+
SHStrDupW(L"test/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
&pList[1]);
+ DoTestCaseA(rcWork.right - 100, rcWork.bottom - 30, 80, 40, L"test/",
+ pList, nCount, FALSE, FALSE);
+
+ // Test case #4 (A)
+ trace("Testcase #4 (upper, short) ------------------------------\n");
+ nCount = 2;
+ pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
+ SHStrDupW(L"testtest\\AA", &pList[0]);
+ SHStrDupW(L"testtest\\BBB", &pList[0]);
+
SHStrDupW(L"testtest\\CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
&pList[1]);
+ DoTestCaseA(rcWork.right - 100, rcWork.bottom - 30, 80, 40, L"testtest\\",
+ pList, nCount, FALSE, FALSE);
+
+ // Test case #5 (A)
+ trace("Testcase #5 (upper, long) ------------------------------\n");
+ nCount = 300;
+ pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
+ for (UINT i = 0; i < nCount; ++i)
+ {
+ StringCbPrintfW(szText, sizeof(szText), L"testtest/%u", i);
+ SHStrDupW(szText, &pList[i]);
+ }
+ DoTestCaseA(0, rcWork.bottom - 30, 80, 30, L"testtest/", pList, nCount,
FALSE, TRUE);
+
+ // Test case #6 (A)
+ trace("Testcase #6 (upper, long) ------------------------------\n");
+ nCount = 2000;
+ pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
+ for (UINT i = 0; i < nCount; ++i)
+ {
+ StringCbPrintfW(szText, sizeof(szText), L"testtest\\item-%u", i);
+ SHStrDupW(szText, &pList[i]);
+ }
+ DoTestCaseA(0, rcWork.bottom - 30, 80, 40, L"testtest\\", pList, nCount,
FALSE, TRUE);
+
+ // Test case #7 (B)
+ trace("Testcase #7 ------------------------------\n");
+ nCount = 500;
+ pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR));
+ for (UINT i = 0; i < nCount; ++i)
+ {
+ StringCbPrintfW(szText, sizeof(szText), L"testtest\\item-%u", i);
+ SHStrDupW(szText, &pList[i]);
+ }
+ DoTestCaseB(0, 0, 100, 30, L"testtest\\iX", pList, nCount);
+}
diff --git a/modules/rostests/apitests/browseui/testlist.c
b/modules/rostests/apitests/browseui/testlist.c
index 9c5d8dfd204..20918608eeb 100644
--- a/modules/rostests/apitests/browseui/testlist.c
+++ b/modules/rostests/apitests/browseui/testlist.c
@@ -5,6 +5,7 @@
extern void func_ACListISF(void);
extern void func_IACLCustomMRU(void);
+extern void func_IAutoComplete(void);
extern void func_SHEnumClassesOfCategories(void);
extern void func_SHExplorerParseCmdLine(void);
@@ -12,6 +13,7 @@ const struct test winetest_testlist[] =
{
{ "ACListISF", func_ACListISF },
{ "IACLCustomMRU", func_IACLCustomMRU },
+ { "IAutoComplete", func_IAutoComplete },
{ "SHEnumClassesOfCategories", func_SHEnumClassesOfCategories },
{ "SHExplorerParseCmdLine", func_SHExplorerParseCmdLine },
{ 0, 0 }