inputprocessor.c 75.5 KB
Newer Older
Aric Stewart's avatar
Aric Stewart committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Unit tests for ITfInputProcessor
 *
 * Copyright 2009 Aric Stewart, CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdio.h>

#define COBJMACROS
24
#define CONST_VTABLE
Aric Stewart's avatar
Aric Stewart committed
25 26
#include "wine/test.h"
#include "winuser.h"
27
#include "initguid.h"
Aric Stewart's avatar
Aric Stewart committed
28 29 30 31
#include "shlwapi.h"
#include "shlguid.h"
#include "comcat.h"
#include "msctf.h"
32
#include "olectl.h"
Aric Stewart's avatar
Aric Stewart committed
33 34 35

static ITfInputProcessorProfiles* g_ipp;
static LANGID gLangid;
36 37
static ITfCategoryMgr * g_cm = NULL;
static ITfThreadMgr* g_tm = NULL;
38
static ITfDocumentMgr *g_dm = NULL;
39 40 41
static TfClientId cid = 0;
static TfClientId tid = 0;

42 43
static ITextStoreACPSink *ACPSink;

44 45 46
#define SINK_UNEXPECTED 0
#define SINK_EXPECTED 1
#define SINK_FIRED 2
47
#define SINK_IGNORE 3
48
#define SINK_OPTIONAL 4
49
#define SINK_SAVE 5
50 51 52 53 54 55

#define SINK_ACTION_MASK 0xff
#define SINK_OPTION_MASK 0xff00
#define SINK_EXPECTED_COUNT_MASK 0xff0000

#define SINK_OPTION_TODO      0x0100
56

57
#define FOCUS_IGNORE    (ITfDocumentMgr*)0xffffffff
58
#define FOCUS_SAVE      (ITfDocumentMgr*)0xfffffffe
59

60 61
static BOOL test_ShouldActivate = FALSE;
static BOOL test_ShouldDeactivate = FALSE;
62

63 64
static DWORD tmSinkCookie;
static DWORD tmSinkRefCount;
65
static DWORD dmSinkCookie;
66
static DWORD documentStatus;
67 68
static ITfDocumentMgr *test_CurrentFocus = NULL;
static ITfDocumentMgr *test_PrevFocus = NULL;
69 70
static ITfDocumentMgr *test_LastCurrentFocus = FOCUS_SAVE;
static ITfDocumentMgr *test_FirstPrevFocus = FOCUS_SAVE;
71
static INT  test_OnSetFocus = SINK_UNEXPECTED;
72 73 74
static INT  test_OnInitDocumentMgr = SINK_UNEXPECTED;
static INT  test_OnPushContext = SINK_UNEXPECTED;
static INT  test_OnPopContext = SINK_UNEXPECTED;
75
static INT  test_KEV_OnSetFocus = SINK_UNEXPECTED;
76
static INT  test_ACP_AdviseSink = SINK_UNEXPECTED;
77
static INT  test_ACP_UnadviseSink = SINK_UNEXPECTED;
78 79
static INT  test_ACP_GetStatus = SINK_UNEXPECTED;
static INT  test_ACP_RequestLock = SINK_UNEXPECTED;
80
static INT  test_ACP_GetEndACP = SINK_UNEXPECTED;
81
static INT  test_ACP_GetSelection = SINK_UNEXPECTED;
82
static INT  test_DoEditSession = SINK_UNEXPECTED;
83
static INT  test_ACP_InsertTextAtSelection = SINK_UNEXPECTED;
84
static INT  test_ACP_SetSelection = SINK_UNEXPECTED;
85
static INT  test_OnEndEdit = SINK_UNEXPECTED;
86 87


88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
static inline int expected_count(int *sink)
{
    return (*sink & SINK_EXPECTED_COUNT_MASK)>>16;
}

static inline void _sink_fire_ok(INT *sink, const CHAR* name)
{
    int count;
    int todo = *sink & SINK_OPTION_TODO;
    int action = *sink & SINK_ACTION_MASK;

    if (winetest_interactive)
        winetest_trace("firing %s\n",name);

    switch (action)
    {
        case SINK_OPTIONAL:
        case SINK_EXPECTED:
            count = expected_count(sink);
            if (count > 1)
            {
                count --;
                *sink = (*sink & ~SINK_EXPECTED_COUNT_MASK) + (count << 16);
                return;
            }
            break;
        case SINK_IGNORE:
            winetest_trace("Ignoring %s\n",name);
            return;
117 118 119 120
        case SINK_SAVE:
            count = expected_count(sink) + 1;
            *sink = (*sink & ~SINK_EXPECTED_COUNT_MASK) + (count << 16);
            return;
121
        default:
122
            todo_wine_if (todo)
123 124 125 126 127 128 129
                winetest_ok(0, "Unexpected %s sink\n",name);
    }
    *sink = SINK_FIRED;
}

#define sink_fire_ok(a,b) (winetest_set_location(__FILE__,__LINE__), 0) ? 0 : _sink_fire_ok(a,b)

130
static inline void _sink_check_ok(INT *sink, const CHAR* name)
131 132
{
    int action = *sink & SINK_ACTION_MASK;
133
    int todo = *sink & SINK_OPTION_TODO;
134
    int count = expected_count(sink);
135 136 137 138

    switch (action)
    {
        case SINK_OPTIONAL:
139 140 141
            if (winetest_interactive)
                winetest_trace("optional sink %s not fired\n",name);
        case SINK_FIRED:
142 143 144
            break;
        case SINK_IGNORE:
            return;
145 146 147 148
        case SINK_SAVE:
            if (count == 0 && winetest_interactive)
                winetest_trace("optional sink %s not fired\n",name);
            break;
149
        default:
150
            todo_wine_if (todo)
151
                winetest_ok(0, "%s not fired as expected, in state %x\n",name,*sink);
152 153 154 155 156 157
    }
    *sink = SINK_UNEXPECTED;
}

#define sink_check_ok(a,b) (winetest_set_location(__FILE__,__LINE__), 0) ? 0 : _sink_check_ok(a,b)

158 159 160 161 162 163 164 165 166 167 168 169 170 171
static inline void _sink_check_saved(INT *sink, ITfDocumentMgr *PrevFocus, ITfDocumentMgr *CurrentFocus, const CHAR* name)
{
    int count = expected_count(sink);
    _sink_check_ok(sink, name);
    if (PrevFocus != FOCUS_IGNORE && count != 0)
        winetest_ok(PrevFocus == test_FirstPrevFocus, "%s expected prev focus %p got %p\n", name, PrevFocus, test_FirstPrevFocus);
    if (CurrentFocus != FOCUS_IGNORE && count != 0)
        winetest_ok(CurrentFocus == test_LastCurrentFocus, "%s expected current focus %p got %p\n", name, CurrentFocus, test_LastCurrentFocus);
    test_FirstPrevFocus = FOCUS_SAVE;
    test_LastCurrentFocus = FOCUS_SAVE;
}

#define sink_check_saved(s,p,c,n) (winetest_set_location(__FILE__,__LINE__), 0) ? 0 : _sink_check_saved(s,p,c,n)

172
/**********************************************************************
173
 * ITextStoreACP
174
 **********************************************************************/
175
typedef struct tagTextStoreACP
176
{
177
    ITextStoreACP ITextStoreACP_iface;
178
    LONG refCount;
179

180
} TextStoreACP;
181

182 183 184 185 186
static inline TextStoreACP *impl_from_ITextStoreACP(ITextStoreACP *iface)
{
    return CONTAINING_RECORD(iface, TextStoreACP, ITextStoreACP_iface);
}

187
static void TextStoreACP_Destructor(TextStoreACP *This)
188 189 190 191
{
    HeapFree(GetProcessHeap(),0,This);
}

192
static HRESULT WINAPI TextStoreACP_QueryInterface(ITextStoreACP *iface, REFIID iid, LPVOID *ppvOut)
193 194 195
{
    *ppvOut = NULL;

196
    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITextStoreACP))
197
    {
198
        *ppvOut = iface;
199 200 201 202
    }

    if (*ppvOut)
    {
203
        ITextStoreACP_AddRef(iface);
204 205 206 207 208 209
        return S_OK;
    }

    return E_NOINTERFACE;
}

210
static ULONG WINAPI TextStoreACP_AddRef(ITextStoreACP *iface)
211
{
212
    TextStoreACP *This = impl_from_ITextStoreACP(iface);
213 214 215
    return InterlockedIncrement(&This->refCount);
}

216
static ULONG WINAPI TextStoreACP_Release(ITextStoreACP *iface)
217
{
218
    TextStoreACP *This = impl_from_ITextStoreACP(iface);
219 220 221 222
    ULONG ret;

    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
223
        TextStoreACP_Destructor(This);
224 225 226
    return ret;
}

227 228
static HRESULT WINAPI TextStoreACP_AdviseSink(ITextStoreACP *iface,
    REFIID riid, IUnknown *punk, DWORD dwMask)
229
{
230
    ITextStoreACPServices *services;
231 232
    HRESULT hr;

233
    sink_fire_ok(&test_ACP_AdviseSink,"TextStoreACP_AdviseSink");
234

235 236 237
    if(ACPSink)
        return S_OK;

238
    hr = IUnknown_QueryInterface(punk, &IID_ITextStoreACPSink, (void**)&ACPSink);
239
    ok(SUCCEEDED(hr),"Unable to QueryInterface on sink\n");
240 241 242 243 244

    hr = ITextStoreACPSink_QueryInterface(ACPSink, &IID_ITextStoreACPServices, (void**)&services);
    ok(hr == S_OK, "got 0x%08x\n", hr);
    ITextStoreACPServices_Release(services);

245 246 247
    return S_OK;
}

248 249
static HRESULT WINAPI TextStoreACP_UnadviseSink(ITextStoreACP *iface,
    IUnknown *punk)
250
{
251
    sink_fire_ok(&test_ACP_UnadviseSink,"TextStoreACP_UnadviseSink");
252 253
    return S_OK;
}
254

255 256
static HRESULT WINAPI TextStoreACP_RequestLock(ITextStoreACP *iface,
    DWORD dwLockFlags, HRESULT *phrSession)
257
{
258
    sink_fire_ok(&test_ACP_RequestLock,"TextStoreACP_RequestLock");
259
    *phrSession = ITextStoreACPSink_OnLockGranted(ACPSink, dwLockFlags);
260 261
    return S_OK;
}
262 263
static HRESULT WINAPI TextStoreACP_GetStatus(ITextStoreACP *iface,
    TS_STATUS *pdcs)
264
{
265
    sink_fire_ok(&test_ACP_GetStatus,"TextStoreACP_GetStatus");
266
    pdcs->dwDynamicFlags = documentStatus;
267 268
    return S_OK;
}
269 270 271
static HRESULT WINAPI TextStoreACP_QueryInsert(ITextStoreACP *iface,
    LONG acpTestStart, LONG acpTestEnd, ULONG cch, LONG *pacpResultStart,
    LONG *pacpResultEnd)
272 273 274 275
{
    trace("\n");
    return S_OK;
}
276 277
static HRESULT WINAPI TextStoreACP_GetSelection(ITextStoreACP *iface,
    ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched)
278
{
279
    sink_fire_ok(&test_ACP_GetSelection,"TextStoreACP_GetSelection");
280 281 282 283 284 285 286

    pSelection->acpStart = 10;
    pSelection->acpEnd = 20;
    pSelection->style.fInterimChar = 0;
    pSelection->style.ase = TS_AE_NONE;
    *pcFetched = 1;

287 288 289 290
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_SetSelection(ITextStoreACP *iface,
    ULONG ulCount, const TS_SELECTION_ACP *pSelection)
291
{
292
    sink_fire_ok(&test_ACP_SetSelection,"TextStoreACP_SetSelection");
293 294
    return S_OK;
}
295 296 297 298
static HRESULT WINAPI TextStoreACP_GetText(ITextStoreACP *iface,
    LONG acpStart, LONG acpEnd, WCHAR *pchPlain, ULONG cchPlainReq,
    ULONG *pcchPlainRet, TS_RUNINFO *prgRunInfo, ULONG cRunInfoReq,
    ULONG *pcRunInfoRet, LONG *pacpNext)
299
{
300 301
    trace("\n");
    return S_OK;
302
}
303 304 305
static HRESULT WINAPI TextStoreACP_SetText(ITextStoreACP *iface,
    DWORD dwFlags, LONG acpStart, LONG acpEnd, const WCHAR *pchText,
    ULONG cch, TS_TEXTCHANGE *pChange)
306
{
307 308
    trace("\n");
    return S_OK;
309
}
310 311
static HRESULT WINAPI TextStoreACP_GetFormattedText(ITextStoreACP *iface,
    LONG acpStart, LONG acpEnd, IDataObject **ppDataObject)
312
{
313 314
    trace("\n");
    return S_OK;
315
}
316 317
static HRESULT WINAPI TextStoreACP_GetEmbedded(ITextStoreACP *iface,
    LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
318
{
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_QueryInsertEmbedded(ITextStoreACP *iface,
    const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_InsertEmbedded(ITextStoreACP *iface,
    DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject,
    TS_TEXTCHANGE *pChange)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_InsertTextAtSelection(ITextStoreACP *iface,
    DWORD dwFlags, const WCHAR *pchText, ULONG cch, LONG *pacpStart,
    LONG *pacpEnd, TS_TEXTCHANGE *pChange)
{
339
    sink_fire_ok(&test_ACP_InsertTextAtSelection,"TextStoreACP_InsertTextAtSelection");
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_InsertEmbeddedAtSelection(ITextStoreACP *iface,
    DWORD dwFlags, IDataObject *pDataObject, LONG *pacpStart, LONG *pacpEnd,
    TS_TEXTCHANGE *pChange)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_RequestSupportedAttrs(ITextStoreACP *iface,
    DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_RequestAttrsAtPosition(ITextStoreACP *iface,
    LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
    DWORD dwFlags)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_RequestAttrsTransitioningAtPosition(ITextStoreACP *iface,
    LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
    DWORD dwFlags)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_FindNextAttrTransition(ITextStoreACP *iface,
    LONG acpStart, LONG acpHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
    DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_RetrieveRequestedAttrs(ITextStoreACP *iface,
    ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_GetEndACP(ITextStoreACP *iface,
    LONG *pacp)
{
385
    sink_fire_ok(&test_ACP_GetEndACP,"TextStoreACP_GetEndACP");
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_GetActiveView(ITextStoreACP *iface,
    TsViewCookie *pvcView)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_GetACPFromPoint(ITextStoreACP *iface,
    TsViewCookie vcView, const POINT *ptScreen, DWORD dwFlags,
    LONG *pacp)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_GetTextExt(ITextStoreACP *iface,
    TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc,
    BOOL *pfClipped)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_GetScreenExt(ITextStoreACP *iface,
    TsViewCookie vcView, RECT *prc)
{
    trace("\n");
    return S_OK;
}
static HRESULT WINAPI TextStoreACP_GetWnd(ITextStoreACP *iface,
    TsViewCookie vcView, HWND *phwnd)
{
    trace("\n");
    return S_OK;
}
420

421 422 423 424 425
static const ITextStoreACPVtbl TextStoreACP_TextStoreACPVtbl =
{
    TextStoreACP_QueryInterface,
    TextStoreACP_AddRef,
    TextStoreACP_Release,
426

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
    TextStoreACP_AdviseSink,
    TextStoreACP_UnadviseSink,
    TextStoreACP_RequestLock,
    TextStoreACP_GetStatus,
    TextStoreACP_QueryInsert,
    TextStoreACP_GetSelection,
    TextStoreACP_SetSelection,
    TextStoreACP_GetText,
    TextStoreACP_SetText,
    TextStoreACP_GetFormattedText,
    TextStoreACP_GetEmbedded,
    TextStoreACP_QueryInsertEmbedded,
    TextStoreACP_InsertEmbedded,
    TextStoreACP_InsertTextAtSelection,
    TextStoreACP_InsertEmbeddedAtSelection,
    TextStoreACP_RequestSupportedAttrs,
    TextStoreACP_RequestAttrsAtPosition,
    TextStoreACP_RequestAttrsTransitioningAtPosition,
    TextStoreACP_FindNextAttrTransition,
    TextStoreACP_RetrieveRequestedAttrs,
    TextStoreACP_GetEndACP,
    TextStoreACP_GetActiveView,
    TextStoreACP_GetACPFromPoint,
    TextStoreACP_GetTextExt,
    TextStoreACP_GetScreenExt,
    TextStoreACP_GetWnd
};
454

455
static HRESULT TextStoreACP_Constructor(IUnknown **ppOut)
456 457
{
    TextStoreACP *This;
458

459 460 461
    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextStoreACP));
    if (This == NULL)
        return E_OUTOFMEMORY;
462

463
    This->ITextStoreACP_iface.lpVtbl = &TextStoreACP_TextStoreACPVtbl;
464
    This->refCount = 1;
465

466
    *ppOut = (IUnknown*)&This->ITextStoreACP_iface;
467 468
    return S_OK;
}
469

470 471 472 473 474
/**********************************************************************
 * ITfThreadMgrEventSink
 **********************************************************************/
typedef struct tagThreadMgrEventSink
{
475
    ITfThreadMgrEventSink ITfThreadMgrEventSink_iface;
476 477
    LONG refCount;
} ThreadMgrEventSink;
478

479 480 481 482 483
static inline ThreadMgrEventSink *impl_from_ITfThreadMgrEventSink(ITfThreadMgrEventSink *iface)
{
    return CONTAINING_RECORD(iface, ThreadMgrEventSink, ITfThreadMgrEventSink_iface);
}

484 485 486 487
static void ThreadMgrEventSink_Destructor(ThreadMgrEventSink *This)
{
    HeapFree(GetProcessHeap(),0,This);
}
488

489 490 491
static HRESULT WINAPI ThreadMgrEventSink_QueryInterface(ITfThreadMgrEventSink *iface, REFIID iid, LPVOID *ppvOut)
{
    *ppvOut = NULL;
492

493 494
    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfThreadMgrEventSink))
    {
495
        *ppvOut = iface;
496
    }
497

498 499
    if (*ppvOut)
    {
500
        ITfThreadMgrEventSink_AddRef(iface);
501 502
        return S_OK;
    }
503

504 505
    return E_NOINTERFACE;
}
506

507 508
static ULONG WINAPI ThreadMgrEventSink_AddRef(ITfThreadMgrEventSink *iface)
{
509
    ThreadMgrEventSink *This = impl_from_ITfThreadMgrEventSink(iface);
510 511 512
    ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
    return InterlockedIncrement(&This->refCount);
}
513

514 515
static ULONG WINAPI ThreadMgrEventSink_Release(ITfThreadMgrEventSink *iface)
{
516
    ThreadMgrEventSink *This = impl_from_ITfThreadMgrEventSink(iface);
517
    ULONG ret;
518

519 520 521 522 523
    ok (tmSinkRefCount == This->refCount,"ThreadMgrEventSink refcount off %i vs %i\n",This->refCount,tmSinkRefCount);
    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
        ThreadMgrEventSink_Destructor(This);
    return ret;
524 525
}

526 527
static HRESULT WINAPI ThreadMgrEventSink_OnInitDocumentMgr(ITfThreadMgrEventSink *iface,
ITfDocumentMgr *pdim)
528
{
529
    sink_fire_ok(&test_OnInitDocumentMgr,"ThreadMgrEventSink_OnInitDocumentMgr");
530
    return S_OK;
531 532
}

533 534
static HRESULT WINAPI ThreadMgrEventSink_OnUninitDocumentMgr(ITfThreadMgrEventSink *iface,
ITfDocumentMgr *pdim)
535
{
536 537 538
    trace("\n");
    return S_OK;
}
539

540 541 542
static HRESULT WINAPI ThreadMgrEventSink_OnSetFocus(ITfThreadMgrEventSink *iface,
ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus)
{
543
    sink_fire_ok(&test_OnSetFocus,"ThreadMgrEventSink_OnSetFocus");
544 545 546
    if (test_CurrentFocus == FOCUS_SAVE)
        test_LastCurrentFocus = pdimFocus;
    else if (test_CurrentFocus != FOCUS_IGNORE)
547
        ok(pdimFocus == test_CurrentFocus,"Sink reports wrong focus\n");
548 549 550 551 552 553
    if (test_PrevFocus == FOCUS_SAVE)
    {
        if (test_FirstPrevFocus == FOCUS_SAVE)
            test_FirstPrevFocus = pdimPrevFocus;
    }
    else if (test_PrevFocus != FOCUS_IGNORE)
554
        ok(pdimPrevFocus == test_PrevFocus,"Sink reports wrong previous focus\n");
555 556
    return S_OK;
}
557

558 559 560
static HRESULT WINAPI ThreadMgrEventSink_OnPushContext(ITfThreadMgrEventSink *iface,
ITfContext *pic)
{
561 562 563 564 565
    HRESULT hr;
    ITfDocumentMgr *docmgr;
    ITfContext *test;

    hr = ITfContext_GetDocumentMgr(pic,&docmgr);
566
    ok(SUCCEEDED(hr),"GetDocumentMgr failed\n");
567 568 569
    test = (ITfContext*)0xdeadbeef;
    hr = ITfDocumentMgr_GetTop(docmgr,&test);
    ok(SUCCEEDED(hr),"GetTop failed\n");
570
    ITfDocumentMgr_Release(docmgr);
571 572 573 574
    ok(test == pic, "Wrong context is on top\n");
    if (test)
        ITfContext_Release(test);

575
    sink_fire_ok(&test_OnPushContext,"ThreadMgrEventSink_OnPushContext");
576 577
    return S_OK;
}
578

579 580 581
static HRESULT WINAPI ThreadMgrEventSink_OnPopContext(ITfThreadMgrEventSink *iface,
ITfContext *pic)
{
582 583 584 585 586
    HRESULT hr;
    ITfDocumentMgr *docmgr;
    ITfContext *test;

    hr = ITfContext_GetDocumentMgr(pic,&docmgr);
587
    ok(SUCCEEDED(hr),"GetDocumentMgr failed\n");
588 589 590 591 592 593 594 595
    ITfDocumentMgr_Release(docmgr);
    test = (ITfContext*)0xdeadbeef;
    hr = ITfDocumentMgr_GetTop(docmgr,&test);
    ok(SUCCEEDED(hr),"GetTop failed\n");
    ok(test == pic, "Wrong context is on top\n");
    if (test)
        ITfContext_Release(test);

596
    sink_fire_ok(&test_OnPopContext,"ThreadMgrEventSink_OnPopContext");
597
    return S_OK;
598 599
}

600
static const ITfThreadMgrEventSinkVtbl ThreadMgrEventSink_ThreadMgrEventSinkVtbl =
601
{
602 603 604
    ThreadMgrEventSink_QueryInterface,
    ThreadMgrEventSink_AddRef,
    ThreadMgrEventSink_Release,
605

606 607 608 609 610 611
    ThreadMgrEventSink_OnInitDocumentMgr,
    ThreadMgrEventSink_OnUninitDocumentMgr,
    ThreadMgrEventSink_OnSetFocus,
    ThreadMgrEventSink_OnPushContext,
    ThreadMgrEventSink_OnPopContext
};
612

613
static HRESULT ThreadMgrEventSink_Constructor(IUnknown **ppOut)
614 615
{
    ThreadMgrEventSink *This;
616

617 618 619
    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ThreadMgrEventSink));
    if (This == NULL)
        return E_OUTOFMEMORY;
620

621
    This->ITfThreadMgrEventSink_iface.lpVtbl = &ThreadMgrEventSink_ThreadMgrEventSinkVtbl;
622 623
    This->refCount = 1;

624
    *ppOut = (IUnknown*)&This->ITfThreadMgrEventSink_iface;
625
    return S_OK;
Aric Stewart's avatar
Aric Stewart committed
626
}
627

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
static HRESULT WINAPI TfTransitoryExtensionSink_QueryInterface(ITfTransitoryExtensionSink *iface, REFIID riid, void **ppv)
{
    if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ITfTransitoryExtensionSink, riid)) {
        *ppv = iface;
        return S_OK;
    }

    *ppv = NULL;
    return E_NOINTERFACE;
}

static ULONG WINAPI TfTransitoryExtensionSink_AddRef(ITfTransitoryExtensionSink *iface)
{
    return 2;
}

static ULONG WINAPI TfTransitoryExtensionSink_Release(ITfTransitoryExtensionSink *iface)
{
    return 1;
}

static HRESULT WINAPI TfTransitoryExtensionSink_OnTransitoryExtensionUpdated(ITfTransitoryExtensionSink *iface, ITfContext *pic,
        TfEditCookie ecReadOnly, ITfRange *pResultRange, ITfRange *pCompositionRange, BOOL *pfDeleteResultRange)
{
    ok(0, "unexpected call\n");
    return E_NOTIMPL;
}

static const ITfTransitoryExtensionSinkVtbl TfTransitoryExtensionSinkVtbl = {
    TfTransitoryExtensionSink_QueryInterface,
    TfTransitoryExtensionSink_AddRef,
    TfTransitoryExtensionSink_Release,
    TfTransitoryExtensionSink_OnTransitoryExtensionUpdated
};

static ITfTransitoryExtensionSink TfTransitoryExtensionSink = { &TfTransitoryExtensionSinkVtbl };
664 665 666 667 668 669 670 671 672 673 674 675 676

/********************************************************************************************
 * Stub text service for testing
 ********************************************************************************************/

static LONG TS_refCount;
static IClassFactory *cf;
static DWORD regid;

typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut);

typedef struct tagClassFactory
{
677
    IClassFactory IClassFactory_iface;
678 679 680 681
    LONG   ref;
    LPFNCONSTRUCTOR ctor;
} ClassFactory;

682 683 684 685 686
static inline ClassFactory *impl_from_IClassFactory(IClassFactory *iface)
{
    return CONTAINING_RECORD(iface, ClassFactory, IClassFactory_iface);
}

687
typedef struct tagTextService
688
{
689
    ITfTextInputProcessor ITfTextInputProcessor_iface;
690
    LONG refCount;
691
} TextService;
692

693 694 695 696 697
static inline TextService *impl_from_ITfTextInputProcessor(ITfTextInputProcessor *iface)
{
    return CONTAINING_RECORD(iface, TextService, ITfTextInputProcessor_iface);
}

698
static void ClassFactory_Destructor(ClassFactory *This)
699 700
{
    HeapFree(GetProcessHeap(),0,This);
701
    TS_refCount--;
702 703
}

704
static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppvOut)
705 706
{
    *ppvOut = NULL;
707
    if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown))
708
    {
709 710
        IClassFactory_AddRef(iface);
        *ppvOut = iface;
711 712 713 714 715 716
        return S_OK;
    }

    return E_NOINTERFACE;
}

717
static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
718
{
719
    ClassFactory *This = impl_from_IClassFactory(iface);
720
    return InterlockedIncrement(&This->ref);
721 722
}

723
static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
724
{
725
    ClassFactory *This = impl_from_IClassFactory(iface);
726
    ULONG ret = InterlockedDecrement(&This->ref);
727 728

    if (ret == 0)
729
        ClassFactory_Destructor(This);
730 731 732
    return ret;
}

733
static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID iid, LPVOID *ppvOut)
734
{
735
    ClassFactory *This = impl_from_IClassFactory(iface);
736 737
    HRESULT ret;
    IUnknown *obj;
738

739 740 741 742 743 744
    ret = This->ctor(punkOuter, &obj);
    if (FAILED(ret))
        return ret;
    ret = IUnknown_QueryInterface(obj, iid, ppvOut);
    IUnknown_Release(obj);
    return ret;
745
}
746 747

static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
748
{
749 750 751 752 753
    if(fLock)
        InterlockedIncrement(&TS_refCount);
    else
        InterlockedDecrement(&TS_refCount);

754 755
    return S_OK;
}
756 757 758 759 760 761 762 763 764 765 766 767 768

static const IClassFactoryVtbl ClassFactoryVtbl = {
    /* IUnknown */
    ClassFactory_QueryInterface,
    ClassFactory_AddRef,
    ClassFactory_Release,

    /* IClassFactory*/
    ClassFactory_CreateInstance,
    ClassFactory_LockServer
};

static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut)
769
{
770
    ClassFactory *This = HeapAlloc(GetProcessHeap(),0,sizeof(ClassFactory));
771
    This->IClassFactory_iface.lpVtbl = &ClassFactoryVtbl;
772 773
    This->ref = 1;
    This->ctor = ctor;
774
    *ppvOut = &This->IClassFactory_iface;
775
    TS_refCount++;
776 777
    return S_OK;
}
778 779

static void TextService_Destructor(TextService *This)
780
{
781
    HeapFree(GetProcessHeap(),0,This);
782
}
783 784

static HRESULT WINAPI TextService_QueryInterface(ITfTextInputProcessor *iface, REFIID iid, LPVOID *ppvOut)
785
{
786 787 788 789
    *ppvOut = NULL;

    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfTextInputProcessor))
    {
790
        *ppvOut = iface;
791 792 793 794
    }

    if (*ppvOut)
    {
795
        ITfTextInputProcessor_AddRef(iface);
796 797 798 799
        return S_OK;
    }

    return E_NOINTERFACE;
800
}
801 802

static ULONG WINAPI TextService_AddRef(ITfTextInputProcessor *iface)
803
{
804
    TextService *This = impl_from_ITfTextInputProcessor(iface);
805
    return InterlockedIncrement(&This->refCount);
806
}
807 808

static ULONG WINAPI TextService_Release(ITfTextInputProcessor *iface)
809
{
810
    TextService *This = impl_from_ITfTextInputProcessor(iface);
811 812 813 814 815 816
    ULONG ret;

    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
        TextService_Destructor(This);
    return ret;
817
}
818 819 820

static HRESULT WINAPI TextService_Activate(ITfTextInputProcessor *iface,
        ITfThreadMgr *ptim, TfClientId id)
821
{
822 823 824
    trace("TextService_Activate\n");
    ok(test_ShouldActivate,"Activation came unexpectedly\n");
    tid = id;
825 826
    return S_OK;
}
827 828

static HRESULT WINAPI TextService_Deactivate(ITfTextInputProcessor *iface)
829
{
830 831
    trace("TextService_Deactivate\n");
    ok(test_ShouldDeactivate,"Deactivation came unexpectedly\n");
832 833
    return S_OK;
}
834 835

static const ITfTextInputProcessorVtbl TextService_TextInputProcessorVtbl=
836
{
837 838 839 840 841 842 843 844
    TextService_QueryInterface,
    TextService_AddRef,
    TextService_Release,

    TextService_Activate,
    TextService_Deactivate
};

845
static HRESULT TextService_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
846
{
847 848 849 850 851 852 853 854
    TextService *This;
    if (pUnkOuter)
        return CLASS_E_NOAGGREGATION;

    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextService));
    if (This == NULL)
        return E_OUTOFMEMORY;

855
    This->ITfTextInputProcessor_iface.lpVtbl = &TextService_TextInputProcessorVtbl;
856 857
    This->refCount = 1;

858
    *ppOut = (IUnknown*)&This->ITfTextInputProcessor_iface;
859 860
    return S_OK;
}
861

862
static HRESULT RegisterTextService(REFCLSID rclsid)
863
{
864 865
    ClassFactory_Constructor( TextService_Constructor ,(LPVOID*)&cf);
    return CoRegisterClassObject(rclsid, (IUnknown*) cf, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &regid);
866
}
867

868
static HRESULT UnregisterTextService(void)
869
{
870
    return CoRevokeClassObject(regid);
871
}
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886

/*
 * The tests
 */

DEFINE_GUID(CLSID_FakeService, 0xEDE1A7AD,0x66DE,0x47E0,0xB6,0x20,0x3E,0x92,0xF8,0x24,0x6B,0xF3);
DEFINE_GUID(CLSID_TF_InputProcessorProfiles, 0x33c53a50,0xf456,0x4884,0xb0,0x49,0x85,0xfd,0x64,0x3e,0xcf,0xed);
DEFINE_GUID(CLSID_TF_CategoryMgr,         0xA4B544A1,0x438D,0x4B41,0x93,0x25,0x86,0x95,0x23,0xE2,0xD6,0xC7);
DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD,     0x34745c63,0xb2f0,0x4784,0x8b,0x67,0x5e,0x12,0xc8,0x70,0x1a,0x31);
DEFINE_GUID(GUID_TFCAT_TIP_SPEECH,       0xB5A73CD1,0x8355,0x426B,0xA1,0x61,0x25,0x98,0x08,0xF2,0x6B,0x14);
DEFINE_GUID(GUID_TFCAT_TIP_HANDWRITING,  0x246ecb87,0xc2f2,0x4abe,0x90,0x5b,0xc8,0xb3,0x8a,0xdd,0x2c,0x43);
DEFINE_GUID (GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER,  0x046B8C80,0x1647,0x40F7,0x9B,0x21,0xB9,0x3B,0x81,0xAA,0xBC,0x1B);
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
DEFINE_GUID(CLSID_TF_ThreadMgr,           0x529a9e6b,0x6587,0x4f23,0xab,0x9e,0x9c,0x7d,0x68,0x3e,0x3c,0x50);
DEFINE_GUID(CLSID_PreservedKey,           0xA0ED8E55,0xCD3B,0x4274,0xB2,0x95,0xF6,0xC9,0xBA,0x2B,0x84,0x72);
887 888 889 890 891 892 893 894 895
DEFINE_GUID(GUID_COMPARTMENT_KEYBOARD_DISABLED,     0x71a5b253,0x1951,0x466b,0x9f,0xbc,0x9c,0x88,0x08,0xfa,0x84,0xf2);
DEFINE_GUID(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,    0x58273aad,0x01bb,0x4164,0x95,0xc6,0x75,0x5b,0xa0,0xb5,0x16,0x2d);
DEFINE_GUID(GUID_COMPARTMENT_HANDWRITING_OPENCLOSE, 0xf9ae2c6b,0x1866,0x4361,0xaf,0x72,0x7a,0xa3,0x09,0x48,0x89,0x0e);
DEFINE_GUID(GUID_COMPARTMENT_SPEECH_DISABLED,       0x56c5c607,0x0703,0x4e59,0x8e,0x52,0xcb,0xc8,0x4e,0x8b,0xbe,0x35);
DEFINE_GUID(GUID_COMPARTMENT_SPEECH_OPENCLOSE,      0x544d6a63,0xe2e8,0x4752,0xbb,0xd1,0x00,0x09,0x60,0xbc,0xa0,0x83);
DEFINE_GUID(GUID_COMPARTMENT_SPEECH_GLOBALSTATE,    0x2a54fe8e,0x0d08,0x460c,0xa7,0x5d,0x87,0x03,0x5f,0xf4,0x36,0xc5);
DEFINE_GUID(GUID_COMPARTMENT_PERSISTMENUENABLED,    0x575f3783,0x70c8,0x47c8,0xae,0x5d,0x91,0xa0,0x1a,0x1f,0x75,0x92);
DEFINE_GUID(GUID_COMPARTMENT_EMPTYCONTEXT,          0xd7487dbf,0x804e,0x41c5,0x89,0x4d,0xad,0x96,0xfd,0x4e,0xea,0x13);
DEFINE_GUID(GUID_COMPARTMENT_TIPUISTATUS,           0x148ca3ec,0x0366,0x401c,0x8d,0x75,0xed,0x97,0x8d,0x85,0xfb,0xc9);
896 897

static HRESULT initialize(void)
898
{
899 900 901 902 903 904 905 906 907 908 909 910 911 912
    HRESULT hr;
    CoInitialize(NULL);
    hr = CoCreateInstance (&CLSID_TF_InputProcessorProfiles, NULL,
          CLSCTX_INPROC_SERVER, &IID_ITfInputProcessorProfiles, (void**)&g_ipp);
    if (SUCCEEDED(hr))
        hr = CoCreateInstance (&CLSID_TF_CategoryMgr, NULL,
          CLSCTX_INPROC_SERVER, &IID_ITfCategoryMgr, (void**)&g_cm);
    if (SUCCEEDED(hr))
        hr = CoCreateInstance (&CLSID_TF_ThreadMgr, NULL,
          CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (void**)&g_tm);
    return hr;
}

static void cleanup(void)
913
{
914 915 916 917 918 919 920
    if (g_ipp)
        ITfInputProcessorProfiles_Release(g_ipp);
    if (g_cm)
        ITfCategoryMgr_Release(g_cm);
    if (g_tm)
        ITfThreadMgr_Release(g_tm);
    CoUninitialize();
921
}
922 923

static void test_Register(void)
924
{
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
    HRESULT hr;

    static const WCHAR szDesc[] = {'F','a','k','e',' ','W','i','n','e',' ','S','e','r','v','i','c','e',0};
    static const WCHAR szFile[] = {'F','a','k','e',' ','W','i','n','e',' ','S','e','r','v','i','c','e',' ','F','i','l','e',0};

    hr = ITfInputProcessorProfiles_GetCurrentLanguage(g_ipp,&gLangid);
    ok(SUCCEEDED(hr),"Unable to get current language id\n");
    trace("Current Language %x\n",gLangid);

    hr = RegisterTextService(&CLSID_FakeService);
    ok(SUCCEEDED(hr),"Unable to register COM for TextService\n");
    hr = ITfInputProcessorProfiles_Register(g_ipp, &CLSID_FakeService);
    ok(SUCCEEDED(hr),"Unable to register text service(%x)\n",hr);
    hr = ITfInputProcessorProfiles_AddLanguageProfile(g_ipp, &CLSID_FakeService, gLangid, &CLSID_FakeService, szDesc, sizeof(szDesc)/sizeof(WCHAR), szFile, sizeof(szFile)/sizeof(WCHAR), 1);
    ok(SUCCEEDED(hr),"Unable to add Language Profile (%x)\n",hr);
940
}
941 942

static void test_Unregister(void)
943
{
944 945 946 947
    HRESULT hr;
    hr = ITfInputProcessorProfiles_Unregister(g_ipp, &CLSID_FakeService);
    ok(SUCCEEDED(hr),"Unable to unregister text service(%x)\n",hr);
    UnregisterTextService();
948
}
949 950

static void test_EnumInputProcessorInfo(void)
951
{
952 953 954 955 956 957 958 959 960 961 962 963
    IEnumGUID *ppEnum;
    BOOL found = FALSE;

    if (SUCCEEDED(ITfInputProcessorProfiles_EnumInputProcessorInfo(g_ipp, &ppEnum)))
    {
        ULONG fetched;
        GUID g;
        while (IEnumGUID_Next(ppEnum, 1, &g, &fetched) == S_OK)
        {
            if(IsEqualGUID(&g,&CLSID_FakeService))
                found = TRUE;
        }
964
        IEnumGUID_Release(ppEnum);
965 966
    }
    ok(found,"Did not find registered text service\n");
967
}
968 969

static void test_EnumLanguageProfiles(void)
970
{
971 972
    BOOL found = FALSE;
    IEnumTfLanguageProfiles *ppEnum;
973 974 975 976 977
    HRESULT hr;

    hr = ITfInputProcessorProfiles_EnumLanguageProfiles(g_ipp, gLangid, NULL);
    ok(hr == E_INVALIDARG, "EnumLanguageProfiles failed: %x\n", hr);

978 979 980 981 982 983 984 985 986
    if (SUCCEEDED(ITfInputProcessorProfiles_EnumLanguageProfiles(g_ipp,gLangid,&ppEnum)))
    {
        TF_LANGUAGEPROFILE profile;
        while (IEnumTfLanguageProfiles_Next(ppEnum,1,&profile,NULL)==S_OK)
        {
            if (IsEqualGUID(&profile.clsid,&CLSID_FakeService))
            {
                found = TRUE;
                ok(profile.langid == gLangid, "LangId Incorrect\n");
987 988
                ok(IsEqualGUID(&profile.catid,&GUID_TFCAT_TIP_KEYBOARD) ||
                   broken(IsEqualGUID(&profile.catid,&GUID_NULL) /* Win8 */), "CatId Incorrect\n");
989 990 991
                ok(IsEqualGUID(&profile.guidProfile,&CLSID_FakeService), "guidProfile Incorrect\n");
            }
        }
992
        IEnumTfLanguageProfiles_Release(ppEnum);
993 994
    }
    ok(found,"Registered text service not found\n");
995
}
996 997

static void test_RegisterCategory(void)
998
{
999 1000 1001 1002 1003
    HRESULT hr;
    hr = ITfCategoryMgr_RegisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_TIP_KEYBOARD, &CLSID_FakeService);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterCategory failed\n");
    hr = ITfCategoryMgr_RegisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, &CLSID_FakeService);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterCategory failed\n");
1004
}
1005 1006

static void test_UnregisterCategory(void)
1007
{
1008 1009 1010 1011 1012
    HRESULT hr;
    hr = ITfCategoryMgr_UnregisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_TIP_KEYBOARD, &CLSID_FakeService);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_UnregisterCategory failed\n");
    hr = ITfCategoryMgr_UnregisterCategory(g_cm, &CLSID_FakeService, &GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, &CLSID_FakeService);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_UnregisterCategory failed\n");
1013
}
1014 1015

static void test_FindClosestCategory(void)
1016
{
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
    GUID output;
    HRESULT hr;
    const GUID *list[3] = {&GUID_TFCAT_TIP_SPEECH, &GUID_TFCAT_TIP_KEYBOARD, &GUID_TFCAT_TIP_HANDWRITING};

    hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, NULL, 0);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
    ok(IsEqualGUID(&output,&GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER),"Wrong GUID\n");

    hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, list, 1);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
    ok(IsEqualGUID(&output,&GUID_NULL),"Wrong GUID\n");

    hr = ITfCategoryMgr_FindClosestCategory(g_cm, &CLSID_FakeService, &output, list, 3);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_FindClosestCategory failed (%x)\n",hr);
    ok(IsEqualGUID(&output,&GUID_TFCAT_TIP_KEYBOARD),"Wrong GUID\n");
1032
}
1033 1034

static void test_Enable(void)
1035
{
1036 1037 1038 1039 1040 1041 1042 1043
    HRESULT hr;
    BOOL enabled = FALSE;

    hr = ITfInputProcessorProfiles_EnableLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, TRUE);
    ok(SUCCEEDED(hr),"Failed to enable text service\n");
    hr = ITfInputProcessorProfiles_IsEnabledLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, &enabled);
    ok(SUCCEEDED(hr),"Failed to get enabled state\n");
    ok(enabled == TRUE,"enabled state incorrect\n");
1044 1045
}

1046
static void test_Disable(void)
1047
{
1048
    HRESULT hr;
1049

1050 1051 1052 1053
    trace("Disabling\n");
    hr = ITfInputProcessorProfiles_EnableLanguageProfile(g_ipp,&CLSID_FakeService, gLangid, &CLSID_FakeService, FALSE);
    ok(SUCCEEDED(hr),"Failed to disable text service\n");
}
1054

1055
static void test_ThreadMgrAdviseSinks(void)
1056
{
1057 1058 1059
    ITfSource *source = NULL;
    HRESULT hr;
    IUnknown *sink;
1060

1061 1062 1063 1064
    hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfSource, (LPVOID*)&source);
    ok(SUCCEEDED(hr),"Failed to get IID_ITfSource for ThreadMgr\n");
    if (!source)
        return;
1065

1066 1067 1068
    hr = ThreadMgrEventSink_Constructor(&sink);
    ok(hr == S_OK, "got %08x\n", hr);
    if(FAILED(hr)) return;
1069

1070 1071 1072 1073 1074 1075
    tmSinkRefCount = 1;
    tmSinkCookie = 0;
    hr = ITfSource_AdviseSink(source,&IID_ITfThreadMgrEventSink, sink, &tmSinkCookie);
    ok(SUCCEEDED(hr),"Failed to Advise Sink\n");
    ok(tmSinkCookie!=0,"Failed to get sink cookie\n");

1076
    /* Advising the sink adds a ref, Releasing here lets the object be deleted
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
       when unadvised */
    tmSinkRefCount = 2;
    IUnknown_Release(sink);
    ITfSource_Release(source);
}

static void test_ThreadMgrUnadviseSinks(void)
{
    ITfSource *source = NULL;
    HRESULT hr;

    hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfSource, (LPVOID*)&source);
    ok(SUCCEEDED(hr),"Failed to get IID_ITfSource for ThreadMgr\n");
    if (!source)
        return;

    tmSinkRefCount = 1;
    hr = ITfSource_UnadviseSink(source, tmSinkCookie);
    ok(SUCCEEDED(hr),"Failed to unadvise Sink\n");
    ITfSource_Release(source);
1097 1098
}

1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
static void test_DocumentMgrAdviseSinks(void)
{
    ITfSource *source;
    HRESULT hr;

    hr = ITfDocumentMgr_QueryInterface(g_dm, &IID_ITfSource, (void**)&source);
    ok(hr == S_OK,"Failed to get IID_ITfSource for DocumentMgr\n");

    dmSinkCookie = 0;
    hr = ITfSource_AdviseSink(source, &IID_ITfTransitoryExtensionSink, (IUnknown*)&TfTransitoryExtensionSink, &dmSinkCookie);
    ok(hr == S_OK,"Failed to Advise Sink\n");

    ITfSource_Release(source);
}

static void test_DocumentMgrUnadviseSinks(void)
{
    ITfSource *source = NULL;
    HRESULT hr;

    hr = ITfDocumentMgr_QueryInterface(g_dm, &IID_ITfSource, (void**)&source);
    ok(hr == S_OK,"Failed to get IID_ITfSource for DocumentMgr\n");

    hr = ITfSource_UnadviseSink(source, dmSinkCookie);
    ok(hr == S_OK,"Failed to unadvise Sink\n");
    ITfSource_Release(source);
}

1127
/**********************************************************************
1128
 * ITfKeyEventSink
1129
 **********************************************************************/
1130
typedef struct tagKeyEventSink
1131
{
1132
    ITfKeyEventSink ITfKeyEventSink_iface;
1133
    LONG refCount;
1134
} KeyEventSink;
1135

1136 1137 1138 1139 1140
static inline KeyEventSink *impl_from_ITfKeyEventSink(ITfKeyEventSink *iface)
{
    return CONTAINING_RECORD(iface, KeyEventSink, ITfKeyEventSink_iface);
}

1141
static void KeyEventSink_Destructor(KeyEventSink *This)
1142 1143 1144 1145
{
    HeapFree(GetProcessHeap(),0,This);
}

1146
static HRESULT WINAPI KeyEventSink_QueryInterface(ITfKeyEventSink *iface, REFIID iid, LPVOID *ppvOut)
1147 1148 1149
{
    *ppvOut = NULL;

1150
    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfKeyEventSink))
1151
    {
1152
        *ppvOut = iface;
1153 1154 1155 1156
    }

    if (*ppvOut)
    {
1157
        ITfKeyEventSink_AddRef(iface);
1158 1159 1160 1161 1162 1163
        return S_OK;
    }

    return E_NOINTERFACE;
}

1164
static ULONG WINAPI KeyEventSink_AddRef(ITfKeyEventSink *iface)
1165
{
1166
    KeyEventSink *This = impl_from_ITfKeyEventSink(iface);
1167 1168 1169
    return InterlockedIncrement(&This->refCount);
}

1170
static ULONG WINAPI KeyEventSink_Release(ITfKeyEventSink *iface)
1171
{
1172
    KeyEventSink *This = impl_from_ITfKeyEventSink(iface);
1173 1174 1175 1176
    ULONG ret;

    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
1177
        KeyEventSink_Destructor(This);
1178 1179 1180
    return ret;
}

1181 1182
static HRESULT WINAPI KeyEventSink_OnSetFocus(ITfKeyEventSink *iface,
        BOOL fForeground)
1183
{
1184
    sink_fire_ok(&test_KEV_OnSetFocus,"KeyEventSink_OnSetFocus");
1185 1186 1187
    return S_OK;
}

1188 1189
static HRESULT WINAPI KeyEventSink_OnTestKeyDown(ITfKeyEventSink *iface,
        ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
1190 1191 1192 1193 1194
{
    trace("\n");
    return S_OK;
}

1195 1196
static HRESULT WINAPI KeyEventSink_OnTestKeyUp(ITfKeyEventSink *iface,
        ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
1197
{
1198
    trace("\n");
1199 1200 1201
    return S_OK;
}

1202 1203
static HRESULT WINAPI KeyEventSink_OnKeyDown(ITfKeyEventSink *iface,
        ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
1204
{
1205
    trace("\n");
1206 1207 1208
    return S_OK;
}

1209 1210
static HRESULT WINAPI KeyEventSink_OnKeyUp(ITfKeyEventSink *iface,
        ITfContext *pic, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
1211
{
1212
    trace("\n");
1213 1214 1215
    return S_OK;
}

1216 1217
static HRESULT WINAPI KeyEventSink_OnPreservedKey(ITfKeyEventSink *iface,
    ITfContext *pic, REFGUID rguid, BOOL *pfEaten)
1218
{
1219 1220 1221
    trace("\n");
    return S_OK;
}
1222

1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
static const ITfKeyEventSinkVtbl KeyEventSink_KeyEventSinkVtbl =
{
    KeyEventSink_QueryInterface,
    KeyEventSink_AddRef,
    KeyEventSink_Release,

    KeyEventSink_OnSetFocus,
    KeyEventSink_OnTestKeyDown,
    KeyEventSink_OnTestKeyUp,
    KeyEventSink_OnKeyDown,
    KeyEventSink_OnKeyUp,
    KeyEventSink_OnPreservedKey
1235 1236
};

1237
static HRESULT KeyEventSink_Constructor(ITfKeyEventSink **ppOut)
1238
{
1239
    KeyEventSink *This;
1240

1241
    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(KeyEventSink));
1242 1243 1244
    if (This == NULL)
        return E_OUTOFMEMORY;

1245
    This->ITfKeyEventSink_iface.lpVtbl = &KeyEventSink_KeyEventSinkVtbl;
1246 1247
    This->refCount = 1;

1248
    *ppOut = &This->ITfKeyEventSink_iface;
1249 1250
    return S_OK;
}
1251 1252


1253 1254 1255 1256 1257 1258
static void test_KeystrokeMgr(void)
{
    ITfKeystrokeMgr *keymgr= NULL;
    HRESULT hr;
    TF_PRESERVEDKEY tfpk;
    BOOL preserved;
1259
    ITfKeyEventSink *sink = NULL;
1260

1261
    KeyEventSink_Constructor(&sink);
1262

1263 1264
    hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfKeystrokeMgr, (LPVOID*)&keymgr);
    ok(SUCCEEDED(hr),"Failed to get IID_ITfKeystrokeMgr for ThreadMgr\n");
1265

1266 1267
    tfpk.uVKey = 'A';
    tfpk.uModifiers = TF_MOD_SHIFT;
1268

1269
    test_KEV_OnSetFocus = SINK_EXPECTED;
1270 1271
    hr = ITfKeystrokeMgr_AdviseKeyEventSink(keymgr,tid,sink,TRUE);
    ok(SUCCEEDED(hr),"ITfKeystrokeMgr_AdviseKeyEventSink failed\n");
1272
    sink_check_ok(&test_KEV_OnSetFocus,"KeyEventSink_OnSetFocus");
1273 1274 1275 1276
    hr = ITfKeystrokeMgr_AdviseKeyEventSink(keymgr,tid,sink,TRUE);
    ok(hr == CONNECT_E_ADVISELIMIT,"Wrong return, expected CONNECT_E_ADVISELIMIT\n");
    hr = ITfKeystrokeMgr_AdviseKeyEventSink(keymgr,cid,sink,TRUE);
    ok(hr == E_INVALIDARG,"Wrong return, expected E_INVALIDARG\n");
1277

1278
    hr =ITfKeystrokeMgr_PreserveKey(keymgr, 0, &CLSID_PreservedKey, &tfpk, NULL, 0);
1279
    ok(hr==E_INVALIDARG,"ITfKeystrokeMgr_PreserveKey improperly succeeded\n");
1280 1281 1282 1283 1284

    hr =ITfKeystrokeMgr_PreserveKey(keymgr, tid, &CLSID_PreservedKey, &tfpk, NULL, 0);
    ok(SUCCEEDED(hr),"ITfKeystrokeMgr_PreserveKey failed\n");

    hr =ITfKeystrokeMgr_PreserveKey(keymgr, tid, &CLSID_PreservedKey, &tfpk, NULL, 0);
1285
    ok(hr == TF_E_ALREADY_EXISTS,"ITfKeystrokeMgr_PreserveKey improperly succeeded\n");
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299

    preserved = FALSE;
    hr = ITfKeystrokeMgr_IsPreservedKey(keymgr, &CLSID_PreservedKey, &tfpk, &preserved);
    ok(hr == S_OK, "ITfKeystrokeMgr_IsPreservedKey failed\n");
    if (hr == S_OK) ok(preserved == TRUE,"misreporting preserved key\n");

    hr = ITfKeystrokeMgr_UnpreserveKey(keymgr, &CLSID_PreservedKey,&tfpk);
    ok(SUCCEEDED(hr),"ITfKeystrokeMgr_UnpreserveKey failed\n");

    hr = ITfKeystrokeMgr_IsPreservedKey(keymgr, &CLSID_PreservedKey, &tfpk, &preserved);
    ok(hr == S_FALSE, "ITfKeystrokeMgr_IsPreservedKey failed\n");
    if (hr == S_FALSE) ok(preserved == FALSE,"misreporting preserved key\n");

    hr = ITfKeystrokeMgr_UnpreserveKey(keymgr, &CLSID_PreservedKey,&tfpk);
1300
    ok(hr==CONNECT_E_NOCONNECTION,"ITfKeystrokeMgr_UnpreserveKey improperly succeeded\n");
1301 1302 1303 1304 1305 1306

    hr = ITfKeystrokeMgr_UnadviseKeyEventSink(keymgr,tid);
    ok(SUCCEEDED(hr),"ITfKeystrokeMgr_UnadviseKeyEventSink failed\n");

    ITfKeystrokeMgr_Release(keymgr);
    ITfKeyEventSink_Release(sink);
1307 1308
}

1309
static void test_Activate(void)
1310
{
1311
    HRESULT hr;
1312

1313
    test_ShouldActivate = TRUE; /* Win7 */
1314 1315
    hr = ITfInputProcessorProfiles_ActivateLanguageProfile(g_ipp,&CLSID_FakeService,gLangid,&CLSID_FakeService);
    ok(SUCCEEDED(hr),"Failed to Activate text service\n");
1316
    test_ShouldActivate = FALSE;
1317 1318
}

1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347

static void test_EnumContexts(ITfDocumentMgr *dm, ITfContext *search)
{
    HRESULT hr;
    IEnumTfContexts* pEnum;
    BOOL found = FALSE;

    hr = ITfDocumentMgr_EnumContexts(dm,&pEnum);
    ok(SUCCEEDED(hr),"EnumContexts failed\n");
    if (SUCCEEDED(hr))
    {
        ULONG fetched;
        ITfContext *cxt;
        while (IEnumTfContexts_Next(pEnum, 1, &cxt, &fetched) == S_OK)
        {
            if (!search)
                found = TRUE;
            else if (search == cxt)
                found = TRUE;
            ITfContext_Release(cxt);
        }
        IEnumTfContexts_Release(pEnum);
    }
    if (search)
        ok(found,"Did not find proper ITfContext\n");
    else
        ok(!found,"Found an ITfContext we should should not have\n");
}

1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
static void test_EnumDocumentMgr(ITfThreadMgr *tm, ITfDocumentMgr *search, ITfDocumentMgr *absent)
{
    HRESULT hr;
    IEnumTfDocumentMgrs* pEnum;
    BOOL found = FALSE;
    BOOL notfound = TRUE;

    hr = ITfThreadMgr_EnumDocumentMgrs(tm,&pEnum);
    ok(SUCCEEDED(hr),"EnumDocumentMgrs failed\n");
    if (SUCCEEDED(hr))
    {
        ULONG fetched;
        ITfDocumentMgr *dm;
        while (IEnumTfDocumentMgrs_Next(pEnum, 1, &dm, &fetched) == S_OK)
        {
            if (!search)
                found = TRUE;
            else if (search == dm)
                found = TRUE;
            if (absent && dm == absent)
                notfound = FALSE;
            ITfDocumentMgr_Release(dm);
        }
        IEnumTfDocumentMgrs_Release(pEnum);
    }
    if (search)
        ok(found,"Did not find proper ITfDocumentMgr\n");
    else
        ok(!found,"Found an ITfDocumentMgr we should should not have\n");
    if (absent)
        ok(notfound,"Found an ITfDocumentMgr we believe should be absent\n");
}

1381
static inline int check_context_refcount(ITfContext *iface)
1382
{
1383 1384
    ITfContext_AddRef(iface);
    return ITfContext_Release(iface);
1385 1386
}

1387 1388 1389 1390 1391 1392

/**********************************************************************
 * ITfTextEditSink
 **********************************************************************/
typedef struct tagTextEditSink
{
1393
    ITfTextEditSink ITfTextEditSink_iface;
1394 1395 1396
    LONG refCount;
} TextEditSink;

1397 1398 1399 1400 1401
static inline TextEditSink *impl_from_ITfTextEditSink(ITfTextEditSink *iface)
{
    return CONTAINING_RECORD(iface, TextEditSink, ITfTextEditSink_iface);
}

1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
static void TextEditSink_Destructor(TextEditSink *This)
{
    HeapFree(GetProcessHeap(),0,This);
}

static HRESULT WINAPI TextEditSink_QueryInterface(ITfTextEditSink *iface, REFIID iid, LPVOID *ppvOut)
{
    *ppvOut = NULL;

    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfTextEditSink))
    {
1413
        *ppvOut = iface;
1414 1415 1416 1417
    }

    if (*ppvOut)
    {
1418
        ITfTextEditSink_AddRef(iface);
1419 1420 1421 1422 1423 1424 1425 1426
        return S_OK;
    }

    return E_NOINTERFACE;
}

static ULONG WINAPI TextEditSink_AddRef(ITfTextEditSink *iface)
{
1427
    TextEditSink *This = impl_from_ITfTextEditSink(iface);
1428 1429 1430 1431 1432
    return InterlockedIncrement(&This->refCount);
}

static ULONG WINAPI TextEditSink_Release(ITfTextEditSink *iface)
{
1433
    TextEditSink *This = impl_from_ITfTextEditSink(iface);
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444
    ULONG ret;

    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
        TextEditSink_Destructor(This);
    return ret;
}

static HRESULT WINAPI TextEditSink_OnEndEdit(ITfTextEditSink *iface,
    ITfContext *pic, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord)
{
1445
    sink_fire_ok(&test_OnEndEdit,"TextEditSink_OnEndEdit");
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466
    return S_OK;
}

static const ITfTextEditSinkVtbl TextEditSink_TextEditSinkVtbl =
{
    TextEditSink_QueryInterface,
    TextEditSink_AddRef,
    TextEditSink_Release,

    TextEditSink_OnEndEdit
};

static HRESULT TextEditSink_Constructor(ITfTextEditSink **ppOut)
{
    TextEditSink *This;

    *ppOut = NULL;
    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(TextEditSink));
    if (This == NULL)
        return E_OUTOFMEMORY;

1467
    This->ITfTextEditSink_iface.lpVtbl = &TextEditSink_TextEditSinkVtbl;
1468 1469
    This->refCount = 1;

1470
    *ppOut = &This->ITfTextEditSink_iface;
1471 1472 1473
    return S_OK;
}

1474
static void test_startSession(void)
1475
{
1476 1477 1478 1479 1480
    HRESULT hr;
    DWORD cnt;
    DWORD editCookie;
    ITfDocumentMgr *dmtest;
    ITfContext *cxt,*cxt2,*cxt3,*cxtTest;
1481
    ITextStoreACP *ts = NULL;
1482
    TfClientId cid2 = 0;
1483
    ITfThreadMgrEx *tmex;
1484

1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495
    hr = ITfThreadMgr_Deactivate(g_tm);
    ok(hr == E_UNEXPECTED,"Deactivate should have failed with E_UNEXPECTED\n");

    test_ShouldActivate = TRUE;
    hr  = ITfThreadMgr_Activate(g_tm,&cid);
    ok(SUCCEEDED(hr),"Failed to Activate\n");
    ok(cid != tid,"TextService id mistakenly matches Client id\n");

    test_ShouldActivate = FALSE;
    hr = ITfThreadMgr_Activate(g_tm,&cid2);
    ok(SUCCEEDED(hr),"Failed to Activate\n");
1496
    ok(cid == cid2, "Second activate client ID does not match\n");
1497

1498
    hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfThreadMgrEx, (void **)&tmex);
1499 1500 1501 1502 1503
    if (hr == S_OK)
    {
        hr = ITfThreadMgrEx_ActivateEx(tmex, &cid2, 0);
        ok(SUCCEEDED(hr), "Failed to Activate\n");
        ok(cid == cid2, "ActivateEx client ID does not match\n");
1504

1505 1506 1507 1508
        ITfThreadMgrEx_Release(tmex);
    }
    else
        win_skip("ITfThreadMgrEx is not supported\n");
1509 1510 1511

    hr = ITfThreadMgr_Deactivate(g_tm);
    ok(SUCCEEDED(hr), "Failed to Deactivate\n");
1512
    hr = ITfThreadMgr_Deactivate(g_tm);
1513
    ok(SUCCEEDED(hr), "Failed to Deactivate\n");
1514

1515 1516
    test_EnumDocumentMgr(g_tm,NULL,NULL);

1517 1518 1519
    hr = ITfThreadMgr_CreateDocumentMgr(g_tm,&g_dm);
    ok(SUCCEEDED(hr),"CreateDocumentMgr failed\n");

1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
    test_EnumDocumentMgr(g_tm,g_dm,NULL);

    hr = ITfThreadMgr_CreateDocumentMgr(g_tm,&dmtest);
    ok(SUCCEEDED(hr),"CreateDocumentMgr failed\n");

    test_EnumDocumentMgr(g_tm,dmtest,NULL);

    ITfDocumentMgr_Release(dmtest);
    test_EnumDocumentMgr(g_tm,g_dm,dmtest);

1530 1531 1532 1533 1534 1535
    hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
    ok(SUCCEEDED(hr),"GetFocus Failed\n");
    ok(dmtest == NULL,"Initial focus not null\n");

    test_CurrentFocus = g_dm;
    test_PrevFocus = NULL;
1536
    test_OnSetFocus  = SINK_OPTIONAL; /* Doesn't always fire on Win7 */
1537 1538
    hr = ITfThreadMgr_SetFocus(g_tm,g_dm);
    ok(SUCCEEDED(hr),"SetFocus Failed\n");
1539
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
1540 1541 1542 1543 1544

    hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
    ok(SUCCEEDED(hr),"GetFocus Failed\n");
    ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");

1545
    ITfDocumentMgr_Release(g_dm);
1546 1547 1548 1549

    hr = ITfThreadMgr_GetFocus(g_tm,&dmtest);
    ok(SUCCEEDED(hr),"GetFocus Failed\n");
    ok(g_dm == dmtest,"Expected DocumentMgr not focused\n");
1550
    ITfDocumentMgr_Release(dmtest);
1551

1552
    hr = TextStoreACP_Constructor((IUnknown**)&ts);
1553 1554 1555 1556 1557
    ok(SUCCEEDED(hr),"Constructor Failed\n");
    if (FAILED(hr)) return;

    hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, (IUnknown*)ts, &cxt, &editCookie);
    ok(SUCCEEDED(hr),"CreateContext Failed\n");
1558 1559 1560 1561 1562 1563 1564

    hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt2, &editCookie);
    ok(SUCCEEDED(hr),"CreateContext Failed\n");

    hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, NULL, &cxt3, &editCookie);
    ok(SUCCEEDED(hr),"CreateContext Failed\n");

1565 1566
    test_EnumContexts(g_dm, NULL);

1567 1568 1569 1570 1571
    hr = ITfContext_GetDocumentMgr(cxt,&dmtest);
    ok(hr == S_OK, "ITfContext_GetDocumentMgr failed with %x\n",hr);
    ok(dmtest == g_dm, "Wrong documentmgr\n");
    ITfDocumentMgr_Release(dmtest);

1572 1573
    cnt = check_context_refcount(cxt);
    test_OnPushContext = SINK_EXPECTED;
1574
    test_ACP_AdviseSink = SINK_EXPECTED;
1575 1576 1577 1578
    test_OnInitDocumentMgr = SINK_EXPECTED;
    hr = ITfDocumentMgr_Push(g_dm, cxt);
    ok(SUCCEEDED(hr),"Push Failed\n");
    ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
1579 1580 1581
    sink_check_ok(&test_OnPushContext,"OnPushContext");
    sink_check_ok(&test_OnInitDocumentMgr,"OnInitDocumentMgr");
    sink_check_ok(&test_ACP_AdviseSink,"TextStoreACP_AdviseSink");
1582

1583 1584
    test_EnumContexts(g_dm, cxt);

1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600
    hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetTop Failed\n");
    ok(cxtTest == cxt, "Wrong context on top\n");
    ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
    cnt = ITfContext_Release(cxtTest);

    hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetBase Failed\n");
    ok(cxtTest == cxt, "Wrong context on Base\n");
    ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
    ITfContext_Release(cxtTest);

    check_context_refcount(cxt2);
    test_OnPushContext = SINK_EXPECTED;
    hr = ITfDocumentMgr_Push(g_dm, cxt2);
    ok(SUCCEEDED(hr),"Push Failed\n");
1601
    sink_check_ok(&test_OnPushContext,"OnPushContext");
1602

1603 1604 1605 1606 1607 1608
    cnt = check_context_refcount(cxt2);
    hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetTop Failed\n");
    ok(cxtTest == cxt2, "Wrong context on top\n");
    ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
    ITfContext_Release(cxtTest);
1609

1610 1611 1612 1613 1614 1615
    cnt = check_context_refcount(cxt);
    hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetBase Failed\n");
    ok(cxtTest == cxt, "Wrong context on Base\n");
    ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
    ITfContext_Release(cxtTest);
1616

1617 1618
    cnt = check_context_refcount(cxt3);
    hr = ITfDocumentMgr_Push(g_dm, cxt3);
1619
    ok(FAILED(hr),"Push Succeeded\n");
1620
    ok(check_context_refcount(cxt3) == cnt, "Ref changed\n");
1621

1622 1623 1624 1625 1626 1627
    cnt = check_context_refcount(cxt2);
    hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetTop Failed\n");
    ok(cxtTest == cxt2, "Wrong context on top\n");
    ok(check_context_refcount(cxt2) > cnt, "Ref count did not increase\n");
    ITfContext_Release(cxtTest);
1628

1629 1630 1631 1632 1633 1634
    cnt = check_context_refcount(cxt);
    hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetBase Failed\n");
    ok(cxtTest == cxt, "Wrong context on Base\n");
    ok(check_context_refcount(cxt) > cnt, "Ref count did not increase\n");
    ITfContext_Release(cxtTest);
1635

1636 1637 1638 1639 1640
    cnt = check_context_refcount(cxt2);
    test_OnPopContext = SINK_EXPECTED;
    hr = ITfDocumentMgr_Pop(g_dm, 0);
    ok(SUCCEEDED(hr),"Pop Failed\n");
    ok(check_context_refcount(cxt2) < cnt, "Ref count did not decrease\n");
1641
    sink_check_ok(&test_OnPopContext,"OnPopContext");
1642

1643 1644 1645 1646 1647
    dmtest = (void *)0xfeedface;
    hr = ITfContext_GetDocumentMgr(cxt2,&dmtest);
    ok(hr == S_FALSE, "ITfContext_GetDocumentMgr wrong rc %x\n",hr);
    ok(dmtest == NULL,"returned documentmgr should be null\n");

1648 1649
    ITfContext_Release(cxt2);

1650 1651 1652 1653
    hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetTop Failed\n");
    ok(cxtTest == cxt, "Wrong context on top\n");
    ITfContext_Release(cxtTest);
1654

1655 1656 1657 1658
    hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetBase Failed\n");
    ok(cxtTest == cxt, "Wrong context on base\n");
    ITfContext_Release(cxtTest);
1659

1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678
    hr = ITfDocumentMgr_CreateContext(g_dm, cid, 0, (IUnknown*)ts, &cxt2, &editCookie);
    ok(hr == S_OK,"CreateContext Failed\n");

    test_OnPushContext = SINK_EXPECTED;
    test_ACP_AdviseSink = SINK_EXPECTED;
    hr = ITfDocumentMgr_Push(g_dm, cxt2);
    ok(hr == S_OK,"Push Failed\n");
    sink_check_ok(&test_OnPushContext,"OnPushContext");
    sink_check_ok(&test_ACP_AdviseSink,"TextStoreACP_AdviseSink");

    test_ACP_UnadviseSink = SINK_EXPECTED;
    cnt = check_context_refcount(cxt2);
    test_OnPopContext = SINK_EXPECTED;
    hr = ITfDocumentMgr_Pop(g_dm, 0);
    ok(hr == S_OK,"Pop Failed\n");
    ok(check_context_refcount(cxt2) < cnt, "Ref count did not decrease\n");
    sink_check_ok(&test_OnPopContext,"OnPopContext");
    sink_check_ok(&test_ACP_UnadviseSink,"TextStoreACP_AdviseSink");

1679
    hr = ITfDocumentMgr_Pop(g_dm, 0);
1680
    ok(FAILED(hr),"Pop Succeeded\n");
1681

1682 1683 1684 1685
    hr = ITfDocumentMgr_GetTop(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetTop Failed\n");
    ok(cxtTest == cxt, "Wrong context on top\n");
    ITfContext_Release(cxtTest);
1686

1687 1688 1689 1690
    hr = ITfDocumentMgr_GetBase(g_dm, &cxtTest);
    ok(SUCCEEDED(hr),"GetBase Failed\n");
    ok(cxtTest == cxt, "Wrong context on base\n");
    ITfContext_Release(cxtTest);
1691

1692 1693 1694
    ITfContext_Release(cxt);
    ITfContext_Release(cxt2);
    ITfContext_Release(cxt3);
1695
    ITextStoreACP_Release(ts);
1696 1697
}

1698
static void test_endSession(void)
1699
{
1700 1701 1702 1703
    HRESULT hr;
    test_ShouldDeactivate = TRUE;
    test_CurrentFocus = NULL;
    test_PrevFocus = g_dm;
1704
    test_OnSetFocus  = SINK_OPTIONAL; /* Doesn't fire on Win7 */
1705 1706
    hr = ITfThreadMgr_Deactivate(g_tm);
    ok(SUCCEEDED(hr),"Failed to Deactivate\n");
1707
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
1708
    test_OnSetFocus  = SINK_UNEXPECTED;
1709 1710
}

1711
static void test_TfGuidAtom(void)
1712
{
1713 1714 1715 1716
    GUID gtest,g1;
    HRESULT hr;
    TfGuidAtom atom1,atom2;
    BOOL equal;
1717

1718
    CoCreateGuid(&gtest);
1719

1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739
    /* msdn reports this should return E_INVALIDARG.  However my test show it crashing (winxp)*/
    /*
    hr = ITfCategoryMgr_RegisterGUID(g_cm,&gtest,NULL);
    ok(hr==E_INVALIDARG,"ITfCategoryMgr_RegisterGUID should have failed\n");
    */
    hr = ITfCategoryMgr_RegisterGUID(g_cm,&gtest,&atom1);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterGUID failed\n");
    hr = ITfCategoryMgr_RegisterGUID(g_cm,&gtest,&atom2);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_RegisterGUID failed\n");
    ok(atom1 == atom2,"atoms do not match\n");
    hr = ITfCategoryMgr_GetGUID(g_cm,atom2,NULL);
    ok(hr==E_INVALIDARG,"ITfCategoryMgr_GetGUID should have failed\n");
    hr = ITfCategoryMgr_GetGUID(g_cm,atom2,&g1);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_GetGUID failed\n");
    ok(IsEqualGUID(&g1,&gtest),"guids do not match\n");
    hr = ITfCategoryMgr_IsEqualTfGuidAtom(g_cm,atom1,&gtest,NULL);
    ok(hr==E_INVALIDARG,"ITfCategoryMgr_IsEqualTfGuidAtom should have failed\n");
    hr = ITfCategoryMgr_IsEqualTfGuidAtom(g_cm,atom1,&gtest,&equal);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_IsEqualTfGuidAtom failed\n");
    ok(equal == TRUE,"Equal value invalid\n");
1740

1741 1742 1743 1744 1745 1746 1747
    /* show that cid and tid TfClientIds are also TfGuidAtoms */
    hr = ITfCategoryMgr_IsEqualTfGuidAtom(g_cm,tid,&CLSID_FakeService,&equal);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_IsEqualTfGuidAtom failed\n");
    ok(equal == TRUE,"Equal value invalid\n");
    hr = ITfCategoryMgr_GetGUID(g_cm,cid,&g1);
    ok(SUCCEEDED(hr),"ITfCategoryMgr_GetGUID failed\n");
    ok(!IsEqualGUID(&g1,&GUID_NULL),"guid should not be NULL\n");
1748 1749
}

1750
static void test_ClientId(void)
1751
{
1752 1753 1754 1755
    ITfClientId *pcid;
    TfClientId id1,id2;
    HRESULT hr;
    GUID g2;
1756

1757
    hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfClientId, (LPVOID*)&pcid);
1758
    ok(SUCCEEDED(hr),"Unable to acquire ITfClientId interface\n");
1759

1760
    CoCreateGuid(&g2);
1761

1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777
    hr = ITfClientId_GetClientId(pcid,&GUID_NULL,&id1);
    ok(SUCCEEDED(hr),"GetClientId failed\n");
    hr = ITfClientId_GetClientId(pcid,&GUID_NULL,&id2);
    ok(SUCCEEDED(hr),"GetClientId failed\n");
    ok(id1==id2,"Id's for GUID_NULL do not match\n");
    hr = ITfClientId_GetClientId(pcid,&CLSID_FakeService,&id2);
    ok(SUCCEEDED(hr),"GetClientId failed\n");
    ok(id2!=id1,"Id matches GUID_NULL\n");
    ok(id2==tid,"Id for CLSID_FakeService not matching tid\n");
    ok(id2!=cid,"Id for CLSID_FakeService matching cid\n");
    hr = ITfClientId_GetClientId(pcid,&g2,&id2);
    ok(SUCCEEDED(hr),"GetClientId failed\n");
    ok(id2!=id1,"Id matches GUID_NULL\n");
    ok(id2!=tid,"Id for random guid matching tid\n");
    ok(id2!=cid,"Id for random guid matching cid\n");
    ITfClientId_Release(pcid);
1778 1779
}

1780 1781 1782 1783 1784
/**********************************************************************
 * ITfEditSession
 **********************************************************************/
typedef struct tagEditSession
{
1785
    ITfEditSession ITfEditSession_iface;
1786 1787 1788
    LONG refCount;
} EditSession;

1789 1790 1791 1792 1793
static inline EditSession *impl_from_ITfEditSession(ITfEditSession *iface)
{
    return CONTAINING_RECORD(iface, EditSession, ITfEditSession_iface);
}

1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804
static void EditSession_Destructor(EditSession *This)
{
    HeapFree(GetProcessHeap(),0,This);
}

static HRESULT WINAPI EditSession_QueryInterface(ITfEditSession *iface, REFIID iid, LPVOID *ppvOut)
{
    *ppvOut = NULL;

    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfEditSession))
    {
1805
        *ppvOut = iface;
1806 1807 1808 1809
    }

    if (*ppvOut)
    {
1810
        ITfEditSession_AddRef(iface);
1811 1812 1813 1814 1815 1816 1817 1818
        return S_OK;
    }

    return E_NOINTERFACE;
}

static ULONG WINAPI EditSession_AddRef(ITfEditSession *iface)
{
1819
    EditSession *This = impl_from_ITfEditSession(iface);
1820 1821 1822 1823 1824
    return InterlockedIncrement(&This->refCount);
}

static ULONG WINAPI EditSession_Release(ITfEditSession *iface)
{
1825
    EditSession *This = impl_from_ITfEditSession(iface);
1826 1827 1828 1829 1830 1831 1832 1833
    ULONG ret;

    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
        EditSession_Destructor(This);
    return ret;
}

1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845
static void test_InsertAtSelection(TfEditCookie ec, ITfContext *cxt)
{
    HRESULT hr;
    ITfInsertAtSelection *iis;
    ITfRange *range=NULL;
    static const WCHAR txt[] = {'H','e','l','l','o',' ','W','o','r','l','d',0};

    hr = ITfContext_QueryInterface(cxt, &IID_ITfInsertAtSelection , (LPVOID*)&iis);
    ok(SUCCEEDED(hr),"Failed to get ITfInsertAtSelection interface\n");
    test_ACP_InsertTextAtSelection = SINK_EXPECTED;
    hr = ITfInsertAtSelection_InsertTextAtSelection(iis, ec, 0, txt, 11, &range);
    ok(SUCCEEDED(hr),"ITfInsertAtSelection_InsertTextAtSelection failed %x\n",hr);
1846
    sink_check_ok(&test_ACP_InsertTextAtSelection,"InsertTextAtSelection");
1847 1848 1849 1850 1851
    ok(range != NULL,"No range returned\n");
    ITfRange_Release(range);
    ITfInsertAtSelection_Release(iis);
}

1852 1853 1854
static HRESULT WINAPI EditSession_DoEditSession(ITfEditSession *iface,
TfEditCookie ec)
{
1855 1856 1857
    ITfContext *cxt;
    ITfDocumentMgr *dm;
    ITfRange *range;
1858 1859
    TF_SELECTION selection;
    ULONG fetched;
1860 1861
    HRESULT hr;

1862
    sink_fire_ok(&test_DoEditSession,"EditSession_DoEditSession");
1863
    sink_check_ok(&test_ACP_RequestLock,"RequestLock");
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879

    ITfThreadMgr_GetFocus(g_tm, &dm);
    ITfDocumentMgr_GetTop(dm,&cxt);

    hr = ITfContext_GetStart(cxt,ec,NULL);
    ok(hr == E_INVALIDARG,"Unexpected return code %x\n",hr);

    range = (ITfRange*)0xdeaddead;
    hr = ITfContext_GetStart(cxt,0xdeadcafe,&range);
    ok(hr == TF_E_NOLOCK,"Unexpected return code %x\n",hr);
    ok(range == NULL,"Range not set to NULL\n");

    hr = ITfContext_GetStart(cxt,ec,&range);
    ok(SUCCEEDED(hr),"Unexpected return code %x\n",hr);
    ok(range != NULL,"Range set to NULL\n");

1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893
    ITfRange_Release(range);

    hr = ITfContext_GetEnd(cxt,ec,NULL);
    ok(hr == E_INVALIDARG,"Unexpected return code %x\n",hr);

    range = (ITfRange*)0xdeaddead;
    hr = ITfContext_GetEnd(cxt,0xdeadcafe,&range);
    ok(hr == TF_E_NOLOCK,"Unexpected return code %x\n",hr);
    ok(range == NULL,"Range not set to NULL\n");

    test_ACP_GetEndACP = SINK_EXPECTED;
    hr = ITfContext_GetEnd(cxt,ec,&range);
    ok(SUCCEEDED(hr),"Unexpected return code %x\n",hr);
    ok(range != NULL,"Range set to NULL\n");
1894
    sink_check_ok(&test_ACP_GetEndACP,"GetEndACP");
1895

1896
    ITfRange_Release(range);
1897 1898 1899 1900 1901 1902 1903

    selection.range = NULL;
    test_ACP_GetSelection = SINK_EXPECTED;
    hr = ITfContext_GetSelection(cxt, ec, TF_DEFAULT_SELECTION, 1, &selection, &fetched);
    ok(SUCCEEDED(hr),"ITfContext_GetSelection failed\n");
    ok(fetched == 1,"fetched incorrect\n");
    ok(selection.range != NULL,"NULL range\n");
1904
    sink_check_ok(&test_ACP_GetSelection,"ACP_GetSepection");
1905 1906
    ITfRange_Release(selection.range);

1907 1908
    test_InsertAtSelection(ec, cxt);

1909 1910 1911 1912
    test_ACP_GetEndACP = SINK_EXPECTED;
    hr = ITfContext_GetEnd(cxt,ec,&range);
    ok(SUCCEEDED(hr),"Unexpected return code %x\n",hr);
    ok(range != NULL,"Range set to NULL\n");
1913
    sink_check_ok(&test_ACP_GetEndACP,"GetEndACP");
1914 1915 1916 1917 1918 1919

    selection.range = range;
    selection.style.ase = TF_AE_NONE;
    selection.style.fInterimChar = FALSE;
    test_ACP_SetSelection = SINK_EXPECTED;
    hr = ITfContext_SetSelection(cxt, ec, 1, &selection);
1920
    ok(SUCCEEDED(hr),"ITfContext_SetSelection failed\n");
1921
    sink_check_ok(&test_ACP_SetSelection,"SetSelection");
1922 1923
    ITfRange_Release(range);

1924 1925
    ITfContext_Release(cxt);
    ITfDocumentMgr_Release(dm);
1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937
    return 0xdeadcafe;
}

static const ITfEditSessionVtbl EditSession_EditSessionVtbl =
{
    EditSession_QueryInterface,
    EditSession_AddRef,
    EditSession_Release,

    EditSession_DoEditSession
};

1938
static HRESULT EditSession_Constructor(ITfEditSession **ppOut)
1939 1940 1941
{
    EditSession *This;

1942
    *ppOut = NULL;
1943 1944 1945 1946
    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(EditSession));
    if (This == NULL)
        return E_OUTOFMEMORY;

1947
    This->ITfEditSession_iface.lpVtbl = &EditSession_EditSessionVtbl;
1948 1949
    This->refCount = 1;

1950
    *ppOut = &This->ITfEditSession_iface;
1951 1952 1953 1954 1955 1956 1957 1958 1959
    return S_OK;
}

static void test_TStoApplicationText(void)
{
    HRESULT hr, hrSession;
    ITfEditSession *es;
    ITfContext *cxt;
    ITfDocumentMgr *dm;
1960 1961 1962
    ITfTextEditSink *sink;
    ITfSource *source = NULL;
    DWORD editSinkCookie = -1;
1963 1964 1965 1966 1967

    ITfThreadMgr_GetFocus(g_tm, &dm);
    EditSession_Constructor(&es);
    ITfDocumentMgr_GetTop(dm,&cxt);

1968 1969 1970 1971 1972 1973 1974 1975 1976 1977
    TextEditSink_Constructor(&sink);
    hr = ITfContext_QueryInterface(cxt,&IID_ITfSource,(LPVOID*)&source);
    ok(SUCCEEDED(hr),"Failed to get IID_ITfSource for Context\n");
    if (source)
    {
        hr = ITfSource_AdviseSink(source, &IID_ITfTextEditSink, (LPVOID)sink, &editSinkCookie);
        ok(SUCCEEDED(hr),"Failed to advise Sink\n");
        ok(editSinkCookie != -1,"Failed to get sink cookie\n");
    }

1978
    hrSession = 0xfeedface;
1979
    /* Test no permissions flags */
1980
    hr = ITfContext_RequestEditSession(cxt, tid, es, TF_ES_SYNC, &hrSession);
1981 1982
    ok(hr == E_INVALIDARG,"RequestEditSession should have failed with %x not %x\n",E_INVALIDARG,hr);
    ok(hrSession == E_FAIL,"hrSession should be %x not %x\n",E_FAIL,hrSession);
1983

1984
    documentStatus = TS_SD_READONLY;
1985 1986
    hrSession = 0xfeedface;
    test_ACP_GetStatus = SINK_EXPECTED;
1987 1988 1989
    hr = ITfContext_RequestEditSession(cxt, tid, es, TF_ES_SYNC|TF_ES_READWRITE, &hrSession);
    ok(SUCCEEDED(hr),"ITfContext_RequestEditSession failed\n");
    ok(hrSession == TS_E_READONLY,"Unexpected hrSession (%x)\n",hrSession);
1990
    sink_check_ok(&test_ACP_GetStatus,"GetStatus");
1991

1992 1993
    /* signal a change to allow readwrite sessions */
    documentStatus = 0;
1994
    test_ACP_RequestLock = SINK_EXPECTED;
1995
    ITextStoreACPSink_OnStatusChange(ACPSink,documentStatus);
1996
    sink_check_ok(&test_ACP_RequestLock,"RequestLock");
1997 1998 1999 2000

    test_ACP_GetStatus = SINK_EXPECTED;
    test_ACP_RequestLock = SINK_EXPECTED;
    test_DoEditSession = SINK_EXPECTED;
2001
    hrSession = 0xfeedface;
2002
    test_OnEndEdit = SINK_EXPECTED;
2003
    hr = ITfContext_RequestEditSession(cxt, tid, es, TF_ES_SYNC|TF_ES_READWRITE, &hrSession);
2004
    ok(SUCCEEDED(hr),"ITfContext_RequestEditSession failed\n");
2005 2006 2007
    sink_check_ok(&test_OnEndEdit,"OnEndEdit");
    sink_check_ok(&test_DoEditSession,"DoEditSession");
    sink_check_ok(&test_ACP_GetStatus,"GetStatus");
2008
    ok(hrSession == 0xdeadcafe,"Unexpected hrSession (%x)\n",hrSession);
2009

2010 2011 2012 2013 2014 2015
    if (source)
    {
        hr = ITfSource_UnadviseSink(source, editSinkCookie);
        ok(SUCCEEDED(hr),"Failed to unadvise Sink\n");
        ITfSource_Release(source);
    }
2016
    ITfTextEditSink_Release(sink);
2017 2018 2019 2020 2021
    ITfContext_Release(cxt);
    ITfDocumentMgr_Release(dm);
    ITfEditSession_Release(es);
}

2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035
static void enum_compartments(ITfCompartmentMgr *cmpmgr, REFGUID present, REFGUID absent)
{
    BOOL found,found2;
    IEnumGUID *ppEnum;
    found = FALSE;
    found2 = FALSE;
    if (SUCCEEDED(ITfCompartmentMgr_EnumCompartments(cmpmgr, &ppEnum)))
    {
        ULONG fetched;
        GUID g;
        while (IEnumGUID_Next(ppEnum, 1, &g, &fetched) == S_OK)
        {
            WCHAR str[50];
            CHAR strA[50];
2036 2037
            StringFromGUID2(&g,str,sizeof(str)/sizeof(str[0]));
            WideCharToMultiByte(CP_ACP,0,str,-1,strA,sizeof(strA),0,0);
2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101
            trace("found %s\n",strA);
            if (present && IsEqualGUID(present,&g))
                found = TRUE;
            if (absent && IsEqualGUID(absent, &g))
                found2 = TRUE;
        }
        IEnumGUID_Release(ppEnum);
    }
    if (present)
        ok(found,"Did not find compartment\n");
    if (absent)
        ok(!found2,"Found compartment that should be absent\n");
}

static void test_Compartments(void)
{
    ITfContext *cxt;
    ITfDocumentMgr *dm;
    ITfCompartmentMgr *cmpmgr;
    ITfCompartment *cmp;
    HRESULT hr;

    ITfThreadMgr_GetFocus(g_tm, &dm);
    ITfDocumentMgr_GetTop(dm,&cxt);

    /* Global */
    hr = ITfThreadMgr_GetGlobalCompartment(g_tm, &cmpmgr);
    ok(SUCCEEDED(hr),"GetGlobalCompartment failed\n");
    hr = ITfCompartmentMgr_GetCompartment(cmpmgr, &GUID_COMPARTMENT_SPEECH_OPENCLOSE, &cmp);
    ok(SUCCEEDED(hr),"GetCompartment failed\n");
    ITfCompartment_Release(cmp);
    enum_compartments(cmpmgr,&GUID_COMPARTMENT_SPEECH_OPENCLOSE,NULL);
    ITfCompartmentMgr_Release(cmpmgr);

    /* Thread */
    hr = ITfThreadMgr_QueryInterface(g_tm, &IID_ITfCompartmentMgr, (LPVOID*)&cmpmgr);
    ok(SUCCEEDED(hr),"ThreadMgr QI for IID_ITfCompartmentMgr failed\n");
    hr = ITfCompartmentMgr_GetCompartment(cmpmgr, &CLSID_FakeService, &cmp);
    ok(SUCCEEDED(hr),"GetCompartment failed\n");
    enum_compartments(cmpmgr,&CLSID_FakeService,&GUID_COMPARTMENT_SPEECH_OPENCLOSE);
    ITfCompartmentMgr_ClearCompartment(cmpmgr,tid,&CLSID_FakeService);
    enum_compartments(cmpmgr,NULL,&CLSID_FakeService);
    ITfCompartmentMgr_Release(cmpmgr);
    ITfCompartment_Release(cmp);

    /* DocumentMgr */
    hr = ITfDocumentMgr_QueryInterface(dm, &IID_ITfCompartmentMgr, (LPVOID*)&cmpmgr);
    ok(SUCCEEDED(hr),"DocumentMgr QI for IID_ITfCompartmentMgr failed\n");

    hr = ITfCompartmentMgr_GetCompartment(cmpmgr, &GUID_COMPARTMENT_PERSISTMENUENABLED, &cmp);
    ok(SUCCEEDED(hr),"GetCompartment failed\n");
    enum_compartments(cmpmgr,&GUID_COMPARTMENT_PERSISTMENUENABLED,&GUID_COMPARTMENT_SPEECH_OPENCLOSE);
    ITfCompartmentMgr_Release(cmpmgr);

    /* Context */
    hr = ITfContext_QueryInterface(cxt, &IID_ITfCompartmentMgr, (LPVOID*)&cmpmgr);
    ok(SUCCEEDED(hr),"Context QI for IID_ITfCompartmentMgr failed\n");
    enum_compartments(cmpmgr,NULL,&GUID_COMPARTMENT_PERSISTMENUENABLED);
    ITfCompartmentMgr_Release(cmpmgr);

    ITfContext_Release(cxt);
    ITfDocumentMgr_Release(dm);
}

2102 2103 2104
static void processPendingMessages(void)
{
    MSG msg;
2105 2106 2107 2108 2109 2110 2111 2112
    int diff = 200;
    int min_timeout = 100;
    DWORD time = GetTickCount() + diff;

    while (diff > 0)
    {
        if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT)
            break;
2113
        while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
2114 2115
        {
            TranslateMessage(&msg);
2116
            DispatchMessageW(&msg);
2117 2118
        }
        diff = time - GetTickCount();
2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130
    }
}

static void test_AssociateFocus(void)
{
    ITfDocumentMgr *dm1, *dm2, *olddm, *dmcheck, *dmorig;
    HWND wnd1, wnd2, wnd3;
    HRESULT hr;

    ITfThreadMgr_GetFocus(g_tm, &dmorig);
    test_CurrentFocus = NULL;
    test_PrevFocus = dmorig;
2131 2132
    test_OnSetFocus  = SINK_OPTIONAL; /* Doesn't always fire on Win7 */
    test_ACP_GetStatus = SINK_OPTIONAL;
2133
    hr = ITfThreadMgr_SetFocus(g_tm,NULL);
2134
    ok(SUCCEEDED(hr),"ITfThreadMgr_SetFocus failed\n");
2135
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
2136
    test_ACP_GetStatus = SINK_UNEXPECTED;
2137 2138 2139 2140 2141 2142 2143 2144
    ITfDocumentMgr_Release(dmorig);

    hr = ITfThreadMgr_CreateDocumentMgr(g_tm,&dm1);
    ok(SUCCEEDED(hr),"CreateDocumentMgr failed\n");

    hr = ITfThreadMgr_CreateDocumentMgr(g_tm,&dm2);
    ok(SUCCEEDED(hr),"CreateDocumentMgr failed\n");

2145
    wnd1 = CreateWindowA("edit",NULL,WS_POPUP,0,0,200,60,NULL,NULL,NULL,NULL);
2146
    ok(wnd1!=NULL,"Unable to create window 1\n");
2147
    wnd2 = CreateWindowA("edit",NULL,WS_POPUP,0,65,200,60,NULL,NULL,NULL,NULL);
2148
    ok(wnd2!=NULL,"Unable to create window 2\n");
2149
    wnd3 = CreateWindowA("edit",NULL,WS_POPUP,0,130,200,60,NULL,NULL,NULL,NULL);
2150 2151 2152 2153
    ok(wnd3!=NULL,"Unable to create window 3\n");

    processPendingMessages();

2154 2155
    test_OnInitDocumentMgr = SINK_OPTIONAL; /* Vista and greater */
    test_OnPushContext = SINK_OPTIONAL; /* Vista and greater */
2156 2157 2158
    test_OnSetFocus = SINK_OPTIONAL; /* Win7 */
    test_PrevFocus = NULL;
    test_CurrentFocus = FOCUS_IGNORE;
2159 2160

    ShowWindow(wnd1,SW_SHOWNORMAL);
2161
    test_OnSetFocus = SINK_UNEXPECTED;
2162
    SetFocus(wnd1);
2163 2164 2165 2166
    sink_check_ok(&test_OnInitDocumentMgr,"OnInitDocumentMgr");
    sink_check_ok(&test_OnPushContext,"OnPushContext");

    test_OnSetFocus  = SINK_OPTIONAL; /* Vista and greater */
2167 2168
    test_ACP_RequestLock = SINK_OPTIONAL; /* Win7 x64 */
    test_ACP_GetSelection = SINK_OPTIONAL; /* Win7 x64 */
2169 2170
    ITfThreadMgr_GetFocus(g_tm, &test_PrevFocus);
    test_CurrentFocus = FOCUS_IGNORE; /* This is a default system context */
2171
    processPendingMessages();
2172
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
2173 2174
    test_ACP_RequestLock = SINK_UNEXPECTED;
    test_ACP_GetSelection = SINK_UNEXPECTED;
2175

2176
    test_CurrentFocus = dm1;
2177
    test_PrevFocus = FOCUS_IGNORE;
2178
    test_OnSetFocus  = SINK_OPTIONAL;
2179
    test_ShouldDeactivate = TRUE;
2180 2181
    hr = ITfThreadMgr_AssociateFocus(g_tm,wnd1,dm1,&olddm);
    ok(SUCCEEDED(hr),"AssociateFocus failed\n");
2182
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
2183
    test_ShouldDeactivate = FALSE;
2184 2185 2186 2187

    processPendingMessages();

    ITfThreadMgr_GetFocus(g_tm, &dmcheck);
2188 2189 2190 2191 2192 2193 2194 2195 2196
    ok(dmcheck == dm1 || broken(dmcheck == dmorig /* Win7+ */), "Expected DocumentMgr not focused\n");
    ITfDocumentMgr_Release(dmcheck);

    /* We need to explicitly set focus on Win7+ */
    test_CurrentFocus = dm1;
    test_PrevFocus = FOCUS_IGNORE;
    test_OnSetFocus = SINK_OPTIONAL; /* Doesn't always fire on Win7+ */
    ITfThreadMgr_SetFocus(g_tm, dm1);
    sink_check_ok(&test_OnSetFocus, "OnSetFocus");
2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211

    hr = ITfThreadMgr_AssociateFocus(g_tm,wnd2,dm2,&olddm);
    ok(SUCCEEDED(hr),"AssociateFocus failed\n");
    processPendingMessages();
    ITfThreadMgr_GetFocus(g_tm, &dmcheck);
    ok(dmcheck == dm1, "Expected DocumentMgr not focused\n");
    ITfDocumentMgr_Release(dmcheck);

    hr = ITfThreadMgr_AssociateFocus(g_tm,wnd3,dm2,&olddm);
    ok(SUCCEEDED(hr),"AssociateFocus failed\n");
    processPendingMessages();
    ITfThreadMgr_GetFocus(g_tm, &dmcheck);
    ok(dmcheck == dm1, "Expected DocumentMgr not focused\n");
    ITfDocumentMgr_Release(dmcheck);

2212 2213 2214
    test_CurrentFocus = FOCUS_SAVE;
    test_PrevFocus = FOCUS_SAVE;
    test_OnSetFocus = SINK_SAVE;
2215
    test_ShouldDeactivate = TRUE; /* win 8/10 */
2216
    ShowWindow(wnd2,SW_SHOWNORMAL);
2217
    SetFocus(wnd2);
2218
    sink_check_saved(&test_OnSetFocus,dm1,dm2,"OnSetFocus");
2219 2220 2221
    test_CurrentFocus = FOCUS_IGNORE; /* occasional wine race */
    test_PrevFocus = FOCUS_IGNORE; /* occasional wine race */
    test_OnSetFocus = SINK_IGNORE; /* occasional wine race */
2222
    test_ShouldDeactivate = FALSE;
2223
    processPendingMessages();
2224

2225
    ShowWindow(wnd3,SW_SHOWNORMAL);
2226 2227 2228
    SetFocus(wnd3);
    processPendingMessages();

2229 2230 2231
    test_CurrentFocus = FOCUS_SAVE;
    test_PrevFocus = FOCUS_SAVE;
    test_OnSetFocus = SINK_SAVE;
2232 2233
    SetFocus(wnd1);
    processPendingMessages();
2234
    sink_check_saved(&test_OnSetFocus,dm2,dm1,"OnSetFocus");
2235 2236 2237 2238 2239 2240 2241 2242

    hr = ITfThreadMgr_AssociateFocus(g_tm,wnd3,NULL,&olddm);
    ok(SUCCEEDED(hr),"AssociateFocus failed\n");
    ok(olddm == dm2, "incorrect old DocumentMgr returned\n");
    ITfDocumentMgr_Release(olddm);

    test_CurrentFocus = dmorig;
    test_PrevFocus = dm1;
2243
    test_OnSetFocus  = SINK_OPTIONAL; /* Doesn't always fire on Win7+ */
2244
    test_ACP_GetStatus = SINK_IGNORE;
2245
    ITfThreadMgr_SetFocus(g_tm,dmorig);
2246
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
2247

2248 2249 2250
    test_CurrentFocus = FOCUS_SAVE;
    test_PrevFocus = FOCUS_SAVE;
    test_OnSetFocus = SINK_SAVE;
2251 2252
    SetFocus(wnd3);
    processPendingMessages();
2253
    sink_check_saved(&test_OnSetFocus,dmorig,FOCUS_IGNORE,"OnSetFocus"); /* CurrentFocus NULL on XP, system default on Vista */
2254 2255 2256 2257 2258 2259 2260 2261 2262 2263

    hr = ITfThreadMgr_AssociateFocus(g_tm,wnd2,NULL,&olddm);
    ok(SUCCEEDED(hr),"AssociateFocus failed\n");
    ok(olddm == dm2, "incorrect old DocumentMgr returned\n");
    ITfDocumentMgr_Release(olddm);
    hr = ITfThreadMgr_AssociateFocus(g_tm,wnd1,NULL,&olddm);
    ok(SUCCEEDED(hr),"AssociateFocus failed\n");
    ok(olddm == dm1, "incorrect old DocumentMgr returned\n");
    ITfDocumentMgr_Release(olddm);

2264 2265 2266
    test_OnSetFocus = SINK_IGNORE; /* OnSetFocus fires a couple of times on Win7 */
    test_CurrentFocus = FOCUS_IGNORE;
    test_PrevFocus = FOCUS_IGNORE;
2267 2268 2269 2270
    SetFocus(wnd2);
    processPendingMessages();
    SetFocus(wnd1);
    processPendingMessages();
2271
    test_OnSetFocus = SINK_UNEXPECTED;
2272 2273 2274 2275 2276

    ITfDocumentMgr_Release(dm1);
    ITfDocumentMgr_Release(dm2);

    test_CurrentFocus = dmorig;
2277
    test_PrevFocus = FOCUS_IGNORE;
2278
    test_OnSetFocus  = SINK_OPTIONAL;
2279 2280
    test_ACP_GetStatus = SINK_IGNORE;
    ITfThreadMgr_SetFocus(g_tm,dmorig);
2281
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
2282

2283 2284 2285
    test_OnSetFocus = SINK_IGNORE; /* OnSetFocus fires a couple of times on Win7 */
    test_CurrentFocus = FOCUS_IGNORE;
    test_PrevFocus = FOCUS_IGNORE;
2286 2287 2288 2289 2290 2291
    DestroyWindow(wnd1);
    DestroyWindow(wnd2);
    test_OnPopContext = SINK_OPTIONAL; /* Vista and greater */
    test_OnSetFocus = SINK_OPTIONAL; /* Vista and greater */
    ITfThreadMgr_GetFocus(g_tm, &test_PrevFocus);
    test_CurrentFocus = NULL;
2292
    test_ShouldDeactivate = TRUE; /* Win7 */
2293
    DestroyWindow(wnd3);
2294
    test_ShouldDeactivate = FALSE;
2295 2296
    sink_check_ok(&test_OnSetFocus,"OnSetFocus");
    sink_check_ok(&test_OnPopContext,"OnPopContext");
2297 2298
}

2299
static void test_profile_mgr(void)
2300
{
2301
    IEnumTfInputProcessorProfiles *enum_profiles;
2302 2303 2304 2305
    ITfInputProcessorProfileMgr *ipp_mgr;
    HRESULT hres;

    hres = ITfInputProcessorProfiles_QueryInterface(g_ipp, &IID_ITfInputProcessorProfileMgr, (void**)&ipp_mgr);
2306 2307 2308 2309 2310
    if (hres != S_OK)
    {
        win_skip("ITfInputProcessorProfileMgr is not supported.\n");
        return;
    }
2311 2312
    ok(hres == S_OK, "Could not get ITfInputProcessorProfileMgr iface: %08x\n", hres);

2313 2314 2315 2316 2317
    hres = ITfInputProcessorProfileMgr_EnumProfiles(ipp_mgr, 0, &enum_profiles);
    ok(hres == S_OK, "EnumProfiles failed: %08x\n", hres);

    IEnumTfInputProcessorProfiles_Release(enum_profiles);

2318 2319 2320
    ITfInputProcessorProfileMgr_Release(ipp_mgr);
}

2321
START_TEST(inputprocessor)
2322
{
2323 2324 2325 2326
    if (SUCCEEDED(initialize()))
    {
        test_Register();
        test_RegisterCategory();
2327 2328
        Sleep(2000); /* Win7 needs some time before the registrations become active */
        processPendingMessages();
2329
        test_EnumLanguageProfiles();
2330 2331 2332 2333 2334
        test_EnumInputProcessorInfo();
        test_Enable();
        test_ThreadMgrAdviseSinks();
        test_Activate();
        test_startSession();
2335
        test_DocumentMgrAdviseSinks();
2336 2337 2338
        test_TfGuidAtom();
        test_ClientId();
        test_KeystrokeMgr();
2339
        test_TStoApplicationText();
2340
        test_Compartments();
2341
        test_AssociateFocus();
2342 2343 2344 2345
        test_endSession();
        test_FindClosestCategory();
        test_Disable();
        test_ThreadMgrUnadviseSinks();
2346
        test_DocumentMgrUnadviseSinks();
2347 2348
        test_UnregisterCategory();
        test_Unregister();
2349
        test_profile_mgr();
2350 2351 2352

        ITextStoreACPSink_Release(ACPSink);
        ITfDocumentMgr_Release(g_dm);
2353 2354 2355 2356
    }
    else
        skip("Unable to create InputProcessor\n");
    cleanup();
2357
}