46c3a564671b8bde8c3485e528e691ed71860d82
[platform/upstream/gstreamer.git] / libs / gst / check / gstconsistencychecker.c
1 /* GStreamer
2  *
3  * unit testing helper lib
4  *
5  * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
6  * Copyright (C) 2012 Stefan Sauer <ensonic@users.sf.net>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /**
25  * SECTION:gstcheckconsistencychecker
26  * @short_description: Data flow consistency checker for GStreamer unit tests.
27  *
28  * These macros and functions are for internal use of the unit tests found
29  * inside the 'check' directories of various GStreamer packages.
30  */
31
32 #include "gstconsistencychecker.h"
33
34 struct _GstStreamConsistency
35 {
36   /* FIXME: do we want to track some states per pad? */
37   volatile gboolean flushing;
38   volatile gboolean segment;
39   volatile gboolean eos;
40   volatile gboolean expect_flush;
41   volatile gboolean saw_serialized_event;
42   volatile gboolean saw_stream_start;
43   GstObject *parent;
44   GList *pads;
45 };
46
47 typedef struct _GstStreamConsistencyProbe
48 {
49   GstPad *pad;
50   gulong probeid;
51 } GstStreamConsistencyProbe;
52
53 static gboolean
54 source_pad_data_cb (GstPad * pad, GstPadProbeInfo * info,
55     GstStreamConsistency * consist)
56 {
57   GstMiniObject *data = GST_PAD_PROBE_INFO_DATA (info);
58
59   GST_DEBUG_OBJECT (pad, "%p: %d %d %d %d", consist, consist->flushing,
60       consist->segment, consist->eos, consist->expect_flush);
61
62   if (GST_IS_BUFFER (data)) {
63     GST_DEBUG_OBJECT (pad,
64         "Buffer pts %" GST_TIME_FORMAT ", dts %" GST_TIME_FORMAT,
65         GST_TIME_ARGS (GST_BUFFER_PTS (GST_BUFFER_CAST (data))),
66         GST_TIME_ARGS (GST_BUFFER_DTS (GST_BUFFER_CAST (data))));
67     /* If an EOS went through, a buffer would be invalid */
68     fail_if (consist->eos, "Buffer received after EOS on pad %s:%s",
69         GST_DEBUG_PAD_NAME (pad));
70     /* Buffers need to be preceded by a segment event */
71     fail_unless (consist->segment, "Buffer received without segment "
72         "on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
73   } else if (GST_IS_EVENT (data)) {
74     GstEvent *event = (GstEvent *) data;
75
76     GST_DEBUG_OBJECT (pad, "Event : %s", GST_EVENT_TYPE_NAME (event));
77     switch (GST_EVENT_TYPE (event)) {
78       case GST_EVENT_FLUSH_START:
79         /* getting two flush_start in a row seems to be okay
80            fail_if (consist->flushing, "Received another FLUSH_START");
81          */
82         consist->flushing = TRUE;
83         break;
84       case GST_EVENT_FLUSH_STOP:
85         /* Receiving a flush-stop is only valid after receiving a flush-start */
86         fail_unless (consist->flushing,
87             "Received a FLUSH_STOP without a FLUSH_START on pad %s:%s",
88             GST_DEBUG_PAD_NAME (pad));
89         fail_if (consist->eos, "Received a FLUSH_STOP after an EOS on "
90             "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
91         consist->flushing = consist->expect_flush = FALSE;
92         break;
93       case GST_EVENT_STREAM_START:
94         fail_if (consist->saw_serialized_event && !consist->saw_stream_start,
95             "Got a STREAM_START event after a serialized event on pad %s:%s",
96             GST_DEBUG_PAD_NAME (pad));
97         consist->saw_stream_start = TRUE;
98         break;
99       case GST_EVENT_STREAM_CONFIG:
100       case GST_EVENT_CAPS:
101         /* ok to have these before segment event */
102         /* FIXME check order more precisely, if so spec'ed somehow ? */
103         break;
104       case GST_EVENT_SEGMENT:
105         fail_if ((consist->expect_flush && consist->flushing),
106             "Received SEGMENT while in a flushing seek on pad %s:%s",
107             GST_DEBUG_PAD_NAME (pad));
108         consist->segment = TRUE;
109         consist->eos = FALSE;
110         break;
111       case GST_EVENT_EOS:
112         /* FIXME : not 100% sure about whether two eos in a row is valid */
113         fail_if (consist->eos, "Received EOS just after another EOS on "
114             "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
115         consist->eos = TRUE;
116         consist->segment = FALSE;
117         break;
118       case GST_EVENT_TAG:
119         GST_DEBUG_OBJECT (pad, "tag %" GST_PTR_FORMAT,
120             gst_event_get_structure (event));
121         /* fall through */
122       default:
123         if (GST_EVENT_IS_SERIALIZED (event) && GST_EVENT_IS_DOWNSTREAM (event)) {
124           fail_if (consist->eos, "Event received after EOS");
125           fail_unless (consist->segment, "Event %s received before segment "
126               "on pad %s:%s", GST_EVENT_TYPE_NAME (event),
127               GST_DEBUG_PAD_NAME (pad));
128         }
129         /* FIXME : Figure out what to do for other events */
130         break;
131     }
132     if (GST_EVENT_IS_SERIALIZED (event)) {
133       fail_if (!consist->saw_stream_start
134           && GST_EVENT_TYPE (event) != GST_EVENT_STREAM_START,
135           "Got a serialized event (%s) before a STREAM_START on pad %s:%s",
136           GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad));
137       consist->saw_serialized_event = TRUE;
138     }
139   }
140
141   return TRUE;
142 }
143
144 static gboolean
145 sink_pad_data_cb (GstPad * pad, GstPadProbeInfo * info,
146     GstStreamConsistency * consist)
147 {
148   GstMiniObject *data = GST_PAD_PROBE_INFO_DATA (info);
149
150   GST_DEBUG_OBJECT (pad, "%p: %d %d %d %d", consist, consist->flushing,
151       consist->segment, consist->eos, consist->expect_flush);
152
153   if (GST_IS_BUFFER (data)) {
154     GST_DEBUG_OBJECT (pad, "Buffer %" GST_TIME_FORMAT,
155         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (GST_BUFFER (data))));
156     /* If an EOS went through, a buffer would be invalid */
157     fail_if (consist->eos, "Buffer received after EOS on pad %s:%s",
158         GST_DEBUG_PAD_NAME (pad));
159     /* Buffers need to be preceded by a segment event */
160     fail_unless (consist->segment, "Buffer received without segment "
161         "on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
162   } else if (GST_IS_EVENT (data)) {
163     GstEvent *event = (GstEvent *) data;
164
165     GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
166     switch (GST_EVENT_TYPE (event)) {
167       case GST_EVENT_SEEK:
168       {
169         GstSeekFlags flags;
170
171         gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL,
172             NULL);
173         consist->expect_flush =
174             ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
175         break;
176       }
177       case GST_EVENT_SEGMENT:
178         fail_if ((consist->expect_flush && consist->flushing),
179             "Received SEGMENT while in a flushing seek on pad %s:%s",
180             GST_DEBUG_PAD_NAME (pad));
181         consist->segment = TRUE;
182         consist->eos = FALSE;
183         break;
184       default:
185         /* FIXME : Figure out what to do for other events */
186         break;
187     }
188   }
189
190   return TRUE;
191 }
192
193 static void
194 add_pad (GstStreamConsistency * consist, GstPad * pad)
195 {
196   GstStreamConsistencyProbe *p;
197   GstPadDirection dir;
198
199   p = g_new0 (GstStreamConsistencyProbe, 1);
200   p->pad = g_object_ref (pad);
201   dir = gst_pad_get_direction (pad);
202   if (dir == GST_PAD_SRC) {
203
204     p->probeid =
205         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
206         (GstPadProbeCallback) source_pad_data_cb, consist, NULL);
207
208   } else if (dir == GST_PAD_SINK) {
209     p->probeid =
210         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
211         (GstPadProbeCallback) sink_pad_data_cb, consist, NULL);
212   }
213   consist->pads = g_list_prepend (consist->pads, p);
214 }
215
216 /**
217  * gst_consistency_checker_new:
218  * @pad: The #GstPad on which the dataflow will be checked.
219  *
220  * Sets up a data probe on the given pad which will raise assertions if the
221  * data flow is inconsistent.
222  *
223  * Returns: A #GstStreamConsistency structure used to track data flow.
224  */
225 GstStreamConsistency *
226 gst_consistency_checker_new (GstPad * pad)
227 {
228   GstStreamConsistency *consist;
229
230   g_return_val_if_fail (pad != NULL, NULL);
231
232   consist = g_new0 (GstStreamConsistency, 1);
233
234   if (!consist->pads) {
235     consist->parent = GST_OBJECT_PARENT (pad);
236   }
237   add_pad (consist, pad);
238   return consist;
239 }
240
241 /**
242  * gst_consistency_checker_add_pad:
243  * @consist: The #GstStreamConsistency handle
244  * @pad: The #GstPad on which the dataflow will be checked.
245  *
246  * Sets up a data probe on the given pad which will raise assertions if the
247  * data flow is inconsistent.
248  *
249  * Returns: %TRUE if the pad was added
250  */
251 gboolean
252 gst_consistency_checker_add_pad (GstStreamConsistency * consist, GstPad * pad)
253 {
254   g_return_val_if_fail (consist != NULL, FALSE);
255   g_return_val_if_fail (pad != NULL, FALSE);
256   g_return_val_if_fail (GST_OBJECT_PARENT (pad) == consist->parent, FALSE);
257
258   add_pad (consist, pad);
259   return TRUE;
260 }
261
262 /**
263  * gst_consistency_checker_reset:
264  * @consist: The #GstStreamConsistency to reset.
265  *
266  * Reset the stream checker's internal variables.
267  */
268
269 void
270 gst_consistency_checker_reset (GstStreamConsistency * consist)
271 {
272   consist->flushing = FALSE;
273   consist->segment = FALSE;
274   consist->eos = FALSE;
275   consist->expect_flush = FALSE;
276   consist->saw_serialized_event = FALSE;
277   consist->saw_stream_start = FALSE;
278 }
279
280 /**
281  * gst_consistency_checker_free:
282  * @consist: The #GstStreamConsistency to free.
283  *
284  * Frees the allocated data and probes associated with @consist.
285  */
286
287 void
288 gst_consistency_checker_free (GstStreamConsistency * consist)
289 {
290   GList *node;
291   GstStreamConsistencyProbe *p;
292
293   /* Remove the data probes */
294   for (node = consist->pads; node; node = g_list_next (node)) {
295     p = (GstStreamConsistencyProbe *) node->data;
296     gst_pad_remove_probe (p->pad, p->probeid);
297     gst_object_unref (p->pad);
298     g_free (p);
299   }
300   g_list_free (consist->pads);
301   g_free (consist);
302 }