e152c6a087e27b5caba3b14a3703a359b6dc4351
[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., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, 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_CAPS:
100         /* ok to have these before segment event */
101         /* FIXME check order more precisely, if so spec'ed somehow ? */
102         break;
103       case GST_EVENT_SEGMENT:
104         fail_if ((consist->expect_flush && consist->flushing),
105             "Received SEGMENT while in a flushing seek on pad %s:%s",
106             GST_DEBUG_PAD_NAME (pad));
107         consist->segment = TRUE;
108         consist->eos = FALSE;
109         break;
110       case GST_EVENT_EOS:
111         /* FIXME : not 100% sure about whether two eos in a row is valid */
112         fail_if (consist->eos, "Received EOS just after another EOS on "
113             "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
114         consist->eos = TRUE;
115         consist->segment = FALSE;
116         break;
117       case GST_EVENT_TAG:
118         GST_DEBUG_OBJECT (pad, "tag %" GST_PTR_FORMAT,
119             gst_event_get_structure (event));
120         /* fall through */
121       default:
122         if (GST_EVENT_IS_SERIALIZED (event) && GST_EVENT_IS_DOWNSTREAM (event)) {
123           fail_if (consist->eos, "Event received after EOS");
124           fail_unless (consist->segment, "Event %s received before segment "
125               "on pad %s:%s", GST_EVENT_TYPE_NAME (event),
126               GST_DEBUG_PAD_NAME (pad));
127         }
128         /* FIXME : Figure out what to do for other events */
129         break;
130     }
131     if (GST_EVENT_IS_SERIALIZED (event)) {
132       fail_if (!consist->saw_stream_start
133           && GST_EVENT_TYPE (event) != GST_EVENT_STREAM_START,
134           "Got a serialized event (%s) before a STREAM_START on pad %s:%s",
135           GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad));
136       consist->saw_serialized_event = TRUE;
137     }
138   }
139
140   return TRUE;
141 }
142
143 static gboolean
144 sink_pad_data_cb (GstPad * pad, GstPadProbeInfo * info,
145     GstStreamConsistency * consist)
146 {
147   GstMiniObject *data = GST_PAD_PROBE_INFO_DATA (info);
148
149   GST_DEBUG_OBJECT (pad, "%p: %d %d %d %d", consist, consist->flushing,
150       consist->segment, consist->eos, consist->expect_flush);
151
152   if (GST_IS_BUFFER (data)) {
153     GST_DEBUG_OBJECT (pad, "Buffer %" GST_TIME_FORMAT,
154         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (GST_BUFFER (data))));
155     /* If an EOS went through, a buffer would be invalid */
156     fail_if (consist->eos, "Buffer received after EOS on pad %s:%s",
157         GST_DEBUG_PAD_NAME (pad));
158     /* Buffers need to be preceded by a segment event */
159     fail_unless (consist->segment, "Buffer received without segment "
160         "on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
161   } else if (GST_IS_EVENT (data)) {
162     GstEvent *event = (GstEvent *) data;
163
164     GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
165     switch (GST_EVENT_TYPE (event)) {
166       case GST_EVENT_SEEK:
167       {
168         GstSeekFlags flags;
169
170         gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL,
171             NULL);
172         consist->expect_flush =
173             ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
174         break;
175       }
176       case GST_EVENT_SEGMENT:
177         fail_if ((consist->expect_flush && consist->flushing),
178             "Received SEGMENT while in a flushing seek on pad %s:%s",
179             GST_DEBUG_PAD_NAME (pad));
180         consist->segment = TRUE;
181         consist->eos = FALSE;
182         break;
183       default:
184         /* FIXME : Figure out what to do for other events */
185         break;
186     }
187   }
188
189   return TRUE;
190 }
191
192 static void
193 add_pad (GstStreamConsistency * consist, GstPad * pad)
194 {
195   GstStreamConsistencyProbe *p;
196   GstPadDirection dir;
197
198   p = g_new0 (GstStreamConsistencyProbe, 1);
199   p->pad = g_object_ref (pad);
200   dir = gst_pad_get_direction (pad);
201   if (dir == GST_PAD_SRC) {
202
203     p->probeid =
204         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
205         (GstPadProbeCallback) source_pad_data_cb, consist, NULL);
206
207   } else if (dir == GST_PAD_SINK) {
208     p->probeid =
209         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
210         (GstPadProbeCallback) sink_pad_data_cb, consist, NULL);
211   }
212   consist->pads = g_list_prepend (consist->pads, p);
213 }
214
215 /**
216  * gst_consistency_checker_new: (skip)
217  * @pad: The #GstPad on which the dataflow will be checked.
218  *
219  * Sets up a data probe on the given pad which will raise assertions if the
220  * data flow is inconsistent.
221  *
222  * Returns: A #GstStreamConsistency structure used to track data flow.
223  */
224 GstStreamConsistency *
225 gst_consistency_checker_new (GstPad * pad)
226 {
227   GstStreamConsistency *consist;
228
229   g_return_val_if_fail (pad != NULL, NULL);
230
231   consist = g_new0 (GstStreamConsistency, 1);
232
233   if (!consist->pads) {
234     consist->parent = GST_OBJECT_PARENT (pad);
235   }
236   add_pad (consist, pad);
237   return consist;
238 }
239
240 /**
241  * gst_consistency_checker_add_pad:
242  * @consist: The #GstStreamConsistency handle
243  * @pad: The #GstPad on which the dataflow will be checked.
244  *
245  * Sets up a data probe on the given pad which will raise assertions if the
246  * data flow is inconsistent.
247  *
248  * Returns: %TRUE if the pad was added
249  */
250 gboolean
251 gst_consistency_checker_add_pad (GstStreamConsistency * consist, GstPad * pad)
252 {
253   g_return_val_if_fail (consist != NULL, FALSE);
254   g_return_val_if_fail (pad != NULL, FALSE);
255   g_return_val_if_fail (GST_OBJECT_PARENT (pad) == consist->parent, FALSE);
256
257   add_pad (consist, pad);
258   return TRUE;
259 }
260
261 /**
262  * gst_consistency_checker_reset:
263  * @consist: The #GstStreamConsistency to reset.
264  *
265  * Reset the stream checker's internal variables.
266  */
267
268 void
269 gst_consistency_checker_reset (GstStreamConsistency * consist)
270 {
271   consist->flushing = FALSE;
272   consist->segment = FALSE;
273   consist->eos = FALSE;
274   consist->expect_flush = FALSE;
275   consist->saw_serialized_event = FALSE;
276   consist->saw_stream_start = FALSE;
277 }
278
279 /**
280  * gst_consistency_checker_free:
281  * @consist: The #GstStreamConsistency to free.
282  *
283  * Frees the allocated data and probes associated with @consist.
284  */
285
286 void
287 gst_consistency_checker_free (GstStreamConsistency * consist)
288 {
289   GList *node;
290   GstStreamConsistencyProbe *p;
291
292   /* Remove the data probes */
293   for (node = consist->pads; node; node = g_list_next (node)) {
294     p = (GstStreamConsistencyProbe *) node->data;
295     gst_pad_remove_probe (p->pad, p->probeid);
296     gst_object_unref (p->pad);
297     g_free (p);
298   }
299   g_list_free (consist->pads);
300   g_free (consist);
301 }