[PATCH 5/5] windows.media.speech/tests: Add some recognition tests.

Bernhard Kölbl wine at gitlab.winehq.org
Wed May 4 05:35:56 CDT 2022


From: Bernhard Kölbl <besentv at gmail.com>

Signed-off-by: Bernhard Kölbl <besentv at gmail.com>
---
 dlls/windows.media.speech/tests/speech.c | 376 +++++++++++++++++++++++
 1 file changed, 376 insertions(+)

diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c
index c7d80385b7a..894ed5c35f8 100644
--- a/dlls/windows.media.speech/tests/speech.c
+++ b/dlls/windows.media.speech/tests/speech.c
@@ -100,6 +100,83 @@ static const char *debugstr_hstring(HSTRING hstr)
     return wine_dbgstr_wn(str, len);
 }
 
+struct async_action_handler
+{
+    IAsyncActionCompletedHandler IAsyncActionCompletedHandler_iface;
+    LONG ref;
+
+    HANDLE event_block;
+    HANDLE event_finished;
+};
+
+static inline struct async_action_handler *impl_from_IAsyncActionCompletedHandler(IAsyncActionCompletedHandler *iface)
+{
+    return CONTAINING_RECORD(iface, struct async_action_handler, IAsyncActionCompletedHandler_iface);
+}
+
+HRESULT WINAPI async_action_handler_QueryInterface( IAsyncActionCompletedHandler *iface, REFIID iid, void **out )
+{
+    if (IsEqualGUID(iid, &IID_IUnknown) ||
+        IsEqualGUID(iid, &IID_IAgileObject) ||
+        IsEqualGUID(iid, &IID_IAsyncActionCompletedHandler))
+    {
+        IUnknown_AddRef(iface);
+        *out = iface;
+        return S_OK;
+    }
+
+    trace("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+ULONG WINAPI async_action_handler_AddRef( IAsyncActionCompletedHandler *iface )
+{
+    struct async_action_handler *impl = impl_from_IAsyncActionCompletedHandler(iface);
+    ULONG ref = InterlockedIncrement(&impl->ref);
+    return ref;
+}
+
+ULONG WINAPI async_action_handler_Release( IAsyncActionCompletedHandler *iface )
+{
+    struct async_action_handler *impl = impl_from_IAsyncActionCompletedHandler(iface);
+    ULONG ref = InterlockedDecrement(&impl->ref);
+    return ref;
+}
+
+HRESULT WINAPI async_action_handler_Invoke( IAsyncActionCompletedHandler *iface, IAsyncAction *sender, AsyncStatus status )
+{
+    struct async_action_handler *impl = impl_from_IAsyncActionCompletedHandler(iface);
+
+    trace("iface %p, sender %p, status %d.\n", iface, sender, status);
+
+    /* Signal finishing of the handler. */
+    if (impl->event_finished) SetEvent(impl->event_finished);
+    /* Block handler until event is set. */
+    if (impl->event_block) WaitForSingleObject(impl->event_block, INFINITE);
+
+    return S_OK;
+}
+
+static const struct IAsyncActionCompletedHandlerVtbl async_action_handler_vtbl =
+{
+    /* IUnknown methods */
+    async_action_handler_QueryInterface,
+    async_action_handler_AddRef,
+    async_action_handler_Release,
+    /* IAsyncActionCompletedHandler methods */
+    async_action_handler_Invoke
+};
+
+
+static HRESULT WINAPI async_action_handler_create_static( struct async_action_handler *impl )
+{
+    impl->IAsyncActionCompletedHandler_iface.lpVtbl = &async_action_handler_vtbl;
+    impl->ref = 1;
+
+    return S_OK;
+}
+
 struct completed_event_handler
 {
     IHandler_RecognitionCompleted IHandler_RecognitionCompleted_iface;
@@ -1339,6 +1416,304 @@ done:
     RoUninitialize();
 }
 
+struct async_action_block_param
+{
+    IAsyncActionCompletedHandler *handler;
+    IAsyncAction *action;
+};
+
+static DWORD WINAPI async_action_block_thread(void *arg)
+{
+    struct async_action_block_param *param = arg;
+    HRESULT hr;
+
+    hr = IAsyncAction_put_Completed(param->action, param->handler);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    return 0;
+}
+
+static void test_Recognition(void)
+{
+    static const WCHAR *list_constraint_name = L"Windows.Media.SpeechRecognition.SpeechRecognitionListConstraint";
+    static const WCHAR *recognizer_name = L"Windows.Media.SpeechRecognition.SpeechRecognizer";
+    static const WCHAR *speech_constraint_tag = L"test_message";
+    static const WCHAR *speech_constraints[] = { L"This is a test.", L"Number 5!", L"What time is it?" };
+    ISpeechRecognitionListConstraintFactory *listconstraint_factory = NULL;
+    IAsyncOperation_SpeechRecognitionCompilationResult *operation = NULL;
+    IVector_ISpeechRecognitionConstraint *constraints = NULL;
+    ISpeechContinuousRecognitionSession *session = NULL;
+    ISpeechRecognitionListConstraint *listconstraint = NULL;
+    ISpeechRecognitionConstraint *constraint = NULL;
+    ISpeechRecognizer *recognizer = NULL;
+    ISpeechRecognizer2 *recognizer2 = NULL;
+    IAsyncActionCompletedHandler *handler = NULL;
+    IAsyncAction *action = NULL, *action2 = NULL;
+    IInspectable *inspectable = NULL;
+    IAsyncInfo *info = NULL;
+    struct recognition_result_handler result_handler;
+    struct compilation_handler compilation_handler;
+    struct async_action_block_param block_param;
+    struct async_action_handler action_handler;
+    struct iterator_hstring iterator_hstring;
+    struct iterable_hstring iterable_hstring;
+    EventRegistrationToken token = { .value = 0 };
+    HSTRING commands[3], hstr, tag;
+    AsyncStatus async_status;
+    HANDLE blocked_thread;
+    HRESULT hr, error_code;
+    LONG ref, old_ref;
+    UINT32 i, id;
+    BOOL set;
+
+    hr = RoInitialize(RO_INIT_MULTITHREADED);
+    ok(hr == S_OK, "RoInitialize failed, hr %#lx.\n", hr);
+
+    for (i = 0; i < ARRAY_SIZE(commands); i++)
+    {
+        hr = WindowsCreateString(speech_constraints[i], wcslen(speech_constraints[i]), &commands[i]);
+        ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+    }
+
+    /* The create functions for ListConstraint are broken on Win10 1507 x32 - abort early.*/
+    if (broken(is_win10_1507 && (sizeof(void*) == 4)))
+    {
+        win_skip("SpeechRecognitionListConstraint object creation broken on Win10 1507 x32!\n");
+        goto done;
+    }
+
+    hr = WindowsCreateString(recognizer_name, wcslen(recognizer_name), &hstr);
+    ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+    hr = RoActivateInstance(hstr, &inspectable);
+    ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR || hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr);
+    WindowsDeleteString(hstr);
+
+    if (FAILED(hr))  /* Win 8 and 8.1 and Win10 without enabled SR. */
+    {
+        win_skip("SpeechRecognizer cannot be activated!\n");
+        goto done;
+    }
+
+    hr = WindowsCreateString(list_constraint_name, wcslen(list_constraint_name), &hstr);
+    ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+    hr = RoGetActivationFactory(hstr, &IID_ISpeechRecognitionListConstraintFactory, (void **)&listconstraint_factory);
+    ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG), "RoGetActivationFactory failed, hr %#lx.\n", hr);
+    WindowsDeleteString(hstr);
+
+    hr = WindowsCreateString(speech_constraint_tag, wcslen(speech_constraint_tag), &tag);
+    ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+    iterator_hstring_create_static(&iterator_hstring, commands, ARRAY_SIZE(commands));
+    iterable_hstring_create_static(&iterable_hstring, &iterator_hstring);
+    hr = ISpeechRecognitionListConstraintFactory_CreateWithTag(listconstraint_factory, &iterable_hstring.IIterable_HSTRING_iface, tag, &listconstraint);
+    ok(hr == S_OK, "ISpeechRecognitionListConstraintFactory_Create failed, hr %#lx.\n", hr);
+    WindowsDeleteString(tag);
+
+    ref = ISpeechRecognitionListConstraintFactory_Release(listconstraint_factory);
+    ok(ref == 1, "Got unexpected ref %lu.\n", ref);
+
+    hr = ISpeechRecognitionListConstraint_QueryInterface(listconstraint, &IID_ISpeechRecognitionConstraint, (void **)&constraint);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    hr = ISpeechRecognitionConstraint_put_IsEnabled(constraint, TRUE);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    hr = IInspectable_QueryInterface(inspectable, &IID_ISpeechRecognizer, (void **)&recognizer);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    hr = IInspectable_QueryInterface(inspectable, &IID_ISpeechRecognizer2, (void **)&recognizer2);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    hr = ISpeechRecognizer2_get_ContinuousRecognitionSession(recognizer2, &session);
+    ok(hr == S_OK, "ISpeechRecognizer2_get_ContinuousRecognitionSession failed, hr %#lx.\n", hr);
+
+    hr = ISpeechRecognizer_get_Constraints(recognizer, &constraints);
+    ok(hr == S_OK, "ISpeechContinuousRecognitionSession_get_Constraints failed, hr %#lx.\n", hr);
+
+    hr = IVector_ISpeechRecognitionConstraint_Clear(constraints);
+    ok(hr == S_OK, "IVector_ISpeechRecognitionConstraint_Clear failed, hr %#lx.\n", hr);
+
+    hr = IVector_ISpeechRecognitionConstraint_Append(constraints, constraint);
+    ok(hr == S_OK, "IVector_ISpeechRecognitionConstraint_Append failed, hr %#lx.\n", hr);
+
+    ref = IVector_ISpeechRecognitionConstraint_Release(constraints);
+    ok(ref == 1, "Got unexpected ref %lu.\n", ref);
+
+    ref = ISpeechRecognitionConstraint_Release(constraint);
+    ok(ref == 2, "Got unexpected ref %lu.\n", ref);
+
+    ref = ISpeechRecognitionListConstraint_Release(listconstraint);
+    ok(ref == 1, "Got unexpected ref %lu.\n", ref);
+
+    token.value = 0xdeadbeef;
+    recognition_result_handler_create_static(&result_handler);
+    hr = ISpeechContinuousRecognitionSession_add_ResultGenerated(session, &result_handler.IHandler_RecognitionResult_iface, &token);
+    ok(hr == S_OK, "ISpeechContinuousRecognitionSession_add_ResultGenerated failed, hr %#lx.\n", hr);
+    ok(token.value != 0xdeadbeef, "Got unexpexted token: %#I64x.\n", token.value);
+
+    compilation_handler_create_static(&compilation_handler);
+    compilation_handler.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL);
+    compilation_handler.thread_id = GetCurrentThreadId();
+    ok(!!compilation_handler.event_finished, "Finished event wasn't created.\n");
+
+    ISpeechRecognizer_CompileConstraintsAsync(recognizer, &operation);
+    ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr);
+
+    hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, &compilation_handler.IAsyncHandler_Compilation_iface);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+
+    ok(!WaitForSingleObject(compilation_handler.event_finished, 5000), "Wait for event_finished failed.\n");
+    IAsyncOperation_SpeechRecognitionCompilationResult_Release(operation);
+
+    hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action);
+    todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr);
+
+    if (FAILED(hr)) goto skip_action;
+
+    async_action_handler_create_static(&action_handler);
+    action_handler.event_block = NULL;
+    action_handler.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL);
+    ok(!!action_handler.event_finished, "Finished event wasn't created.\n");
+
+    hr = IAsyncAction_put_Completed(action, &action_handler.IAsyncActionCompletedHandler_iface);
+    todo_wine ok(hr == S_OK, "IAsyncAction_put_Completed failed, hr %#lx.\n", hr);
+    todo_wine ok(!WaitForSingleObject(action_handler.event_finished , 5000), "Wait for event_finished failed.\n");
+    CloseHandle(action_handler.event_finished);
+
+    handler = (void *)0xdeadbeef;
+    hr = IAsyncAction_get_Completed(action, &handler);
+    todo_wine ok(hr == S_OK, "IAsyncAction_put_Completed failed, hr %#lx.\n", hr);
+    todo_wine ok(handler == NULL, "Handler was %p.\n", handler);
+
+    hr = IAsyncAction_QueryInterface(action, &IID_IAsyncInfo, (void **)&info);
+    todo_wine ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr);
+
+    id = 0xdeadbeef;
+    hr = IAsyncInfo_get_Id(info, &id);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_get_Id failed, hr %#lx.\n", hr);
+    todo_wine ok(id != 0xdeadbeef, "Id was %#x.\n", id);
+
+    async_status = 0xdeadbeef;
+    hr = IAsyncInfo_get_Status(info, &async_status);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr);
+    todo_wine ok(async_status == Completed, "Status was %#x.\n", async_status);
+
+    error_code = 0xdeadbeef;
+    hr = IAsyncInfo_get_ErrorCode(info, &error_code);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_get_ErrorCode failed, hr %#lx.\n", hr);
+    todo_wine ok(error_code == S_OK, "ErrorCode was %#lx.\n", error_code);
+
+    hr = IAsyncInfo_Cancel(info);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_Cancel failed, hr %#lx.\n", hr);
+
+    async_status = 0xdeadbeef;
+    hr = IAsyncInfo_get_Status(info, &async_status);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr);
+    todo_wine ok(async_status == Completed, "Status was %#x.\n", async_status);
+
+    hr = IAsyncInfo_Close(info);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr);
+
+    hr = IAsyncInfo_Close(info);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr);
+
+    hr = IAsyncInfo_Cancel(info);
+    todo_wine ok(hr == S_OK, "IAsyncInfo_Cancel failed, hr %#lx.\n", hr);
+
+    async_status = 0xdeadbeef;
+    hr = IAsyncInfo_get_Status(info, &async_status);
+    todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr);
+    todo_wine ok(async_status == AsyncStatus_Closed, "Status was %#x.\n", async_status);
+
+    error_code = 0xdeadbeef;
+    hr = IAsyncInfo_get_ErrorCode(info, &error_code);
+    todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "IAsyncInfo_get_ErrorCode failed, hr %#lx.\n", hr);
+    todo_wine ok(error_code == E_ILLEGAL_METHOD_CALL, "ErrorCode was %#lx.\n", error_code);
+
+    ref = IAsyncInfo_Release(info);
+    todo_wine ok(ref == 1, "Got unexpected ref %lu.\n", ref);
+
+    /*
+     * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality.
+     */
+
+    hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action2);
+    todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr);
+
+    async_action_handler_create_static(&action_handler);
+    action_handler.event_block = CreateEventW(NULL, FALSE, FALSE, NULL);
+    action_handler.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL);
+    ok(!!action_handler.event_block, "Block event wasn't created.\n");
+    ok(!!action_handler.event_finished, "Finished event wasn't created.\n");
+
+    /* Check if IAsyncInfo_Close is non blocking. */
+    block_param.handler = &action_handler.IAsyncActionCompletedHandler_iface;
+    block_param.action = action2;
+    blocked_thread = CreateThread(NULL, 0, async_action_block_thread, &block_param, 0, NULL);
+    todo_wine ok(!WaitForSingleObject(action_handler.event_finished , 5000), "Wait for event_finished failed.\n");
+    todo_wine ok(WaitForSingleObject(blocked_thread, 100) == WAIT_TIMEOUT, "Wait for block_thread didn't time out.\n");
+
+    handler = (void *)0xdeadbeef;
+    old_ref = action_handler.ref;
+    hr = IAsyncAction_get_Completed(action2, &handler);
+    todo_wine ok(hr == S_OK, "IAsyncAction_get_Completed failed, hr %#lx.\n", hr);
+
+    todo_wine ok(handler == &action_handler.IAsyncActionCompletedHandler_iface || /* Broken on 1507. */
+                            broken(handler != NULL && handler != (void *)0xdeadbeef), "Handler was %p.\n", handler);
+
+    ref = action_handler.ref - old_ref;
+    todo_wine ok(ref == 1, "The ref was increased by %lu.\n", ref);
+    IAsyncActionCompletedHandler_Release(handler);
+
+    hr = IAsyncAction_QueryInterface(action2, &IID_IAsyncInfo, (void **)&info);
+    todo_wine ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr);
+
+    hr = IAsyncInfo_Close(info); /* If IAsyncInfo_Close would wait for the handler to finish, the test would get stuck here. */
+    todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr);
+
+    set = SetEvent(action_handler.event_block);
+    todo_wine ok(set == TRUE, "Event 'event_block' wasn't set.\n");
+    todo_wine ok(!WaitForSingleObject(blocked_thread , 1000), "Wait for blocked_thread failed.\n");
+
+    ref = IAsyncInfo_Release(info);
+    todo_wine ok(ref == 1, "Got unexpected ref %lu.\n", ref);
+
+    CloseHandle(action_handler.event_finished);
+    CloseHandle(action_handler.event_block);
+
+    todo_wine ok(action != action2, "actions were the same!\n");
+
+    ref = IAsyncAction_Release(action2);
+    todo_wine ok(!ref, "Got unexpected ref %lu.\n", ref);
+
+    ref = IAsyncAction_Release(action);
+    todo_wine ok(!ref, "Got unexpected ref %lu.\n", ref);
+
+skip_action:
+    hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token);
+    ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr);
+
+    ref = ISpeechContinuousRecognitionSession_Release(session);
+    ok(ref == 1, "Got unexpected ref %lu.\n", ref);
+
+    ref = ISpeechRecognizer2_Release(recognizer2);
+    ok(ref == 2, "Got unexpected ref %lu.\n", ref);
+
+    ref = ISpeechRecognizer_Release(recognizer);
+    ok(ref == 1, "Got unexpected ref %lu.\n", ref);
+
+    ref = IInspectable_Release(inspectable);
+    ok(!ref, "Got unexpected ref %lu.\n", ref);
+
+done:
+    for (i = 0; i < ARRAY_SIZE(commands); i++)
+        WindowsDeleteString(commands[i]);
+
+    RoUninitialize();
+}
+
 START_TEST(speech)
 {
     test_ActivationFactory();
@@ -1346,4 +1721,5 @@ START_TEST(speech)
     test_VoiceInformation();
     test_SpeechRecognizer();
     test_SpeechRecognitionListConstraint();
+    test_Recognition();
 }
-- 
GitLab

https://gitlab.winehq.org/wine/wine/-/merge_requests/34



More information about the wine-devel mailing list