ieframe: Moved InternetShortcut implementation to ieframe.dll.
[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 #define TEST_INTERFACE(riid,expected) do { \
93     hr = IUnknown_QueryInterface(pAviSplitter, &riid, (void**)&iface); \
94     ok( hr == expected, #riid" should %s got %08X\n", expected==S_OK ? "exist" : "not be present", GetLastError() ); \
95     if (hr == S_OK) { \
96         ref = IUnknown_Release(iface); \
97         ok(ref == 1, "Reference is %u, expected 1\n", ref); \
98     } \
99     iface = NULL; \
100     } while(0)
101
102     TEST_INTERFACE(IID_IBaseFilter,S_OK);
103     TEST_INTERFACE(IID_IMediaSeeking,E_NOINTERFACE);
104     TEST_INTERFACE(IID_IKsPropertySet,E_NOINTERFACE);
105     TEST_INTERFACE(IID_IMediaPosition,E_NOINTERFACE);
106     TEST_INTERFACE(IID_IQualityControl,E_NOINTERFACE);
107     TEST_INTERFACE(IID_IQualProp,E_NOINTERFACE);
108 #undef TEST_INTERFACE
109 }
110
111 static void test_pin(IPin *pin)
112 {
113     IMemInputPin *mpin = NULL;
114
115     IPin_QueryInterface(pin, &IID_IMemInputPin, (void **)&mpin);
116
117     ok(mpin == NULL, "IMemInputPin found!\n");
118     if (mpin)
119         IMemInputPin_Release(mpin);
120     /* TODO */
121 }
122
123 static void test_basefilter(void)
124 {
125     IEnumPins *pin_enum = NULL;
126     IBaseFilter *base = NULL;
127     IPin *pins[2];
128     ULONG ref;
129     HRESULT hr;
130
131     IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter, (void *)&base);
132     if (base == NULL)
133     {
134         /* test_query_interface handles this case */
135         skip("No IBaseFilter\n");
136         return;
137     }
138
139     hr = IBaseFilter_EnumPins(base, NULL);
140     ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
141
142     hr= IBaseFilter_EnumPins(base, &pin_enum);
143     ok(hr == S_OK, "hr = %08x and not S_OK\n", hr);
144
145     hr = IEnumPins_Next(pin_enum, 1, NULL, NULL);
146     ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
147
148     hr = IEnumPins_Next(pin_enum, 2, pins, NULL);
149     ok(hr == E_INVALIDARG, "hr = %08x and not E_INVALIDARG\n", hr);
150
151     pins[0] = (void *)0xdead;
152     pins[1] = (void *)0xdeed;
153
154     hr = IEnumPins_Next(pin_enum, 2, pins, &ref);
155     ok(hr == S_FALSE, "hr = %08x instead of S_FALSE\n", hr);
156     ok(pins[0] != (void *)0xdead && pins[0] != NULL,
157         "pins[0] = %p\n", pins[0]);
158     if (pins[0] != (void *)0xdead && pins[0] != NULL)
159     {
160         test_pin(pins[0]);
161         IPin_Release(pins[0]);
162     }
163
164     ok(pins[1] == (void *)0xdeed, "pins[1] = %p\n", pins[1]);
165
166     ref = IEnumPins_Release(pin_enum);
167     ok(ref == 0, "ref is %u and not 0!\n", ref);
168
169     IBaseFilter_Release(base);
170 }
171
172 static const WCHAR wfile[] = {'t','e','s','t','.','a','v','i',0};
173 static const char afile[] = "test.avi";
174
175 /* This test doesn't use the quartz filtergraph because it makes it impossible
176  * to be certain that a thread is really one owned by the avi splitter
177  * A lot of the decoder filters will also have their own thread, and windows'
178  * filtergraph has a separate thread for start/stop/seeking requests.
179  * By avoiding the filtergraph all together and connecting streams directly to
180  * the null renderer I am sure that this is not the case here.
181  */
182 static void test_threads(void)
183 {
184     IFileSourceFilter *pfile = NULL;
185     IBaseFilter *preader = NULL, *pavi = NULL;
186     IEnumPins *enumpins = NULL;
187     IPin *filepin = NULL, *avipin = NULL;
188     HRESULT hr;
189     int baselevel, curlevel, expected;
190     HANDLE file = NULL;
191     PIN_DIRECTION dir = PINDIR_OUTPUT;
192     char buffer[13];
193     DWORD readbytes;
194     FILTER_STATE state;
195
196     /* We need another way of counting threads on NT4. Skip these tests (for now) */
197     if (!pCreateToolhelp32Snapshot || !pThread32First || !pThread32Next)
198     {
199         win_skip("Needed thread functions are not available (NT4)\n");
200         return;
201     }
202
203     /* Before doing anything (number of threads at the start differs per OS) */
204     baselevel = count_threads();
205
206     file = CreateFileW(wfile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
207         NULL, OPEN_EXISTING, 0, NULL);
208     if (file == INVALID_HANDLE_VALUE)
209     {
210         skip("Could not read test file \"%s\", skipping test\n", afile);
211         return;
212     }
213
214     memset(buffer, 0, 13);
215     readbytes = 12;
216     ReadFile(file, buffer, readbytes, &readbytes, NULL);
217     CloseHandle(file);
218     if (strncmp(buffer, "RIFF", 4) || strcmp(buffer + 8, "AVI "))
219     {
220         skip("%s is not an avi riff file, not doing the avi splitter test\n",
221             afile);
222         return;
223     }
224
225     hr = IUnknown_QueryInterface(pAviSplitter, &IID_IFileSourceFilter,
226         (void **)&pfile);
227     ok(hr == E_NOINTERFACE,
228         "Avi splitter returns unexpected error: %08x\n", hr);
229     if (pfile)
230         IUnknown_Release(pfile);
231     pfile = NULL;
232
233     hr = CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
234         &IID_IBaseFilter, (LPVOID*)&preader);
235     ok(hr == S_OK, "Could not create asynchronous reader: %08x\n", hr);
236     if (hr != S_OK)
237         goto fail;
238
239     hr = IUnknown_QueryInterface(preader, &IID_IFileSourceFilter,
240         (void**)&pfile);
241     ok(hr == S_OK, "Could not get IFileSourceFilter: %08x\n", hr);
242     if (hr != S_OK)
243         goto fail;
244
245     hr = IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter,
246         (void**)&pavi);
247     ok(hr == S_OK, "Could not get base filter: %08x\n", hr);
248     if (hr != S_OK)
249         goto fail;
250
251     hr = IFileSourceFilter_Load(pfile, wfile, NULL);
252     if (hr != S_OK)
253     {
254         trace("Could not load file\n");
255         goto fail;
256     }
257
258     hr = IBaseFilter_EnumPins(preader, &enumpins);
259     ok(hr == S_OK, "No enumpins: %08x\n", hr);
260     if (hr != S_OK)
261         goto fail;
262
263     hr = IEnumPins_Next(enumpins, 1, &filepin, NULL);
264     ok(hr == S_OK, "No pin: %08x\n", hr);
265     if (hr != S_OK)
266         goto fail;
267
268     IUnknown_Release(enumpins);
269     enumpins = NULL;
270
271     hr = IBaseFilter_EnumPins(pavi, &enumpins);
272     ok(hr == S_OK, "No enumpins: %08x\n", hr);
273     if (hr != S_OK)
274         goto fail;
275
276     hr = IEnumPins_Next(enumpins, 1, &avipin, NULL);
277     ok(hr == S_OK, "No pin: %08x\n", hr);
278     if (hr != S_OK)
279         goto fail;
280
281     curlevel = count_threads();
282     ok(curlevel == baselevel,
283         "Amount of threads should be %d not %d\n", baselevel, curlevel);
284
285     hr = IPin_Connect(filepin, avipin, NULL);
286     ok(hr == S_OK, "Could not connect: %08x\n", hr);
287     if (hr != S_OK)
288         goto fail;
289
290     expected = 1 + baselevel;
291     curlevel = count_threads();
292     ok(curlevel == expected,
293         "Amount of threads should be %d not %d\n", expected, curlevel);
294
295     IUnknown_Release(avipin);
296     avipin = NULL;
297
298     IEnumPins_Reset(enumpins);
299
300     /* Windows puts the pins in the order: Outputpins - Inputpin,
301      * wine does the reverse, just don't test it for now
302      * Hate to admit it, but windows way makes more sense
303      */
304     while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
305     {
306         ok(hr == S_OK, "hr: %08x\n", hr);
307         IPin_QueryDirection(avipin, &dir);
308         if (dir == PINDIR_OUTPUT)
309         {
310             /* Well, connect it to a null renderer! */
311             IBaseFilter *pnull = NULL;
312             IEnumPins *nullenum = NULL;
313             IPin *nullpin = NULL;
314
315             hr = CoCreateInstance(&CLSID_NullRenderer, NULL,
316                 CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&pnull);
317             ok(hr == S_OK, "Could not create null renderer: %08x\n", hr);
318             if (hr != S_OK)
319                 break;
320
321             IBaseFilter_EnumPins(pnull, &nullenum);
322             IEnumPins_Next(nullenum, 1, &nullpin, NULL);
323             IEnumPins_Release(nullenum);
324             IPin_QueryDirection(nullpin, &dir);
325
326             hr = IPin_Connect(avipin, nullpin, NULL);
327             ok(hr == S_OK, "Failed to connect output pin: %08x\n", hr);
328             IPin_Release(nullpin);
329             if (hr != S_OK)
330             {
331                 IBaseFilter_Release(pnull);
332                 break;
333             }
334             IBaseFilter_Run(pnull, 0);
335             ++expected;
336         }
337
338         IUnknown_Release(avipin);
339         avipin = NULL;
340     }
341
342     if (avipin)
343         IUnknown_Release(avipin);
344     avipin = NULL;
345
346     if (hr != S_OK)
347         goto fail2;
348     /* At this point there is a minimalistic connected avi splitter that can
349      * Be used for all sorts of source filter tests, however that still needs
350      * to be written at a later time.
351      *
352      * Interesting tests:
353      * - Can you disconnect an output pin while running?
354      *   Expecting: Yes
355      * - Can you disconnect the pullpin while running?
356      *   Expecting: No
357      * - Is the reference count incremented during playback or when connected?
358      *   Does this happen once for every output pin? Or is there something else
359      *   going on.
360      *   Expecting: You tell me
361      */
362
363     IBaseFilter_Run(preader, 0);
364     IBaseFilter_Run(pavi, 0);
365     IBaseFilter_GetState(pavi, INFINITE, &state);
366
367     curlevel = count_threads();
368     ok(curlevel == expected,
369         "Amount of threads should be %d not %d\n", expected, curlevel);
370
371     IBaseFilter_Pause(pavi);
372     IBaseFilter_Pause(preader);
373     IBaseFilter_Stop(pavi);
374     IBaseFilter_Stop(preader);
375     IBaseFilter_GetState(pavi, INFINITE, &state);
376     IBaseFilter_GetState(preader, INFINITE, &state);
377
378 fail2:
379     IEnumPins_Reset(enumpins);
380     while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
381     {
382         IPin *to = NULL;
383
384         IPin_QueryDirection(avipin, &dir);
385         IPin_ConnectedTo(avipin, &to);
386         if (to)
387         {
388             IPin_Release(to);
389
390             if (dir == PINDIR_OUTPUT)
391             {
392                 PIN_INFO info;
393                 IPin_QueryPinInfo(to, &info);
394
395                 /* Release twice: Once normal, second from the
396                  * previous while loop
397                  */
398                 IBaseFilter_Stop(info.pFilter);
399                 IPin_Disconnect(to);
400                 IPin_Disconnect(avipin);
401                 IBaseFilter_Release(info.pFilter);
402                 IBaseFilter_Release(info.pFilter);
403             }
404             else
405             {
406                 IPin_Disconnect(to);
407                 IPin_Disconnect(avipin);
408             }
409         }
410         IPin_Release(avipin);
411         avipin = NULL;
412     }
413
414 fail:
415     if (hr != S_OK)
416         skip("Prerequisites not matched, skipping remainder of test\n");
417     if (enumpins)
418         IUnknown_Release(enumpins);
419
420     if (avipin)
421         IUnknown_Release(avipin);
422     if (filepin)
423     {
424         IPin *to = NULL;
425
426         IPin_ConnectedTo(filepin, &to);
427         if (to)
428         {
429             IPin_Disconnect(filepin);
430             IPin_Disconnect(to);
431         }
432         IUnknown_Release(filepin);
433     }
434
435     if (preader)
436         IUnknown_Release(preader);
437     if (pavi)
438         IUnknown_Release(pavi);
439     if (pfile)
440         IUnknown_Release(pfile);
441
442     curlevel = count_threads();
443     todo_wine
444     ok(curlevel == baselevel,
445         "Amount of threads should be %d not %d\n", baselevel, curlevel);
446 }
447
448 START_TEST(avisplitter)
449 {
450     HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
451
452     pCreateToolhelp32Snapshot = (void*)GetProcAddress(kernel32, "CreateToolhelp32Snapshot");
453     pThread32First = (void*)GetProcAddress(kernel32, "Thread32First");
454     pThread32Next = (void*)GetProcAddress(kernel32, "Thread32Next");
455
456     CoInitialize(NULL);
457
458     if (!create_avisplitter())
459     {
460         skip("Could not create avisplitter\n");
461         return;
462     }
463
464     test_query_interface();
465     test_basefilter();
466     test_threads();
467
468     release_avisplitter();
469
470     CoUninitialize();
471 }