40ef3e854d9babae31e7f3d0dfbe087d08e3f019
[platform/upstream/gstreamer.git] / tests / check / libs / insertbin.c
1 /* GStreamer
2  *
3  * unit test for autoconvert element
4  * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
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 <gst/gst.h>
27 #include <gst/check/gstcheck.h>
28 #include <gst/insertbin/gstinsertbin.h>
29
30 GstStaticPadTemplate sinkpad_template = GST_STATIC_PAD_TEMPLATE ("sink",        /* the name of the pad */
31     GST_PAD_SINK,               /* the direction of the pad */
32     GST_PAD_ALWAYS,             /* when this pad will be present */
33     GST_STATIC_CAPS (           /* the capabilities of the padtemplate */
34         "video/test")
35     );
36
37 GstStaticPadTemplate srcpad_template = GST_STATIC_PAD_TEMPLATE ("src",  /* the name of the pad */
38     GST_PAD_SRC,                /* the direction of the pad */
39     GST_PAD_ALWAYS,             /* when this pad will be present */
40     GST_STATIC_CAPS (           /* the capabilities of the padtemplate */
41         "video/test")
42     );
43
44 gint cb_count = 0;
45
46 GMutex lock;
47 GCond cond;
48
49 GThread *push_thread = NULL;
50 GThread *streaming_thread = NULL;
51 gulong block_probe_id = 0;
52 gboolean is_blocked = FALSE;
53
54 static void
55 success_cb (GstInsertBin * insertbin, GstElement * element, gboolean success,
56     gpointer user_data)
57 {
58   fail_unless (g_thread_self () == push_thread);
59   fail_unless (success == TRUE);
60   fail_unless (GST_IS_ELEMENT (insertbin));
61   fail_unless (GST_IS_ELEMENT (element));
62   cb_count++;
63 }
64
65 static void
66 fail_cb (GstInsertBin * insertbin, GstElement * element, gboolean success,
67     gpointer user_data)
68 {
69   fail_unless (GST_IS_ELEMENT (insertbin));
70   fail_unless (GST_IS_ELEMENT (element));
71   fail_unless (success == FALSE);
72   cb_count++;
73 }
74
75 /*
76  * This is a macro so the line number of any error is more useful
77  */
78 #define push_buffer(srcpad, count)                              \
79   {                                                             \
80     fail_unless (cb_count == 0);                                \
81     gst_pad_push (srcpad, gst_buffer_new ());                   \
82     fail_unless (g_list_length (buffers) == 1);                 \
83     gst_check_drop_buffers ();                                  \
84     fail_unless (cb_count == (count));                          \
85     cb_count = 0;                                               \
86   }
87
88 #define check_reset_cb_count(count)                             \
89   {                                                             \
90     fail_unless (cb_count == (count));                          \
91     cb_count = 0;                                               \
92   }
93
94 static gpointer
95 thread_push_buffer (gpointer data)
96 {
97   GstPad *pad = data;
98
99   gst_pad_push (pad, gst_buffer_new ());
100   return NULL;
101 }
102
103 static GstPadProbeReturn
104 got_buffer_block (GstPad * pad, GstPadProbeInfo * info, gpointer data)
105 {
106   g_mutex_lock (&lock);
107   is_blocked = TRUE;
108   g_cond_broadcast (&cond);
109   g_mutex_unlock (&lock);
110
111   return GST_PAD_PROBE_OK;
112 }
113
114 #define block_thread()                                                  \
115 {                                                                       \
116   fail_unless (cb_count == 0);                                          \
117   fail_unless (block_probe_id == 0);                                    \
118   fail_unless (is_blocked == FALSE);                                    \
119   fail_unless (push_thread == NULL);                                    \
120   block_probe_id = gst_pad_add_probe (sinkpad,                           \
121       GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER,             \
122       got_buffer_block, NULL, NULL);                                    \
123   push_thread = g_thread_new ("push block", thread_push_buffer, srcpad); \
124   fail_unless (push_thread != NULL);                                    \
125   g_mutex_lock (&lock);                                                 \
126   while (is_blocked == FALSE)                                           \
127     g_cond_wait (&cond, &lock);                                         \
128   g_mutex_unlock (&lock);                                               \
129 }
130
131 #define unblock_thread()                                                \
132 {                                                                       \
133   fail_unless (cb_count == 0);                                          \
134   fail_unless (push_thread != NULL);                                    \
135   fail_unless (is_blocked == TRUE);                                     \
136   fail_unless (block_probe_id != 0);                                    \
137   gst_pad_remove_probe (sinkpad, block_probe_id);                       \
138   g_thread_join (push_thread);                                          \
139   fail_unless (g_list_length (buffers) == 1);                           \
140   gst_check_drop_buffers ();                                            \
141   block_probe_id = 0;                                                   \
142   push_thread = NULL;                                                   \
143   is_blocked = FALSE;                                                   \
144 }
145
146 GST_START_TEST (test_insertbin_simple)
147 {
148   GstElement *insertbin;
149   GstElement *elem;
150   GstElement *elem2;
151   GstElement *elem3;
152   GstElement *elem4;
153   GstPad *srcpad;
154   GstPad *sinkpad;
155   GstCaps *caps;
156
157   g_mutex_init (&lock);
158   g_cond_init (&cond);
159
160   insertbin = gst_insert_bin_new (NULL);
161   fail_unless (insertbin != NULL);
162   ASSERT_OBJECT_REFCOUNT (insertbin, insertbin, 1);
163   srcpad = gst_check_setup_src_pad (insertbin, &srcpad_template);
164   sinkpad = gst_check_setup_sink_pad (insertbin, &sinkpad_template);
165
166   g_assert (srcpad && sinkpad);
167
168   ASSERT_CRITICAL (gst_insert_bin_append (GST_INSERT_BIN (insertbin), NULL,
169           NULL, NULL));
170   ASSERT_CRITICAL (gst_insert_bin_append (GST_INSERT_BIN (insertbin), NULL,
171           fail_cb, NULL));
172   fail_unless (cb_count == 0);
173
174   /* insertbin is stopped and pads are idle, should be called immediately
175    * from this same thread */
176   push_thread = g_thread_self ();
177   elem = gst_element_factory_make ("identity", NULL);
178   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
179   check_reset_cb_count (1);
180
181   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
182   check_reset_cb_count (1);
183
184   fail_unless (gst_pad_set_active (srcpad, TRUE));
185   fail_unless (gst_pad_set_active (sinkpad, TRUE));
186   fail_unless (gst_element_set_state (insertbin,
187           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
188
189   caps = gst_caps_new_empty_simple ("video/test");
190   gst_check_setup_events (srcpad, insertbin, caps, GST_FORMAT_BYTES);
191   gst_caps_unref (caps);
192
193   fail_unless (cb_count == 0);
194   fail_unless (buffers == NULL);
195
196   push_thread = g_thread_self ();
197   push_buffer (srcpad, 0);
198
199   /* now the pad should be active, the change should come from the
200    * 'streaming thread' */
201   push_thread = NULL;
202   block_thread ();
203   elem = gst_element_factory_make ("identity", NULL);
204   gst_insert_bin_prepend (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
205   unblock_thread ();
206   check_reset_cb_count (1);
207
208   /* can't add the same element twice */
209   block_thread ();
210   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL);
211   check_reset_cb_count (1);
212   unblock_thread ();
213   push_buffer (srcpad, 0);
214
215   /* remove the element */
216   block_thread ();
217   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
218   unblock_thread ();
219   check_reset_cb_count (1);
220   push_buffer (srcpad, 0);
221
222   /* try adding multiple elements, one at a time */
223   block_thread ();
224   elem = gst_element_factory_make ("identity", NULL);
225   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
226   unblock_thread ();
227   check_reset_cb_count (1);
228   push_buffer (srcpad, 0);
229
230   block_thread ();
231   elem2 = gst_element_factory_make ("identity", NULL);
232   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
233   unblock_thread ();
234   check_reset_cb_count (1);
235   push_buffer (srcpad, 0);
236
237   block_thread ();
238   elem3 = gst_element_factory_make ("identity", NULL);
239   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL);
240   unblock_thread ();
241   check_reset_cb_count (1);
242   push_buffer (srcpad, 0);
243
244   block_thread ();
245   elem4 = gst_element_factory_make ("identity", NULL);
246   gst_insert_bin_prepend (GST_INSERT_BIN (insertbin), elem4, success_cb, NULL);
247   unblock_thread ();
248   check_reset_cb_count (1);
249   push_buffer (srcpad, 0);
250
251   /* remove 2 of those elements at once */
252   block_thread ();
253   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL);
254   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
255   unblock_thread ();
256   check_reset_cb_count (2);
257   push_buffer (srcpad, 0);
258
259   /* add another 2 elements at once */
260   block_thread ();
261   elem2 = gst_element_factory_make ("identity", NULL);
262   elem3 = gst_element_factory_make ("identity", NULL);
263   gst_insert_bin_insert_after (GST_INSERT_BIN (insertbin), elem2, elem,
264       success_cb, NULL);
265   gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem3, elem4,
266       success_cb, NULL);
267   unblock_thread ();
268   check_reset_cb_count (2);
269   push_buffer (srcpad, 0);
270
271   /* remove 2 elements */
272   block_thread ();
273   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL);
274   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
275   unblock_thread ();
276   check_reset_cb_count (2);
277   push_buffer (srcpad, 0);
278
279   /* and add again */
280   block_thread ();
281   elem2 = gst_element_factory_make ("identity", NULL);
282   elem3 = gst_element_factory_make ("identity", NULL);
283   gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem3, elem4,
284       success_cb, NULL);
285   gst_insert_bin_insert_after (GST_INSERT_BIN (insertbin), elem2, elem,
286       success_cb, NULL);
287   unblock_thread ();
288   check_reset_cb_count (2);
289   push_buffer (srcpad, 0);
290
291   /* try to add an element that has no pads */
292   block_thread ();
293   elem = gst_bin_new (NULL);
294   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL);
295   check_reset_cb_count (1);
296   unblock_thread ();
297
298   /* try to add an element that has a parent */
299   block_thread ();
300   elem = gst_bin_new (NULL);
301   elem2 = gst_element_factory_make ("identity", NULL);
302   gst_bin_add (GST_BIN (elem), elem2);
303   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, fail_cb, NULL);
304   check_reset_cb_count (1);
305   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, fail_cb, NULL);
306   check_reset_cb_count (1);
307   unblock_thread ();
308   gst_object_unref (elem);
309   push_buffer (srcpad, 0);
310
311   /* when removing an element insertbin will look at the pending operations list
312    * and check if that element is pending and remove it before adding.
313    * So we check that the callback count hapenned before the end, and it
314    * also happens from this same main thread. So we need to store the
315    * streaming thread to restore it after the check */
316   elem = gst_element_factory_make ("identity", NULL);
317   elem2 = gst_element_factory_make ("identity", NULL);
318   block_thread ();
319   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
320   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
321   streaming_thread = push_thread;
322   push_thread = g_thread_self ();
323   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL);
324   push_thread = streaming_thread;
325   check_reset_cb_count (2);
326   unblock_thread ();
327   check_reset_cb_count (1);
328   push_buffer (srcpad, 0);
329
330   /* fail when trying to add an element before another that isn't in
331    * insertbin */
332   block_thread ();
333   elem = gst_element_factory_make ("identity", NULL);
334   elem2 = gst_element_factory_make ("identity", NULL);
335   gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem, elem2,
336       fail_cb, NULL);
337   check_reset_cb_count (1);
338   unblock_thread ();
339   push_buffer (srcpad, 0);
340   gst_object_unref (elem2);
341
342   fail_unless (gst_element_set_state (insertbin,
343           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
344   gst_pad_set_active (srcpad, FALSE);
345   gst_pad_set_active (sinkpad, FALSE);
346
347   cb_count = 0;
348   push_thread = g_thread_self ();
349   elem = gst_element_factory_make ("identity", NULL);
350   gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL);
351   check_reset_cb_count (1);
352
353   gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL);
354   check_reset_cb_count (1);
355
356   gst_check_teardown_sink_pad (insertbin);
357   gst_check_teardown_src_pad (insertbin);
358   gst_check_teardown_element (insertbin);
359
360   fail_unless (cb_count == 0);
361
362   g_mutex_clear (&lock);
363   g_cond_clear (&cond);
364 }
365
366 GST_END_TEST;
367
368
369 static Suite *
370 insert_bin_suite (void)
371 {
372   Suite *s = suite_create ("insertbin");
373   TCase *tc_basic = tcase_create ("general");
374
375   suite_add_tcase (s, tc_basic);
376   tcase_add_test (tc_basic, test_insertbin_simple);
377
378   return s;
379 }
380
381
382 GST_CHECK_MAIN (insert_bin);