sapi: Implement ISpVoice::WaitUntilDone.
[wine.git] / dlls / sapi / tests / tts.c
blob39f62ac551eba4b5f6328c8f3b76a0d898e0a8bb
1 /*
2 * Speech API (SAPI) text-to-speech tests.
4 * Copyright 2019 Jactry Zeng for CodeWeavers
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.
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.
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
21 #define COBJMACROS
23 #include "sapiddk.h"
24 #include "sperror.h"
26 #include "wine/test.h"
28 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
29 static void _expect_ref(IUnknown *obj, ULONG ref, int line)
31 ULONG rc;
32 IUnknown_AddRef(obj);
33 rc = IUnknown_Release(obj);
34 ok_(__FILE__,line)(rc == ref, "Unexpected refcount %ld, expected %ld.\n", rc, ref);
37 static void test_interfaces(void)
39 ISpeechVoice *speech_voice, *speech_voice2;
40 IConnectionPointContainer *container;
41 ISpTTSEngineSite *site;
42 ISpVoice *spvoice, *spvoice2;
43 IDispatch *dispatch;
44 IUnknown *unk;
45 HRESULT hr;
47 hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER,
48 &IID_ISpeechVoice, (void **)&speech_voice);
49 ok(hr == S_OK, "Failed to create ISpeechVoice interface: %#lx.\n", hr);
50 EXPECT_REF(speech_voice, 1);
52 hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER,
53 &IID_IDispatch, (void **)&dispatch);
54 ok(hr == S_OK, "Failed to create IDispatch interface: %#lx.\n", hr);
55 EXPECT_REF(dispatch, 1);
56 EXPECT_REF(speech_voice, 1);
57 IDispatch_Release(dispatch);
59 hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER,
60 &IID_IUnknown, (void **)&unk);
61 ok(hr == S_OK, "Failed to create IUnknown interface: %#lx.\n", hr);
62 EXPECT_REF(unk, 1);
63 EXPECT_REF(speech_voice, 1);
64 IUnknown_Release(unk);
66 hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER,
67 &IID_ISpVoice, (void **)&spvoice);
68 ok(hr == S_OK, "Failed to create ISpVoice interface: %#lx.\n", hr);
69 EXPECT_REF(spvoice, 1);
70 EXPECT_REF(speech_voice, 1);
72 hr = ISpVoice_QueryInterface(spvoice, &IID_ISpeechVoice, (void **)&speech_voice2);
73 ok(hr == S_OK, "ISpVoice_QueryInterface failed: %#lx.\n", hr);
74 EXPECT_REF(speech_voice2, 2);
75 EXPECT_REF(spvoice, 2);
76 EXPECT_REF(speech_voice, 1);
77 ISpeechVoice_Release(speech_voice2);
79 hr = ISpeechVoice_QueryInterface(speech_voice, &IID_ISpVoice, (void **)&spvoice2);
80 ok(hr == S_OK, "ISpeechVoice_QueryInterface failed: %#lx.\n", hr);
81 EXPECT_REF(speech_voice, 2);
82 EXPECT_REF(spvoice2, 2);
83 EXPECT_REF(spvoice, 1);
84 ISpVoice_Release(spvoice2);
85 ISpVoice_Release(spvoice);
87 hr = ISpeechVoice_QueryInterface(speech_voice, &IID_IConnectionPointContainer,
88 (void **)&container);
89 ok(hr == S_OK, "ISpeechVoice_QueryInterface failed: %#lx.\n", hr);
90 EXPECT_REF(speech_voice, 2);
91 EXPECT_REF(container, 2);
92 IConnectionPointContainer_Release(container);
94 hr = ISpeechVoice_QueryInterface(speech_voice, &IID_ISpTTSEngineSite,
95 (void **)&site);
96 ok(hr == E_NOINTERFACE, "ISpeechVoice_QueryInterface for ISpTTSEngineSite returned: %#lx.\n", hr);
98 ISpeechVoice_Release(speech_voice);
101 #define TESTENGINE_CLSID L"{57C7E6B1-2FC2-4E8E-B968-1410A39E7198}"
102 static const GUID CLSID_TestEngine = {0x57C7E6B1,0x2FC2,0x4E8E,{0xB9,0x68,0x14,0x10,0xA3,0x9E,0x71,0x98}};
104 struct test_engine
106 ISpTTSEngine ISpTTSEngine_iface;
107 ISpObjectWithToken ISpObjectWithToken_iface;
109 ISpObjectToken *token;
111 BOOL speak_called;
112 DWORD flags;
113 GUID fmtid;
114 SPVTEXTFRAG *frag_list;
115 LONG rate;
116 USHORT volume;
119 static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frag_list)
121 SPVTEXTFRAG *frag, *prev = NULL;
123 if (!frag_list)
125 *ret_frag_list = NULL;
126 return;
129 while (frag_list)
131 frag = malloc(sizeof(*frag) + frag_list->ulTextLen * sizeof(WCHAR));
132 memcpy(frag, frag_list, sizeof(*frag));
134 if (frag_list->pTextStart)
136 frag->pTextStart = (WCHAR *)(frag + 1);
137 memcpy(frag + 1, frag_list->pTextStart, frag->ulTextLen * sizeof(WCHAR));
140 frag->pNext = NULL;
142 if (prev)
143 prev->pNext = frag;
144 else
145 *ret_frag_list = frag;
147 prev = frag;
148 frag_list = frag_list->pNext;
152 static void reset_engine_params(struct test_engine *engine)
154 SPVTEXTFRAG *frag, *next;
156 engine->speak_called = FALSE;
157 engine->flags = 0xdeadbeef;
158 memset(&engine->fmtid, 0xde, sizeof(engine->fmtid));
159 engine->rate = 0xdeadbeef;
160 engine->volume = 0xbeef;
162 for (frag = engine->frag_list; frag; frag = next)
164 next = frag->pNext;
165 free(frag);
167 engine->frag_list = NULL;
170 static inline struct test_engine *impl_from_ISpTTSEngine(ISpTTSEngine *iface)
172 return CONTAINING_RECORD(iface, struct test_engine, ISpTTSEngine_iface);
175 static inline struct test_engine *impl_from_ISpObjectWithToken(ISpObjectWithToken *iface)
177 return CONTAINING_RECORD(iface, struct test_engine, ISpObjectWithToken_iface);
180 static HRESULT WINAPI test_engine_QueryInterface(ISpTTSEngine *iface, REFIID iid, void **obj)
182 struct test_engine *engine = impl_from_ISpTTSEngine(iface);
184 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ISpTTSEngine))
185 *obj = &engine->ISpTTSEngine_iface;
186 else if (IsEqualIID(iid, &IID_ISpObjectWithToken))
187 *obj = &engine->ISpObjectWithToken_iface;
188 else
190 *obj = NULL;
191 return E_NOINTERFACE;
194 IUnknown_AddRef((IUnknown *)*obj);
195 return S_OK;
198 static ULONG WINAPI test_engine_AddRef(ISpTTSEngine *iface)
200 return 2;
203 static ULONG WINAPI test_engine_Release(ISpTTSEngine *iface)
205 return 1;
208 static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID fmtid,
209 const WAVEFORMATEX *wfx, const SPVTEXTFRAG *frag_list,
210 ISpTTSEngineSite *site)
212 struct test_engine *engine = impl_from_ISpTTSEngine(iface);
213 DWORD actions;
214 char *buf;
215 int i;
216 HRESULT hr;
218 engine->flags = flags;
219 engine->fmtid = *fmtid;
220 copy_frag_list(frag_list, &engine->frag_list);
221 engine->speak_called = TRUE;
223 actions = ISpTTSEngineSite_GetActions(site);
224 ok(actions == (SPVES_CONTINUE | SPVES_RATE | SPVES_VOLUME), "got %#lx.\n", actions);
226 hr = ISpTTSEngineSite_GetRate(site, &engine->rate);
227 ok(hr == S_OK, "got %#lx.\n", hr);
228 actions = ISpTTSEngineSite_GetActions(site);
229 ok(actions == (SPVES_CONTINUE | SPVES_VOLUME), "got %#lx.\n", actions);
231 hr = ISpTTSEngineSite_GetVolume(site, &engine->volume);
232 ok(hr == S_OK, "got %#lx.\n", hr);
233 actions = ISpTTSEngineSite_GetActions(site);
234 ok(actions == SPVES_CONTINUE, "got %#lx.\n", actions);
236 buf = calloc(1, 22050 * 2 / 5);
237 for (i = 0; i < 5; i++)
239 if (ISpTTSEngineSite_GetActions(site) & SPVES_ABORT)
240 break;
241 hr = ISpTTSEngineSite_Write(site, buf, 22050 * 2 / 5, NULL);
242 ok(hr == S_OK || hr == SP_AUDIO_STOPPED, "got %#lx.\n", hr);
243 Sleep(100);
245 free(buf);
247 return S_OK;
250 static HRESULT WINAPI test_engine_GetOutputFormat(ISpTTSEngine *iface, const GUID *fmtid,
251 const WAVEFORMATEX *wfx, GUID *out_fmtid,
252 WAVEFORMATEX **out_wfx)
254 *out_fmtid = SPDFID_WaveFormatEx;
255 *out_wfx = CoTaskMemAlloc(sizeof(WAVEFORMATEX));
256 (*out_wfx)->wFormatTag = WAVE_FORMAT_PCM;
257 (*out_wfx)->nChannels = 1;
258 (*out_wfx)->nSamplesPerSec = 22050;
259 (*out_wfx)->wBitsPerSample = 16;
260 (*out_wfx)->nBlockAlign = 2;
261 (*out_wfx)->nAvgBytesPerSec = 22050 * 2;
262 (*out_wfx)->cbSize = 0;
264 return S_OK;
267 static ISpTTSEngineVtbl test_engine_vtbl =
269 test_engine_QueryInterface,
270 test_engine_AddRef,
271 test_engine_Release,
272 test_engine_Speak,
273 test_engine_GetOutputFormat,
276 static HRESULT WINAPI objwithtoken_QueryInterface(ISpObjectWithToken *iface, REFIID iid, void **obj)
278 struct test_engine *engine = impl_from_ISpObjectWithToken(iface);
280 return ISpTTSEngine_QueryInterface(&engine->ISpTTSEngine_iface, iid, obj);
283 static ULONG WINAPI objwithtoken_AddRef(ISpObjectWithToken *iface)
285 return 2;
288 static ULONG WINAPI objwithtoken_Release(ISpObjectWithToken *iface)
290 return 1;
293 static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token)
295 struct test_engine *engine = impl_from_ISpObjectWithToken(iface);
297 if (!token)
298 return E_INVALIDARG;
300 ISpObjectToken_AddRef(token);
301 engine->token = token;
303 return S_OK;
306 static HRESULT WINAPI objwithtoken_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token)
308 struct test_engine *engine = impl_from_ISpObjectWithToken(iface);
310 *token = engine->token;
311 if (*token)
312 ISpObjectToken_AddRef(*token);
314 return S_OK;
317 static const ISpObjectWithTokenVtbl objwithtoken_vtbl =
319 objwithtoken_QueryInterface,
320 objwithtoken_AddRef,
321 objwithtoken_Release,
322 objwithtoken_SetObjectToken,
323 objwithtoken_GetObjectToken
326 static struct test_engine test_engine = {{&test_engine_vtbl}, {&objwithtoken_vtbl}};
328 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv)
330 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IClassFactory, riid))
332 *ppv = iface;
333 return S_OK;
336 *ppv = NULL;
337 return E_NOINTERFACE;
340 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
342 return 2;
345 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
347 return 1;
350 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface,
351 IUnknown *pUnkOuter, REFIID riid, void **ppv)
353 ok(pUnkOuter == NULL, "pUnkOuter != NULL.\n");
354 ok(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISpTTSEngine),
355 "riid = %s.\n", wine_dbgstr_guid(riid));
357 *ppv = &test_engine.ISpTTSEngine_iface;
358 return S_OK;
361 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
363 ok(0, "unexpected call.\n");
364 return E_NOTIMPL;
367 static const IClassFactoryVtbl ClassFactoryVtbl = {
368 ClassFactory_QueryInterface,
369 ClassFactory_AddRef,
370 ClassFactory_Release,
371 ClassFactory_CreateInstance,
372 ClassFactory_LockServer
375 static IClassFactory test_engine_cf = { &ClassFactoryVtbl };
377 static void test_spvoice(void)
379 static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Wine\\Winetest\\sapi\\tts\\TestEngine";
380 static const WCHAR test_text[] = L"Hello! This is a test sentence.";
382 ISpVoice *voice;
383 ISpMMSysAudio *audio_out;
384 ISpObjectTokenCategory *token_cat;
385 ISpObjectToken *token;
386 WCHAR *token_id = NULL, *default_token_id = NULL;
387 ISpDataKey *attrs_key;
388 LONG rate;
389 USHORT volume;
390 ULONG stream_num;
391 DWORD regid;
392 DWORD start, duration;
393 HRESULT hr;
395 if (waveOutGetNumDevs() == 0) {
396 skip("no wave out devices.\n");
397 return;
400 hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER,
401 &IID_ISpVoice, (void **)&voice);
402 ok(hr == S_OK, "Failed to create SpVoice: %#lx.\n", hr);
404 hr = ISpVoice_SetOutput(voice, NULL, TRUE);
405 ok(hr == S_OK, "got %#lx.\n", hr);
407 hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER,
408 &IID_ISpMMSysAudio, (void **)&audio_out);
409 ok(hr == S_OK, "Failed to create SpMMAudioOut: %#lx.\n", hr);
411 hr = ISpVoice_SetOutput(voice, (IUnknown *)audio_out, TRUE);
412 todo_wine ok(hr == S_FALSE, "got %#lx.\n", hr);
414 hr = ISpVoice_SetVoice(voice, NULL);
415 todo_wine ok(hr == S_OK, "got %#lx.\n", hr);
417 hr = ISpVoice_GetVoice(voice, &token);
418 todo_wine ok(hr == S_OK, "got %#lx.\n", hr);
420 if (SUCCEEDED(hr))
422 hr = ISpObjectToken_GetId(token, &token_id);
423 ok(hr == S_OK, "got %#lx.\n", hr);
425 hr = CoCreateInstance(&CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER,
426 &IID_ISpObjectTokenCategory, (void **)&token_cat);
427 ok(hr == S_OK, "Failed to create SpObjectTokenCategory: %#lx.\n", hr);
429 hr = ISpObjectTokenCategory_SetId(token_cat, SPCAT_VOICES, FALSE);
430 ok(hr == S_OK, "got %#lx.\n", hr);
431 hr = ISpObjectTokenCategory_GetDefaultTokenId(token_cat, &default_token_id);
432 ok(hr == S_OK, "got %#lx.\n", hr);
434 ok(!wcscmp(token_id, default_token_id), "token_id != default_token_id\n");
436 CoTaskMemFree(token_id);
437 CoTaskMemFree(default_token_id);
438 ISpObjectToken_Release(token);
439 ISpObjectTokenCategory_Release(token_cat);
442 rate = 0xdeadbeef;
443 hr = ISpVoice_GetRate(voice, &rate);
444 ok(hr == S_OK, "got %#lx.\n", hr);
445 ok(rate == 0, "rate = %ld\n", rate);
447 hr = ISpVoice_SetRate(voice, 1);
448 ok(hr == S_OK, "got %#lx.\n", hr);
450 rate = 0xdeadbeef;
451 hr = ISpVoice_GetRate(voice, &rate);
452 ok(hr == S_OK, "got %#lx.\n", hr);
453 ok(rate == 1, "rate = %ld\n", rate);
455 hr = ISpVoice_SetRate(voice, -1000);
456 ok(hr == S_OK, "got %#lx.\n", hr);
458 rate = 0xdeadbeef;
459 hr = ISpVoice_GetRate(voice, &rate);
460 ok(hr == S_OK, "got %#lx.\n", hr);
461 ok(rate == -1000, "rate = %ld\n", rate);
463 hr = ISpVoice_SetRate(voice, 1000);
464 ok(hr == S_OK, "got %#lx.\n", hr);
466 rate = 0xdeadbeef;
467 hr = ISpVoice_GetRate(voice, &rate);
468 ok(hr == S_OK, "got %#lx.\n", hr);
469 ok(rate == 1000, "rate = %ld\n", rate);
471 volume = 0xbeef;
472 hr = ISpVoice_GetVolume(voice, &volume);
473 ok(hr == S_OK, "got %#lx.\n", hr);
474 ok(volume == 100, "volume = %d\n", volume);
476 hr = ISpVoice_SetVolume(voice, 0);
477 ok(hr == S_OK, "got %#lx.\n", hr);
479 volume = 0xbeef;
480 hr = ISpVoice_GetVolume(voice, &volume);
481 ok(hr == S_OK, "got %#lx.\n", hr);
482 ok(volume == 0, "volume = %d\n", volume);
484 hr = ISpVoice_SetVolume(voice, 100);
485 ok(hr == S_OK, "got %#lx.\n", hr);
487 volume = 0xbeef;
488 hr = ISpVoice_GetVolume(voice, &volume);
489 ok(hr == S_OK, "got %#lx.\n", hr);
490 ok(volume == 100, "volume = %d\n", volume);
492 hr = ISpVoice_SetVolume(voice, 101);
493 ok(hr == E_INVALIDARG, "got %#lx.\n", hr);
495 hr = CoRegisterClassObject(&CLSID_TestEngine, (IUnknown *)&test_engine_cf,
496 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &regid);
497 ok(hr == S_OK, "got %#lx.\n", hr);
499 hr = CoCreateInstance(&CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER,
500 &IID_ISpObjectToken, (void **)&token);
501 ok(hr == S_OK, "got %#lx.\n", hr);
503 hr = ISpObjectToken_SetId(token, NULL, test_token_id, TRUE);
504 ok(hr == S_OK || broken(hr == E_ACCESSDENIED) /* w1064_adm */, "got %#lx.\n", hr);
505 if (hr == E_ACCESSDENIED)
507 win_skip("token SetId access denied.\n");
508 goto done;
511 ISpObjectToken_SetStringValue(token, L"CLSID", TESTENGINE_CLSID);
512 hr = ISpObjectToken_CreateKey(token, L"Attributes", &attrs_key);
513 ok(hr == S_OK, "got %#lx.\n", hr);
514 ISpDataKey_SetStringValue(attrs_key, L"Language", L"409");
515 ISpDataKey_Release(attrs_key);
517 hr = ISpVoice_SetVoice(voice, token);
518 ok(hr == S_OK, "got %#lx.\n", hr);
520 test_engine.speak_called = FALSE;
521 hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL);
522 ok(hr == S_OK, "got %#lx.\n", hr);
523 ok(!test_engine.speak_called, "ISpTTSEngine::Speak was called.\n");
525 stream_num = 0xdeadbeef;
526 hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, &stream_num);
527 ok(hr == S_OK, "got %#lx.\n", hr);
528 ok(stream_num == 0xdeadbeef, "got %lu.\n", stream_num);
530 ISpVoice_SetRate(voice, 0);
531 ISpVoice_SetVolume(voice, 100);
533 reset_engine_params(&test_engine);
534 stream_num = 0xdeadbeef;
535 start = GetTickCount();
536 hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, &stream_num);
537 duration = GetTickCount() - start;
538 ok(hr == S_OK, "got %#lx.\n", hr);
539 ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n");
540 ok(test_engine.flags == SPF_DEFAULT, "got %#lx.\n", test_engine.flags);
541 ok(test_engine.frag_list != NULL, "frag_list is NULL.\n");
542 ok(test_engine.frag_list->pNext == NULL, "frag_list->pNext != NULL.\n");
543 ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen);
544 ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)),
545 "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart));
546 ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate);
547 ok(test_engine.volume == 100, "got %d.\n", test_engine.volume);
548 ok(stream_num == 1, "got %lu.\n", stream_num);
549 ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration);
551 start = GetTickCount();
552 hr = ISpVoice_WaitUntilDone(voice, INFINITE);
553 duration = GetTickCount() - start;
554 ok(hr == S_OK, "got %#lx.\n", hr);
555 ok(duration < 200, "took %lu ms.\n", duration);
557 reset_engine_params(&test_engine);
558 stream_num = 0xdeadbeef;
559 start = GetTickCount();
560 hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC | SPF_NLP_SPEAK_PUNC, &stream_num);
561 duration = GetTickCount() - start;
562 ok(hr == S_OK, "got %#lx.\n", hr);
563 todo_wine ok(stream_num == 1, "got %lu.\n", stream_num);
564 ok(duration < 500, "took %lu ms.\n", duration);
566 hr = ISpVoice_WaitUntilDone(voice, 100);
567 ok(hr == S_FALSE, "got %#lx.\n", hr);
569 hr = ISpVoice_WaitUntilDone(voice, INFINITE);
570 duration = GetTickCount() - start;
571 ok(hr == S_OK, "got %#lx.\n", hr);
572 ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration);
574 ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n");
575 ok(test_engine.flags == SPF_NLP_SPEAK_PUNC, "got %#lx.\n", test_engine.flags);
576 ok(test_engine.frag_list != NULL, "frag_list is NULL.\n");
577 ok(test_engine.frag_list->pNext == NULL, "frag_list->pNext != NULL.\n");
578 ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen);
579 ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)),
580 "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart));
581 ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate);
582 ok(test_engine.volume == 100, "got %d.\n", test_engine.volume);
584 reset_engine_params(&test_engine);
585 hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC, NULL);
586 ok(hr == S_OK, "got %#lx.\n", hr);
588 Sleep(200);
589 start = GetTickCount();
590 hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL);
591 duration = GetTickCount() - start;
592 ok(hr == S_OK, "got %#lx.\n", hr);
593 ok(duration < 300, "took %lu ms.\n", duration);
595 done:
596 reset_engine_params(&test_engine);
597 ISpVoice_Release(voice);
598 ISpObjectToken_Release(token);
599 ISpMMSysAudio_Release(audio_out);
602 START_TEST(tts)
604 CoInitialize(NULL);
605 test_interfaces();
606 test_spvoice();
607 CoUninitialize();