[PATCH v4 1/4] windows.media.speech/tests: Add test to check if IAsyncInfo_Close non is blocking.

Rémi Bernon rbernon at codeweavers.com
Tue Apr 19 04:13:30 CDT 2022


Hi Bernhard,

On 4/18/22 23:42, Bernhard Kölbl wrote:
> Signed-off-by: Bernhard Kölbl <besentv at gmail.com>
> ---
> v4: Fix races in tests, leaks and implement stubs. + Some rework.
> ---
>   dlls/windows.media.speech/tests/speech.c | 136 +++++++++++++++++++----
>   1 file changed, 112 insertions(+), 24 deletions(-)
> 
> diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c
> index ef75178f8c1..7fca3fa6179 100644
> --- a/dlls/windows.media.speech/tests/speech.c
> +++ b/dlls/windows.media.speech/tests/speech.c
> @@ -239,8 +239,8 @@ struct compilation_handler
>       IAsyncHandler_Compilation IAsyncHandler_Compilation_iface;
>       LONG ref;
>   
> +    HANDLE event_block;
>       HANDLE event_finished;
> -    BOOLEAN sleeping;
>       DWORD thread_id;
>   };
>   
> @@ -290,7 +290,9 @@ HRESULT WINAPI compilation_handler_Invoke( IAsyncHandler_Compilation *iface,
>       trace("Iface %p, info %p, status %d.\n", iface, info, status);
>       trace("Caller thread id %lu callback thread id %lu.\n", impl->thread_id, id);
>   
> -    ok(status != Started, "Got unexpected status %#x.\n", status);
> +    /* Block handler until event is set. */
> +    if (impl->event_block) WaitForSingleObject(impl->event_block, INFINITE);
> +    /* Signal finishing of the handler. */
>       if (impl->event_finished) SetEvent(impl->event_finished);
>   
>       return S_OK;
> @@ -813,6 +815,39 @@ static void test_VoiceInformation(void)
>       RoUninitialize();
>   }
>   
> +struct async_operation_block_param
> +{
> +    IAsyncOperationCompletedHandler_SpeechRecognitionCompilationResult *handler;
> +    IAsyncOperation_SpeechRecognitionCompilationResult *operation;
> +};
> +
> +static DWORD WINAPI async_operation_block_thread(void *arg)
> +{
> +    struct async_operation_block_param *param = arg;
> +    HRESULT hr;
> +
> +    hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(param->operation, param->handler);
> +    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
> +
> +    return 0;
> +}
> +
> +struct async_operation_close_param
> +{
> +    IAsyncInfo *info;
> +};
> +
> +static DWORD WINAPI async_operation_close_thread(void *arg)
> +{
> +    struct async_operation_close_param *param = arg;
> +    HRESULT hr;
> +
> +    hr = IAsyncInfo_Close(param->info);
> +    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
> +
> +    return 0;
> +}
> +
>   static void test_SpeechRecognizer(void)
>   {
>       static const WCHAR *speech_recognition_name = L"Windows.Media.SpeechRecognition.SpeechRecognizer";
> @@ -824,21 +859,24 @@ static void test_SpeechRecognizer(void)
>       ISpeechRecognizerFactory *sr_factory = NULL;
>       ISpeechRecognizerStatics *sr_statics = NULL;
>       ISpeechRecognizerStatics2 *sr_statics2 = NULL;
> -    ISpeechRecognizer *recognizer = NULL;
> +    ISpeechRecognizer *recognizer = NULL, *recognizer_2 = NULL;
>       ISpeechRecognizer2 *recognizer2 = NULL;
>       IActivationFactory *factory = NULL;
> -    IInspectable *inspectable = NULL;
> +    IInspectable *inspectable = NULL, *inspectable_2 = NULL;
>       IClosable *closable = NULL;
>       ILanguage *language = NULL;
>       IAsyncInfo *info = NULL;
> +    struct compilation_handler compilation_handler, compilation_handler2, compilation_handler3;
>       struct completed_event_handler completed_handler;
>       struct recognition_result_handler result_handler;
> -    struct compilation_handler compilation_handler;
> -    struct compilation_handler compilation_handler2;
> +    struct async_operation_block_param block_param;
> +    struct async_operation_close_param close_param;
>       SpeechRecognitionResultStatus result_status;
>       EventRegistrationToken token = { .value = 0 };
> +    HANDLE close_thread, blocked_thread;
>       AsyncStatus async_status;
>       HSTRING hstr, hstr_lang;
> +    DWORD thread_status;
>       HRESULT hr;
>       UINT32 id;
>       LONG ref;
> @@ -945,6 +983,7 @@ static void test_SpeechRecognizer(void)
>           ok(ref == 1, "Got unexpected ref %lu.\n", ref);
>   
>           compilation_handler_create_static(&compilation_handler);
> +        compilation_handler.event_block = NULL;
>           compilation_handler.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL);
>           compilation_handler.thread_id = GetCurrentThreadId();
>   
> @@ -965,9 +1004,8 @@ static void test_SpeechRecognizer(void)
>   
>           hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, &compilation_handler.IAsyncHandler_Compilation_iface);
>           todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
> -        todo_wine check_refcount(&compilation_handler.IAsyncHandler_Compilation_iface, 1);
>   


See below, if you remove the test because it's broken on some windows 
version it'd be better to do it in a dedicated patch to make it clearer.


> -        WaitForSingleObject(compilation_handler.event_finished, INFINITE);
> +        todo_wine ok(!WaitForSingleObject(compilation_handler.event_finished, 1000), "Wait for event_finished failed.\n");
>           CloseHandle(compilation_handler.event_finished);
>   
>           hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, NULL);


I don't understand why you need to change this now? If it is going to 
fail and timeout after you stubbed some method, maybe it'd be better to 
make the change with the stub.


> @@ -976,7 +1014,7 @@ static void test_SpeechRecognizer(void)
>           handler = (void*)0xdeadbeef;
>           hr = IAsyncOperation_SpeechRecognitionCompilationResult_get_Completed(operation, &handler);
>           todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
> -        todo_wine ok(handler == NULL, "Handler had value %p.\n", handler);
> +        todo_wine ok(handler == NULL || broken(handler != NULL), "Handler had value %p.\n", handler); /* Broken on Win10 1507 x32... */
>   
>           hr = IAsyncOperation_SpeechRecognitionCompilationResult_GetResults(operation, &compilation_result);
>           todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);


Maybe this could check that handler value is the same as the handler you 
passed? The condition looks a bit too lax otherwise. It would also 
deserve to be in dedicated patch adding/changing broken results imho.


> @@ -1048,7 +1086,7 @@ static void test_SpeechRecognizer(void)
>           compilation_handler2.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL);
>           compilation_handler2.thread_id = GetCurrentThreadId();
>   
> -        ok(compilation_handler2.event_finished != NULL, "Finished event wasn't created.\n");
> +        todo_wine ok(compilation_handler2.event_finished != NULL, "Finished event wasn't created.\n");
>   
>           hr = ISpeechRecognizer_CompileConstraintsAsync(recognizer, &operation);
>           todo_wine ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr);


The todo_wine here looks wrong?


> @@ -1057,23 +1095,10 @@ static void test_SpeechRecognizer(void)
>           hr = IAsyncOperation_SpeechRecognitionCompilationResult_QueryInterface(operation, &IID_IAsyncInfo, (void **)&info);
>           todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
>   
> -        /* This one can fail, if the async operation had already finished */
> -        compilation_result = (void*)0xdeadbeef;
> -        hr = IAsyncOperation_SpeechRecognitionCompilationResult_GetResults(operation, &compilation_result);
> -        todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "Got unexpected hr %#lx.\n", hr);
> -        todo_wine ok(compilation_result == (void*)0xdeadbeef, "Compilation result had value %p.\n", compilation_result);
> -
> -        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 == Started || async_status == Completed, "Status was %#x.\n", async_status);
> -
> -        IAsyncInfo_Release(info);
> -


Why remove these tests?


>           hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, &compilation_handler2.IAsyncHandler_Compilation_iface);
>           todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
>   
> -        WaitForSingleObject(compilation_handler2.event_finished, INFINITE);
> +        todo_wine ok(!WaitForSingleObject(compilation_handler2.event_finished, 1000), "Wait for event_finished failed.\n");
>           CloseHandle(compilation_handler2.event_finished);
>   


Same as above.


>           async_status = 0xdeadbeef;
> @@ -1081,10 +1106,73 @@ static void test_SpeechRecognizer(void)
>           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);
>   
> +        ref = IAsyncInfo_Release(info);
> +        todo_wine ok(ref == 1, "Got unexpected ref %lu.\n", ref);
> +        ref = IAsyncOperation_SpeechRecognitionCompilationResult_Release(operation);
> +        todo_wine ok(!ref, "Got unexpected ref %lu.\n", ref);
> +
> +        /* Test if AsyncInfo_Close waits for the handler to finish. */
> +        hr = RoActivateInstance(hstr, &inspectable_2);
> +        todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
> +
> +        hr = IInspectable_QueryInterface(inspectable_2, &IID_ISpeechRecognizer, (void **)&recognizer_2);
> +        todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
> +
> +        compilation_handler_create_static(&compilation_handler3);
> +        compilation_handler3.event_block = CreateEventW(NULL, FALSE, FALSE, NULL);
> +        compilation_handler3.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL);
> +        compilation_handler3.thread_id = GetCurrentThreadId();
> +


Do you really need new variables here? I think you should just re-use 
the existing ones, eventually cleaning them up and re-creating if really 
necessary.


> +        todo_wine ok(compilation_handler3.event_finished != NULL, "Finished event wasn't created.\n");
> +


The todo_wine looks wrong here too.


> +        hr = ISpeechRecognizer_CompileConstraintsAsync(recognizer_2, &operation);
> +        todo_wine ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr);
> +
> +        block_param.handler = &compilation_handler3.IAsyncHandler_Compilation_iface;
> +        block_param.operation = operation;
> +        blocked_thread = CreateThread(NULL, 0, async_operation_block_thread, &block_param, 0, NULL);
> +        thread_status = WaitForSingleObject(blocked_thread, 1000);
> +        todo_wine ok(thread_status == WAIT_TIMEOUT || broken(thread_status == WAIT_OBJECT_0), "Wait for block_thread didn't time out.\n"); /* Broken on Win10 1507 x32... */
> +


I think the problem you have here is that sometimes the operation isn't 
completed when you set the handler, and put_Completed doesn't call it 
immediately, and your thread doesn't block in that case.

You probably shouldn't wait for the thread here, and instead only wait 
for it after you've waited on event_finished.


> +        if (thread_status == WAIT_TIMEOUT)
> +        {
> +            todo_wine ok(compilation_handler3.ref == 3, "Got unexpected ref %lu.\n", compilation_handler3.ref);
> +            todo_wine check_refcount(operation, 3);
> +
> +            hr = IAsyncOperation_SpeechRecognitionCompilationResult_QueryInterface(operation, &IID_IAsyncInfo, (void **)&info);
> +            todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
> +
> +            close_param.info = info;
> +            close_thread = CreateThread(NULL, 0, async_operation_close_thread, &close_param, 0, NULL);
> +            todo_wine ok(!WaitForSingleObject(close_thread, 100), "Wait for close_thread failed.\n");
> +


If IAsyncInfo_Close doesn't actually block then you don't need a thread 
to call it, right?


> +            CloseHandle(close_thread);
> +        }
> +
> +        SetEvent(compilation_handler3.event_block);
> +        todo_wine ok(!WaitForSingleObject(compilation_handler3.event_finished, 500), "Wait for event_finished failed.\n");
> +        todo_wine ok(!WaitForSingleObject(blocked_thread, 1000), "Wait for block_thread failed.\n");
> +
> +        CloseHandle(blocked_thread);
> +        CloseHandle(compilation_handler3.event_block);
> +        CloseHandle(compilation_handler3.event_finished);
> +
> +        ref = IAsyncInfo_Release(info);
> +        todo_wine ok(ref == 1, "Got unexpected ref %lu.\n", ref);
> +
>   skip_operation:
>           ref = IAsyncOperation_SpeechRecognitionCompilationResult_Release(operation);
>           ok(!ref, "Got unexpected ref %lu.\n", ref);
>   
> +        if (recognizer_2)
> +        {
> +            ref = ISpeechRecognizer_Release(recognizer_2);
> +            ok(ref == 1, "Got unexpected ref %lu.\n", ref);
> +
> +            ref = IInspectable_Release(inspectable_2);
> +            ok(!ref, "Got unexpected ref %lu.\n", ref);
> +        }
> +
>           ref = ISpeechContinuousRecognitionSession_Release(session);
>           ok(ref == 1, "Got unexpected ref %lu.\n", ref);
>   


Cheers,
-- 
Rémi Bernon <rbernon at codeweavers.com>



More information about the wine-devel mailing list