quartz/tests: Skip some tests on NT4.
[wine] / dlls / quartz / tests / avisplitter.c
1 /*
2  * Unit tests for the avi splitter functions
3  *
4  * Copyright (C) 2007 Google (Lei Zhang)
5  * Copyright (C) 2008 Google (Maarten Lankhorst)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23
24 #include "wine/test.h"
25 #include "dshow.h"
26 #include "tlhelp32.h"
27
28 static HANDLE (WINAPI *pCreateToolhelp32Snapshot)(DWORD, DWORD);
29 static BOOL (WINAPI *pThread32First)(HANDLE, LPTHREADENTRY32);
30 static BOOL (WINAPI *pThread32Next)(HANDLE, LPTHREADENTRY32);
31
32 static IUnknown *pAviSplitter = NULL;
33
34 static int count_threads(void)
35 {
36     THREADENTRY32 te;
37     int threads;
38     HANDLE h;
39
40     h = pCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
41     te.dwSize = sizeof(te);
42
43     if (h == INVALID_HANDLE_VALUE)
44         return -1;
45
46     pThread32First(h, &te);
47     if (te.th32OwnerProcessID == GetCurrentProcessId())
48         threads = 1;
49     else
50         threads = 0;
51
52     while (pThread32Next(h, &te))
53         if (te.th32OwnerProcessID == GetCurrentProcessId())
54             ++threads;
55
56     CloseHandle(h);
57     return threads;
58 }
59
60 static int create_avisplitter(void)
61 {
62     HRESULT hr;
63
64     hr = CoCreateInstance(&CLSID_AviSplitter, NULL, CLSCTX_INPROC_SERVER,
65                           &IID_IUnknown, (LPVOID*)&pAviSplitter);
66     return (hr == S_OK && pAviSplitter != NULL);
67 }
68
69 static void release_avisplitter(void)
70 {
71     HRESULT hr;
72
73     Sleep(1000);
74     hr = IUnknown_Release(pAviSplitter);
75
76     /* Looks like wine has a reference leak somewhere on test_threads tests,
77      * it passes in windows
78      */
79     ok(hr == 0, "IUnknown_Release failed with %d\n", (INT)hr);
80
81     while (hr > 0)
82         hr = IUnknown_Release(pAviSplitter);
83     pAviSplitter = NULL;
84 }
85
86 static void test_query_interface(void)
87 {
88     HRESULT hr;
89     ULONG ref;
90     IUnknown *iface= NULL;
91
92     hr = IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter,
93         (void**)&iface);
94
95     ok(hr == S_OK,
96         "IID_IBaseFilter should exist, got %08x!\n", GetLastError());
97     if (hr == S_OK)
98     {
99         ref = IUnknown_Release(iface);
100         iface = NULL;
101         ok(ref == 1, "Reference is %u, expected 1\n", ref);
102     }
103
104     hr = IUnknown_QueryInterface(pAviSplitter, &IID_IMediaSeeking,
105         (void**)&iface);
106     if (hr == S_OK)
107         ref = IUnknown_Release(iface);
108     iface = NULL;
109     todo_wine ok(hr == E_NOINTERFACE,
110         "Query for IMediaSeeking returned: %08x\n", hr);
111
112 /* These interfaces should not be present:
113     IID_IKsPropertySet, IID_IMediaPosition, IID_IQualityControl, IID_IQualProp
114 */
115 }
116
117 static void test_pin(IPin *pin)
118 {
119     IMemInputPin *mpin = NULL;
120
121     IPin_QueryInterface(pin, &IID_IMemInputPin, (void **)&mpin);
122
123     ok(mpin == NULL, "IMemInputPin found!\n");
124     if (mpin)
125         IMemInputPin_Release(mpin);
126     /* TODO */
127 }
128
129 static void test_basefilter(void)
130 {
131     IEnumPins *pin_enum = NULL;
132     IBaseFilter *base = NULL;
133     IPin *pins[2];
134     ULONG ref;
135     HRESULT hr;
136
137     IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter, (void *)&base);
138     if (base == NULL)
139     {
140         /* test_query_interface handles this case */
141         skip("No IBaseFilter\n");
142         return;
143     }
144
145     hr = IBaseFilter_EnumPins(base, NULL);
146     ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
147
148     hr= IBaseFilter_EnumPins(base, &pin_enum);
149     ok(hr == S_OK, "hr = %08x and not S_OK\n", hr);
150
151     hr = IEnumPins_Next(pin_enum, 1, NULL, NULL);
152     ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
153
154     hr = IEnumPins_Next(pin_enum, 2, pins, NULL);
155     ok(hr == E_INVALIDARG, "hr = %08x and not E_INVALIDARG\n", hr);
156
157     pins[0] = (void *)0xdead;
158     pins[1] = (void *)0xdeed;
159
160     hr = IEnumPins_Next(pin_enum, 2, pins, &ref);
161     ok(hr == S_FALSE, "hr = %08x instead of S_FALSE\n", hr);
162     ok(pins[0] != (void *)0xdead && pins[0] != NULL,
163         "pins[0] = %p\n", pins[0]);
164     if (pins[0] != (void *)0xdead && pins[0] != NULL)
165     {
166         test_pin(pins[0]);
167         IPin_Release(pins[0]);
168     }
169
170     ok(pins[1] == (void *)0xdeed, "pins[1] = %p\n", pins[1]);
171
172     ref = IEnumPins_Release(pin_enum);
173     ok(ref == 0, "ref is %u and not 0!\n", ref);
174
175     IBaseFilter_Release(base);
176 }
177
178 static const WCHAR wfile[] = {'t','e','s','t','.','a','v','i',0};
179 static const char afile[] = "test.avi";
180
181 /* This test doesn't use the quartz filtergraph because it makes it impossible
182  * to be certain that a thread is really one owned by the avi splitter
183  * A lot of the decoder filters will also have their own thread, and windows'
184  * filtergraph has a separate thread for start/stop/seeking requests.
185  * By avoiding the filtergraph all together and connecting streams directly to
186  * the null renderer I am sure that this is not the case here.
187  */
188 static void test_threads()
189 {
190     IFileSourceFilter *pfile = NULL;
191     IBaseFilter *preader = NULL, *pavi = NULL;
192     IEnumPins *enumpins = NULL;
193     IPin *filepin = NULL, *avipin = NULL;
194     HRESULT hr;
195     int baselevel, curlevel, expected;
196     HANDLE file = NULL;
197     PIN_DIRECTION dir = PINDIR_OUTPUT;
198     char buffer[13];
199     DWORD readbytes;
200     FILTER_STATE state;
201
202     /* We need another way of counting threads on NT4. Skip these tests (for now) */
203     if (!pCreateToolhelp32Snapshot || !pThread32First || !pThread32Next)
204     {
205         win_skip("Needed thread functions are not available (NT4)\n");
206         return;
207     }
208
209     /* Before doing anything */
210     baselevel = count_threads();
211     expected = 1;
212     ok(baselevel == expected,
213         "Basic amount of threads should be %d, not %d!\n", expected, baselevel);
214
215     file = CreateFileW(wfile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
216         NULL, OPEN_EXISTING, 0, NULL);
217     if (file == INVALID_HANDLE_VALUE)
218     {
219         skip("Could not read test file \"%s\", skipping test\n", afile);
220         return;
221     }
222
223     memset(buffer, 0, 13);
224     readbytes = 12;
225     ReadFile(file, buffer, readbytes, &readbytes, NULL);
226     CloseHandle(file);
227     if (strncmp(buffer, "RIFF", 4) || strcmp(buffer + 8, "AVI "))
228     {
229         skip("%s is not an avi riff file, not doing the avi splitter test\n",
230             afile);
231         return;
232     }
233
234     hr = IUnknown_QueryInterface(pAviSplitter, &IID_IFileSourceFilter,
235         (void **)&pfile);
236     ok(hr == E_NOINTERFACE,
237         "Avi splitter returns unexpected error: %08x\n", hr);
238     if (pfile)
239         IUnknown_Release(pfile);
240     pfile = NULL;
241
242     hr = CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
243         &IID_IBaseFilter, (LPVOID*)&preader);
244     ok(hr == S_OK, "Could not create asynchronous reader: %08x\n", hr);
245     if (hr != S_OK)
246         goto fail;
247
248     hr = IUnknown_QueryInterface(preader, &IID_IFileSourceFilter,
249         (void**)&pfile);
250     ok(hr == S_OK, "Could not get IFileSourceFilter: %08x\n", hr);
251     if (hr != S_OK)
252         goto fail;
253
254     hr = IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter,
255         (void**)&pavi);
256     ok(hr == S_OK, "Could not get base filter: %08x\n", hr);
257     if (hr != S_OK)
258         goto fail;
259
260     hr = IFileSourceFilter_Load(pfile, wfile, NULL);
261     if (hr != S_OK)
262     {
263         trace("Could not load file\n");
264         goto fail;
265     }
266
267     hr = IBaseFilter_EnumPins(preader, &enumpins);
268     ok(hr == S_OK, "No enumpins: %08x\n", hr);
269     if (hr != S_OK)
270         goto fail;
271
272     hr = IEnumPins_Next(enumpins, 1, &filepin, NULL);
273     ok(hr == S_OK, "No pin: %08x\n", hr);
274     if (hr != S_OK)
275         goto fail;
276
277     IUnknown_Release(enumpins);
278     enumpins = NULL;
279
280     hr = IBaseFilter_EnumPins(pavi, &enumpins);
281     ok(hr == S_OK, "No enumpins: %08x\n", hr);
282     if (hr != S_OK)
283         goto fail;
284
285     hr = IEnumPins_Next(enumpins, 1, &avipin, NULL);
286     ok(hr == S_OK, "No pin: %08x\n", hr);
287     if (hr != S_OK)
288         goto fail;
289
290     curlevel = count_threads();
291     ok(curlevel == baselevel,
292         "Amount of threads should be %d not %d\n", baselevel, curlevel);
293
294     hr = IPin_Connect(filepin, avipin, NULL);
295     ok(hr == S_OK, "Could not connect: %08x\n", hr);
296     if (hr != S_OK)
297         goto fail;
298
299     expected = 1 + baselevel;
300     curlevel = count_threads();
301     ok(curlevel == expected,
302         "Amount of threads should be %d not %d\n", expected, curlevel);
303
304     IUnknown_Release(avipin);
305     avipin = NULL;
306
307     IEnumPins_Reset(enumpins);
308
309     /* Windows puts the pins in the order: Outputpins - Inputpin,
310      * wine does the reverse, just don't test it for now
311      * Hate to admit it, but windows way makes more sense
312      */
313     while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
314     {
315         ok(hr == S_OK, "hr: %08x\n", hr);
316         IPin_QueryDirection(avipin, &dir);
317         if (dir == PINDIR_OUTPUT)
318         {
319             /* Well, connect it to a null renderer! */
320             IBaseFilter *pnull = NULL;
321             IEnumPins *nullenum = NULL;
322             IPin *nullpin = NULL;
323
324             hr = CoCreateInstance(&CLSID_NullRenderer, NULL,
325                 CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&pnull);
326             ok(hr == S_OK, "Could not create null renderer: %08x\n", hr);
327             if (hr != S_OK)
328                 break;
329
330             IBaseFilter_EnumPins(pnull, &nullenum);
331             IEnumPins_Next(nullenum, 1, &nullpin, NULL);
332             IEnumPins_Release(nullenum);
333             IPin_QueryDirection(nullpin, &dir);
334
335             hr = IPin_Connect(avipin, nullpin, NULL);
336             ok(hr == S_OK, "Failed to connect output pin: %08x\n", hr);
337             IPin_Release(nullpin);
338             if (hr != S_OK)
339             {
340                 IBaseFilter_Release(pnull);
341                 break;
342             }
343             IBaseFilter_Run(pnull, 0);
344             ++expected;
345         }
346
347         IUnknown_Release(avipin);
348         avipin = NULL;
349     }
350
351     if (avipin)
352         IUnknown_Release(avipin);
353     avipin = NULL;
354
355     if (hr != S_OK)
356         goto fail2;
357     /* At this point there is a minimalistic connected avi splitter that can
358      * Be used for all sorts of source filter tests, however that still needs
359      * to be written at a later time.
360      *
361      * Interesting tests:
362      * - Can you disconnect an output pin while running?
363      *   Expecting: Yes
364      * - Can you disconnect the pullpin while running?
365      *   Expecting: No
366      * - Is the reference count incremented during playback or when connected?
367      *   Does this happen once for every output pin? Or is there something else
368      *   going on.
369      *   Expecting: You tell me
370      */
371
372     IBaseFilter_Run(preader, 0);
373     IBaseFilter_Run(pavi, 0);
374     IBaseFilter_GetState(pavi, INFINITE, &state);
375
376     curlevel = count_threads();
377     /* On a 2 stream filter, there are 4 or 5 threads (seems to be 5)
378      * One is the thread we are in. That leaves 3 or 4 for other dark purposes
379      * Wine is 1 thread short!
380      */
381     ok(curlevel == expected || curlevel == expected + 1,
382         "Amount of threads should be %d not %d\n", expected, curlevel);
383
384     IBaseFilter_Pause(pavi);
385     IBaseFilter_Pause(preader);
386     IBaseFilter_Stop(pavi);
387     IBaseFilter_Stop(preader);
388     IBaseFilter_GetState(pavi, INFINITE, &state);
389     IBaseFilter_GetState(preader, INFINITE, &state);
390
391 fail2:
392     IEnumPins_Reset(enumpins);
393     while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
394     {
395         IPin *to = NULL;
396
397         IPin_QueryDirection(avipin, &dir);
398         IPin_ConnectedTo(avipin, &to);
399         if (to)
400         {
401             IPin_Release(to);
402
403             if (dir == PINDIR_OUTPUT)
404             {
405                 PIN_INFO info;
406                 IPin_QueryPinInfo(to, &info);
407
408                 /* Release twice: Once normal, second from the
409                  * previous while loop
410                  */
411                 IBaseFilter_Stop(info.pFilter);
412                 IPin_Disconnect(to);
413                 IPin_Disconnect(avipin);
414                 IBaseFilter_Release(info.pFilter);
415                 IBaseFilter_Release(info.pFilter);
416             }
417             else
418             {
419                 IPin_Disconnect(to);
420                 IPin_Disconnect(avipin);
421             }
422         }
423         IPin_Release(avipin);
424         avipin = NULL;
425     }
426
427 fail:
428     if (hr != S_OK)
429         skip("Prerequisites not matched, skipping remainder of test\n");
430     if (enumpins)
431         IUnknown_Release(enumpins);
432
433     if (avipin)
434         IUnknown_Release(avipin);
435     if (filepin)
436     {
437         IPin *to = NULL;
438
439         IPin_ConnectedTo(filepin, &to);
440         if (to)
441         {
442             IPin_Disconnect(filepin);
443             IPin_Disconnect(to);
444         }
445         IUnknown_Release(filepin);
446     }
447
448     if (preader)
449         IUnknown_Release(preader);
450     if (pavi)
451         IUnknown_Release(pavi);
452     if (pfile)
453         IUnknown_Release(pfile);
454
455     ok(baselevel == 1,
456         "Basic amount of threads should be %d, not %d!\n", 1, baselevel);
457 }
458
459 START_TEST(avisplitter)
460 {
461     HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
462
463     pCreateToolhelp32Snapshot = (void*)GetProcAddress(kernel32, "CreateToolhelp32Snapshot");
464     pThread32First = (void*)GetProcAddress(kernel32, "Thread32First");
465     pThread32Next = (void*)GetProcAddress(kernel32, "Thread32Next");
466
467     CoInitialize(NULL);
468
469     if (!create_avisplitter())
470     {
471         skip("Could not create avisplitter\n");
472         return;
473     }
474
475     test_query_interface();
476     test_basefilter();
477     test_threads();
478
479     release_avisplitter();
480 }