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