https://git.reactos.org/?p=reactos.git;a=commitdiff;h=003b19dc3cf9c944f74197c5ab88cf58b84a8d61

commit 003b19dc3cf9c944f74197c5ab88cf58b84a8d61
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Tue Mar 2 07:25:31 2021 +0900
Commit:     GitHub <[email protected]>
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 
<[email protected]>
+ */
+#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 }

Reply via email to