576b5750275d191275874f0d98af6917144e7527
[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  * Since: 0.10.24
32  */
33
34 #include "gstconsistencychecker.h"
35
36 struct _GstStreamConsistency
37 {
38   /* FIXME: do we want to track some states per pad? */
39   volatile gboolean flushing;
40   volatile gboolean segment;
41   volatile gboolean eos;
42   volatile gboolean expect_flush;
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");
69     /* Buffers need to be preceded by a segment event */
70     fail_unless (consist->segment, "Buffer received without segment");
71   } else if (GST_IS_EVENT (data)) {
72     GstEvent *event = (GstEvent *) data;
73
74     GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
75     switch (GST_EVENT_TYPE (event)) {
76       case GST_EVENT_FLUSH_START:
77         /* getting two flush_start in a row seems to be okay
78            fail_if (consist->flushing, "Received another FLUSH_START");
79          */
80         consist->flushing = TRUE;
81         break;
82       case GST_EVENT_FLUSH_STOP:
83         /* Receiving a flush-stop is only valid after receiving a flush-start */
84         fail_unless (consist->flushing,
85             "Received a FLUSH_STOP without a FLUSH_START");
86         fail_if (consist->eos, "Received a FLUSH_STOP after an EOS");
87         consist->flushing = consist->expect_flush = FALSE;
88         break;
89       case GST_EVENT_STREAM_START:
90       case GST_EVENT_STREAM_CONFIG:
91       case GST_EVENT_CAPS:
92         /* ok to have these before segment event */
93         /* FIXME check order more precisely, if so spec'ed somehow ? */
94         break;
95       case GST_EVENT_SEGMENT:
96         fail_if ((consist->expect_flush && consist->flushing),
97             "Received SEGMENT while in a flushing seek");
98         consist->segment = TRUE;
99         consist->eos = FALSE;
100         break;
101       case GST_EVENT_EOS:
102         /* FIXME : not 100% sure about whether two eos in a row is valid */
103         fail_if (consist->eos, "Received EOS just after another EOS");
104         consist->eos = TRUE;
105         consist->segment = FALSE;
106         break;
107       case GST_EVENT_TAG:
108         GST_DEBUG_OBJECT (pad, "tag %" GST_PTR_FORMAT,
109             gst_event_get_structure (event));
110         /* fall through */
111       default:
112         if (GST_EVENT_IS_SERIALIZED (event) && GST_EVENT_IS_DOWNSTREAM (event)) {
113           fail_if (consist->eos, "Event received after EOS");
114           fail_unless (consist->segment, "Event received before segment");
115         }
116         /* FIXME : Figure out what to do for other events */
117         break;
118     }
119   }
120
121   return TRUE;
122 }
123
124 static gboolean
125 sink_pad_data_cb (GstPad * pad, GstPadProbeInfo * info,
126     GstStreamConsistency * consist)
127 {
128   GstMiniObject *data = GST_PAD_PROBE_INFO_DATA (info);
129
130   GST_DEBUG_OBJECT (pad, "%p: %d %d %d %d", consist, consist->flushing,
131       consist->segment, consist->eos, consist->expect_flush);
132
133   if (GST_IS_BUFFER (data)) {
134     GST_DEBUG_OBJECT (pad, "Buffer %" GST_TIME_FORMAT,
135         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (GST_BUFFER (data))));
136     /* If an EOS went through, a buffer would be invalid */
137     fail_if (consist->eos, "Buffer received after EOS");
138     /* Buffers need to be preceded by a segment event */
139     fail_unless (consist->segment, "Buffer received without segment");
140   } else if (GST_IS_EVENT (data)) {
141     GstEvent *event = (GstEvent *) data;
142
143     GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
144     switch (GST_EVENT_TYPE (event)) {
145       case GST_EVENT_SEEK:
146       {
147         GstSeekFlags flags;
148
149         gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL,
150             NULL);
151         consist->expect_flush =
152             ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
153         break;
154       }
155       case GST_EVENT_SEGMENT:
156         fail_if ((consist->expect_flush && consist->flushing),
157             "Received SEGMENT while in a flushing seek");
158         consist->segment = TRUE;
159         consist->eos = FALSE;
160         break;
161       default:
162         /* FIXME : Figure out what to do for other events */
163         break;
164     }
165   }
166
167   return TRUE;
168 }
169
170 static void
171 add_pad (GstStreamConsistency * consist, GstPad * pad)
172 {
173   GstStreamConsistencyProbe *p;
174   GstPadDirection dir;
175
176   p = g_new0 (GstStreamConsistencyProbe, 1);
177   p->pad = g_object_ref (pad);
178   dir = gst_pad_get_direction (pad);
179   if (dir == GST_PAD_SRC) {
180
181     p->probeid =
182         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
183         (GstPadProbeCallback) source_pad_data_cb, consist, NULL);
184
185   } else if (dir == GST_PAD_SINK) {
186     p->probeid =
187         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
188         (GstPadProbeCallback) sink_pad_data_cb, consist, NULL);
189   }
190   consist->pads = g_list_prepend (consist->pads, p);
191 }
192
193 /**
194  * gst_consistency_checker_new:
195  * @pad: The #GstPad on which the dataflow will be checked.
196  *
197  * Sets up a data probe on the given pad which will raise assertions if the
198  * data flow is inconsistent.
199  *
200  * Returns: A #GstStreamConsistency structure used to track data flow.
201  *
202  * Since: 0.10.24
203  */
204 GstStreamConsistency *
205 gst_consistency_checker_new (GstPad * pad)
206 {
207   GstStreamConsistency *consist;
208
209   g_return_val_if_fail (pad != NULL, NULL);
210
211   consist = g_new0 (GstStreamConsistency, 1);
212
213   if (!consist->pads) {
214     consist->parent = GST_OBJECT_PARENT (pad);
215   }
216   add_pad (consist, pad);
217   return consist;
218 }
219
220 /**
221  * gst_consistency_checker_add_pad:
222  * @consist: The #GstStreamConsistency handle
223  * @pad: The #GstPad on which the dataflow will be checked.
224  *
225  * Sets up a data probe on the given pad which will raise assertions if the
226  * data flow is inconsistent.
227  *
228  * Returns: %TRUE if the pad was added
229  *
230  * Since: 0.10.37
231  */
232 gboolean
233 gst_consistency_checker_add_pad (GstStreamConsistency * consist, GstPad * pad)
234 {
235   g_return_val_if_fail (consist != NULL, FALSE);
236   g_return_val_if_fail (pad != NULL, FALSE);
237   g_return_val_if_fail (GST_OBJECT_PARENT (pad) == consist->parent, FALSE);
238
239   add_pad (consist, pad);
240   return TRUE;
241 }
242
243 /**
244  * gst_consistency_checker_reset:
245  * @consist: The #GstStreamConsistency to reset.
246  *
247  * Reset the stream checker's internal variables.
248  *
249  * Since: 0.10.24
250  */
251
252 void
253 gst_consistency_checker_reset (GstStreamConsistency * consist)
254 {
255   consist->eos = FALSE;
256   consist->flushing = FALSE;
257   consist->segment = FALSE;
258 }
259
260 /**
261  * gst_consistency_checker_free:
262  * @consist: The #GstStreamConsistency to free.
263  *
264  * Frees the allocated data and probes associated with @consist.
265  *
266  * Since: 0.10.24
267  */
268
269 void
270 gst_consistency_checker_free (GstStreamConsistency * consist)
271 {
272   GList *node;
273   GstStreamConsistencyProbe *p;
274
275   /* Remove the data probes */
276   for (node = consist->pads; node; node = g_list_next (node)) {
277     p = (GstStreamConsistencyProbe *) node->data;
278     gst_pad_remove_probe (p->pad, p->probeid);
279     gst_object_unref (p->pad);
280     g_free (p);
281   }
282   g_list_free (consist->pads);
283   g_free (consist);
284 }