Fix some of the leaks exposed by extending the parse-launch testsuite, and move the...
[platform/upstream/gstreamer.git] / tests / check / pipelines / parse-launch.c
1 /* GStreamer
2  * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
3  *
4  * cleanup.c: Unit test for cleanup of pipelines
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <valgrind/valgrind.h>
27 #include <valgrind/memcheck.h>
28
29 #include <gst/check/gstcheck.h>
30
31 static GstElement *
32 setup_pipeline (const gchar * pipe_descr)
33 {
34   GstElement *pipeline;
35   GError *error = NULL;
36
37   pipeline = gst_parse_launch (pipe_descr, &error);
38   if (error != NULL) {
39     fail_if (error != NULL, "Error parsing pipeline %s: %s", pipe_descr,
40         error->message);
41     g_error_free (error);
42   }
43   fail_unless (pipeline != NULL, "Failed to create pipeline %s", pipe_descr);
44   return pipeline;
45 }
46
47 static void
48 expected_fail_pipe (const gchar * pipe_descr)
49 {
50   GstElement *pipeline;
51   GError *error = NULL;
52
53 #ifndef GST_DISABLE_GST_DEBUG
54   // gst_debug_set_default_threshold (GST_LEVEL_NONE);
55 #endif
56
57   pipeline = gst_parse_launch (pipe_descr, &error);
58   fail_unless (pipeline == NULL || error != NULL,
59       "Expected failure pipeline %s: succeeded!", pipe_descr);
60   g_error_free (error);
61
62   /* We get a pipeline back even when parsing has failed, sometimes! */
63   if (pipeline)
64     gst_object_unref (pipeline);
65 }
66
67 static void
68 check_pipeline_runs (GstElement * p)
69 {
70   GstStateChangeReturn ret;
71
72   /* Check that the pipeline changes state to PAUSED and back to NULL */
73   ret = gst_element_set_state (p, GST_STATE_PAUSED);
74   if (ret == GST_STATE_CHANGE_ASYNC)
75     ret = gst_element_get_state (p, NULL, NULL, GST_CLOCK_TIME_NONE);
76   fail_unless (ret != GST_STATE_CHANGE_FAILURE,
77       "Could not set pipeline to paused");
78
79   ret = gst_element_set_state (p, GST_STATE_NULL);
80   if (ret == GST_STATE_CHANGE_ASYNC)
81     ret = gst_element_get_state (p, NULL, NULL, GST_CLOCK_TIME_NONE);
82   fail_unless (ret != GST_STATE_CHANGE_FAILURE,
83       "Could not set pipeline to null");
84 }
85
86 static const gchar *test_lines[] = {
87   "filesrc location=music.mp3 ! identity ! fakesink",
88   "filesrc location=music.ogg ! tee ! identity ! identity ! fakesink",
89   "filesrc location=http://domain.com/music.mp3 ! identity ! fakesink",
90   "filesrc location=movie.avi ! tee name=demuxer ! ( queue ! identity ! fakesink ) ( demuxer. ! queue ! identity ! fakesink )",
91   "fakesrc ! video/x-raw-yuv ! fakesink",
92   "fakesrc !   video/raw,  format=(fourcc)YUY2; video/raw, format=(fourcc)YV12 ! fakesink",
93   "fakesrc ! audio/x-raw-int, width=[16,  32], depth={16, 24, 32}, signed=TRUE ! fakesink",
94   "fakesrc ! identity ! identity ! identity ! fakesink",
95   NULL
96 };
97
98 GST_START_TEST (test_launch_lines)
99 {
100   GstElement *pipeline;
101   const gchar **s;
102
103   for (s = test_lines; *s != NULL; s++) {
104     pipeline = setup_pipeline (*s);
105     gst_object_unref (pipeline);
106   }
107 }
108
109 GST_END_TEST;
110
111 #define PIPELINE1  "fakesrc"
112 #define PIPELINE2  "fakesrc name=donald num-buffers= 27 silent =TruE sizetype = 3 data=   Subbuffer\\ data"
113 #define PIPELINE3  "fakesrc identity fakesink"
114 #define PIPELINE4  "fakesrc num-buffers=4 .src ! identity !.sink identity .src ! .sink fakesink"
115 #define PIPELINE5  "fakesrc num-buffers=4 name=src identity name=id1 identity name = id2 fakesink name =sink src. ! id1. id1.! id2.sink id2.src!sink.sink"
116 #define PIPELINE6  "pipeline.(name=\"john\" fakesrc num-buffers=4 ( bin. ( ! queue ! identity !( queue ! fakesink )) ))"
117 #define PIPELINE7  "fakesrc num-buffers=4 ! tee name=tee .src%d! queue ! fakesink tee.src%d ! queue ! fakesink queue name =\"foo\" ! fakesink tee.src%d ! foo."
118 /* aggregator is borked
119  * #define PIPELINE8  "fakesrc num-buffers=4 ! tee name=tee1 .src0,src1 ! .sink0, sink1 aggregator ! fakesink"
120  * */
121 #define PIPELINE8  "fakesrc num-buffers=4 ! fakesink"
122 #define PIPELINE9  "fakesrc num-buffers=4 ! test. fakesink name=test"
123 #define PIPELINE10 "( fakesrc num-buffers=\"4\" ! ) identity ! fakesink"
124 #define PIPELINE11 "fakesink name = sink identity name=id ( fakesrc num-buffers=\"4\" ! id. ) id. ! sink."
125 #define PIPELINE12 "file:///tmp/test.file ! fakesink"
126 #define PIPELINE13 "fakesrc ! file:///tmp/test.file"
127
128 GST_START_TEST (test_launch_lines2)
129 {
130   GstElement *cur;
131   gint i;
132   gboolean b;
133   gchar *s = NULL;
134
135   /**
136    * checks:
137    * - specifying an element works :)
138    * - if only 1 element is requested, no bin is returned, but the element
139    */
140   cur = setup_pipeline (PIPELINE1);
141   fail_unless (G_OBJECT_TYPE (cur) == g_type_from_name ("GstFakeSrc"),
142       "parse_launch did not produce a fakesrc");
143   gst_object_unref (cur);
144
145   /**
146    * checks:
147    * - properties works
148    * - string, int, boolean and enums can be properly set
149    * - first test of escaping strings
150    */
151   cur = setup_pipeline (PIPELINE2);
152   g_object_get (G_OBJECT (cur), "name", &s, "num-buffers", &i,
153       "silent", &b, NULL);
154   fail_if (s == NULL, "name was NULL");
155   fail_unless (strcmp (s, "donald") == 0, "fakesrc name was not 'donald'");
156   fail_unless (i == 27, "num-buffers was not 27");
157   fail_unless (b == TRUE, "silent was not TRUE");
158   g_free (s);
159
160   g_object_get (G_OBJECT (cur), "sizetype", &i, NULL);
161   fail_unless (i == 3, "sizetype != 3");
162
163   g_object_get (G_OBJECT (cur), "data", &i, NULL);
164   fail_unless (i == 2, "data != 2");
165   gst_object_unref (cur);
166
167   /**
168    * checks:
169    * - specifying multiple elements without links works
170    * - if multiple toplevel elements exist, a pipeline is returned
171    */
172   cur = setup_pipeline (PIPELINE3);
173   fail_unless (GST_BIN_NUMCHILDREN (cur) == 3,
174       "Pipeline does not contain 3 children");
175   gst_object_unref (cur);
176
177   /**
178    * checks:
179    * - test default link "!"
180    * - test if specifying pads on links works
181    */
182   cur = setup_pipeline (PIPELINE4);
183   check_pipeline_runs (cur);
184   gst_object_unref (cur);
185
186   /**
187    * checks:
188    * - test if appending the links works, too
189    * - check if the pipeline constructed works the same as the one before (how?)
190    */
191   cur = setup_pipeline (PIPELINE5);
192   check_pipeline_runs (cur);
193   gst_object_unref (cur);
194
195   /**
196    * checks:
197    * - test various types of bins
198    * - test if linking across bins works
199    * - test if escaping strings works
200    */
201   cur = setup_pipeline (PIPELINE6);
202   fail_unless (GST_IS_PIPELINE (cur), "Parse did not produce a pipeline");
203   g_object_get (G_OBJECT (cur), "name", &s, NULL);
204   fail_if (s == NULL, "name was NULL");
205   fail_unless (strcmp (s, "john") == 0, "Name was not 'john'");
206   g_free (s);
207   check_pipeline_runs (cur);
208   gst_object_unref (cur);
209
210   /**
211    * checks:
212    * - test request pads
213    */
214   cur = setup_pipeline (PIPELINE7);
215   check_pipeline_runs (cur);
216   gst_object_unref (cur);
217
218   /**
219    * checks:
220    * - multiple pads on 1 link
221    */
222   cur = setup_pipeline (PIPELINE8);
223   check_pipeline_runs (cur);
224   gst_object_unref (cur);
225
226   /**
227    * checks:
228    * - failed in grammar.y cvs version 1.17
229    */
230   cur = setup_pipeline (PIPELINE9);
231   check_pipeline_runs (cur);
232   gst_object_unref (cur);
233
234   /**
235    * checks:
236    * - failed in grammar.y cvs version 1.17
237    */
238   cur = setup_pipeline (PIPELINE10);
239   check_pipeline_runs (cur);
240   gst_object_unref (cur);
241
242   /**
243    * checks:
244    * - failed in grammar.y cvs version 1.18
245    */
246   cur = setup_pipeline (PIPELINE11);
247   check_pipeline_runs (cur);
248   gst_object_unref (cur);
249
250   /**
251    * checks:
252    * - URI detection works
253    */
254   cur = setup_pipeline (PIPELINE12);
255   gst_object_unref (cur);
256
257   /** * checks:
258    * - URI sink detection works
259    */
260   cur = setup_pipeline (PIPELINE13);
261   gst_object_unref (cur);
262
263   /* Checks handling of a assignment followed by error inside a bin. 
264    * This should warn, but ignore the error and carry on */
265   cur = setup_pipeline ("( filesrc blocksize=4 location=/dev/null @ )");
266   gst_object_unref (cur);
267 }
268
269 GST_END_TEST;
270
271 static const gchar *expected_failures[] = {
272   /* checks: fails because a=b. is not a valid element reference in parse.l */
273   "fakesrc num-buffers=4 name=\"a=b\"  a=b. ! fakesink",
274   /* checks: Error branch for a non-deserialisable property value */
275   "filesrc blocksize=absdff",
276   /* checks: That broken caps which don't parse can't create a pipeline */
277   "fakesrc ! video/raw,format=(antwerp)monkeys ! fakesink",
278   /* checks: Empty pipeline is invalid */
279   "",
280   /* checks: Link without sink element failes */
281   "fakesrc ! ",
282   /* checks: Link without src element failes */
283   " ! fakesink",
284   /* checks: Source URI for which no element exists is a failure */
285   "borky://fdaffd ! fakesink",
286   /* checks: Sink URI for which no element exists is a failure */
287   "fakesrc ! borky://fdaffd",
288   /* checks: Referencing non-existent source element by name can't link */
289   "fakesrc name=src fakesink name=sink noexiste. ! sink.",
290   /* checks: Referencing non-existent sink element by name can't link */
291   "fakesrc name=src fakesink name=sink src. ! noexiste.",
292   /* checks: Can't link 2 elements that only have sink pads */
293   "fakesink ! fakesink",
294   /* checks multi-chain link without src element fails. */
295   "! identity ! identity ! fakesink",
296   /* END: */
297   NULL
298 };
299
300 GST_START_TEST (expected_to_fail_pipes)
301 {
302   const gchar **s;
303
304   for (s = expected_failures; *s != NULL; s++) {
305     expected_fail_pipe (*s);
306   }
307 }
308
309 GST_END_TEST;
310
311 static const gchar *leaking_failures[] = {
312   /* checks: Invalid pipeline syntax fails */
313   "fakesrc ! identity ! sgsdfagfd @ gfdgfdsgfsgSF",
314   /* checks: Attempting to link to a non-existent pad on an element 
315    * created via URI handler should fail */
316   "fakesrc ! .foo file:///dev/null",
317   /* checks: That requesting an element which doesn't exist doesn't work */
318   "error-does-not-exist-src",
319   NULL
320 };
321
322 GST_START_TEST (leaking_fail_pipes)
323 {
324   const gchar **s;
325
326   for (s = leaking_failures; *s != NULL; s++) {
327     g_print ("Trying pipe: %s\n", *s);
328     /* Uncomment if you want to try fixing the leaks */
329     /* expected_fail_pipe (*s); */
330     VALGRIND_DO_LEAK_CHECK;
331   }
332 }
333
334 GST_END_TEST;
335
336 /* Helper function to test delayed linking support in parse_launch by creating
337  * a test element based on bin, which contains a fakesrc and a sometimes 
338  * pad-template, and trying to link to a fakesink. When the bin transitions
339  * to paused it adds a pad, which should get linked to the fakesink */
340 static void
341 run_delayed_test (const gchar * pipe_str, const gchar * peer,
342     gboolean expect_link)
343 {
344   GstElement *pipe, *src, *sink;
345   GstPad *srcpad, *sinkpad, *peerpad = NULL;
346
347   pipe = setup_pipeline (pipe_str);
348
349   src = gst_bin_get_by_name (GST_BIN (pipe), "src");
350   fail_if (src == NULL, "Test source element was not created");
351
352   sink = gst_bin_get_by_name (GST_BIN (pipe), "sink");
353   fail_if (sink == NULL, "Test sink element was not created");
354
355   /* The src should not yet have a src pad */
356   srcpad = gst_element_get_pad (src, "src");
357   fail_unless (srcpad == NULL, "Source element already has a source pad");
358
359   /* Set the state to PAUSED and wait until the src at least reaches that
360    * state */
361   fail_if (gst_element_set_state (pipe, GST_STATE_PAUSED) ==
362       GST_STATE_CHANGE_FAILURE);
363
364   /* Also set the src state manually to make sure it is changing to that
365    * state */
366   fail_if (gst_element_set_state (src, GST_STATE_PAUSED) ==
367       GST_STATE_CHANGE_FAILURE);
368   fail_if (gst_element_get_state (src, NULL, NULL, GST_CLOCK_TIME_NONE) ==
369       GST_STATE_CHANGE_FAILURE);
370
371   /* Now, the source element should have a src pad, and if "peer" was passed, 
372    * then the src pad should have gotten linked to the 'sink' pad of that 
373    * peer */
374   srcpad = gst_element_get_pad (src, "src");
375   fail_if (srcpad == NULL, "Source element did not create source pad");
376
377   peerpad = gst_pad_get_peer (srcpad);
378
379   if (expect_link == TRUE) {
380     fail_if (peerpad == NULL, "Source element pad did not get linked");
381   } else {
382     fail_if (peerpad != NULL,
383         "Source element pad got linked but should not have");
384   }
385   if (peerpad != NULL)
386     gst_object_unref (peerpad);
387
388   if (peer != NULL) {
389     GstElement *peer_elem = gst_bin_get_by_name (GST_BIN (pipe), peer);
390
391     fail_if (peer_elem == NULL, "Could not retrieve peer %s", peer);
392
393     sinkpad = gst_element_get_pad (peer_elem, "sink");
394     fail_if (sinkpad == NULL, "Peer element did not have a 'sink' pad");
395
396     fail_unless (peerpad == sinkpad,
397         "Source src pad got connected to the wrong peer");
398     gst_object_unref (sinkpad);
399   }
400
401   gst_object_unref (srcpad);
402
403   gst_object_unref (src);
404   gst_object_unref (sink);
405   gst_object_unref (pipe);
406 }
407
408 GST_START_TEST (delayed_link)
409 {
410   /* This tests the delayed linking support in parse_launch by creating
411    * a test element based on bin, which contains a fakesrc and a sometimes 
412    * pad-template, and trying to link to a fakesink. When the bin transitions
413    * to paused it adds a pad, which should get linked to the fakesink */
414   run_delayed_test ("parsetestelement name=src ! fakesink name=sink",
415       "sink", TRUE);
416
417   /* Test, but this time specifying both pad names */
418   run_delayed_test ("parsetestelement name=src .src ! "
419       ".sink fakesink name=sink", "sink", TRUE);
420
421   /* Now try with a caps filter, but not testing that
422    * the peerpad == sinkpad, because the peer will actually
423    * be a capsfilter */
424   run_delayed_test ("parsetestelement name=src ! application/x-test-caps ! "
425       "fakesink name=sink", NULL, TRUE);
426
427   /* Now try with mutually exclusive caps filters that 
428    * will prevent linking, but only once gets around to happening -
429    * ie, the pipeline should create ok but fail to change state */
430   run_delayed_test ("parsetestelement name=src ! application/x-test-caps ! "
431       "identity ! application/x-other-caps ! "
432       "fakesink name=sink", NULL, FALSE);
433 }
434
435 GST_END_TEST;
436
437 #define GST_TYPE_PARSE_TEST_ELEMENT (gst_parse_test_element_get_type())
438
439 typedef struct _GstParseTestElement
440 {
441   GstBin parent;
442
443   GstElement *fakesrc;
444 } GstParseTestElement;
445
446 typedef struct _GstParseTestElementClass
447 {
448   GstBinClass parent;
449 } GstParseTestElementClass;
450
451 static GstStaticPadTemplate test_element_pad_template =
452 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
453     GST_PAD_SOMETIMES, GST_STATIC_CAPS ("application/x-test-caps"));
454
455 GST_BOILERPLATE (GstParseTestElement, gst_parse_test_element, GstBin,
456     GST_TYPE_BIN);
457
458 static GstStateChangeReturn
459 gst_parse_test_element_change_state (GstElement * element,
460     GstStateChange transition);
461
462 static void
463 gst_parse_test_element_base_init (gpointer g_class)
464 {
465   static const GstElementDetails cdfoo_details =
466       GST_ELEMENT_DETAILS ("Test element for parse launch tests",
467       "Source",
468       "Test element for parse launch tests in core",
469       "GStreamer Devel <gstreamer-devel@lists.sf.net>");
470   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
471
472   gst_element_class_set_details (element_class, &cdfoo_details);
473 }
474
475 static void
476 gst_parse_test_element_class_init (GstParseTestElementClass * klass)
477 {
478   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
479
480   gst_element_class_add_pad_template (gstelement_class,
481       gst_static_pad_template_get (&test_element_pad_template));
482
483   gstelement_class->change_state = gst_parse_test_element_change_state;
484 }
485
486 static void
487 gst_parse_test_element_init (GstParseTestElement * src,
488     GstParseTestElementClass * klass)
489 {
490   /* Create a fakesrc and add it to ourselves */
491   src->fakesrc = gst_element_factory_make ("fakesrc", NULL);
492   if (src->fakesrc)
493     gst_bin_add (GST_BIN (src), src->fakesrc);
494 }
495
496 static GstStateChangeReturn
497 gst_parse_test_element_change_state (GstElement * element,
498     GstStateChange transition)
499 {
500   GstParseTestElement *src = (GstParseTestElement *) element;
501
502   if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
503     /* Add our pad */
504     GstPad *pad;
505     GstPad *ghost;
506
507     if (src->fakesrc == NULL)
508       return GST_STATE_CHANGE_FAILURE;
509
510     pad = gst_element_get_pad (src->fakesrc, "src");
511     if (pad == NULL)
512       return GST_STATE_CHANGE_FAILURE;
513
514     ghost = gst_ghost_pad_new ("src", pad);
515     fail_if (ghost == NULL, "Failed to create ghost pad");
516     gst_element_add_pad (GST_ELEMENT (src), ghost);
517     gst_object_unref (pad);
518   }
519
520   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
521 }
522
523 static gboolean
524 gst_register_parse_element (GstPlugin * plugin)
525 {
526   if (!gst_element_register (plugin, "parsetestelement", GST_RANK_NONE,
527           GST_TYPE_PARSE_TEST_ELEMENT))
528     return FALSE;
529   return TRUE;
530 }
531
532 GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR, GST_VERSION_MINOR,
533     "parsetestelement", "Test element for parse launch",
534     gst_register_parse_element, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
535     GST_PACKAGE_ORIGIN);
536
537 Suite *
538 parse_suite (void)
539 {
540   Suite *s = suite_create ("Parse Launch syntax");
541   TCase *tc_chain = tcase_create ("parselaunch");
542
543   /* time out after 20s, not the default 3 */
544   tcase_set_timeout (tc_chain, 20);
545
546   suite_add_tcase (s, tc_chain);
547   tcase_add_test (tc_chain, test_launch_lines);
548   tcase_add_test (tc_chain, test_launch_lines2);
549   tcase_add_test (tc_chain, expected_to_fail_pipes);
550   tcase_add_test (tc_chain, leaking_fail_pipes);
551   tcase_add_test (tc_chain, delayed_link);
552   return s;
553 }
554
555 GST_CHECK_MAIN (parse);