msctf/tests: Reorder the code to avoid forward declarations.
[wine] / dlls / msctf / tests / inputprocessor.c
1 /*
2  * Unit tests for ITfInputProcessor
3  *
4  * Copyright 2009 Aric Stewart, CodeWeavers
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 #include <stdio.h>
22
23 #define COBJMACROS
24 #include "wine/test.h"
25 #include "winuser.h"
26 #include "initguid.h"
27 #include "shlwapi.h"
28 #include "shlguid.h"
29 #include "comcat.h"
30 #include "msctf.h"
31 #include "olectl.h"
32
33 static ITfInputProcessorProfiles* g_ipp;
34 static LANGID gLangid;
35 static ITfCategoryMgr * g_cm = NULL;
36 static ITfThreadMgr* g_tm = NULL;
37 static ITfDocumentMgr *g_dm = NULL;
38 static TfClientId cid = 0;
39 static TfClientId tid = 0;
40
41 #define SINK_UNEXPECTED 0
42 #define SINK_EXPECTED 1
43 #define SINK_FIRED 2
44
45 static BOOL test_ShouldActivate = FALSE;
46 static BOOL test_ShouldDeactivate = FALSE;
47
48 static DWORD tmSinkCookie;
49 static DWORD tmSinkRefCount;
50 static ITfDocumentMgr *test_CurrentFocus = NULL;
51 static ITfDocumentMgr *test_PrevFocus = NULL;
52 static INT  test_OnSetFocus = SINK_UNEXPECTED;
53 static INT  test_OnInitDocumentMgr = SINK_UNEXPECTED;
54 static INT  test_OnPushContext = SINK_UNEXPECTED;
55 static INT  test_OnPopContext = SINK_UNEXPECTED;
56 static INT  test_KEV_OnSetFocus = SINK_UNEXPECTED;
57
58
59 /**********************************************************************
60  * ITextStoreACP
61  **********************************************************************/
62 typedef struct tagTextStoreACP
63 {
64     const ITextStoreACPVtbl *TextStoreACPVtbl;
65     LONG refCount;
66 } TextStoreACP;
67
68 static void TextStoreACP_Destructor(TextStoreACP *This)
69 {
70     HeapFree(GetProcessHeap(),0,This);
71 }
72
73 static HRESULT WINAPI TextStoreACP_QueryInterface(ITextStoreACP *iface, REFIID iid, LPVOID *ppvOut)
74 {
75     TextStoreACP *This = (TextStoreACP *)iface;
76     *ppvOut = NULL;
77
78     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACP))
79     {
80         *ppvOut = This;
81     }
82
83     if (*ppvOut)
84     {
85         IUnknown_AddRef(iface);
86         return S_OK;
87     }
88
89     return E_NOINTERFACE;
90 }
91
92 static ULONG WINAPI TextStoreACP_AddRef(ITextStoreACP *iface)
93 {
94     TextStoreACP *This = (TextStoreACP *)iface;
95     return InterlockedIncrement(&This->refCount);
96 }
97
98 static ULONG WINAPI TextStoreACP_Release(ITextStoreACP *iface)
99 {
100     TextStoreACP *This = (TextStoreACP *)iface;
101     ULONG ret;
102
103     ret = InterlockedDecrement(&This->refCount);
104     if (ret == 0)
105         TextStoreACP_Destructor(This);
106     return ret;
107 }
108
109 static HRESULT WINAPI TextStoreACP_AdviseSink(ITextStoreACP *iface,
110     REFIID riid, IUnknown *punk, DWORD dwMask)
111 {
112     trace("\n");
113     return S_OK;
114 }
115
116 static HRESULT WINAPI TextStoreACP_UnadviseSink(ITextStoreACP *iface,
117     IUnknown *punk)
118 {
119     trace("\n");
120     return S_OK;
121 }
122 static HRESULT WINAPI TextStoreACP_RequestLock(ITextStoreACP *iface,
123     DWORD dwLockFlags, HRESULT *phrSession)
124 {
125     trace("\n");
126     return S_OK;
127 }
128 static HRESULT WINAPI TextStoreACP_GetStatus(ITextStoreACP *iface,
129     TS_STATUS *pdcs)
130 {
131     trace("\n");
132     return S_OK;
133 }
134 static HRESULT WINAPI TextStoreACP_QueryInsert(ITextStoreACP *iface,
135     LONG acpTestStart, LONG acpTestEnd, ULONG cch, LONG *pacpResultStart,
136     LONG *pacpResultEnd)
137 {
138     trace("\n");
139     return S_OK;
140 }
141 static HRESULT WINAPI TextStoreACP_GetSelection(ITextStoreACP *iface,
142     ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched)
143 {
144     trace("\n");
145     return S_OK;
146 }
147 static HRESULT WINAPI TextStoreACP_SetSelection(ITextStoreACP *iface,
148     ULONG ulCount, const TS_SELECTION_ACP *pSelection)
149 {
150     trace("\n");
151     return S_OK;
152 }
153 static HRESULT WINAPI TextStoreACP_GetText(ITextStoreACP *iface,
154     LONG acpStart, LONG acpEnd, WCHAR *pchPlain, ULONG cchPlainReq,
155     ULONG *pcchPlainRet, TS_RUNINFO *prgRunInfo, ULONG cRunInfoReq,
156     ULONG *pcRunInfoRet, LONG *pacpNext)
157 {
158     trace("\n");
159     return S_OK;
160 }
161 static HRESULT WINAPI TextStoreACP_SetText(ITextStoreACP *iface,
162     DWORD dwFlags, LONG acpStart, LONG acpEnd, const WCHAR *pchText,
163     ULONG cch, TS_TEXTCHANGE *pChange)
164 {
165     trace("\n");
166     return S_OK;
167 }
168 static HRESULT WINAPI TextStoreACP_GetFormattedText(ITextStoreACP *iface,
169     LONG acpStart, LONG acpEnd, IDataObject **ppDataObject)
170 {
171     trace("\n");
172     return S_OK;
173 }
174 static HRESULT WINAPI TextStoreACP_GetEmbedded(ITextStoreACP *iface,
175     LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
176 {
177     trace("\n");
178     return S_OK;
179 }
180 static HRESULT WINAPI TextStoreACP_QueryInsertEmbedded(ITextStoreACP *iface,
181     const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable)
182 {
183     trace("\n");
184     return S_OK;
185 }
186 static HRESULT WINAPI TextStoreACP_InsertEmbedded(ITextStoreACP *iface,
187     DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject,
188     TS_TEXTCHANGE *pChange)
189 {
190     trace("\n");
191     return S_OK;
192 }
193 static HRESULT WINAPI TextStoreACP_InsertTextAtSelection(ITextStoreACP *iface,
194     DWORD dwFlags, const WCHAR *pchText, ULONG cch, LONG *pacpStart,
195     LONG *pacpEnd, TS_TEXTCHANGE *pChange)
196 {
197     trace("\n");
198     return S_OK;
199 }
200 static HRESULT WINAPI TextStoreACP_InsertEmbeddedAtSelection(ITextStoreACP *iface,
201     DWORD dwFlags, IDataObject *pDataObject, LONG *pacpStart, LONG *pacpEnd,
202     TS_TEXTCHANGE *pChange)
203 {
204     trace("\n");
205     return S_OK;
206 }
207 static HRESULT WINAPI TextStoreACP_RequestSupportedAttrs(ITextStoreACP *iface,
208     DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
209 {
210     trace("\n");
211     return S_OK;
212 }
213 static HRESULT WINAPI TextStoreACP_RequestAttrsAtPosition(ITextStoreACP *iface,
214     LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
215     DWORD dwFlags)
216 {
217     trace("\n");
218     return S_OK;
219 }
220 static HRESULT WINAPI TextStoreACP_RequestAttrsTransitioningAtPosition(ITextStoreACP *iface,
221     LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
222     DWORD dwFlags)
223 {
224     trace("\n");
225     return S_OK;
226 }
227 static HRESULT WINAPI TextStoreACP_FindNextAttrTransition(ITextStoreACP *iface,
228     LONG acpStart, LONG acpHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
229     DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset)
230 {
231     trace("\n");
232     return S_OK;
233 }
234 static HRESULT WINAPI TextStoreACP_RetrieveRequestedAttrs(ITextStoreACP *iface,
235     ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched)
236 {
237     trace("\n");
238     return S_OK;
239 }
240 static HRESULT WINAPI TextStoreACP_GetEndACP(ITextStoreACP *iface,
241     LONG *pacp)
242 {
243     trace("\n");
244     return S_OK;
245 }
246 static HRESULT WINAPI TextStoreACP_GetActiveView(ITextStoreACP *iface,
247     TsViewCookie *pvcView)
248 {
249     trace("\n");
250     return S_OK;
251 }
252 static HRESULT WINAPI TextStoreACP_GetACPFromPoint(ITextStoreACP *iface,
253     TsViewCookie vcView, const POINT *ptScreen, DWORD dwFlags,
254     LONG *pacp)
255 {
256     trace("\n");
257     return S_OK;
258 }
259 static HRESULT WINAPI TextStoreACP_GetTextExt(ITextStoreACP *iface,
260     TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc,
261     BOOL *pfClipped)
262 {
263     trace("\n");
264     return S_OK;
265 }
266 static HRESULT WINAPI TextStoreACP_GetScreenExt(ITextStoreACP *iface,
267     TsViewCookie vcView, RECT *prc)
268 {
269     trace("\n");
270     return S_OK;
271 }
272 static HRESULT WINAPI TextStoreACP_GetWnd(ITextStoreACP *iface,
273     TsViewCookie vcView, HWND *phwnd)
274 {
275     trace("\n");
276     return S_OK;
277 }
278
279 static const ITextStoreACPVtbl TextStoreACP_TextStoreACPVtbl =
280 {
281     TextStoreACP_QueryInterface,
282     TextStoreACP_AddRef,
283     TextStoreACP_Release,
284
285     TextStoreACP_AdviseSink,
286     TextStoreACP_UnadviseSink,
287     TextStoreACP_RequestLock,
288     TextStoreACP_GetStatus,
289     TextStoreACP_QueryInsert,
290     TextStoreACP_GetSelection,
291     TextStoreACP_SetSelection,
292     TextStoreACP_GetText,
293     TextStoreACP_SetText,
294     TextStoreACP_GetFormattedText,
295     TextStoreACP_GetEmbedded,
296     TextStoreACP_QueryInsertEmbedded,
297     TextStoreACP_InsertEmbedded,
298     TextStoreACP_InsertTextAtSelection,
299     TextStoreACP_InsertEmbeddedAtSelection,
300     TextStoreACP_RequestSupportedAttrs,
301     TextStoreACP_RequestAttrsAtPosition,
302     TextStoreACP_RequestAttrsTransitioningAtPosition,
303     TextStoreACP_FindNextAttrTransition,
304     TextStoreACP_RetrieveRequestedAttrs,
305     TextStoreACP_GetEndACP,
306     TextStoreACP_GetActiveView,
307     TextStoreACP_GetACPFromPoint,
308     TextStoreACP_GetTextExt,
309     TextStoreACP_GetScreenExt,
310     TextStoreACP_GetWnd
311 };
312
313 HRESULT TextStoreACP_Constructor(IUnknown **ppOut)
314 {
315     TextStoreACP *This;
316
317     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextStoreACP));
318     if (This == NULL)
319         return E_OUTOFMEMORY;
320
321     This->TextStoreACPVtbl = &TextStoreACP_TextStoreACPVtbl;
322     This->refCount = 1;
323
324     *ppOut = (IUnknown *)This;
325     return S_OK;
326 }
327
328 /**********************************************************************
329  * ITfThreadMgrEventSink
330  **********************************************************************/
331 typedef struct tagThreadMgrEventSink
332 {
333     const ITfThreadMgrEventSinkVtbl *ThreadMgrEventSinkVtbl;
334     LONG refCount;
335 } ThreadMgrEventSink;
336
337 static void ThreadMgrEventSink_Destructor(ThreadMgrEventSink *This)
338 {
339     HeapFree(GetProcessHeap(),0,This);
340 }
341
342 static HRESULT WINAPI ThreadMgrEventSink_QueryInterface(ITfThreadMgrEventSink *iface, REFIID iid, LPVOID *ppvOut)
343 {
344     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
345     *ppvOut = NULL;
346
347     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfThreadMgrEventSink))
348     {
349         *ppvOut = This;
350     }
351
352     if (*ppvOut)
353     {
354         IUnknown_AddRef(iface);
355         return S_OK;
356     }
357
358     return E_NOINTERFACE;
359 }
360
361 static ULONG WINAPI ThreadMgrEventSink_AddRef(ITfThreadMgrEventSink *iface)
362 {
363     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
364     ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
365     return InterlockedIncrement(&This->refCount);
366 }
367
368 static ULONG WINAPI ThreadMgrEventSink_Release(ITfThreadMgrEventSink *iface)
369 {
370     ThreadMgrEventSink *This = (ThreadMgrEventSink *)iface;
371     ULONG ret;
372
373     ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
374     ret = InterlockedDecrement(&This->refCount);
375     if (ret == 0)
376         ThreadMgrEventSink_Destructor(This);
377     return ret;
378 }
379
380 static HRESULT WINAPI ThreadMgrEventSink_OnInitDocumentMgr(ITfThreadMgrEventSink *iface,
381 ITfDocumentMgr *pdim)
382 {
383     ok(test_OnInitDocumentMgr == SINK_EXPECTED, "Unexpected OnInitDocumentMgr sink\n");
384     test_OnInitDocumentMgr = SINK_FIRED;
385     return S_OK;
386 }
387
388 static HRESULT WINAPI ThreadMgrEventSink_OnUninitDocumentMgr(ITfThreadMgrEventSink *iface,
389 ITfDocumentMgr *pdim)
390 {
391     trace("\n");
392     return S_OK;
393 }
394
395 static HRESULT WINAPI ThreadMgrEventSink_OnSetFocus(ITfThreadMgrEventSink *iface,
396 ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus)
397 {
398     ok(test_OnSetFocus == SINK_EXPECTED, "Unexpected OnSetFocus sink\n");
399     ok(pdimFocus == test_CurrentFocus,"Sink reports wrong focus\n");
400     ok(pdimPrevFocus == test_PrevFocus,"Sink reports wrong previous focus\n");
401     test_OnSetFocus = SINK_FIRED;
402     return S_OK;
403 }
404
405 static HRESULT WINAPI ThreadMgrEventSink_OnPushContext(ITfThreadMgrEventSink *iface,
406 ITfContext *pic)
407 {
408     ok(test_OnPushContext == SINK_EXPECTED, "Unexpected OnPushContext sink\n");
409     test_OnPushContext = SINK_FIRED;
410     return S_OK;
411 }
412
413 static HRESULT WINAPI ThreadMgrEventSink_OnPopContext(ITfThreadMgrEventSink *iface,
414 ITfContext *pic)
415 {
416     ok(test_OnPopContext == SINK_EXPECTED, "Unexpected OnPopContext sink\n");
417     test_OnPopContext = SINK_FIRED;
418     return S_OK;
419 }
420
421 static const ITfThreadMgrEventSinkVtbl ThreadMgrEventSink_ThreadMgrEventSinkVtbl =
422 {
423     ThreadMgrEventSink_QueryInterface,
424     ThreadMgrEventSink_AddRef,
425     ThreadMgrEventSink_Release,
426
427     ThreadMgrEventSink_OnInitDocumentMgr,
428     ThreadMgrEventSink_OnUninitDocumentMgr,
429     ThreadMgrEventSink_OnSetFocus,
430     ThreadMgrEventSink_OnPushContext,
431     ThreadMgrEventSink_OnPopContext
432 };
433
434 HRESULT ThreadMgrEventSink_Constructor(IUnknown **ppOut)
435 {
436     ThreadMgrEventSink *This;
437
438     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgrEventSink));
439     if (This == NULL)
440         return E_OUTOFMEMORY;
441
442     This->ThreadMgrEventSinkVtbl = &ThreadMgrEventSink_ThreadMgrEventSinkVtbl;
443     This->refCount = 1;
444
445     *ppOut = (IUnknown *)This;
446     return S_OK;
447 }
448
449
450 /********************************************************************************************
451  * Stub text service for testing
452  ********************************************************************************************/
453
454 static LONG TS_refCount;
455 static IClassFactory *cf;
456 static DWORD regid;
457
458 typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut);
459
460 typedef struct tagClassFactory
461 {
462     const IClassFactoryVtbl *vtbl;
463     LONG   ref;
464     LPFNCONSTRUCTOR ctor;
465 } ClassFactory;
466
467 typedef struct tagTextService
468 {
469     const ITfTextInputProcessorVtbl *TextInputProcessorVtbl;
470     LONG refCount;
471 } TextService;
472
473 static void ClassFactory_Destructor(ClassFactory *This)
474 {
475     HeapFree(GetProcessHeap(),0,This);
476     TS_refCount--;
477 }
478
479 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppvOut)
480 {
481     *ppvOut = NULL;
482     if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown))
483     {
484         IClassFactory_AddRef(iface);
485         *ppvOut = iface;
486         return S_OK;
487     }
488
489     return E_NOINTERFACE;
490 }
491
492 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
493 {
494     ClassFactory *This = (ClassFactory *)iface;
495     return InterlockedIncrement(&This->ref);
496 }
497
498 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
499 {
500     ClassFactory *This = (ClassFactory *)iface;
501     ULONG ret = InterlockedDecrement(&This->ref);
502
503     if (ret == 0)
504         ClassFactory_Destructor(This);
505     return ret;
506 }
507
508 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID iid, LPVOID *ppvOut)
509 {
510     ClassFactory *This = (ClassFactory *)iface;
511     HRESULT ret;
512     IUnknown *obj;
513
514     ret = This->ctor(punkOuter, &obj);
515     if (FAILED(ret))
516         return ret;
517     ret = IUnknown_QueryInterface(obj, iid, ppvOut);
518     IUnknown_Release(obj);
519     return ret;
520 }
521
522 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
523 {
524     if(fLock)
525         InterlockedIncrement(&TS_refCount);
526     else
527         InterlockedDecrement(&TS_refCount);
528
529     return S_OK;
530 }
531
532 static const IClassFactoryVtbl ClassFactoryVtbl = {
533     /* IUnknown */
534     ClassFactory_QueryInterface,
535     ClassFactory_AddRef,
536     ClassFactory_Release,
537
538     /* IClassFactory*/
539     ClassFactory_CreateInstance,
540     ClassFactory_LockServer
541 };
542
543 static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut)
544 {
545     ClassFactory *This = HeapAlloc(GetProcessHeap(),0,sizeof(ClassFactory));
546     This->vtbl = &ClassFactoryVtbl;
547     This->ref = 1;
548     This->ctor = ctor;
549     *ppvOut = (LPVOID)This;
550     TS_refCount++;
551     return S_OK;
552 }
553
554 static void TextService_Destructor(TextService *This)
555 {
556     HeapFree(GetProcessHeap(),0,This);
557 }
558
559 static HRESULT WINAPI TextService_QueryInterface(ITfTextInputProcessor *iface, REFIID iid, LPVOID *ppvOut)
560 {
561     TextService *This = (TextService *)iface;
562     *ppvOut = NULL;
563
564     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfTextInputProcessor))
565     {
566         *ppvOut = This;
567     }
568
569     if (*ppvOut)
570     {
571         IUnknown_AddRef(iface);
572         return S_OK;
573     }
574
575     return E_NOINTERFACE;
576 }
577
578 static ULONG WINAPI TextService_AddRef(ITfTextInputProcessor *iface)
579 {
580     TextService *This = (TextService *)iface;
581     return InterlockedIncrement(&This->refCount);
582 }
583
584 static ULONG WINAPI TextService_Release(ITfTextInputProcessor *iface)
585 {
586     TextService *This = (TextService *)iface;
587     ULONG ret;
588
589     ret = InterlockedDecrement(&This->refCount);
590     if (ret == 0)
591         TextService_Destructor(This);
592     return ret;
593 }
594
595 static HRESULT WINAPI TextService_Activate(ITfTextInputProcessor *iface,
596         ITfThreadMgr *ptim, TfClientId id)
597 {
598     trace("TextService_Activate\n");
599     ok(test_ShouldActivate,"Activation came unexpectedly\n");
600     tid = id;
601     return S_OK;
602 }
603
604 static HRESULT WINAPI TextService_Deactivate(ITfTextInputProcessor *iface)
605 {
606     trace("TextService_Deactivate\n");
607     ok(test_ShouldDeactivate,"Deactivation came unexpectedly\n");
608     return S_OK;
609 }
610
611 static const ITfTextInputProcessorVtbl TextService_TextInputProcessorVtbl=
612 {
613     TextService_QueryInterface,
614     TextService_AddRef,
615     TextService_Release,
616
617     TextService_Activate,
618     TextService_Deactivate
619 };
620
621 HRESULT TextService_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
622 {
623     TextService *This;
624     if (pUnkOuter)
625         return CLASS_E_NOAGGREGATION;
626
627     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextService));
628     if (This == NULL)
629         return E_OUTOFMEMORY;
630
631     This->TextInputProcessorVtbl= &TextService_TextInputProcessorVtbl;
632     This->refCount = 1;
633
634     *ppOut = (IUnknown *)This;
635     return S_OK;
636 }
637
638 HRESULT RegisterTextService(REFCLSID rclsid)
639 {
640     ClassFactory_Constructor( TextService_Constructor ,(LPVOID*)&cf);
641     return CoRegisterClassObject(rclsid, (IUnknown*) cf, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &regid);
642 }
643
644 HRESULT UnregisterTextService()
645 {
646     return CoRevokeClassObject(regid);
647 }
648
649 /*
650  * The tests
651  */
652
653 DEFINE_GUID(CLSID_FakeService, 0xEDE1A7AD,0x66DE,0x47E0,0xB6,0x20,0x3E,0x92,0xF8,0x24,0x6B,0xF3);
654 DEFINE_GUID(CLSID_TF_InputProcessorProfiles, 0x33c53a50,0xf456,0x4884,0xb0,0x49,0x85,0xfd,0x64,0x3e,0xcf,0xed);
655 DEFINE_GUID(CLSID_TF_CategoryMgr,         0xA4B544A1,0x438D,0x4B41,0x93,0x25,0x86,0x95,0x23,0xE2,0xD6,0xC7);
656 DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD,     0x34745c63,0xb2f0,0x4784,0x8b,0x67,0x5e,0x12,0xc8,0x70,0x1a,0x31);
657 DEFINE_GUID(GUID_TFCAT_TIP_SPEECH,       0xB5A73CD1,0x8355,0x426B,0xA1,0x61,0x25,0x98,0x08,0xF2,0x6B,0x14);
658 DEFINE_GUID(GUID_TFCAT_TIP_HANDWRITING,  0x246ecb87,0xc2f2,0x4abe,0x90,0x5b,0xc8,0xb3,0x8a,0xdd,0x2c,0x43);
659 DEFINE_GUID (GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER,  0x046B8C80,0x1647,0x40F7,0x9B,0x21,0xB9,0x3B,0x81,0xAA,0xBC,0x1B);
660 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
661 DEFINE_GUID(CLSID_TF_ThreadMgr,           0x529a9e6b,0x6587,0x4f23,0xab,0x9e,0x9c,0x7d,0x68,0x3e,0x3c,0x50);
662 DEFINE_GUID(CLSID_PreservedKey,           0xA0ED8E55,0xCD3B,0x4274,0xB2,0x95,0xF6,0xC9,0xBA,0x2B,0x84,0x72);
663
664
665 static HRESULT initialize(void)
666 {
667     HRESULT hr;
668     CoInitialize(NULL);
669     hr = CoCreateInstance (&CLSID_TF_InputProcessorProfiles, NULL,
670           CLSCTX_INPROC_SERVER, &IID_ITfInputProcessorProfiles, (void**)&g_ipp);
671     if (SUCCEEDED(hr))
672         hr = CoCreateInstance (&CLSID_TF_CategoryMgr, NULL,
673           CLSCTX_INPROC_SERVER, &IID_ITfCategoryMgr, (void**)&g_cm);
674     if (SUCCEEDED(hr))
675         hr = CoCreateInstance (&CLSID_TF_ThreadMgr, NULL,
676           CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (void**)&g_tm);
677     return hr;
678 }
679
680 static void cleanup(void)
681 {
682     if (g_ipp)
683         ITfInputProcessorProfiles_Release(g_ipp);
684     if (g_cm)
685         ITfCategoryMgr_Release(g_cm);
686     if (g_tm)
687         ITfThreadMgr_Release(g_tm);
688     CoUninitialize();
689 }
690
691 static void test_Register(void)
692 {
693     HRESULT hr;
694
695     static const WCHAR szDesc[] = {'F','a','k','e',' ','W','i','n','e',' ','S','e','r','v','i','c','e',0};
696     static const WCHAR szFile[] = {'F','a','k','e',' ','W','i','n','e',' ','S','e','r','v','i','c','e',' ','F','i','l','e',0};
697
698     hr = ITfInputProcessorProfiles_GetCurrentLanguage(g_ipp,&gLangid);
699     ok(SUCCEEDED(hr),"Unable to get current language id\n");
700     trace("Current Language %x\n",gLangid);
701
702     hr = RegisterTextService(&CLSID_FakeService);
703     ok(SUCCEEDED(hr),"Unable to register COM for TextService\n");
704     hr = ITfInputProcessorProfiles_Register(g_ipp, &CLSID_FakeService);
705     ok(SUCCEEDED(hr),"Unable to register text service(%x)\n",hr);
706     hr = ITfInputProcessorProfiles_AddLanguageProfile(g_ipp, &CLSID_FakeService, gLangid, &CLSID_FakeService, szDesc, sizeof(szDesc)/sizeof(WCHAR), szFile, sizeof(szFile)/sizeof(WCHAR), 1);
707     ok(SUCCEEDED(hr),"Unable to add Language Profile (%x)\n",hr);
708 }
709
710 static void test_Unregister(void)
711 {
712     HRESULT hr;
713     hr = ITfInputProcessorProfiles_Unregister(g_ipp, &CLSID_FakeService);
714     ok(SUCCEEDED(hr),"Unable to unregister text service(%x)\n",hr);
715     UnregisterTextService();
716 }
717
718 static void test_EnumInputProcessorInfo(void)
719 {
720     IEnumGUID *ppEnum;
721     BOOL found = FALSE;
722
723     if (SUCCEEDED(ITfInputProcessorProfiles_EnumInputProcessorInfo(g_ipp, &ppEnum)))
724     {
725         ULONG fetched;
726         GUID g;
727         while (IEnumGUID_Next(ppEnum, 1, &g, &fetched) == S_OK)
728         {
729             if(IsEqualGUID(&g,&CLSID_FakeService))
730                 found = TRUE;
731         }
732     }
733     ok(found,"Did not find registered text service\n");
734 }
735
736 static void test_EnumLanguageProfiles(void)
737 {
738     BOOL found = FALSE;
739     IEnumTfLanguageProfiles *ppEnum;
740     if (SUCCEEDED(ITfInputProcessorProfiles_EnumLanguageProfiles(g_ipp,gLangid,&ppEnum)))
741     {
742         TF_LANGUAGEPROFILE profile;
743         while (IEnumTfLanguageProfiles_Next(ppEnum,1,&profile,NULL)==S_OK)
744         {
745             if (IsEqualGUID(&profile.clsid,&CLSID_FakeService))
746             {
747                 found = TRUE;
748                 ok(profile.langid == gLangid, "LangId Incorrect\n");
749                 ok(IsEqualGUID(&profile.catid,&GUID_TFCAT_TIP_KEYBOARD), "CatId Incorrect\n");
750                 ok(IsEqualGUID(&profile.guidProfile,&CLSID_FakeService), "guidProfile Incorrect\n");
751             }
752         }
753     }
754     ok(found,"Registered text service not found\n");
755 }
756
757 static void test_RegisterCategory(void)
758 {
759     HRESULT hr;
760     hr = ITfCategoryMgr_RegisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_TIP_KEYBOARD, &CLSID_FakeService);
761     ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterCategory failed\n");
762     hr = ITfCategoryMgr_RegisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, &CLSID_FakeService);
763     ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterCategory failed\n");
764 }
765
766 static void test_UnregisterCategory(void)
767 {
768     HRESULT hr;
769     hr = ITfCategoryMgr_UnregisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_TIP_KEYBOARD, &CLSID_FakeService);
770     ok(SUCCEEDED(hr),"ITfCategoryMgr_UnregisterCategory failed\n");
771     hr = ITfCategoryMgr_UnregisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, &CLSID_FakeService);
772     ok(SUCCEEDED(hr),"ITfCategoryMgr_UnregisterCategory failed\n");
773 }
774
775 static void test_FindClosestCategory(void)
776 {
777     GUID output;
778     HRESULT hr;
779     const GUID *list[3] = {&GUID_TFCAT_TIP_SPEECH, &GUID_TFCAT_TIP_KEYBOARD, &GUID_TFCAT_TIP_HANDWRITING};
780
781     hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, NULL, 0);
782     ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
783     ok(IsEqualGUID(&output,&GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER),"Wrong GUID\n");
784
785     hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, list, 1);
786     ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
787     ok(IsEqualGUID(&output,&GUID_NULL),"Wrong GUID\n");
788
789     hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, list, 3);
790     ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
791     ok(IsEqualGUID(&output,&GUID_TFCAT_TIP_KEYBOARD),"Wrong GUID\n");
792 }
793
794 static void test_Enable(void)
795 {
796     HRESULT hr;
797     BOOL enabled = FALSE;
798
799     hr = ITfInputProcessorProfiles_EnableLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, TRUE);
800     ok(SUCCEEDED(hr),"Failed to enable text service\n");
801     hr = ITfInputProcessorProfiles_IsEnabledLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, &enabled);
802     ok(SUCCEEDED(hr),"Failed to get enabled state\n");
803     ok(enabled == TRUE,"enabled state incorrect\n");
804 }
805
806 static void test_Disable(void)
807 {
808     HRESULT hr;
809
810     trace("Disabling\n");
811     hr = ITfInputProcessorProfiles_EnableLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, FALSE);
812     ok(SUCCEEDED(hr),"Failed to disable text service\n");
813 }
814
815 static void test_ThreadMgrAdviseSinks(void)
816 {
817     ITfSource *source = NULL;
818     HRESULT hr;
819     IUnknown *sink;
820
821     hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfSource, (LPVOID*)&source);
822     ok(SUCCEEDED(hr),"Failed to get IID_ITfSource for ThreadMgr\n");
823     if (!source)
824         return;
825
826     ThreadMgrEventSink_Constructor(&sink);
827
828     tmSinkRefCount = 1;
829     tmSinkCookie = 0;
830     hr = ITfSource_AdviseSink(source,&IID_ITfThreadMgrEventSink, sink, &tmSinkCookie);
831     ok(SUCCEEDED(hr),"Failed to Advise Sink\n");
832     ok(tmSinkCookie!=0,"Failed to get sink cookie\n");
833
834     /* Advising the sink adds a ref, Relesing here lets the object be deleted
835        when unadvised */
836     tmSinkRefCount = 2;
837     IUnknown_Release(sink);
838     ITfSource_Release(source);
839 }
840
841 static void test_ThreadMgrUnadviseSinks(void)
842 {
843     ITfSource *source = NULL;
844     HRESULT hr;
845
846     hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfSource, (LPVOID*)&source);
847     ok(SUCCEEDED(hr),"Failed to get IID_ITfSource for ThreadMgr\n");
848     if (!source)
849         return;
850
851     tmSinkRefCount = 1;
852     hr = ITfSource_UnadviseSink(source, tmSinkCookie);
853     ok(SUCCEEDED(hr),"Failed to unadvise Sink\n");
854     ITfSource_Release(source);
855 }
856
857 /**********************************************************************
858  * ITfKeyEventSink
859  **********************************************************************/
860 typedef struct tagKeyEventSink
861 {
862     const ITfKeyEventSinkVtbl *KeyEventSinkVtbl;
863     LONG refCount;
864 } KeyEventSink;
865
866 static void KeyEventSink_Destructor(KeyEventSink *This)
867 {
868     HeapFree(GetProcessHeap(),0,This);
869 }
870
871 static HRESULT WINAPI KeyEventSink_QueryInterface(ITfKeyEventSink *iface, REFIID iid, LPVOID *ppvOut)
872 {
873     KeyEventSink *This = (KeyEventSink *)iface;
874     *ppvOut = NULL;
875
876     if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfKeyEventSink))
877     {
878         *ppvOut = This;
879     }
880
881     if (*ppvOut)
882     {
883         IUnknown_AddRef(iface);
884         return S_OK;
885     }
886
887     return E_NOINTERFACE;
888 }
889
890 static ULONG WINAPI KeyEventSink_AddRef(ITfKeyEventSink *iface)
891 {
892     KeyEventSink *This = (KeyEventSink *)iface;
893     return InterlockedIncrement(&This->refCount);
894 }
895
896 static ULONG WINAPI KeyEventSink_Release(ITfKeyEventSink *iface)
897 {
898     KeyEventSink *This = (KeyEventSink *)iface;
899     ULONG ret;
900
901     ret = InterlockedDecrement(&This->refCount);
902     if (ret == 0)
903         KeyEventSink_Destructor(This);
904     return ret;
905 }
906
907 static HRESULT WINAPI KeyEventSink_OnSetFocus(ITfKeyEventSink *iface,
908         BOOL fForeground)
909 {
910     ok(test_KEV_OnSetFocus == SINK_EXPECTED,"Unexpected KeyEventSink_OnSetFocus\n");
911     test_KEV_OnSetFocus = SINK_FIRED;
912     return S_OK;
913 }
914
915 static HRESULT WINAPI KeyEventSink_OnTestKeyDown(ITfKeyEventSink *iface,
916         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
917 {
918     trace("\n");
919     return S_OK;
920 }
921
922 static HRESULT WINAPI KeyEventSink_OnTestKeyUp(ITfKeyEventSink *iface,
923         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
924 {
925     trace("\n");
926     return S_OK;
927 }
928
929 static HRESULT WINAPI KeyEventSink_OnKeyDown(ITfKeyEventSink *iface,
930         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
931 {
932     trace("\n");
933     return S_OK;
934 }
935
936 static HRESULT WINAPI KeyEventSink_OnKeyUp(ITfKeyEventSink *iface,
937         ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
938 {
939     trace("\n");
940     return S_OK;
941 }
942
943 static HRESULT WINAPI KeyEventSink_OnPreservedKey(ITfKeyEventSink *iface,
944     ITfContext *pic, REFGUID rguid, BOOL *pfEaten)
945 {
946     trace("\n");
947     return S_OK;
948 }
949
950 static const ITfKeyEventSinkVtbl KeyEventSink_KeyEventSinkVtbl =
951 {
952     KeyEventSink_QueryInterface,
953     KeyEventSink_AddRef,
954     KeyEventSink_Release,
955
956     KeyEventSink_OnSetFocus,
957     KeyEventSink_OnTestKeyDown,
958     KeyEventSink_OnTestKeyUp,
959     KeyEventSink_OnKeyDown,
960     KeyEventSink_OnKeyUp,
961     KeyEventSink_OnPreservedKey
962 };
963
964 HRESULT KeyEventSink_Constructor(ITfKeyEventSink **ppOut)
965 {
966     KeyEventSink *This;
967
968     This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(KeyEventSink));
969     if (This == NULL)
970         return E_OUTOFMEMORY;
971
972     This->KeyEventSinkVtbl = &KeyEventSink_KeyEventSinkVtbl;
973     This->refCount = 1;
974
975     *ppOut = (ITfKeyEventSink*)This;
976     return S_OK;
977 }
978
979
980 static void test_KeystrokeMgr(void)
981 {
982     ITfKeystrokeMgr *keymgr= NULL;
983     HRESULT hr;
984     TF_PRESERVEDKEY tfpk;
985     BOOL preserved;
986     ITfKeyEventSink *sink;
987
988     KeyEventSink_Constructor(&sink);
989
990     hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfKeystrokeMgr, (LPVOID*)&keymgr);
991     ok(SUCCEEDED(hr),"Failed to get IID_ITfKeystrokeMgr for ThreadMgr\n");
992
993     tfpk.uVKey = 'A';
994     tfpk.uModifiers = TF_MOD_SHIFT;
995
996     test_KEV_OnSetFocus = SINK_EXPECTED;
997     hr = ITfKeystrokeMgr_AdviseKeyEventSink(keymgr,tid,sink,TRUE);
998     ok(SUCCEEDED(hr),"ITfKeystrokeMgr_AdviseKeyEventSink failed\n");
999     ok(test_KEV_OnSetFocus == SINK_FIRED, "KeyEventSink_OnSetFocus not fired as expected\n");
1000     hr = ITfKeystrokeMgr_AdviseKeyEventSink(keymgr,tid,sink,TRUE);
1001     ok(hr == CONNECT_E_ADVISELIMIT,"Wrong return, expected CONNECT_E_ADVISELIMIT\n");
1002     hr = ITfKeystrokeMgr_AdviseKeyEventSink(keymgr,cid,sink,TRUE);
1003     ok(hr == E_INVALIDARG,"Wrong return, expected E_INVALIDARG\n");
1004
1005     hr =ITfKeystrokeMgr_PreserveKey(keymgr, 0, &CLSID_PreservedKey, &tfpk, NULL, 0);
1006     ok(hr==E_INVALIDARG,"ITfKeystrokeMgr_PreserveKey inproperly succeeded\n");
1007
1008     hr =ITfKeystrokeMgr_PreserveKey(keymgr, tid, &CLSID_PreservedKey, &tfpk, NULL, 0);
1009     ok(SUCCEEDED(hr),"ITfKeystrokeMgr_PreserveKey failed\n");
1010
1011     hr =ITfKeystrokeMgr_PreserveKey(keymgr, tid, &CLSID_PreservedKey, &tfpk, NULL, 0);
1012     ok(hr == TF_E_ALREADY_EXISTS,"ITfKeystrokeMgr_PreserveKey inproperly succeeded\n");
1013
1014     preserved = FALSE;
1015     hr = ITfKeystrokeMgr_IsPreservedKey(keymgr, &CLSID_PreservedKey, &tfpk, &preserved);
1016     ok(hr == S_OK, "ITfKeystrokeMgr_IsPreservedKey failed\n");
1017     if (hr == S_OK) ok(preserved == TRUE,"misreporting preserved key\n");
1018
1019     hr = ITfKeystrokeMgr_UnpreserveKey(keymgr, &CLSID_PreservedKey,&tfpk);
1020     ok(SUCCEEDED(hr),"ITfKeystrokeMgr_UnpreserveKey failed\n");
1021
1022     hr = ITfKeystrokeMgr_IsPreservedKey(keymgr, &CLSID_PreservedKey, &tfpk, &preserved);
1023     ok(hr == S_FALSE, "ITfKeystrokeMgr_IsPreservedKey failed\n");
1024     if (hr == S_FALSE) ok(preserved == FALSE,"misreporting preserved key\n");
1025
1026     hr = ITfKeystrokeMgr_UnpreserveKey(keymgr, &CLSID_PreservedKey,&tfpk);
1027     ok(hr==CONNECT_E_NOCONNECTION,"ITfKeystrokeMgr_UnpreserveKey inproperly succeeded\n");
1028
1029     hr = ITfKeystrokeMgr_UnadviseKeyEventSink(keymgr,tid);
1030     ok(SUCCEEDED(hr),"ITfKeystrokeMgr_UnadviseKeyEventSink failed\n");
1031
1032     ITfKeystrokeMgr_Release(keymgr);
1033     ITfKeyEventSink_Release(sink);
1034 }
1035
1036 static void test_Activate(void)
1037 {
1038     HRESULT hr;
1039
1040     hr = ITfInputProcessorProfiles_ActivateLanguageProfile(g_ipp,&CLSID_FakeService,gLangid,&CLSID_FakeService);
1041     ok(SUCCEEDED(hr),"Failed to Activate text service\n");
1042 }
1043
1044 static inline int check_context_refcount(ITfContext *iface)
1045 {
1046     IUnknown_AddRef(iface);
1047     return IUnknown_Release(iface);
1048 }
1049
1050 static void test_startSession(void)
1051 {
1052     HRESULT hr;
1053     DWORD cnt;
1054     DWORD editCookie;
1055     ITfDocumentMgr *dmtest;
1056     ITfContext *cxt,*cxt2,*cxt3,*cxtTest;
1057     ITextStoreACP *ts;
1058     TfClientId cid2 = 0;
1059
1060     hr = ITfThreadMgr_Deactivate(g_tm);
1061     ok(hr == E_UNEXPECTED,"Deactivate should have failed with E_UNEXPECTED\n");
1062
1063     test_ShouldActivate = TRUE;
1064     hr  = ITfThreadMgr_Activate(g_tm,&cid);
1065     ok(SUCCEEDED(hr),"Failed to Activate\n");
1066     ok(cid != tid,"TextService id mistakenly matches Client id\n");
1067
1068     test_ShouldActivate = FALSE;
1069     hr = ITfThreadMgr_Activate(g_tm,&cid2);
1070     ok(SUCCEEDED(hr),"Failed to Activate\n");
1071     ok (cid == cid2, "Second activate client ID does not match\n");
1072
1073     hr = ITfThreadMgr_Deactivate(g_tm);
1074     ok(SUCCEEDED(hr),"Failed to Deactivate\n");
1075
1076     hr = ITfThreadMgr_CreateDocumentMgr(g_tm,&g_dm);
1077     ok(SUCCEEDED(hr),"CreateDocumentMgr failed\n");
1078
1079     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
1080     ok(SUCCEEDED(hr),"GetFocus Failed\n");
1081     ok(dmtest == NULL,"Initial focus not null\n");
1082
1083     test_CurrentFocus = g_dm;
1084     test_PrevFocus = NULL;
1085     test_OnSetFocus  = SINK_EXPECTED;
1086     hr = ITfThreadMgr_SetFocus(g_tm,g_dm);
1087     ok(SUCCEEDED(hr),"SetFocus Failed\n");
1088     ok(test_OnSetFocus == SINK_FIRED, "OnSetFocus sink not called\n");
1089     test_OnSetFocus  = SINK_UNEXPECTED;
1090
1091     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
1092     ok(SUCCEEDED(hr),"GetFocus Failed\n");
1093     ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");
1094
1095     cnt = ITfDocumentMgr_Release(g_dm);
1096     ok(cnt == 2,"DocumentMgr refcount not expected (2 vs %i)\n",cnt);
1097
1098     hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
1099     ok(SUCCEEDED(hr),"GetFocus Failed\n");
1100     ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");
1101
1102     TextStoreACP_Constructor((IUnknown**)&ts);
1103
1104     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, (IUnknown*)ts, &cxt, &editCookie);
1105     ok(SUCCEEDED(hr),"CreateContext Failed\n");
1106
1107     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt2, &editCookie);
1108     ok(SUCCEEDED(hr),"CreateContext Failed\n");
1109
1110     hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt3, &editCookie);
1111     ok(SUCCEEDED(hr),"CreateContext Failed\n");
1112
1113     cnt = check_context_refcount(cxt);
1114     test_OnPushContext = SINK_EXPECTED;
1115     test_OnInitDocumentMgr = SINK_EXPECTED;
1116     hr = ITfDocumentMgr_Push(g_dm, cxt);
1117     ok(SUCCEEDED(hr),"Push Failed\n");
1118     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
1119     ok(test_OnPushContext == SINK_FIRED, "OnPushContext sink not fired\n");
1120     ok(test_OnInitDocumentMgr == SINK_FIRED, "OnInitDocumentMgr sink not fired\n");
1121
1122     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
1123     ok(SUCCEEDED(hr),"GetTop Failed\n");
1124     ok(cxtTest == cxt, "Wrong context on top\n");
1125     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
1126     cnt = ITfContext_Release(cxtTest);
1127
1128     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
1129     ok(SUCCEEDED(hr),"GetBase Failed\n");
1130     ok(cxtTest == cxt, "Wrong context on Base\n");
1131     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
1132     ITfContext_Release(cxtTest);
1133
1134     check_context_refcount(cxt2);
1135     test_OnPushContext = SINK_EXPECTED;
1136     hr = ITfDocumentMgr_Push(g_dm, cxt2);
1137     ok(SUCCEEDED(hr),"Push Failed\n");
1138     ok(test_OnPushContext == SINK_FIRED, "OnPushContext sink not fired\n");
1139
1140     cnt = check_context_refcount(cxt2);
1141     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
1142     ok(SUCCEEDED(hr),"GetTop Failed\n");
1143     ok(cxtTest == cxt2, "Wrong context on top\n");
1144     ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
1145     ITfContext_Release(cxtTest);
1146
1147     cnt = check_context_refcount(cxt);
1148     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
1149     ok(SUCCEEDED(hr),"GetBase Failed\n");
1150     ok(cxtTest == cxt, "Wrong context on Base\n");
1151     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
1152     ITfContext_Release(cxtTest);
1153
1154     cnt = check_context_refcount(cxt3);
1155     hr = ITfDocumentMgr_Push(g_dm, cxt3);
1156     ok(!SUCCEEDED(hr),"Push Succeeded\n");
1157     ok(check_context_refcount(cxt3) == cnt, "Ref changed\n");
1158
1159     cnt = check_context_refcount(cxt2);
1160     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
1161     ok(SUCCEEDED(hr),"GetTop Failed\n");
1162     ok(cxtTest == cxt2, "Wrong context on top\n");
1163     ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
1164     ITfContext_Release(cxtTest);
1165
1166     cnt = check_context_refcount(cxt);
1167     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
1168     ok(SUCCEEDED(hr),"GetBase Failed\n");
1169     ok(cxtTest == cxt, "Wrong context on Base\n");
1170     ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
1171     ITfContext_Release(cxtTest);
1172
1173     cnt = check_context_refcount(cxt2);
1174     test_OnPopContext = SINK_EXPECTED;
1175     hr = ITfDocumentMgr_Pop(g_dm, 0);
1176     ok(SUCCEEDED(hr),"Pop Failed\n");
1177     ok(check_context_refcount(cxt2) < cnt, "Ref count did not decrease\n");
1178     ok(test_OnPopContext == SINK_FIRED, "OnPopContext sink not fired\n");
1179
1180     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
1181     ok(SUCCEEDED(hr),"GetTop Failed\n");
1182     ok(cxtTest == cxt, "Wrong context on top\n");
1183     ITfContext_Release(cxtTest);
1184
1185     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
1186     ok(SUCCEEDED(hr),"GetBase Failed\n");
1187     ok(cxtTest == cxt, "Wrong context on base\n");
1188     ITfContext_Release(cxtTest);
1189
1190     hr = ITfDocumentMgr_Pop(g_dm, 0);
1191     ok(!SUCCEEDED(hr),"Pop Succeeded\n");
1192
1193     hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
1194     ok(SUCCEEDED(hr),"GetTop Failed\n");
1195     ok(cxtTest == cxt, "Wrong context on top\n");
1196     ITfContext_Release(cxtTest);
1197
1198     hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
1199     ok(SUCCEEDED(hr),"GetBase Failed\n");
1200     ok(cxtTest == cxt, "Wrong context on base\n");
1201     ITfContext_Release(cxtTest);
1202
1203     ITfContext_Release(cxt);
1204     ITfContext_Release(cxt2);
1205     ITfContext_Release(cxt3);
1206 }
1207
1208 static void test_endSession(void)
1209 {
1210     HRESULT hr;
1211     test_ShouldDeactivate = TRUE;
1212     test_CurrentFocus = NULL;
1213     test_PrevFocus = g_dm;
1214     test_OnSetFocus  = SINK_EXPECTED;
1215     hr = ITfThreadMgr_Deactivate(g_tm);
1216     ok(SUCCEEDED(hr),"Failed to Deactivate\n");
1217     ok(test_OnSetFocus == SINK_FIRED, "OnSetFocus sink not called\n");
1218     test_OnSetFocus  = SINK_UNEXPECTED;
1219 }
1220
1221 static void test_TfGuidAtom(void)
1222 {
1223     GUID gtest,g1;
1224     HRESULT hr;
1225     TfGuidAtom atom1,atom2;
1226     BOOL equal;
1227
1228     CoCreateGuid(&gtest);
1229
1230     /* msdn reports this should return E_INVALIDARG.  However my test show it crashing (winxp)*/
1231     /*
1232     hr = ITfCategoryMgr_RegisterGUID(g_cm,&gtest,NULL);
1233     ok(hr==E_INVALIDARG,"ITfCategoryMgr_RegisterGUID should have failed\n");
1234     */
1235     hr = ITfCategoryMgr_RegisterGUID(g_cm,&gtest,&atom1);
1236     ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterGUID failed\n");
1237     hr = ITfCategoryMgr_RegisterGUID(g_cm,&gtest,&atom2);
1238     ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterGUID failed\n");
1239     ok(atom1 == atom2,"atoms do not match\n");
1240     hr = ITfCategoryMgr_GetGUID(g_cm,atom2,NULL);
1241     ok(hr==E_INVALIDARG,"ITfCategoryMgr_GetGUID should have failed\n");
1242     hr = ITfCategoryMgr_GetGUID(g_cm,atom2,&g1);
1243     ok(SUCCEEDED(hr),"ITfCategoryMgr_GetGUID failed\n");
1244     ok(IsEqualGUID(&g1,&gtest),"guids do not match\n");
1245     hr = ITfCategoryMgr_IsEqualTfGuidAtom(g_cm,atom1,&gtest,NULL);
1246     ok(hr==E_INVALIDARG,"ITfCategoryMgr_IsEqualTfGuidAtom should have failed\n");
1247     hr = ITfCategoryMgr_IsEqualTfGuidAtom(g_cm,atom1,&gtest,&equal);
1248     ok(SUCCEEDED(hr),"ITfCategoryMgr_IsEqualTfGuidAtom failed\n");
1249     ok(equal == TRUE,"Equal value invalid\n");
1250
1251     /* show that cid and tid TfClientIds are also TfGuidAtoms */
1252     hr = ITfCategoryMgr_IsEqualTfGuidAtom(g_cm,tid,&CLSID_FakeService,&equal);
1253     ok(SUCCEEDED(hr),"ITfCategoryMgr_IsEqualTfGuidAtom failed\n");
1254     ok(equal == TRUE,"Equal value invalid\n");
1255     hr = ITfCategoryMgr_GetGUID(g_cm,cid,&g1);
1256     ok(SUCCEEDED(hr),"ITfCategoryMgr_GetGUID failed\n");
1257     ok(!IsEqualGUID(&g1,&GUID_NULL),"guid should not be NULL\n");
1258 }
1259
1260 static void test_ClientId(void)
1261 {
1262     ITfClientId *pcid;
1263     TfClientId id1,id2;
1264     HRESULT hr;
1265     GUID g2;
1266
1267     hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfClientId, (LPVOID*)&pcid);
1268     ok(SUCCEEDED(hr),"Unable to aquire ITfClientId interface\n");
1269
1270     CoCreateGuid(&g2);
1271
1272     hr = ITfClientId_GetClientId(pcid,&GUID_NULL,&id1);
1273     ok(SUCCEEDED(hr),"GetClientId failed\n");
1274     hr = ITfClientId_GetClientId(pcid,&GUID_NULL,&id2);
1275     ok(SUCCEEDED(hr),"GetClientId failed\n");
1276     ok(id1==id2,"Id's for GUID_NULL do not match\n");
1277     hr = ITfClientId_GetClientId(pcid,&CLSID_FakeService,&id2);
1278     ok(SUCCEEDED(hr),"GetClientId failed\n");
1279     ok(id2!=id1,"Id matches GUID_NULL\n");
1280     ok(id2==tid,"Id for CLSID_FakeService not matching tid\n");
1281     ok(id2!=cid,"Id for CLSID_FakeService matching cid\n");
1282     hr = ITfClientId_GetClientId(pcid,&g2,&id2);
1283     ok(SUCCEEDED(hr),"GetClientId failed\n");
1284     ok(id2!=id1,"Id matches GUID_NULL\n");
1285     ok(id2!=tid,"Id for random guid matching tid\n");
1286     ok(id2!=cid,"Id for random guid matching cid\n");
1287     ITfClientId_Release(pcid);
1288 }
1289
1290 START_TEST(inputprocessor)
1291 {
1292     if (SUCCEEDED(initialize()))
1293     {
1294         test_Register();
1295         test_RegisterCategory();
1296         test_EnumInputProcessorInfo();
1297         test_Enable();
1298         test_ThreadMgrAdviseSinks();
1299         test_Activate();
1300         test_startSession();
1301         test_TfGuidAtom();
1302         test_ClientId();
1303         test_KeystrokeMgr();
1304         test_endSession();
1305         test_EnumLanguageProfiles();
1306         test_FindClosestCategory();
1307         test_Disable();
1308         test_ThreadMgrUnadviseSinks();
1309         test_UnregisterCategory();
1310         test_Unregister();
1311     }
1312     else
1313         skip("Unable to create InputProcessor\n");
1314     cleanup();
1315 }