[PATCH v2 1/5] wmvcore/tests: Add tests for asynchronous reader streaming.

Zebediah Figura zfigura at codeweavers.com
Tue Nov 2 23:51:44 CDT 2021


Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 dlls/wmvcore/tests/wmvcore.c | 291 ++++++++++++++++++++++++++++++++++-
 1 file changed, 283 insertions(+), 8 deletions(-)

diff --git a/dlls/wmvcore/tests/wmvcore.c b/dlls/wmvcore/tests/wmvcore.c
index eae320c4d2e..2a337648761 100644
--- a/dlls/wmvcore/tests/wmvcore.c
+++ b/dlls/wmvcore/tests/wmvcore.c
@@ -420,6 +420,14 @@ static const IStreamVtbl stream_vtbl =
     stream_Clone,
 };
 
+static void teststream_init(struct teststream *stream, HANDLE file)
+{
+    memset(stream, 0, sizeof(*stream));
+    stream->IStream_iface.lpVtbl = &stream_vtbl;
+    stream->refcount = 1;
+    stream->file = file;
+}
+
 static void test_reader_attributes(IWMProfile *profile)
 {
     WORD size, stream_number, ret_stream_number;
@@ -578,7 +586,7 @@ static void test_sync_reader_streaming(void)
     WORD stream_numbers[2], stream_number;
     IWMStreamConfig *config, *config2;
     bool eos[2] = {0}, first = true;
-    struct teststream stream = {{0}};
+    struct teststream stream;
     ULONG i, j, count, ref;
     IWMSyncReader *reader;
     IWMProfile *profile;
@@ -592,9 +600,7 @@ static void test_sync_reader_streaming(void)
     file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
     ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u.\n", debugstr_w(file), GetLastError());
 
-    stream.IStream_iface.lpVtbl = &stream_vtbl;
-    stream.refcount = 1;
-    stream.file = file;
+    teststream_init(&stream, file);
 
     hr = WMCreateSyncReader(NULL, 0, &reader);
     ok(hr == S_OK, "Got hr %#x.\n", hr);
@@ -816,7 +822,7 @@ static void test_sync_reader_types(void)
     bool got_video = false, got_audio = false;
     DWORD size, ret_size, output_number;
     WORD stream_number, stream_number2;
-    struct teststream stream = {{0}};
+    struct teststream stream;
     IWMStreamConfig *config;
     ULONG count, ref, i, j;
     IWMSyncReader *reader;
@@ -829,9 +835,7 @@ static void test_sync_reader_types(void)
     file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
     ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u.\n", debugstr_w(file), GetLastError());
 
-    stream.IStream_iface.lpVtbl = &stream_vtbl;
-    stream.refcount = 1;
-    stream.file = file;
+    teststream_init(&stream, file);
 
     hr = WMCreateSyncReader(NULL, 0, &reader);
     ok(hr == S_OK, "Got hr %#x.\n", hr);
@@ -1051,6 +1055,276 @@ static void test_sync_reader_file(void)
     ok(ret, "Failed to delete %s, error %u.\n", debugstr_w(filename), GetLastError());
 }
 
+struct callback
+{
+    IWMReaderCallback IWMReaderCallback_iface;
+    LONG refcount;
+    HANDLE got_opened, got_stopped, eof_event;
+    unsigned int got_closed, got_started, got_sample, got_end_of_streaming, got_eof;
+};
+
+static struct callback *impl_from_IWMReaderCallback(IWMReaderCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct callback, IWMReaderCallback_iface);
+}
+
+static HRESULT WINAPI callback_QueryInterface(IWMReaderCallback *iface, REFIID iid, void **out)
+{
+    if (winetest_debug > 1)
+        trace("%04x: IWMReaderCallback::QueryInterface(%s)\n", GetCurrentThreadId(), debugstr_guid(iid));
+
+    if (!IsEqualGUID(iid, &IID_IWMReaderCallbackAdvanced) && !IsEqualGUID(iid, &IID_IWMCredentialCallback))
+        ok(0, "Unexpected IID %s.\n", debugstr_guid(iid));
+
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI callback_AddRef(IWMReaderCallback *iface)
+{
+    struct callback *callback = impl_from_IWMReaderCallback(iface);
+
+    return InterlockedIncrement(&callback->refcount);
+}
+
+static ULONG WINAPI callback_Release(IWMReaderCallback *iface)
+{
+    struct callback *callback = impl_from_IWMReaderCallback(iface);
+
+    return InterlockedDecrement(&callback->refcount);
+}
+
+static HRESULT WINAPI callback_OnStatus(IWMReaderCallback *iface, WMT_STATUS status,
+        HRESULT hr, WMT_ATTR_DATATYPE type, BYTE *value, void *context)
+{
+    struct callback *callback = impl_from_IWMReaderCallback(iface);
+
+    if (winetest_debug > 1)
+        trace("%u: %04x: IWMReaderCallback::OnStatus(status %u, hr %#x, type %#x, value %p)\n",
+                GetTickCount(), GetCurrentThreadId(), status, hr, type, value);
+
+    switch (status)
+    {
+        case WMT_OPENED:
+            ok(type == WMT_TYPE_DWORD, "Got type %#x.\n", type);
+            ok(!*(DWORD *)value, "Got value %#x.\n", *(DWORD *)value);
+            ok(context == (void *)0xdeadbeef, "Got unexpected context %p.\n", context);
+            SetEvent(callback->got_opened);
+            break;
+
+        case WMT_STARTED:
+            ok(type == WMT_TYPE_DWORD, "Got type %#x.\n", type);
+            ok(!*(DWORD *)value, "Got value %#x.\n", *(DWORD *)value);
+            ok(context == (void *)0xfacade, "Got unexpected context %p.\n", context);
+            ++callback->got_started;
+            break;
+
+        case WMT_STOPPED:
+            ok(type == WMT_TYPE_DWORD, "Got type %#x.\n", type);
+            ok(!*(DWORD *)value, "Got value %#x.\n", *(DWORD *)value);
+            ok(context == (void *)0xfacade, "Got unexpected context %p.\n", context);
+            SetEvent(callback->got_stopped);
+            break;
+
+        case WMT_CLOSED:
+            ok(type == WMT_TYPE_DWORD, "Got type %#x.\n", type);
+            ok(!*(DWORD *)value, "Got value %#x.\n", *(DWORD *)value);
+            todo_wine ok(context == (void *)0xfacade, "Got unexpected context %p.\n", context);
+            ++callback->got_closed;
+            break;
+
+        case WMT_END_OF_STREAMING:
+            ok(type == WMT_TYPE_DWORD, "Got type %#x.\n", type);
+            ok(!*(DWORD *)value, "Got value %#x.\n", *(DWORD *)value);
+            ok(context == (void *)0xfacade, "Got unexpected context %p.\n", context);
+            ok(!callback->got_eof, "Got %u WMT_EOF callbacks.\n", callback->got_eof);
+            ++callback->got_end_of_streaming;
+            break;
+
+        case WMT_EOF:
+            ok(type == WMT_TYPE_DWORD, "Got type %#x.\n", type);
+            ok(!*(DWORD *)value, "Got value %#x.\n", *(DWORD *)value);
+            ok(context == (void *)0xfacade, "Got unexpected context %p.\n", context);
+            ok(callback->got_sample > 0, "Got no samples.\n");
+            ok(callback->got_end_of_streaming == 1, "Got %u WMT_END_OF_STREAMING callbacks.\n",
+                    callback->got_end_of_streaming);
+            ++callback->got_eof;
+            SetEvent(callback->eof_event);
+            break;
+
+        /* Not sent when not using IWMReaderAdvanced::DeliverTime(). */
+        case WMT_END_OF_SEGMENT:
+            ok(type == WMT_TYPE_QWORD, "Got type %#x.\n", type);
+            ok(*(QWORD *)value == 3000, "Got value %#x.\n", *(DWORD *)value);
+            ok(context == (void *)0xfacade, "Got unexpected context %p.\n", context);
+            ok(callback->got_sample > 0, "Got no samples.\n");
+            ok(callback->got_eof == 1, "Got %u WMT_EOF callbacks.\n", callback->got_eof);
+            break;
+
+        default:
+            ok(0, "Unexpected status %#x.\n", status);
+    }
+
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    return S_OK;
+}
+
+static HRESULT WINAPI callback_OnSample(IWMReaderCallback *iface, DWORD output,
+        QWORD time, QWORD duration, DWORD flags, INSSBuffer *sample, void *context)
+{
+    struct callback *callback = impl_from_IWMReaderCallback(iface);
+    HRESULT hr;
+    DWORD size;
+    BYTE *data;
+
+    if (winetest_debug > 1)
+        trace("%u: %04x: IWMReaderCallback::OnSample(output %u, time %I64u, duration %I64u, flags %#x)\n",
+                GetTickCount(), GetCurrentThreadId(), output, time, duration, flags);
+
+    ok(context == (void *)0xfacade, "Got unexpected context %p.\n", context);
+
+    hr = INSSBuffer_GetBufferAndLength(sample, &data, &size);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+
+    ok(callback->got_started > 0, "Got %u WMT_STARTED callbacks.\n", callback->got_started);
+    ok(!callback->got_eof, "Got %u WMT_EOF callbacks.\n", callback->got_eof);
+    ++callback->got_sample;
+
+    return S_OK;
+}
+
+static const IWMReaderCallbackVtbl callback_vtbl =
+{
+    callback_QueryInterface,
+    callback_AddRef,
+    callback_Release,
+    callback_OnStatus,
+    callback_OnSample,
+};
+
+static void callback_init(struct callback *callback)
+{
+    memset(callback, 0, sizeof(*callback));
+    callback->IWMReaderCallback_iface.lpVtbl = &callback_vtbl;
+    callback->refcount = 1;
+    callback->got_opened = CreateEventW(NULL, FALSE, FALSE, NULL);
+    callback->got_stopped = CreateEventW(NULL, FALSE, FALSE, NULL);
+    callback->eof_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+}
+
+static void callback_cleanup(struct callback *callback)
+{
+    CloseHandle(callback->got_opened);
+    CloseHandle(callback->got_stopped);
+    CloseHandle(callback->eof_event);
+}
+
+static void test_async_reader_streaming(void)
+{
+    const WCHAR *filename = load_resource(L"test.wmv");
+    IWMReaderAdvanced2 *advanced;
+    struct teststream stream;
+    struct callback callback;
+    IWMStreamConfig *config;
+    WORD stream_numbers[2];
+    IWMProfile *profile;
+    ULONG i, count, ref;
+    IWMReader *reader;
+    HANDLE file;
+    HRESULT hr;
+    BOOL ret;
+
+    file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u.\n", debugstr_w(file), GetLastError());
+
+    teststream_init(&stream, file);
+    callback_init(&callback);
+
+    hr = WMCreateReader(NULL, 0, &reader);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    IWMReader_QueryInterface(reader, &IID_IWMProfile, (void **)&profile);
+    IWMReader_QueryInterface(reader, &IID_IWMReaderAdvanced2, (void **)&advanced);
+
+    hr = IWMReaderAdvanced2_OpenStream(advanced, &stream.IStream_iface, &callback.IWMReaderCallback_iface, (void **)0xdeadbeef);
+    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+    if (hr != S_OK)
+        goto out;
+    ok(stream.refcount > 1, "Got refcount %d.\n", stream.refcount);
+    ok(callback.refcount > 1, "Got refcount %d.\n", callback.refcount);
+    ret = WaitForSingleObject(callback.got_opened, 1000);
+    ok(!ret, "Wait timed out.\n");
+
+    count = 0xdeadbeef;
+    hr = IWMReader_GetOutputCount(reader, &count);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
+    todo_wine ok(count == 2, "Got count %u.\n", count);
+
+    for (i = 0; i < 2; ++i)
+    {
+        hr = IWMProfile_GetStream(profile, i, &config);
+        ok(hr == S_OK, "Got hr %#x.\n", hr);
+
+        stream_numbers[i] = 0xdead;
+        hr = IWMStreamConfig_GetStreamNumber(config, &stream_numbers[i]);
+        ok(hr == S_OK, "Got hr %#x.\n", hr);
+        ok(stream_numbers[i] == i + 1, "Got stream number %u.\n", stream_numbers[i]);
+
+        ref = IWMStreamConfig_Release(config);
+        ok(!ref, "Got outstanding refcount %d.\n", ref);
+    }
+
+    hr = IWMReader_Start(reader, 0, 0, 1.0f, (void *)0xfacade);
+    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+
+    if (hr == S_OK)
+    {
+        /* By default the reader will time itself, and attempt to deliver samples
+         * according to their presentation time. Call DeliverTime with the file
+         * duration in order to request all samples as fast as possible. */
+        hr = IWMReaderAdvanced2_DeliverTime(advanced, 3000 * 10000);
+        ok(hr == E_UNEXPECTED, "Got hr %#x.\n", hr);
+        hr = IWMReaderAdvanced2_SetUserProvidedClock(advanced, TRUE);
+        ok(hr == S_OK, "Got hr %#x.\n", hr);
+        hr = IWMReaderAdvanced2_DeliverTime(advanced, 3000 * 10000);
+        ok(hr == S_OK, "Got hr %#x.\n", hr);
+
+        ret = WaitForSingleObject(callback.eof_event, 1000);
+        ok(!ret, "Wait timed out.\n");
+        ok(callback.got_eof == 1, "Got %u WMT_EOF callbacks.\n", callback.got_eof);
+
+        hr = IWMReader_Stop(reader);
+        ok(hr == S_OK, "Got hr %#x.\n", hr);
+        ret = WaitForSingleObject(callback.got_stopped, 1000);
+        ok(!ret, "Wait timed out.\n");
+
+        hr = IWMReader_Stop(reader);
+        ok(hr == S_OK, "Got hr %#x.\n", hr);
+        ret = WaitForSingleObject(callback.got_stopped, 1000);
+        ok(!ret, "Wait timed out.\n");
+    }
+
+    test_reader_attributes(profile);
+
+    hr = IWMReader_Close(reader);
+    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+    todo_wine ok(callback.got_closed == 1, "Got %u WMT_CLOSED callbacks.\n", callback.got_closed);
+    ok(callback.refcount == 1, "Got outstanding refcount %d.\n", callback.refcount);
+    callback_cleanup(&callback);
+
+out:
+    ok(stream.refcount == 1, "Got outstanding refcount %d.\n", stream.refcount);
+    CloseHandle(stream.file);
+    ret = DeleteFileW(filename);
+    ok(ret, "Failed to delete %s, error %u.\n", debugstr_w(filename), GetLastError());
+
+    hr = IWMReader_Close(reader);
+    todo_wine ok(hr == NS_E_INVALID_REQUEST, "Got hr %#x.\n", hr);
+
+    IWMReaderAdvanced2_Release(advanced);
+    IWMProfile_Release(profile);
+    ref = IWMReader_Release(reader);
+    ok(!ref, "Got outstanding refcount %d.\n", ref);
+}
+
 START_TEST(wmvcore)
 {
     HRESULT hr;
@@ -1070,6 +1344,7 @@ START_TEST(wmvcore)
     test_sync_reader_streaming();
     test_sync_reader_types();
     test_sync_reader_file();
+    test_async_reader_streaming();
 
     CoUninitialize();
 }
-- 
2.33.0




More information about the wine-devel mailing list