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