Move GstVideoAggregator, compositor and OpenGL mixers from -bad
[platform/upstream/gstreamer.git] / tests / check / elements / subparse.c
1 /* GStreamer unit tests for subparse
2  * Copyright (C) 2006-2008 Tim-Philipp Müller <tim centricular net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <gst/check/gstcheck.h>
25
26 #include <string.h>
27
28 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
29     GST_PAD_SINK,
30     GST_PAD_ALWAYS,
31     GST_STATIC_CAPS ("text/x-raw, format = { pango-markup, utf8 }")
32     );
33 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
34     GST_PAD_SRC,
35     GST_PAD_ALWAYS,
36     GST_STATIC_CAPS ("ANY")
37     );
38
39 static GstElement *subparse;
40 static GstPad *mysrcpad, *mysinkpad;
41
42 static GstBuffer *
43 buffer_from_static_string (const gchar * s)
44 {
45   GstBuffer *buf;
46   gsize len;
47
48   len = strlen (s);
49
50   buf = gst_buffer_new ();
51   gst_buffer_append_memory (buf,
52       gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
53           (gpointer) s, len, 0, len, NULL, NULL));
54
55   return buf;
56 }
57
58 typedef struct
59 {
60   const gchar *in;
61   GstClockTime from_ts;
62   GstClockTime to_ts;
63   const gchar *out;
64 } SubParseInputChunk;
65
66 static SubParseInputChunk srt_input[] = {
67   {
68         "1\n00:00:01,000 --> 00:00:02,000\nOne\n\n",
69       1 * GST_SECOND, 2 * GST_SECOND, "One"}, {
70         "2\n00:00:02,000 --> 00:00:03,000\nTwo\n\n",
71       2 * GST_SECOND, 3 * GST_SECOND, "Two"}, {
72         "3\n00:00:03,000 --> 00:00:04,000\nThree\n\n",
73       3 * GST_SECOND, 4 * GST_SECOND, "Three"}, {
74         "4\n00:00:04,000 --> 00:00:05,000\nFour\n\n",
75       4 * GST_SECOND, 5 * GST_SECOND, "Four"}, {
76         "5\n00:00:05,000 --> 00:00:06,000\nFive\n\n",
77       5 * GST_SECOND, 6 * GST_SECOND, "Five"}, {
78         /* markup should be preserved */
79         "6\n00:00:06,000 --> 00:00:07,000\n<i>Six</i>\n\n",
80       6 * GST_SECOND, 7 * GST_SECOND, "<i>Six</i>"}, {
81         /* open markup tags should be closed */
82         "7\n00:00:07,000 --> 00:00:08,000\n<i>Seven\n\n",
83       7 * GST_SECOND, 8 * GST_SECOND, "<i>Seven</i>"}, {
84         /* open markup tags should be closed (II) */
85         "8\n00:00:08,000 --> 00:00:09,000\n<b><i>Eight\n\n",
86       8 * GST_SECOND, 9 * GST_SECOND, "<b><i>Eight</i></b>"}, {
87         /* broken markup should be fixed */
88         "9\n00:00:09,000 --> 00:00:10,000\n</b>\n\n",
89       9 * GST_SECOND, 10 * GST_SECOND, ""}, {
90         "10\n00:00:10,000 --> 00:00:11,000\n</b></i>\n\n",
91       10 * GST_SECOND, 11 * GST_SECOND, ""}, {
92         "11\n00:00:11,000 --> 00:00:12,000\n<i>xyz</b></i>\n\n",
93       11 * GST_SECOND, 12 * GST_SECOND, "<i>xyz</i>"}, {
94         "12\n00:00:12,000 --> 00:00:13,000\n<i>xyz</b>\n\n",
95       12 * GST_SECOND, 13 * GST_SECOND, "<i>xyz</i>"}, {
96         "13\n00:00:13,000 --> 00:00:14,000\n<i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i>Keep them comiiiiiing\n\n",
97         13 * GST_SECOND, 14 * GST_SECOND,
98       "<i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i>Keep them comiiiiiing</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>"}, {
99         /* skip a few chunk numbers here, the numbers shouldn't matter */
100         "24\n00:01:00,000 --> 00:02:00,000\nYep, still here\n\n",
101       60 * GST_SECOND, 120 * GST_SECOND, "Yep, still here"}, {
102         /* make sure stuff is escaped properly, but allowed markup stays intact */
103         "25\n00:03:00,000 --> 00:04:00,000\ngave <i>Rock & Roll</i> to\n\n",
104       180 * GST_SECOND, 240 * GST_SECOND, "gave <i>Rock &amp; Roll</i> to"}, {
105         "26\n00:04:00,000 --> 00:05:00,000\n<i>Rock & Roll</i>\n\n",
106       240 * GST_SECOND, 300 * GST_SECOND, "<i>Rock &amp; Roll</i>"}, {
107         "27\n00:06:00,000 --> 00:08:00,000\nRock & Roll\n\n",
108       360 * GST_SECOND, 480 * GST_SECOND, "Rock &amp; Roll"}, {
109         "28\n00:10:00,000 --> 00:11:00,000\n"
110         "<font \"#0000FF\"><joj>This is </xxx>in blue but <5</font>\n\n",
111       600 * GST_SECOND, 660 * GST_SECOND, "This is in blue but &lt;5"}, {
112         /* closing tags should be recognised properly even if there's a space */
113         "29\n00:11:00,000 --> 00:12:00,000\n" "<i>italics</ i>\n\n",
114       660 * GST_SECOND, 720 * GST_SECOND, "<i>italics</i>"}, {
115         /* closing tags should be escaped and fixed up if not recognised */
116         "30\n00:12:00,000 --> 00:12:01,000\n" "<i>italics</ x>\n\n",
117       720 * GST_SECOND, 721 * GST_SECOND, "<i>italics&lt;/ x&gt;</i>"},
118 };
119
120 /* starts with chunk number 0 (not exactly according to spec) */
121 static SubParseInputChunk srt_input0[] = {
122   {
123         "0\n00:00:01,000 --> 00:00:02,000\nOne\n\n",
124       1 * GST_SECOND, 2 * GST_SECOND, "One"}, {
125         "1\n00:00:02,000 --> 00:00:03,000\nTwo\n\n",
126       2 * GST_SECOND, 3 * GST_SECOND, "Two"}, {
127         "2\n00:00:03,000 --> 00:00:04,000\nThree\n\n",
128       3 * GST_SECOND, 4 * GST_SECOND, "Three"}
129 };
130
131 /* has spaces instead of doubled zeroes (not exactly according to spec) */
132 static SubParseInputChunk srt_input1[] = {
133   {
134         "1\n 0: 0:26, 26 --> 0: 0:28, 17\nI cant see.\n\n",
135         26 * GST_SECOND + 26 * GST_MSECOND,
136       28 * GST_SECOND + 17 * GST_MSECOND, "I cant see."},
137   {
138         "2\n 0: 0:30, 30 --> 0: 0:33, 22\nI really cant see.\n\n",
139         30 * GST_SECOND + 30 * GST_MSECOND,
140       33 * GST_SECOND + 22 * GST_MSECOND, "I really cant see."},
141   {
142         "3\n 0: 0:40, 40 --> 0: 0:44, 44\nI still cant see anything.\n\n",
143         40 * GST_SECOND + 40 * GST_MSECOND,
144       44 * GST_SECOND + 44 * GST_MSECOND, "I still cant see anything."}
145 };
146
147 /* has UTF-8 BOM at the start */
148 static SubParseInputChunk srt_input2[] = {
149   {
150         "\xef\xbb\xbf" "1\n00:00:00,000 --> 00:00:03,50\nJust testing.\n\n",
151       0, 3 * GST_SECOND + 500 * GST_MSECOND, "Just testing."}
152 };
153
154 /* starts with chunk number 0 and has less than three digits after the comma
155  * and a few extra spaces before the arrow or at the end of the line */
156 static SubParseInputChunk srt_input3[] = {
157   {
158         "0\n00:00:01,0 --> 00:00:02,0\nOne\n\n",
159       1000 * GST_MSECOND, 2000 * GST_MSECOND, "One"}, {
160         "1\n00:00:02,5   --> 00:00:03,  5 \nTwo\n\n",
161       2500 * GST_MSECOND, 3005 * GST_MSECOND, "Two"}, {
162         "2\n00:00:03, 9 --> 00:00:04,0   \nThree\n\n",
163       3090 * GST_MSECOND, 4000 * GST_MSECOND, "Three"}
164 };
165
166 /* Some WebVTT chunks, this format is similar to SRT but should be
167  * parsed differently nonetheless, the WebVTT tags should be stripped
168  * off. */
169 static SubParseInputChunk srt_input4[] = {
170   {
171         "1\n00:00:01,000 --> 00:00:02,000\n<v>some text\n\n",
172       1 * GST_SECOND, 2 * GST_SECOND, "some text"}
173   ,
174   {
175         "1\n00:00:01,000 --> 00:00:02,000\n<b.loud>some text\n\n",
176       1 * GST_SECOND, 2 * GST_SECOND, "<b>some text</b>"}
177   ,
178   {
179         "1\n00:00:01,000 --> 00:00:02,000\n<ruby>base text<rt>annotation</rt></ruby>\n\n",
180         1 * GST_SECOND, 2 * GST_SECOND,
181       "base textannotation"}
182   ,
183 };
184
185 static void
186 setup_subparse (void)
187 {
188   GstSegment segment;
189   subparse = gst_check_setup_element ("subparse");
190
191   mysrcpad = gst_check_setup_src_pad (subparse, &srctemplate);
192   mysinkpad = gst_check_setup_sink_pad (subparse, &sinktemplate);
193
194   gst_pad_set_active (mysrcpad, TRUE);
195
196   gst_segment_init (&segment, GST_FORMAT_BYTES);
197   gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
198   gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment));
199   gst_pad_set_active (mysinkpad, TRUE);
200
201   fail_unless_equals_int (gst_element_set_state (subparse, GST_STATE_PLAYING),
202       GST_STATE_CHANGE_SUCCESS);
203 }
204
205 static void
206 teardown_subparse (void)
207 {
208   GST_DEBUG ("cleaning up");
209
210   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
211   g_list_free (buffers);
212   buffers = NULL;
213
214   gst_pad_set_active (mysrcpad, FALSE);
215   gst_pad_set_active (mysinkpad, FALSE);
216   gst_check_teardown_sink_pad (subparse);
217   gst_check_teardown_src_pad (subparse);
218   gst_check_teardown_element (subparse);
219   subparse = NULL;
220   mysrcpad = NULL;
221   mysinkpad = NULL;
222 }
223
224 static void
225 test_srt_do_test (SubParseInputChunk * input, guint start_idx, guint num)
226 {
227   guint n;
228   GstCaps *outcaps;
229
230   GST_LOG ("srt test: start_idx = %u, num = %u", start_idx, num);
231
232   setup_subparse ();
233
234   for (n = start_idx; n < start_idx + num; ++n) {
235     GstBuffer *buf;
236
237     buf = buffer_from_static_string (input[n].in);
238     fail_unless_equals_int (gst_pad_push (mysrcpad, buf), GST_FLOW_OK);
239   }
240
241   gst_pad_push_event (mysrcpad, gst_event_new_eos ());
242
243   fail_unless_equals_int (g_list_length (buffers), num);
244
245   outcaps = gst_pad_get_current_caps (mysinkpad);
246
247   for (n = start_idx; n < start_idx + num; ++n) {
248     const GstStructure *buffer_caps_struct;
249     GstBuffer *buf;
250     GstMapInfo map;
251
252     buf = g_list_nth_data (buffers, n - start_idx);
253     fail_unless (buf != NULL);
254     fail_unless (GST_BUFFER_TIMESTAMP_IS_VALID (buf), NULL);
255     fail_unless (GST_BUFFER_DURATION_IS_VALID (buf), NULL);
256     fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf), input[n].from_ts);
257     fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf),
258         input[n].to_ts - input[n].from_ts);
259
260     gst_buffer_map (buf, &map, GST_MAP_READ);
261     /* can be NULL */
262     if (map.data != NULL) {
263       /* shouldn't have trailing newline characters */
264       fail_if (map.size > 0 && map.data[map.size - 1] == '\n');
265       /* shouldn't include NUL-terminator in data size */
266       fail_if (map.size > 0 && map.data[map.size - 1] == '\0');
267       /* but should still have a  NUL-terminator behind the declared data */
268       fail_unless_equals_int (map.data[map.size], '\0');
269       /* make sure out string matches expected string */
270       fail_unless_equals_string ((gchar *) map.data, input[n].out);
271     }
272     gst_buffer_unmap (buf, &map);
273     /* check caps */
274     fail_unless (outcaps != NULL);
275     buffer_caps_struct = gst_caps_get_structure (outcaps, 0);
276     fail_unless (gst_structure_has_name (buffer_caps_struct, "text/x-raw"));
277     fail_unless_equals_string (gst_structure_get_string (buffer_caps_struct,
278             "format"), "pango-markup");
279   }
280   gst_caps_unref (outcaps);
281
282   teardown_subparse ();
283 }
284
285 static void
286 test_vtt_do_test (SubParseInputChunk * input, guint start_idx, guint num)
287 {
288   guint n;
289
290   GST_LOG ("vtt test: start_idx = %u, num = %u", start_idx, num);
291
292   setup_subparse ();
293
294   for (n = start_idx; n < start_idx + num; ++n) {
295     GstBuffer *buf;
296     gchar *data = g_strconcat ("WEBVTT FILE\n", input[n].in, NULL);
297     buf = buffer_from_static_string (data);
298     fail_unless_equals_int (gst_pad_push (mysrcpad, buf), GST_FLOW_OK);
299     g_free (data);
300   }
301
302   gst_pad_push_event (mysrcpad, gst_event_new_eos ());
303
304   fail_unless_equals_int (g_list_length (buffers), num);
305
306   for (n = start_idx; n < start_idx + num; ++n) {
307     const GstStructure *buffer_caps_struct;
308     GstMapInfo map;
309     GstBuffer *buf;
310     GstCaps *outcaps;
311     gchar *out;
312     guint out_size;
313
314     buf = g_list_nth_data (buffers, n - start_idx);
315     fail_unless (buf != NULL);
316     fail_unless (GST_BUFFER_TIMESTAMP_IS_VALID (buf), NULL);
317     fail_unless (GST_BUFFER_DURATION_IS_VALID (buf), NULL);
318     fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf), input[n].from_ts);
319     fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf),
320         input[n].to_ts - input[n].from_ts);
321     fail_unless (gst_buffer_map (buf, &map, GST_MAP_READ));
322     out = (gchar *) map.data;
323
324     out_size = gst_buffer_get_size (buf);
325     /* shouldn't have trailing newline characters */
326     fail_if (out_size > 0 && out[out_size - 1] == '\n');
327     /* shouldn't include NUL-terminator in data size */
328     fail_if (out_size > 0 && out[out_size - 1] == '\0');
329     /* but should still have a  NUL-terminator behind the declared data */
330     fail_unless_equals_int (out[out_size], '\0');
331     /* make sure out string matches expected string */
332     fail_unless_equals_string (out, input[n].out);
333
334     gst_buffer_unmap (buf, &map);
335
336     /* check caps */
337     outcaps = gst_pad_get_current_caps (mysinkpad);
338     fail_unless (outcaps != NULL);
339     buffer_caps_struct = gst_caps_get_structure (outcaps, 0);
340     fail_unless_equals_string (gst_structure_get_name (buffer_caps_struct),
341         "text/x-raw");
342     fail_unless_equals_string (gst_structure_get_string (buffer_caps_struct,
343             "format"), "pango-markup");
344     gst_caps_unref (outcaps);
345   }
346
347   teardown_subparse ();
348 }
349
350 GST_START_TEST (test_srt)
351 {
352   test_srt_do_test (srt_input, 0, G_N_ELEMENTS (srt_input));
353
354   /* make sure everything works fine if we don't start with chunk 1 */
355   test_srt_do_test (srt_input, 1, G_N_ELEMENTS (srt_input) - 1);
356   test_srt_do_test (srt_input, 2, G_N_ELEMENTS (srt_input) - 2);
357   test_srt_do_test (srt_input, 3, G_N_ELEMENTS (srt_input) - 3);
358   test_srt_do_test (srt_input, 4, G_N_ELEMENTS (srt_input) - 4);
359
360   /* try with empty input, immediate EOS */
361   test_srt_do_test (srt_input, 5, G_N_ELEMENTS (srt_input) - 5);
362
363   /* try with chunk number 0 (which is not exactly according to spec) */
364   test_srt_do_test (srt_input0, 0, G_N_ELEMENTS (srt_input0));
365
366   /* try with spaces instead of doubled zeroes (which is not exactly according to spec) */
367   test_srt_do_test (srt_input1, 0, G_N_ELEMENTS (srt_input1));
368
369   /* try with UTF-8 BOM at the start */
370   test_srt_do_test (srt_input2, 0, G_N_ELEMENTS (srt_input2));
371
372   /* try with fewer than three post-comma digits, and some extra spaces */
373   test_srt_do_test (srt_input3, 0, G_N_ELEMENTS (srt_input3));
374
375   /* try with some WebVTT chunks */
376   test_srt_do_test (srt_input4, 0, G_N_ELEMENTS (srt_input4));
377 }
378
379 GST_END_TEST;
380
381
382 GST_START_TEST (test_webvtt)
383 {
384   SubParseInputChunk webvtt_input[] = {
385     {
386           "1\n00:00:01.000 --> 00:00:02.000 D:vertical T:50%\nOne\n\n",
387         1 * GST_SECOND, 2 * GST_SECOND, "One"}
388     ,
389     {
390           "1\n00:00:01.000 --> 00:00:02.000 D:vertical   T:50%\nOne\n\n",
391         1 * GST_SECOND, 2 * GST_SECOND, "One"}
392     ,
393     {
394           "1\n00:00:01.000 --> 00:00:02.000 D:vertical\tT:50%\nOne\n\n",
395         1 * GST_SECOND, 2 * GST_SECOND, "One"}
396     ,
397     {
398           "1\n00:00:01.000 --> 00:00:02.000 D:vertical-lr\nOne\n\n",
399         1 * GST_SECOND, 2 * GST_SECOND, "One"}
400     ,
401     {
402           "1\n00:00:01.000 --> 00:00:02.000 L:-123\nOne\n\n",
403         1 * GST_SECOND, 2 * GST_SECOND, "One"}
404     ,
405     {
406           "1\n00:00:01.000 --> 00:00:02.000 L:123\nOne\n\n",
407         1 * GST_SECOND, 2 * GST_SECOND, "One"}
408     ,
409     {
410           "1\n00:00:01.000 --> 00:00:02.000 L:12%\nOne\n\n",
411         1 * GST_SECOND, 2 * GST_SECOND, "One"}
412     ,
413     {
414           "1\n00:00:01.000 --> 00:00:02.000 L:12% S:35% A:start\nOne\n\n",
415         1 * GST_SECOND, 2 * GST_SECOND, "One"}
416     ,
417     {
418           "1\n00:00:01.000 --> 00:00:02.000 A:middle\nOne\n\n",
419         1 * GST_SECOND, 2 * GST_SECOND, "One"}
420     ,
421     {
422           "1\n00:00:01.000 --> 00:00:02.000 A:end\nOne\n\n",
423         1 * GST_SECOND, 2 * GST_SECOND, "One"}
424     ,
425     {
426           "1\n00:00:01.000 --> 00:00:02.000\nOne & Two\n\n",
427         1 * GST_SECOND, 2 * GST_SECOND, "One &amp; Two"}
428     ,
429     {
430           "1\n00:00:01.000 --> 00:00:02.000\nOne < Two\n\n",
431         1 * GST_SECOND, 2 * GST_SECOND, "One &lt; Two"}
432     ,
433     {
434           "1\n00:00:01.000 --> 00:00:02.000\n<v Spoke>Live long and prosper\n\n",
435         1 * GST_SECOND, 2 * GST_SECOND, "<v Spoke>Live long and prosper</v>"}
436     ,
437     {
438           "1\n00:00:01.000 --> 00:00:02.000\n<v The Joker>HAHAHA\n\n",
439         1 * GST_SECOND, 2 * GST_SECOND, "<v The Joker>HAHAHA</v>"}
440     ,
441     {
442           "1\n00:00:01.000 --> 00:00:02.000\n<c.someclass>some text\n\n",
443         1 * GST_SECOND, 2 * GST_SECOND, "<c.someclass>some text</c>"}
444     ,
445     {
446           "1\n00:00:01.000 --> 00:00:02.000\n<b.loud>some text\n\n",
447         1 * GST_SECOND, 2 * GST_SECOND, "<b.loud>some text</b>"}
448     ,
449     {
450           "1\n00:00:01.000 --> 00:00:02.000\n<ruby>base text<rt>annotation</rt></ruby>\n\n",
451           1 * GST_SECOND, 2 * GST_SECOND,
452         "<ruby>base text<rt>annotation</rt></ruby>"}
453     ,
454     {
455           "1\n00:00:01.000 --> 00:00:03.000\nOne... <00:00:00,200>Two... <00:00:00,500>Three...\n\n",
456           1 * GST_SECOND, 3 * GST_SECOND,
457         "One... &lt;00:00:00,200&gt;Two... &lt;00:00:00,500&gt;Three..."}
458     ,
459     {"1\n00:00:02.000 --> 00:00:03.000\nHello\nWorld\n\n",
460         2 * GST_SECOND, 3 * GST_SECOND, "Hello\nWorld"}
461     ,
462   };
463   test_vtt_do_test (webvtt_input, 0, G_N_ELEMENTS (webvtt_input));
464 }
465
466 GST_END_TEST;
467
468 static void
469 do_test (SubParseInputChunk * input, guint num, const gchar * format)
470 {
471   guint n;
472   GstCaps *outcaps;
473
474   setup_subparse ();
475
476   for (n = 0; n < num; ++n) {
477     GstBuffer *buf;
478
479     buf = buffer_from_static_string (input[n].in);
480     fail_unless_equals_int (gst_pad_push (mysrcpad, buf), GST_FLOW_OK);
481   }
482
483   gst_pad_push_event (mysrcpad, gst_event_new_eos ());
484
485   fail_unless_equals_int (g_list_length (buffers), num);
486
487   outcaps = gst_pad_get_current_caps (mysinkpad);
488
489   for (n = 0; n < num; ++n) {
490     const GstStructure *buffer_caps_struct;
491     GstBuffer *buf;
492     GstMapInfo map;
493
494     buf = g_list_nth_data (buffers, n);
495     fail_unless (buf != NULL);
496
497     /* check timestamp */
498     fail_unless (GST_BUFFER_TIMESTAMP_IS_VALID (buf), NULL);
499     fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf), input[n].from_ts);
500
501     /* might not be able to put a duration on the last buffer */
502     if (input[n].to_ts != GST_CLOCK_TIME_NONE) {
503       /* check duration */
504       fail_unless (GST_BUFFER_DURATION_IS_VALID (buf), NULL);
505       fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf),
506           input[n].to_ts - input[n].from_ts);
507     }
508
509     gst_buffer_map (buf, &map, GST_MAP_READ);
510     /* can be NULL */
511     if (map.data != NULL) {
512       /* shouldn't have trailing newline characters */
513       fail_if (map.size > 0 && map.data[map.size - 1] == '\n');
514       /* shouldn't include NUL-terminator in data size */
515       fail_if (map.size > 0 && map.data[map.size - 1] == '\0');
516       /* but should still have a  NUL-terminator behind the declared data */
517       fail_unless_equals_int (map.data[map.size], '\0');
518       /* make sure out string matches expected string */
519       fail_unless_equals_string ((gchar *) map.data, input[n].out);
520     }
521     gst_buffer_unmap (buf, &map);
522     /* check caps */
523     fail_unless (outcaps != NULL);
524     buffer_caps_struct = gst_caps_get_structure (outcaps, 0);
525     fail_unless (gst_structure_has_name (buffer_caps_struct, "text/x-raw"));
526     fail_unless_equals_string (gst_structure_get_string (buffer_caps_struct,
527             "format"), format);
528   }
529   gst_caps_unref (outcaps);
530
531   teardown_subparse ();
532 }
533
534 static void
535 test_tmplayer_do_test (SubParseInputChunk * input, guint num)
536 {
537   do_test (input, num, "utf8");
538 }
539
540 static void
541 test_microdvd_do_test (SubParseInputChunk * input, guint num)
542 {
543   do_test (input, num, "pango-markup");
544 }
545
546 GST_START_TEST (test_tmplayer_multiline)
547 {
548   static SubParseInputChunk tmplayer_multiline_input[] = {
549     {
550           "00:00:10,1=This is the Earth at a time\n"
551           "00:00:10,2=when the dinosaurs roamed...\n" "00:00:13,1=\n",
552           10 * GST_SECOND, 13 * GST_SECOND,
553         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
554           "00:00:14,1=a lush and fertile planet.\n" "00:00:16,1=\n",
555           14 * GST_SECOND, 16 * GST_SECOND,
556         "a lush and fertile planet."}
557   };
558
559   test_tmplayer_do_test (tmplayer_multiline_input,
560       G_N_ELEMENTS (tmplayer_multiline_input));
561 }
562
563 GST_END_TEST;
564
565 GST_START_TEST (test_tmplayer_multiline_with_bogus_lines)
566 {
567   static SubParseInputChunk tmplayer_multiline_b_input[] = {
568     {
569           "00:00:10,1=This is the Earth at a time\n"
570           "Yooboo wabahablablahuguug bogus line hello test 1-2-3-4\n"
571           "00:00:10,2=when the dinosaurs roamed...\n" "00:00:13,1=\n",
572           10 * GST_SECOND, 13 * GST_SECOND,
573         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
574           "00:00:14,1=a lush and fertile planet.\n" "00:00:16,1=\n",
575           14 * GST_SECOND, 16 * GST_SECOND,
576         "a lush and fertile planet."}
577   };
578
579   test_tmplayer_do_test (tmplayer_multiline_b_input,
580       G_N_ELEMENTS (tmplayer_multiline_b_input));
581 }
582
583 GST_END_TEST;
584
585 GST_START_TEST (test_tmplayer_style1)
586 {
587   static SubParseInputChunk tmplayer_style1_input[] = {
588     {
589           "00:00:10:This is the Earth at a time|when the dinosaurs roamed...\n"
590           "00:00:13:\n",
591           10 * GST_SECOND, 13 * GST_SECOND,
592         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
593           "00:00:14:a lush and fertile planet.\n" "00:00:16:\n",
594           14 * GST_SECOND, 16 * GST_SECOND,
595         "a lush and fertile planet."}
596   };
597
598   test_tmplayer_do_test (tmplayer_style1_input,
599       G_N_ELEMENTS (tmplayer_style1_input));
600 }
601
602 GST_END_TEST;
603
604 GST_START_TEST (test_tmplayer_style2)
605 {
606   static SubParseInputChunk tmplayer_style2_input[] = {
607     {
608           "00:00:10=This is the Earth at a time|when the dinosaurs roamed...\n"
609           "00:00:13=\n",
610           10 * GST_SECOND, 13 * GST_SECOND,
611         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
612           "00:00:14=a lush and fertile planet.\n" "00:00:16=\n",
613           14 * GST_SECOND, 16 * GST_SECOND,
614         "a lush and fertile planet."}
615   };
616
617   test_tmplayer_do_test (tmplayer_style2_input,
618       G_N_ELEMENTS (tmplayer_style2_input));
619 }
620
621 GST_END_TEST;
622
623 GST_START_TEST (test_tmplayer_style3)
624 {
625   static SubParseInputChunk tmplayer_style3_input[] = {
626     {
627           "0:00:10:This is the Earth at a time|when the dinosaurs roamed...\n"
628           "0:00:13:\n",
629           10 * GST_SECOND, 13 * GST_SECOND,
630         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
631           "0:00:14:a lush and fertile planet.\n" "0:00:16:\n",
632           14 * GST_SECOND, 16 * GST_SECOND,
633         "a lush and fertile planet."}
634   };
635
636   test_tmplayer_do_test (tmplayer_style3_input,
637       G_N_ELEMENTS (tmplayer_style3_input));
638 }
639
640 GST_END_TEST;
641
642 /* also tests the max_duration stuff (see second-last chunk which is supposed
643  * to be clipped to 5s duration) */
644 GST_START_TEST (test_tmplayer_style3b)
645 {
646   static SubParseInputChunk tmplayer_style3b_input[] = {
647     {
648           "0:00:10:This is the Earth at a time|when the dinosaurs roamed...\n",
649           10 * GST_SECOND, 14 * GST_SECOND,
650         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
651           "0:00:14:a lush and fertile planet.\n",
652           14 * GST_SECOND, 16 * GST_SECOND,
653         "a lush and fertile planet."}, {
654           "0:00:16:And they liked it a lot.\n",
655         16 * GST_SECOND, (16 + 5) * GST_SECOND, "And they liked it a lot."}, {
656           "0:00:30:Last line.",
657         30 * GST_SECOND, GST_CLOCK_TIME_NONE, "Last line."}
658   };
659
660   test_tmplayer_do_test (tmplayer_style3b_input,
661       G_N_ELEMENTS (tmplayer_style3b_input));
662 }
663
664 GST_END_TEST;
665
666 GST_START_TEST (test_tmplayer_style4)
667 {
668   static SubParseInputChunk tmplayer_style4_input[] = {
669     {
670           "0:00:10=This is the Earth at a time|when the dinosaurs roamed...\n"
671           "0:00:13=\n",
672           10 * GST_SECOND, 13 * GST_SECOND,
673         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
674           "0:00:14=a lush and fertile planet.\n" "0:00:16=\n",
675           14 * GST_SECOND, 16 * GST_SECOND,
676         "a lush and fertile planet."}
677   };
678
679   test_tmplayer_do_test (tmplayer_style4_input,
680       G_N_ELEMENTS (tmplayer_style4_input));
681 }
682
683 GST_END_TEST;
684
685 GST_START_TEST (test_tmplayer_style4_with_bogus_lines)
686 {
687   static SubParseInputChunk tmplayer_style4b_input[] = {
688     {
689           "0:00:10=This is the Earth at a time|when the dinosaurs roamed...\n"
690           "# This is a bogus line with a comment and should just be skipped\n"
691           "0:00:13=\n",
692           10 * GST_SECOND, 13 * GST_SECOND,
693         "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
694           "0:00:14=a lush and fertile planet.\n"
695           "                                                            \n"
696           "0:00:16=\n",
697           14 * GST_SECOND, 16 * GST_SECOND,
698         "a lush and fertile planet."}
699   };
700
701   test_tmplayer_do_test (tmplayer_style4b_input,
702       G_N_ELEMENTS (tmplayer_style4b_input));
703 }
704
705 GST_END_TEST;
706
707 GST_START_TEST (test_microdvd_with_italics)
708 {
709   static SubParseInputChunk microdvd_italics[] = {
710     {
711           "{1}{1}25.000 movie info: XVID  608x256 25.0fps 699.0 MB|"
712           "/SubEdit b.4060(http://subedit.com.pl)/\n"
713           "{100}{200}/italics/|not italics\n",
714           4 * GST_SECOND, 8 * GST_SECOND,
715         "<span style=\"italic\">italics</span>\n" "<span>not italics</span>"}
716   };
717
718   test_microdvd_do_test (microdvd_italics, G_N_ELEMENTS (microdvd_italics));
719 }
720
721 GST_END_TEST;
722
723 GST_START_TEST (test_microdvd_with_fps)
724 {
725   static SubParseInputChunk microdvd_input[] = {
726     {
727           "{1}{1}12.500\n{100}{200}- Hi, Eddie.|- Hiya, Scotty.\n",
728           8 * GST_SECOND, 16 * GST_SECOND,
729         "<span>- Hi, Eddie.</span>\n<span>- Hiya, Scotty.</span>"}, {
730           "{1250}{1350}- Cold enough for you?|- Well, I'm only faintly alive. "
731           "It's 25 below\n",
732           100 * GST_SECOND, 108 * GST_SECOND,
733         "<span>- Cold enough for you?</span>\n"
734           "<span>- Well, I&apos;m only faintly alive. It&apos;s 25 below</span>"}
735   };
736
737   test_microdvd_do_test (microdvd_input, G_N_ELEMENTS (microdvd_input));
738
739   /* and the same with ',' instead of '.' as floating point divider */
740   microdvd_input[0].in =
741       "{1}{1}12,500\n{100}{200}- Hi, Eddie.|- Hiya, Scotty.\n";
742   test_microdvd_do_test (microdvd_input, G_N_ELEMENTS (microdvd_input));
743 }
744
745 GST_END_TEST;
746
747 GST_START_TEST (test_mpl2)
748 {
749   SubParseInputChunk mpl2_input[] = {
750     {
751           "[123][456] This is the Earth at a time|when the dinosaurs roamed\n",
752           (123 * GST_SECOND) / 10, (456 * GST_SECOND) / 10,
753         "This is the Earth at a time\nwhen the dinosaurs roamed"}, {
754           "[1234][5678]a lush and fertile planet.\n",
755           (1234 * GST_SECOND) / 10, (5678 * GST_SECOND) / 10,
756         "a lush and fertile planet."}, {
757           "[12345][27890] /Italic|Normal\n",
758           (12345 * GST_SECOND) / 10, (27890 * GST_SECOND) / 10,
759         "<i>Italic</i>\nNormal"}, {
760           "[32345][37890]/Italic|/Italic\n",
761           (32345 * GST_SECOND) / 10, (37890 * GST_SECOND) / 10,
762         "<i>Italic</i>\n<i>Italic</i>"}, {
763           "[42345][47890] Normal|/Italic",
764           (42345 * GST_SECOND) / 10, (47890 * GST_SECOND) / 10,
765         "Normal\n<i>Italic</i>"}
766   };
767
768   do_test (mpl2_input, G_N_ELEMENTS (mpl2_input), "pango-markup");
769 }
770
771 GST_END_TEST;
772
773 GST_START_TEST (test_subviewer)
774 {
775   SubParseInputChunk subviewer_input[] = {
776     {
777           "[INFORMATION]\n"
778           "[TITLE]xxxxxxxxxx\n"
779           "[AUTHOR]xxxxxxxx\n"
780           "[SOURCE]xxxxxxxxxxxxxxxx\n"
781           "[FILEPATH]\n"
782           "[DELAY]0\n"
783           "[COMMENT]\n"
784           "[END INFORMATION]\n"
785           "[SUBTITLE]\n"
786           "[COLF]&HFFFFFF,[STYLE]bd,[SIZE]18,[FONT]Arial\n"
787           "00:00:41.00,00:00:44.40\n"
788           "The Age of Gods was closing.\n"
789           "Eternity had come to an end.\n"
790           "\n", 41 * GST_SECOND, 44 * GST_SECOND + 40 * GST_MSECOND,
791         "The Age of Gods was closing.\nEternity had come to an end."}, {
792           "00:00:55.00,00:00:58.40\n"
793           "The heavens shook as the armies\n"
794           "of Falis, God of Light...\n\n", 55 * GST_SECOND,
795           58 * GST_SECOND + 40 * GST_MSECOND,
796         "The heavens shook as the armies\nof Falis, God of Light..."}
797   };
798
799   do_test (subviewer_input, G_N_ELEMENTS (subviewer_input), "utf8");
800 }
801
802 GST_END_TEST;
803
804 GST_START_TEST (test_subviewer2)
805 {
806   SubParseInputChunk subviewer2_input[] = {
807     {
808           "[INFORMATION]\n"
809           "[TITLE]xxxxxxxxxx\n"
810           "[AUTHOR]xxxxxxxxxx\n"
811           "[SOURCE]xxxxxxxxxx\n"
812           "[PRG]\n"
813           "[FILEPATH]\n"
814           "[DELAY]0\n"
815           "[CD TRACK]0\n"
816           "[COMMENT]\n"
817           "[END INFORMATION]\n"
818           "[SUBTITLE]\n"
819           "[COLF]&H00FFFF,[STYLE]no,[SIZE]12,[FONT]Courier New\n"
820           "00:00:07.00,00:00:11.91\n"
821           "THERE IS A PLACE ON EARTH WHERE IT[br]IS STILL THE MORNING OF LIFE...\n\n",
822           7 * GST_SECOND, 11 * GST_SECOND + 91 * GST_MSECOND,
823         "THERE IS A PLACE ON EARTH WHERE IT\nIS STILL THE MORNING OF LIFE..."}, {
824           "00:00:12.48,00:00:15.17\n"
825           "AND THE GREAT HERDS RUN FREE.[br]SO WHAT?!\n\n",
826           12 * GST_SECOND + 48 * GST_MSECOND,
827           15 * GST_SECOND + 17 * GST_MSECOND,
828         "AND THE GREAT HERDS RUN FREE.\nSO WHAT?!"}
829   };
830
831   do_test (subviewer2_input, G_N_ELEMENTS (subviewer2_input), "utf8");
832 }
833
834 GST_END_TEST;
835
836 GST_START_TEST (test_dks)
837 {
838   SubParseInputChunk dks_input[] = {
839     {
840           "[00:00:07]THERE IS A PLACE ON EARTH WHERE IT[br]IS STILL THE MORNING OF LIFE...\n[00:00:12]\n",
841           7 * GST_SECOND, 12 * GST_SECOND,
842         "THERE IS A PLACE ON EARTH WHERE IT\nIS STILL THE MORNING OF LIFE..."}, {
843           "[00:00:13]AND THE GREAT HERDS RUN FREE.[br]SO WHAT?!\n[00:00:15]\n",
844           13 * GST_SECOND, 15 * GST_SECOND,
845         "AND THE GREAT HERDS RUN FREE.\nSO WHAT?!"}
846   };
847
848   do_test (dks_input, G_N_ELEMENTS (dks_input), "utf8");
849 }
850
851 GST_END_TEST;
852
853 GST_START_TEST (test_sami)
854 {
855   SubParseInputChunk sami_input[] = {
856     {"<SAMI>\n"
857           "<HEAD>\n"
858           "    <TITLE>Subtitle</TITLE>\n"
859           "    <STYLE TYPE=\"text/css\">\n"
860           "    <!--\n"
861           "        P {margin-left:8pt; margin-right:8pt; margin-bottom:2pt; margin-top:2pt; text-align:center; font-size:12pt; font-weight:normal; color:black;}\n"
862           "        .CC {Name:English; lang:en-AU; SAMIType:CC;}\n"
863           "        #STDPrn {Name:Standard Print;}\n"
864           "        #LargePrn {Name:Large Print; font-size:24pt;}\n"
865           "        #SmallPrn {Name:Small Print; font-size:16pt;}\n"
866           "    -->\n"
867           "    </Style>\n"
868           "</HEAD>\n"
869           "<BODY>\n"
870           "    <SYNC Start=1000>\n"
871           "        <P Class=CC>\n"
872           "            This is a comment.<br>\n"
873           "            This is a second comment.\n",
874           1000 * GST_MSECOND, 2000 * GST_MSECOND,
875         "This is a comment.\nThis is a second comment."},
876     {"    <SYNC Start=2000>\n"
877           "        <P Class=CC>\n"
878           "            This is a third comment.<br>\n"
879           "            This is a fourth comment.\n" "</BODY>\n" "</SAMI>\n",
880           2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
881         "This is a third comment.\nThis is a fourth comment."}
882   };
883
884   do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
885 }
886
887 GST_END_TEST;
888
889 GST_START_TEST (test_sami_xml_entities)
890 {
891   SubParseInputChunk sami_input[] = {
892     {"<SAMI>\n"
893           "<BODY>\n"
894           "    <SYNC Start=1000>\n"
895           "        <P Class=CC>\n" "            &lt;Hello&gt; &amp;\n",
896           1000 * GST_MSECOND, 2000 * GST_MSECOND,
897         "&lt;Hello&gt; &amp;"},
898     {"    <SYNC Start=2000>\n"
899           "        <P Class=CC>\n"
900           "            &quot;World&apos;\n" "</BODY>\n" "</SAMI>\n",
901           2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
902         "&quot;World&apos;"}
903
904   };
905
906   do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
907 }
908
909 GST_END_TEST;
910
911 GST_START_TEST (test_sami_html_entities)
912 {
913   SubParseInputChunk sami_input[] = {
914     {"<SAMI>\n"
915           "<BODY>\n"
916           "    <SYNC Start=1000>\n"
917           "        <P Class=CC>\n" "            &nbsp; &plusmn; &acute;\n",
918           1000 * GST_MSECOND, 2000 * GST_MSECOND,
919         "\xc2\xa0 \xc2\xb1 \xc2\xb4"},
920     {"    <SYNC Start=2000>\n"
921           "        <P Class=CC>\n" "            &Alpha; &omega;\n",
922           2000 * GST_MSECOND, 3000 * GST_MSECOND,
923         "\xce\x91 \xcf\x89"},
924     {"    <SYNC Start=3000>\n"
925           "        <P Class=CC>\n"
926           "            &#xa0; &#177; &#180;\n" "</BODY>\n" "</SAMI>\n",
927           3000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
928         "\xc2\xa0 \xc2\xb1 \xc2\xb4"}
929   };
930
931   do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
932 }
933
934 GST_END_TEST;
935
936 GST_START_TEST (test_sami_bad_entities)
937 {
938   SubParseInputChunk sami_input[] = {
939     {"<SAMI>\n"
940           "<BODY>\n"
941           "    <SYNC Start=1000>\n"
942           "        <P Class=CC>\n" "            &nbsp &\n",
943           1000 * GST_MSECOND, 2000 * GST_MSECOND,
944         "\xc2\xa0 &amp;"},
945     {"    <SYNC Start=2000>\n"
946           "        <P Class=CC>\n"
947           "            &#xa0 &#177 &#180;\n" "</BODY>\n" "</SAMI>\n",
948           2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
949         "\xc2\xa0 \xc2\xb1 \xc2\xb4"}
950   };
951
952   do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
953 }
954
955 GST_END_TEST;
956
957 GST_START_TEST (test_sami_comment)
958 {
959   SubParseInputChunk sami_input[] = {
960     {"<SAMI>\n"
961           "<!--\n"
962           "=======\n"
963           "foo bar\n"
964           "=======\n"
965           "-->\n"
966           "<BODY>\n"
967           "    <SYNC Start=1000>\n"
968           "        <P Class=\"C====\">\n" "            &nbsp &\n",
969           1000 * GST_MSECOND, 2000 * GST_MSECOND,
970         "\xc2\xa0 &amp;"},
971     {"    <SYNC Start=2000>\n"
972           "        <P Class=CC>\n"
973           "            &#xa0 &#177 &#180;\n" "</BODY>\n" "</SAMI>\n",
974           2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
975         "\xc2\xa0 \xc2\xb1 \xc2\xb4"}
976   };
977
978   do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
979 }
980
981 GST_END_TEST;
982
983 GST_START_TEST (test_lrc)
984 {
985   SubParseInputChunk lrc_input[] = {
986     {"[ar:123]\n" "[ti:Title]\n" "[al:Album]\n" "[00:02.23]Line 1\n",
987           2230 * GST_MSECOND, GST_CLOCK_TIME_NONE,
988         "Line 1"},
989     {"[00:05.10]Line 2\n",
990           5100 * GST_MSECOND, GST_CLOCK_TIME_NONE,
991         "Line 2"},
992     {"[00:06.123]Line 3\n",
993           6123 * GST_MSECOND, GST_CLOCK_TIME_NONE,
994         "Line 3"}
995   };
996
997   do_test (lrc_input, G_N_ELEMENTS (lrc_input), "utf8");
998 }
999
1000 GST_END_TEST;
1001
1002 /* TODO:
1003  *  - add/modify tests so that lines aren't dogfed to the parsers in complete
1004  *    lines or sets of complete lines, but rather in random chunks
1005  */
1006
1007 static Suite *
1008 subparse_suite (void)
1009 {
1010   Suite *s = suite_create ("subparse");
1011   TCase *tc_chain = tcase_create ("general");
1012
1013   suite_add_tcase (s, tc_chain);
1014
1015   tcase_add_test (tc_chain, test_srt);
1016   tcase_add_test (tc_chain, test_webvtt);
1017   tcase_add_test (tc_chain, test_tmplayer_multiline);
1018   tcase_add_test (tc_chain, test_tmplayer_multiline_with_bogus_lines);
1019   tcase_add_test (tc_chain, test_tmplayer_style1);
1020   tcase_add_test (tc_chain, test_tmplayer_style2);
1021   tcase_add_test (tc_chain, test_tmplayer_style3);
1022   tcase_add_test (tc_chain, test_tmplayer_style3b);
1023   tcase_add_test (tc_chain, test_tmplayer_style4);
1024   tcase_add_test (tc_chain, test_tmplayer_style4_with_bogus_lines);
1025   tcase_add_test (tc_chain, test_microdvd_with_fps);
1026   tcase_add_test (tc_chain, test_microdvd_with_italics);
1027   tcase_add_test (tc_chain, test_mpl2);
1028   tcase_add_test (tc_chain, test_subviewer);
1029   tcase_add_test (tc_chain, test_subviewer2);
1030   tcase_add_test (tc_chain, test_dks);
1031   tcase_add_test (tc_chain, test_sami);
1032   tcase_add_test (tc_chain, test_sami_xml_entities);
1033   tcase_add_test (tc_chain, test_sami_html_entities);
1034   tcase_add_test (tc_chain, test_sami_bad_entities);
1035   tcase_add_test (tc_chain, test_sami_comment);
1036   tcase_add_test (tc_chain, test_lrc);
1037   return s;
1038 }
1039
1040 GST_CHECK_MAIN (subparse);