1 /*
2  * Tests for autocomplete
3  *
4  * Copyright 2008 Jan de Mooij
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #define COBJMACROS
22 
23 #include <stdarg.h>
24 
25 #include "windows.h"
26 #include "shobjidl.h"
27 #include "shlguid.h"
28 #include "shldisp.h"
29 #include "shlobj.h"
30 
31 #include "wine/heap.h"
32 #include "wine/test.h"
33 
34 static HWND hMainWnd, hEdit;
35 static HINSTANCE hinst;
36 static int killfocus_count;
37 
38 static void test_invalid_init(void)
39 {
40     HRESULT hr;
41     IAutoComplete *ac;
42     IUnknown *acSource;
43     HWND edit_control;
44 
45     /* AutoComplete instance */
46     hr = CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
47                          &IID_IAutoComplete, (void **)&ac);
48     if (hr == REGDB_E_CLASSNOTREG)
49     {
50         win_skip("CLSID_AutoComplete is not registered\n");
51         return;
52     }
53     ok(hr == S_OK, "no IID_IAutoComplete (0x%08x)\n", hr);
54 
55     /* AutoComplete source */
56     hr = CoCreateInstance(&CLSID_ACLMulti, NULL, CLSCTX_INPROC_SERVER,
57                         &IID_IACList, (void **)&acSource);
58     if (hr == REGDB_E_CLASSNOTREG)
59     {
60         win_skip("CLSID_ACLMulti is not registered\n");
61         IAutoComplete_Release(ac);
62         return;
63     }
64     ok(hr == S_OK, "no IID_IACList (0x%08x)\n", hr);
65 
66     edit_control = CreateWindowExA(0, "EDIT", "Some text", 0, 10, 10, 300, 300,
67                        hMainWnd, NULL, hinst, NULL);
68     ok(edit_control != NULL, "Can't create edit control\n");
69 
70     /* The refcount of acSource would be incremented on older Windows. */
71     hr = IAutoComplete_Init(ac, NULL, acSource, NULL, NULL);
72     ok(hr == E_INVALIDARG ||
73        broken(hr == S_OK), /* Win2k/XP/Win2k3 */
74        "Init returned 0x%08x\n", hr);
75     if (hr == E_INVALIDARG)
76     {
77         LONG ref;
78 
79         IUnknown_AddRef(acSource);
80         ref = IUnknown_Release(acSource);
81         ok(ref == 1, "Expected AutoComplete source refcount to be 1, got %d\n", ref);
82     }
83 
84 if (0)
85 {
86     /* Older Windows versions never check the window handle, while newer
87      * versions only check for NULL. Subsequent attempts to initialize the
88      * object after this call succeeds would fail, because initialization
89      * state is determined by whether a non-NULL window handle is stored. */
90     hr = IAutoComplete_Init(ac, (HWND)0xdeadbeef, acSource, NULL, NULL);
91     ok(hr == S_OK, "Init returned 0x%08x\n", hr);
92 
93     /* Tests crash on older Windows. */
94     hr = IAutoComplete_Init(ac, NULL, NULL, NULL, NULL);
95     ok(hr == E_INVALIDARG, "Init returned 0x%08x\n", hr);
96 
97     hr = IAutoComplete_Init(ac, edit_control, NULL, NULL, NULL);
98     ok(hr == E_INVALIDARG, "Init returned 0x%08x\n", hr);
99 }
100 
101     /* bind to edit control */
102     hr = IAutoComplete_Init(ac, edit_control, acSource, NULL, NULL);
103     ok(hr == S_OK, "Init returned 0x%08x\n", hr);
104 
105     /* try invalid parameters after successful initialization .*/
106     hr = IAutoComplete_Init(ac, NULL, NULL, NULL, NULL);
107     ok(hr == E_INVALIDARG ||
108        hr == E_FAIL, /* Win2k/XP/Win2k3 */
109        "Init returned 0x%08x\n", hr);
110 
111     hr = IAutoComplete_Init(ac, NULL, acSource, NULL, NULL);
112     ok(hr == E_INVALIDARG ||
113        hr == E_FAIL, /* Win2k/XP/Win2k3 */
114        "Init returned 0x%08x\n", hr);
115 
116     hr = IAutoComplete_Init(ac, edit_control, NULL, NULL, NULL);
117     ok(hr == E_INVALIDARG ||
118        hr == E_FAIL, /* Win2k/XP/Win2k3 */
119        "Init returned 0x%08x\n", hr);
120 
121     /* try initializing twice on the same control */
122     hr = IAutoComplete_Init(ac, edit_control, acSource, NULL, NULL);
123     ok(hr == E_FAIL, "Init returned 0x%08x\n", hr);
124 
125     /* try initializing with a different control */
126     hr = IAutoComplete_Init(ac, hEdit, acSource, NULL, NULL);
127     ok(hr == E_FAIL, "Init returned 0x%08x\n", hr);
128 
129     DestroyWindow(edit_control);
130 
131     /* try initializing with a different control after
132      * destroying the original initialization control */
133     hr = IAutoComplete_Init(ac, hEdit, acSource, NULL, NULL);
134     ok(hr == E_UNEXPECTED ||
135        hr == E_FAIL, /* Win2k/XP/Win2k3 */
136        "Init returned 0x%08x\n", hr);
137 
138     IUnknown_Release(acSource);
139     IAutoComplete_Release(ac);
140 }
141 static IAutoComplete *test_init(void)
142 {
143     HRESULT r;
144     IAutoComplete *ac, *ac2;
145     IUnknown *acSource;
146     LONG_PTR user_data;
147 
148     /* AutoComplete instance */
149     r = CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
150                          &IID_IAutoComplete, (LPVOID*)&ac);
151     if (r == REGDB_E_CLASSNOTREG)
152     {
153         win_skip("CLSID_AutoComplete is not registered\n");
154         return NULL;
155     }
156     ok(r == S_OK, "no IID_IAutoComplete (0x%08x)\n", r);
157 
158     /* AutoComplete source */
159     r = CoCreateInstance(&CLSID_ACLMulti, NULL, CLSCTX_INPROC_SERVER,
160                         &IID_IACList, (LPVOID*)&acSource);
161     if (r == REGDB_E_CLASSNOTREG)
162     {
163         win_skip("CLSID_ACLMulti is not registered\n");
164         IAutoComplete_Release(ac);
165         return NULL;
166     }
167     ok(r == S_OK, "no IID_IACList (0x%08x)\n", r);
168 
169     user_data = GetWindowLongPtrA(hEdit, GWLP_USERDATA);
170     ok(user_data == 0, "Expected the edit control user data to be zero\n");
171 
172     /* bind to edit control */
173     r = IAutoComplete_Init(ac, hEdit, acSource, NULL, NULL);
174     ok(r == S_OK, "Init returned 0x%08x\n", r);
175 
176     user_data = GetWindowLongPtrA(hEdit, GWLP_USERDATA);
177     ok(user_data == 0, "Expected the edit control user data to be zero\n");
178 
179     /* bind a different object to the same edit control */
180     r = CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
181                          &IID_IAutoComplete, (LPVOID*)&ac2);
182     ok(r == S_OK, "no IID_IAutoComplete (0x%08x)\n", r);
183 
184     r = IAutoComplete_Init(ac2, hEdit, acSource, NULL, NULL);
185     ok(r == S_OK, "Init returned 0x%08x\n", r);
186     IAutoComplete_Release(ac2);
187 
188     IUnknown_Release(acSource);
189 
190     return ac;
191 }
192 
193 static void test_killfocus(void)
194 {
195     /* Test if WM_KILLFOCUS messages are handled properly by checking if
196      * the parent receives an EN_KILLFOCUS message. */
197     SetFocus(hEdit);
198     killfocus_count = 0;
199     SetFocus(0);
200     ok(killfocus_count == 1, "Expected one EN_KILLFOCUS message, got: %d\n", killfocus_count);
201 }
202 
203 static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
204 {
205     switch(msg) {
206     case WM_CREATE:
207         /* create edit control */
208         hEdit = CreateWindowExA(0, "EDIT", "Some text", 0, 10, 10, 300, 300,
209                     hWnd, NULL, hinst, NULL);
210         ok(hEdit != NULL, "Can't create edit control\n");
211         break;
212     case WM_COMMAND:
213         if(HIWORD(wParam) == EN_KILLFOCUS)
214             killfocus_count++;
215         break;
216     }
217     return DefWindowProcA(hWnd, msg, wParam, lParam);
218 }
219 
220 static void createMainWnd(void)
221 {
222     WNDCLASSA wc;
223     wc.style = CS_HREDRAW | CS_VREDRAW;
224     wc.cbClsExtra = 0;
225     wc.cbWndExtra = 0;
226     wc.hInstance = GetModuleHandleA(NULL);
227     wc.hIcon = NULL;
228     wc.hCursor = LoadCursorA(NULL, (LPSTR)IDC_IBEAM);
229     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
230     wc.lpszMenuName = NULL;
231     wc.lpszClassName = "MyTestWnd";
232     wc.lpfnWndProc = MyWndProc;
233     RegisterClassA(&wc);
234 
235     hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
236       CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
237 }
238 
239 static WNDPROC HijackerWndProc_prev;
240 static const WCHAR HijackerWndProc_txt[] = {'H','i','j','a','c','k','e','d',0};
241 static LRESULT CALLBACK HijackerWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
242 {
243     switch(msg) {
244     case WM_GETTEXT:
245     {
246         size_t len = min(wParam, ARRAY_SIZE(HijackerWndProc_txt));
247         memcpy((void*)lParam, HijackerWndProc_txt, len * sizeof(WCHAR));
248         return len;
249     }
250     case WM_GETTEXTLENGTH:
251         return ARRAY_SIZE(HijackerWndProc_txt) - 1;
252     }
253     return CallWindowProcW(HijackerWndProc_prev, hWnd, msg, wParam, lParam);
254 }
255 
256 static LRESULT CALLBACK HijackerWndProc2(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
257 {
258     switch(msg) {
259     case EM_SETSEL:
260         lParam = wParam;
261         break;
262     case WM_SETTEXT:
263         lParam = (LPARAM)HijackerWndProc_txt;
264         break;
265     }
266     return CallWindowProcW(HijackerWndProc_prev, hWnd, msg, wParam, lParam);
267 }
268 
269 struct string_enumerator
270 {
271     IEnumString IEnumString_iface;
272     IACList IACList_iface;
273     LONG ref;
274     WCHAR **data;
275     int data_len;
276     int cur;
277     UINT num_resets;
278     UINT num_expand;
279     WCHAR last_expand[32];
280 };
281 
282 static struct string_enumerator *impl_from_IEnumString(IEnumString *iface)
283 {
284     return CONTAINING_RECORD(iface, struct string_enumerator, IEnumString_iface);
285 }
286 
287 static HRESULT WINAPI string_enumerator_QueryInterface(IEnumString *iface, REFIID riid, void **ppv)
288 {
289     struct string_enumerator *this = impl_from_IEnumString(iface);
290     if (IsEqualGUID(riid, &IID_IEnumString) || IsEqualGUID(riid, &IID_IUnknown))
291         *ppv = &this->IEnumString_iface;
292     else if (IsEqualGUID(riid, &IID_IACList))
293         *ppv = &this->IACList_iface;
294     else
295     {
296         *ppv = NULL;
297         return E_NOINTERFACE;
298     }
299 
300     IUnknown_AddRef(&this->IEnumString_iface);
301     return S_OK;
302 }
303 
304 static ULONG WINAPI string_enumerator_AddRef(IEnumString *iface)
305 {
306     struct string_enumerator *this = impl_from_IEnumString(iface);
307 
308     ULONG ref = InterlockedIncrement(&this->ref);
309 
310     return ref;
311 }
312 
313 static ULONG WINAPI string_enumerator_Release(IEnumString *iface)
314 {
315     struct string_enumerator *this = impl_from_IEnumString(iface);
316 
317     ULONG ref = InterlockedDecrement(&this->ref);
318 
319     if (!ref)
320         heap_free(this);
321 
322     return ref;
323 }
324 
325 static HRESULT WINAPI string_enumerator_Next(IEnumString *iface, ULONG num, LPOLESTR *strings, ULONG *num_returned)
326 {
327     struct string_enumerator *this = impl_from_IEnumString(iface);
328     int i, len;
329 
330     *num_returned = 0;
331     for (i = 0; i < num; i++)
332     {
333         if (this->cur >= this->data_len)
334             return S_FALSE;
335 
336         len = lstrlenW(this->data[this->cur]) + 1;
337 
338         strings[i] = CoTaskMemAlloc(len * sizeof(WCHAR));
339         memcpy(strings[i], this->data[this->cur], len * sizeof(WCHAR));
340 
341         (*num_returned)++;
342         this->cur++;
343     }
344 
345     return S_OK;
346 }
347 
348 static HRESULT WINAPI string_enumerator_Reset(IEnumString *iface)
349 {
350     struct string_enumerator *this = impl_from_IEnumString(iface);
351 
352     this->cur = 0;
353     this->num_resets++;
354 
355     return S_OK;
356 }
357 
358 static HRESULT WINAPI string_enumerator_Skip(IEnumString *iface, ULONG num)
359 {
360     struct string_enumerator *this = impl_from_IEnumString(iface);
361 
362     this->cur += num;
363 
364     return S_OK;
365 }
366 
367 static HRESULT WINAPI string_enumerator_Clone(IEnumString *iface, IEnumString **out)
368 {
369     *out = NULL;
370     return E_NOTIMPL;
371 }
372 
373 static IEnumStringVtbl string_enumerator_vtbl =
374 {
375     string_enumerator_QueryInterface,
376     string_enumerator_AddRef,
377     string_enumerator_Release,
378     string_enumerator_Next,
379     string_enumerator_Skip,
380     string_enumerator_Reset,
381     string_enumerator_Clone
382 };
383 
384 static struct string_enumerator *impl_from_IACList(IACList *iface)
385 {
386     return CONTAINING_RECORD(iface, struct string_enumerator, IACList_iface);
387 }
388 
389 static HRESULT WINAPI aclist_QueryInterface(IACList *iface, REFIID riid, void **ppv)
390 {
391     return string_enumerator_QueryInterface(&impl_from_IACList(iface)->IEnumString_iface, riid, ppv);
392 }
393 
394 static ULONG WINAPI aclist_AddRef(IACList *iface)
395 {
396     return string_enumerator_AddRef(&impl_from_IACList(iface)->IEnumString_iface);
397 }
398 
399 static ULONG WINAPI aclist_Release(IACList *iface)
400 {
401     return string_enumerator_Release(&impl_from_IACList(iface)->IEnumString_iface);
402 }
403 
404 static HRESULT WINAPI aclist_Expand(IACList *iface, const WCHAR *expand)
405 {
406     struct string_enumerator *this = impl_from_IACList(iface);
407 
408     /* see what we get called with and how many times,
409        don't actually do any expansion of the strings */
410     memcpy(this->last_expand, expand, min((lstrlenW(expand) + 1)*sizeof(WCHAR), sizeof(this->last_expand)));
411     this->last_expand[ARRAY_SIZE(this->last_expand) - 1] = '\0';
412     this->num_expand++;
413 
414     return S_OK;
415 }
416 
417 static IACListVtbl aclist_vtbl =
418 {
419     aclist_QueryInterface,
420     aclist_AddRef,
421     aclist_Release,
422     aclist_Expand
423 };
424 
425 static HRESULT string_enumerator_create(void **ppv, WCHAR **suggestions, int count)
426 {
427     struct string_enumerator *object;
428 
429     object = heap_alloc_zero(sizeof(*object));
430     object->IEnumString_iface.lpVtbl = &string_enumerator_vtbl;
431     object->IACList_iface.lpVtbl = &aclist_vtbl;
432     object->ref = 1;
433     object->data = suggestions;
434     object->data_len = count;
435     object->cur = 0;
436 
437     *ppv = &object->IEnumString_iface;
438 
439     return S_OK;
440 }
441 
442 static void dispatch_messages(void)
443 {
444     MSG msg;
445     Sleep(33);
446     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
447     {
448         TranslateMessage(&msg);
449         DispatchMessageA(&msg);
450     }
451 }
452 
453 #define check_dropdown(acdropdown, hwnd_edit, list, list_num) check_dropdown_(__FILE__, __LINE__, acdropdown, hwnd_edit, list, list_num)
454 static void check_dropdown_(const char *file, UINT line, IAutoCompleteDropDown *acdropdown, HWND hwnd_edit, WCHAR **list, UINT list_num)
455 {
456     UINT i;
457     DWORD flags = 0;
458     LPWSTR str;
459     HRESULT hr;
460 
461     hr = IAutoCompleteDropDown_GetDropDownStatus(acdropdown, &flags, &str);
462     ok_(file, line)(hr == S_OK, "IAutoCompleteDropDown_GetDropDownStatus failed: %x\n", hr);
463     if (hr != S_OK) return;
464     if (list_num) ok_(file, line)(flags & ACDD_VISIBLE, "AutoComplete DropDown not visible\n");
465     else
466     {
467         ok_(file, line)(!(flags & ACDD_VISIBLE), "AutoComplete DropDown visible\n");
468         return;
469     }
470     ok_(file, line)(str == NULL, "Expected (null), got %s\n", wine_dbgstr_w(str));
471     if (str)
472     {
473         CoTaskMemFree(str);
474         return;
475     }
476 
477     for (i = 0; i <= list_num; i++)
478     {
479         flags = 0;
480         SendMessageW(hwnd_edit, WM_KEYDOWN, VK_DOWN, 0);
481         SendMessageW(hwnd_edit, WM_KEYUP, VK_DOWN, 0xc0000000);
482         hr = IAutoCompleteDropDown_GetDropDownStatus(acdropdown, &flags, &str);
483         ok_(file, line)(hr == S_OK, "IAutoCompleteDropDown_GetDropDownStatus failed: %x\n", hr);
484         ok_(file, line)(flags & ACDD_VISIBLE, "AutoComplete DropDown not visible\n");
485         if (hr == S_OK)
486         {
487             if (i < list_num)
488                 ok_(file, line)(str && !lstrcmpW(list[i], str), "Expected %s, got %s\n",
489                                 wine_dbgstr_w(list[i]), wine_dbgstr_w(str));
490             else
491                 ok_(file, line)(str == NULL, "Expected (null), got %s\n", wine_dbgstr_w(str));
492         }
493         CoTaskMemFree(str);
494     }
495 }
496 
497 static void test_aclist_expand(HWND hwnd_edit, void *enumerator, IAutoCompleteDropDown *acdropdown)
498 {
499     struct string_enumerator *obj = (struct string_enumerator*)enumerator;
500     static WCHAR str1[] = {'t','e','s','t',0};
501     static WCHAR str1a[] = {'t','e','s','t','\\',0};
502     static WCHAR str2[] = {'t','e','s','t','\\','f','o','o','\\','b','a','r','\\','b','a',0};
503     static WCHAR str2a[] = {'t','e','s','t','\\','f','o','o','\\','b','a','r','\\',0};
504     static WCHAR str2b[] = {'t','e','s','t','\\','f','o','o','\\','b','a','r','\\','b','a','z','_','b','b','q','\\',0};
505     HRESULT hr;
506     obj->num_resets = 0;
507 
508     ok(obj->num_expand == 0, "Expected 0 expansions, got %u\n", obj->num_expand);
509     SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str1);
510     SendMessageW(hwnd_edit, EM_SETSEL, ARRAY_SIZE(str1) - 1, ARRAY_SIZE(str1) - 1);
511     SendMessageW(hwnd_edit, WM_CHAR, '\\', 1);
512     dispatch_messages();
513     ok(obj->num_expand == 1, "Expected 1 expansion, got %u\n", obj->num_expand);
514     ok(lstrcmpW(obj->last_expand, str1a) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str1a), wine_dbgstr_w(obj->last_expand));
515     ok(obj->num_resets == 1, "Expected 1 reset, got %u\n", obj->num_resets);
516     SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str2);
517     SendMessageW(hwnd_edit, EM_SETSEL, ARRAY_SIZE(str2) - 1, ARRAY_SIZE(str2) - 1);
518     SendMessageW(hwnd_edit, WM_CHAR, 'z', 1);
519     dispatch_messages();
520     ok(obj->num_expand == 2, "Expected 2 expansions, got %u\n", obj->num_expand);
521     ok(lstrcmpW(obj->last_expand, str2a) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str2a), wine_dbgstr_w(obj->last_expand));
522     ok(obj->num_resets == 2, "Expected 2 resets, got %u\n", obj->num_resets);
523     SetFocus(hwnd_edit);
524     SendMessageW(hwnd_edit, WM_CHAR, '_', 1);
525     SendMessageW(hwnd_edit, WM_CHAR, 'b', 1);
526     SetFocus(0);
527     SetFocus(hwnd_edit);
528     SendMessageW(hwnd_edit, WM_CHAR, 'b', 1);
529     SendMessageW(hwnd_edit, WM_CHAR, 'q', 1);
530     dispatch_messages();
531     ok(obj->num_expand == 2, "Expected 2 expansions, got %u\n", obj->num_expand);
532     ok(obj->num_resets == 2, "Expected 2 resets, got %u\n", obj->num_resets);
533     SendMessageW(hwnd_edit, WM_CHAR, '\\', 1);
534     dispatch_messages();
535     ok(obj->num_expand == 3, "Expected 3 expansions, got %u\n", obj->num_expand);
536     ok(lstrcmpW(obj->last_expand, str2b) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str2b), wine_dbgstr_w(obj->last_expand));
537     ok(obj->num_resets == 3, "Expected 3 resets, got %u\n", obj->num_resets);
538     SendMessageW(hwnd_edit, EM_SETSEL, ARRAY_SIZE(str1a) - 1, -1);
539     SendMessageW(hwnd_edit, WM_CHAR, 'x', 1);
540     SendMessageW(hwnd_edit, WM_CHAR, 'y', 1);
541     dispatch_messages();
542     ok(obj->num_expand == 4, "Expected 4 expansions, got %u\n", obj->num_expand);
543     ok(lstrcmpW(obj->last_expand, str1a) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str1a), wine_dbgstr_w(obj->last_expand));
544     ok(obj->num_resets == 4, "Expected 4 resets, got %u\n", obj->num_resets);
545     SendMessageW(hwnd_edit, EM_SETSEL, ARRAY_SIZE(str1) - 1, -1);
546     SendMessageW(hwnd_edit, WM_CHAR, 'x', 1);
547     dispatch_messages();
548     ok(obj->num_expand == 4, "Expected 4 expansions, got %u\n", obj->num_expand);
549     ok(obj->num_resets == 5, "Expected 5 resets, got %u\n", obj->num_resets);
550     SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str1a);
551     SendMessageW(hwnd_edit, EM_SETSEL, ARRAY_SIZE(str1a) - 1, ARRAY_SIZE(str1a) - 1);
552     SendMessageW(hwnd_edit, WM_CHAR, 'f', 1);
553     dispatch_messages();
554     ok(obj->num_expand == 5, "Expected 5 expansions, got %u\n", obj->num_expand);
555     ok(lstrcmpW(obj->last_expand, str1a) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str1a), wine_dbgstr_w(obj->last_expand));
556     ok(obj->num_resets == 6, "Expected 6 resets, got %u\n", obj->num_resets);
557     hr = IAutoCompleteDropDown_ResetEnumerator(acdropdown);
558     ok(hr == S_OK, "IAutoCompleteDropDown_ResetEnumerator failed: %x\n", hr);
559     SendMessageW(hwnd_edit, WM_CHAR, 'o', 1);
560     dispatch_messages();
561     ok(obj->num_expand == 6, "Expected 6 expansions, got %u\n", obj->num_expand);
562     ok(lstrcmpW(obj->last_expand, str1a) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str1a), wine_dbgstr_w(obj->last_expand));
563     ok(obj->num_resets == 7, "Expected 7 resets, got %u\n", obj->num_resets);
564 }
565 
566 static void test_prefix_filtering(HWND hwnd_edit)
567 {
568     static WCHAR htt[]  = {'h','t','t',0};
569     static WCHAR www[]  = {'w','w','w','.',0};
570     static WCHAR str0[] = {'w','w','w','.','a','x',0};
571     static WCHAR str1[] = {'h','t','t','p','s',':','/','/','w','w','w','.','a','c',0};
572     static WCHAR str2[] = {'a','a',0};
573     static WCHAR str3[] = {'a','b',0};
574     static WCHAR str4[] = {'h','t','t','p',':','/','/','a','0',0};
575     static WCHAR str5[] = {'h','t','t','p','s',':','/','/','h','t','a',0};
576     static WCHAR str6[] = {'h','f','o','o',0};
577     static WCHAR str7[] = {'h','t','t','p',':','/','/','w','w','w','.','a','d','d',0};
578     static WCHAR str8[] = {'w','w','w','.','w','w','w','.','?',0};
579     static WCHAR str9[] = {'h','t','t','p',':','/','/','a','b','c','.','a','a','.','c','o','m',0};
580     static WCHAR str10[]= {'f','t','p',':','/','/','a','b','c',0};
581     static WCHAR str11[]= {'f','i','l','e',':','/','/','a','a',0};
582     static WCHAR str12[]= {'f','t','p',':','/','/','w','w','w','.','a','a',0};
583     static WCHAR *suggestions[] = { str0, str1, str2, str3, str4, str5, str6, str7, str8, str9, str10, str11, str12 };
584     static WCHAR *sorted1[] = { str4, str2, str3, str9, str1, str7, str0 };
585     static WCHAR *sorted2[] = { str3, str9 };
586     static WCHAR *sorted3[] = { str1, str7, str0 };
587     static WCHAR *sorted4[] = { str6, str5 };
588     static WCHAR *sorted5[] = { str5 };
589     static WCHAR *sorted6[] = { str4, str9 };
590     static WCHAR *sorted7[] = { str11, str10, str12 };
591     IUnknown *enumerator;
592     IAutoComplete2 *autocomplete;
593     IAutoCompleteDropDown *acdropdown;
594     WCHAR buffer[20];
595     HRESULT hr;
596 
597     hr = CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, &IID_IAutoComplete2, (void**)&autocomplete);
598     ok(hr == S_OK, "CoCreateInstance failed: %x\n", hr);
599 
600     hr = IAutoComplete2_QueryInterface(autocomplete, &IID_IAutoCompleteDropDown, (LPVOID*)&acdropdown);
601     ok(hr == S_OK, "No IAutoCompleteDropDown interface: %x\n", hr);
602 
603     string_enumerator_create((void**)&enumerator, suggestions, ARRAY_SIZE(suggestions));
604 
605     hr = IAutoComplete2_SetOptions(autocomplete, ACO_FILTERPREFIXES | ACO_AUTOSUGGEST | ACO_AUTOAPPEND);
606     ok(hr == S_OK, "IAutoComplete2_SetOptions failed: %x\n", hr);
607     hr = IAutoComplete2_Init(autocomplete, hwnd_edit, enumerator, NULL, NULL);
608     ok(hr == S_OK, "IAutoComplete_Init failed: %x\n", hr);
609 
610     SendMessageW(hwnd_edit, EM_SETSEL, 0, -1);
611     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
612     dispatch_messages();
613     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
614     ok(lstrcmpW(str4 + 7, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str4 + 7), wine_dbgstr_w(buffer));
615     check_dropdown(acdropdown, hwnd_edit, sorted1, ARRAY_SIZE(sorted1));
616 
617     SendMessageW(hwnd_edit, EM_SETSEL, 0, -1);
618     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
619     SendMessageW(hwnd_edit, WM_CHAR, 'b', 1);
620     dispatch_messages();
621     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
622     ok(lstrcmpW(str3, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str3), wine_dbgstr_w(buffer));
623     check_dropdown(acdropdown, hwnd_edit, sorted2, ARRAY_SIZE(sorted2));
624     SendMessageW(hwnd_edit, EM_SETSEL, 0, -1);
625     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
626     SendMessageW(hwnd_edit, WM_CHAR, 'b', 1);
627     SendMessageW(hwnd_edit, WM_CHAR, 'c', 1);
628     dispatch_messages();
629     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
630     ok(lstrcmpW(str9 + 7, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str9 + 7), wine_dbgstr_w(buffer));
631 
632     SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)www);
633     SendMessageW(hwnd_edit, EM_SETSEL, ARRAY_SIZE(www) - 1, ARRAY_SIZE(www) - 1);
634     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
635     dispatch_messages();
636     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
637     ok(lstrcmpW(str1 + 8, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str1 + 8), wine_dbgstr_w(buffer));
638     check_dropdown(acdropdown, hwnd_edit, sorted3, ARRAY_SIZE(sorted3));
639     SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)www);
640     SendMessageW(hwnd_edit, EM_SETSEL, ARRAY_SIZE(www) - 1, ARRAY_SIZE(www) - 1);
641     SendMessageW(hwnd_edit, WM_CHAR, 'w', 1);
642     dispatch_messages();
643     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
644     ok(lstrcmpW(str8, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str8), wine_dbgstr_w(buffer));
645 
646     SendMessageW(hwnd_edit, EM_SETSEL, 0, -1);
647     SendMessageW(hwnd_edit, WM_CHAR, 'h', 1);
648     dispatch_messages();
649     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
650     ok(lstrcmpW(str6, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str6), wine_dbgstr_w(buffer));
651     check_dropdown(acdropdown, hwnd_edit, sorted4, ARRAY_SIZE(sorted4));
652     SendMessageW(hwnd_edit, WM_CHAR, 't', 1);
653     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
654     ok(lstrcmpW(str5 + 8, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str5 + 8), wine_dbgstr_w(buffer));
655     check_dropdown(acdropdown, hwnd_edit, sorted5, ARRAY_SIZE(sorted5));
656     SendMessageW(hwnd_edit, WM_CHAR, 't', 1);
657     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
658     ok(lstrcmpW(htt, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(htt), wine_dbgstr_w(buffer));
659     check_dropdown(acdropdown, hwnd_edit, NULL, 0);
660     SendMessageW(hwnd_edit, WM_CHAR, 'p', 1);
661     SendMessageW(hwnd_edit, WM_CHAR, ':', 1);
662     SendMessageW(hwnd_edit, WM_CHAR, '/', 1);
663     SendMessageW(hwnd_edit, WM_CHAR, '/', 1);
664     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
665     dispatch_messages();
666     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
667     ok(lstrcmpW(str4, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str4), wine_dbgstr_w(buffer));
668     check_dropdown(acdropdown, hwnd_edit, sorted6, ARRAY_SIZE(sorted6));
669     SendMessageW(hwnd_edit, EM_SETSEL, 0, 2);
670     SendMessageW(hwnd_edit, WM_CHAR, 'H', 1);
671     dispatch_messages();
672     check_dropdown(acdropdown, hwnd_edit, NULL, 0);
673     SendMessageW(hwnd_edit, WM_CHAR, 't', 1);
674     dispatch_messages();
675     check_dropdown(acdropdown, hwnd_edit, sorted6, ARRAY_SIZE(sorted6));
676 
677     SendMessageW(hwnd_edit, EM_SETSEL, 0, -1);
678     SendMessageW(hwnd_edit, WM_CHAR, 'F', 1);
679     dispatch_messages();
680     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
681     check_dropdown(acdropdown, hwnd_edit, sorted7, ARRAY_SIZE(sorted7));
682     SendMessageW(hwnd_edit, WM_CHAR, 'i', 1);
683     SendMessageW(hwnd_edit, WM_CHAR, 'L', 1);
684     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
685     check_dropdown(acdropdown, hwnd_edit, sorted7, 1);
686 
687     IAutoCompleteDropDown_Release(acdropdown);
688     IAutoComplete2_Release(autocomplete);
689     IUnknown_Release(enumerator);
690 }
691 
692 static void test_custom_source(void)
693 {
694     static WCHAR str_alpha[] = {'t','e','s','t','1',0};
695     static WCHAR str_alpha2[] = {'t','e','s','t','2',0};
696     static WCHAR str_beta[] = {'a','u','t','o',' ','c','o','m','p','l','e','t','e',0};
697     static WCHAR str_au[] = {'a','u',0};
698     static WCHAR str_aut[] = {'a','u','t',0};
699     static WCHAR *suggestions[] = { str_alpha, str_alpha2, str_beta };
700     struct string_enumerator *obj;
701     IUnknown *enumerator;
702     IAutoComplete2 *autocomplete;
703     IAutoCompleteDropDown *acdropdown;
704     HWND hwnd_edit;
705     DWORD flags = 0;
706     WCHAR buffer[20];
707     HRESULT hr;
708 
709     ShowWindow(hMainWnd, SW_SHOW);
710 
711     hwnd_edit = CreateWindowA("Edit", "", WS_OVERLAPPED | WS_VISIBLE | WS_CHILD | WS_BORDER, 50, 5, 200, 20, hMainWnd, 0, NULL, 0);
712 
713     hr = CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, &IID_IAutoComplete2, (void**)&autocomplete);
714     ok(hr == S_OK, "CoCreateInstance failed: %x\n", hr);
715 
716     hr = IAutoComplete2_QueryInterface(autocomplete, &IID_IAutoCompleteDropDown, (LPVOID*)&acdropdown);
717     ok(hr == S_OK, "No IAutoCompleteDropDown interface: %x\n", hr);
718 
719     string_enumerator_create((void**)&enumerator, suggestions, ARRAY_SIZE(suggestions));
720     obj = (struct string_enumerator*)enumerator;
721 
722     hr = IAutoComplete2_SetOptions(autocomplete, ACO_AUTOSUGGEST | ACO_AUTOAPPEND);
723     ok(hr == S_OK, "IAutoComplete2_SetOptions failed: %x\n", hr);
724     hr = IAutoCompleteDropDown_ResetEnumerator(acdropdown);
725     ok(hr == S_OK, "IAutoCompleteDropDown_ResetEnumerator failed: %x\n", hr);
726     hr = IAutoComplete2_Init(autocomplete, hwnd_edit, enumerator, NULL, NULL);
727     ok(hr == S_OK, "IAutoComplete_Init failed: %x\n", hr);
728 
729     SetFocus(hwnd_edit);
730     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
731     SendMessageW(hwnd_edit, WM_CHAR, 'u', 1);
732     dispatch_messages();
733     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
734     ok(lstrcmpW(str_beta, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str_beta), wine_dbgstr_w(buffer));
735     ok(obj->num_resets == 1, "Expected 1 reset, got %u\n", obj->num_resets);
736     SendMessageW(hwnd_edit, EM_SETSEL, 0, -1);
737     SendMessageW(hwnd_edit, WM_CHAR, '\b', 1);
738     dispatch_messages();
739     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
740     ok(buffer[0] == '\0', "Expected empty string, got %s\n", wine_dbgstr_w(buffer));
741     ok(obj->num_resets == 1, "Expected 1 reset, got %u\n", obj->num_resets);
742     hr = IAutoCompleteDropDown_ResetEnumerator(acdropdown);
743     ok(hr == S_OK, "IAutoCompleteDropDown_ResetEnumerator failed: %x\n", hr);
744     ok(obj->num_resets == 1, "Expected 1 reset, got %u\n", obj->num_resets);
745     obj->num_resets = 0;
746 
747     /* hijack the window procedure */
748     HijackerWndProc_prev = (WNDPROC)SetWindowLongPtrW(hwnd_edit, GWLP_WNDPROC, (LONG_PTR)HijackerWndProc);
749     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
750     ok(lstrcmpW(HijackerWndProc_txt, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(HijackerWndProc_txt), wine_dbgstr_w(buffer));
751 
752     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
753     SendMessageW(hwnd_edit, WM_CHAR, 'u', 1);
754     SetWindowLongPtrW(hwnd_edit, GWLP_WNDPROC, (LONG_PTR)HijackerWndProc_prev);
755     dispatch_messages();
756     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
757     ok(lstrcmpW(str_au, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str_au), wine_dbgstr_w(buffer));
758     ok(obj->num_resets == 1, "Expected 1 reset, got %u\n", obj->num_resets);
759     SendMessageW(hwnd_edit, EM_SETSEL, 0, -1);
760     SendMessageW(hwnd_edit, WM_CHAR, '\b', 1);
761     dispatch_messages();
762     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
763     ok(buffer[0] == '\0', "Expected empty string, got %s\n", wine_dbgstr_w(buffer));
764     hr = IAutoCompleteDropDown_ResetEnumerator(acdropdown);
765     ok(hr == S_OK, "IAutoCompleteDropDown_ResetEnumerator failed: %x\n", hr);
766 
767     HijackerWndProc_prev = (WNDPROC)SetWindowLongPtrW(hwnd_edit, GWLP_WNDPROC, (LONG_PTR)HijackerWndProc2);
768     SendMessageW(hwnd_edit, WM_CHAR, 'a', 1);
769     SendMessageW(hwnd_edit, WM_CHAR, 'u', 1);
770     SetWindowLongPtrW(hwnd_edit, GWLP_WNDPROC, (LONG_PTR)HijackerWndProc_prev);
771     dispatch_messages();
772     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
773     ok(lstrcmpW(str_beta, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str_beta), wine_dbgstr_w(buffer));
774     ok(obj->num_resets == 2, "Expected 2 resets, got %u\n", obj->num_resets);
775     /* end of hijacks */
776 
777     hr = IAutoCompleteDropDown_GetDropDownStatus(acdropdown, &flags, NULL);
778     ok(hr == S_OK, "IAutoCompleteDropDown_GetDropDownStatus failed: %x\n", hr);
779     ok(flags & ACDD_VISIBLE, "AutoComplete DropDown should be visible\n");
780     SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str_au);
781     dispatch_messages();
782     hr = IAutoCompleteDropDown_GetDropDownStatus(acdropdown, &flags, NULL);
783     ok(hr == S_OK, "IAutoCompleteDropDown_GetDropDownStatus failed: %x\n", hr);
784     ok(!(flags & ACDD_VISIBLE), "AutoComplete DropDown should have been hidden\n");
785     SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str_aut);
786     dispatch_messages();
787     hr = IAutoCompleteDropDown_GetDropDownStatus(acdropdown, &flags, NULL);
788     ok(hr == S_OK, "IAutoCompleteDropDown_GetDropDownStatus failed: %x\n", hr);
789     ok(!(flags & ACDD_VISIBLE), "AutoComplete DropDown should be hidden\n");
790     SendMessageW(hwnd_edit, WM_GETTEXT, ARRAY_SIZE(buffer), (LPARAM)buffer);
791     ok(lstrcmpW(str_aut, buffer) == 0, "Expected %s, got %s\n", wine_dbgstr_w(str_aut), wine_dbgstr_w(buffer));
792 
793     test_aclist_expand(hwnd_edit, enumerator, acdropdown);
794     obj->num_resets = 0;
795 
796     hr = IAutoCompleteDropDown_ResetEnumerator(acdropdown);
797     ok(hr == S_OK, "IAutoCompleteDropDown_ResetEnumerator failed: %x\n", hr);
798     SendMessageW(hwnd_edit, WM_CHAR, 'x', 1);
799     dispatch_messages();
800     ok(obj->num_resets == 1, "Expected 1 reset, got %u\n", obj->num_resets);
801     SendMessageW(hwnd_edit, WM_CHAR, 'x', 1);
802     dispatch_messages();
803     ok(obj->num_resets == 1, "Expected 1 reset, got %u\n", obj->num_resets);
804 
805     IAutoCompleteDropDown_Release(acdropdown);
806     IAutoComplete2_Release(autocomplete);
807     IUnknown_Release(enumerator);
808 
809     test_prefix_filtering(hwnd_edit);
810 
811     ShowWindow(hMainWnd, SW_HIDE);
812     DestroyWindow(hwnd_edit);
813 }
814 
815 START_TEST(autocomplete)
816 {
817     HRESULT r;
818     MSG msg;
819     IAutoComplete* ac;
820     RECT win_rect;
821     POINT orig_pos;
822 
823     r = CoInitialize(NULL);
824     ok(r == S_OK, "CoInitialize failed (0x%08x). Tests aborted.\n", r);
825     if (r != S_OK)
826         return;
827 
828     createMainWnd();
829     ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n");
830     if (!hMainWnd) return;
831 
832     /* Move the cursor away from the dropdown listbox */
833     GetWindowRect(hMainWnd, &win_rect);
834     GetCursorPos(&orig_pos);
835     SetCursorPos(win_rect.left, win_rect.top);
836 
837     test_invalid_init();
838     ac = test_init();
839     if (!ac)
840         goto cleanup;
841     test_killfocus();
842 
843     test_custom_source();
844 
845     PostQuitMessage(0);
846     while(GetMessageA(&msg,0,0,0)) {
847         TranslateMessage(&msg);
848         DispatchMessageA(&msg);
849     }
850 
851     IAutoComplete_Release(ac);
852 
853 cleanup:
854     SetCursorPos(orig_pos.x, orig_pos.y);
855     DestroyWindow(hEdit);
856     DestroyWindow(hMainWnd);
857 
858     CoUninitialize();
859 }
860