1 /* GStreamer Editing Services
2 * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
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.
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.
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 #include <gst/check/gstcheck.h>
24 #define GetCurrentDir getcwd
26 #include <gst/audio/audio.h>
28 #define KEY_FILE_START {\
29 if (cmp) g_key_file_free (cmp);\
30 cmp = g_key_file_new ();\
33 #define KEY(group, key, value) \
34 g_key_file_set_value (cmp, group, key, value)
36 #define COMPARE fail_unless(compare (cmp, formatter, timeline))
39 compare (GKeyFile * cmp, GESFormatter * formatter, GESTimeline * timeline)
41 gchar *data, *fmt_data;
43 gboolean result = TRUE;
45 data = g_key_file_to_data (cmp, &length, NULL);
46 ges_formatter_save (formatter, timeline);
47 fmt_data = ges_formatter_get_data (formatter, &length);
49 if (!(g_strcmp0 (data, fmt_data) == 0)) {
50 GST_ERROR ("difference between expected and output");
51 GST_ERROR ("expected: \n%s", data);
52 GST_ERROR ("actual: \n%s", fmt_data);
59 GST_START_TEST (test_keyfile_save)
61 GESTimeline *timeline;
62 GESTimelineLayer *layer, *layer2;
64 GESTimelineObject *source;
65 GESFormatter *formatter;
72 GST_DEBUG ("Create a timeline");
73 timeline = ges_timeline_new ();
74 fail_unless (timeline != NULL);
76 /* create serialization object */
78 GST_DEBUG ("creating a keyfile formatter");
79 formatter = GES_FORMATTER (ges_keyfile_formatter_new ());
81 /* add a layer and make sure it's serialized */
83 GST_DEBUG ("Create a layer");
84 layer = GES_TIMELINE_LAYER (ges_simple_timeline_layer_new ());
85 fail_unless (layer != NULL);
87 GST_DEBUG ("Add the layer to the timeline");
88 fail_unless (ges_timeline_add_layer (timeline, layer));
91 KEY ("General", "version", "1");
92 KEY ("Layer0", "priority", "0");
93 KEY ("Layer0", "type", "simple");
96 /* add a track and make sure it's serialized */
98 GST_DEBUG ("Create a Track");
99 track = ges_track_audio_raw_new ();
100 fail_unless (track != NULL);
102 GST_DEBUG ("Add the track to the timeline");
103 fail_unless (ges_timeline_add_track (timeline, track));
106 KEY ("General", "version", "1");
107 KEY ("Track0", "type", "GES_TRACK_TYPE_AUDIO");
108 KEY ("Track0", "caps", "audio/x-raw");
109 KEY ("Layer0", "priority", "0");
110 KEY ("Layer0", "type", "simple");
115 GST_DEBUG ("Adding first source");
116 source = (GESTimelineObject *) ges_timeline_test_source_new ();
117 ges_simple_timeline_layer_add_object (GES_SIMPLE_TIMELINE_LAYER (layer),
119 g_object_set (G_OBJECT (source), "duration", (guint64) 2 * GST_SECOND, NULL);
121 KEY ("Object0", "type", "GESTimelineTestSource");
122 KEY ("Object0", "start", "0");
123 KEY ("Object0", "in-point", "0");
124 KEY ("Object0", "duration", "2000000000");
125 KEY ("Object0", "priority", "2");
126 KEY ("Object0", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
127 KEY ("Object0", "max-duration", "18446744073709551615");
128 KEY ("Object0", "mute", "false");
129 KEY ("Object0", "vpattern", "100% Black");
130 KEY ("Object0", "freq", "440");
131 KEY ("Object0", "volume", "0");
134 GST_DEBUG ("Adding transition");
135 source = (GESTimelineObject *)
136 ges_timeline_standard_transition_new_for_nick ((gchar *) "bar-wipe-lr");
138 g_object_set (G_OBJECT (source), "duration", (guint64) GST_SECOND / 2, NULL);
139 ges_simple_timeline_layer_add_object (GES_SIMPLE_TIMELINE_LAYER (layer),
142 KEY ("Object1", "type", "GESTimelineStandardTransition");
143 KEY ("Object1", "start", "1500000000");
144 KEY ("Object1", "in-point", "0");
145 KEY ("Object1", "duration", "500000000");
146 KEY ("Object1", "priority", "1");
147 KEY ("Object1", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
148 KEY ("Object1", "max-duration", "18446744073709551615");
149 KEY ("Object1", "vtype", "A bar moves from left to right");
152 GST_DEBUG ("Adding second source");
153 source = (GESTimelineObject *) ges_timeline_test_source_new ();
154 g_object_set (G_OBJECT (source), "duration", (guint64) 2 * GST_SECOND, NULL);
155 ges_simple_timeline_layer_add_object (GES_SIMPLE_TIMELINE_LAYER (layer),
158 KEY ("Object2", "type", "GESTimelineTestSource");
159 KEY ("Object2", "start", "1500000000");
160 KEY ("Object2", "in-point", "0");
161 KEY ("Object2", "duration", "2000000000");
162 KEY ("Object2", "priority", "3");
163 KEY ("Object2", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
164 KEY ("Object2", "max-duration", "18446744073709551615");
165 KEY ("Object2", "mute", "false");
166 KEY ("Object2", "vpattern", "100% Black");
167 KEY ("Object2", "freq", "440");
168 KEY ("Object2", "volume", "0");
171 /* add a second layer to the timeline */
173 GST_DEBUG ("Adding a second layer to the timeline");
174 layer2 = ges_timeline_layer_new ();
175 ges_timeline_layer_set_priority (layer2, 1);
176 fail_unless (layer != NULL);
177 fail_unless (ges_timeline_add_layer (timeline, layer2));
179 KEY ("Layer1", "priority", "1");
180 KEY ("Layer1", "type", "default");
183 GST_DEBUG ("Adding a few more sources");
184 source = (GESTimelineObject *) ges_timeline_title_source_new ();
185 g_object_set (G_OBJECT (source),
186 "duration", (guint64) GST_SECOND,
187 "start", (guint64) 5 * GST_SECOND, "text", "the quick brown fox", NULL);
188 fail_unless (ges_timeline_layer_add_object (layer2, source));
190 KEY ("Object3", "type", "GESTimelineTitleSource");
191 KEY ("Object3", "start", "5000000000");
192 KEY ("Object3", "in-point", "0");
193 KEY ("Object3", "duration", "1000000000");
194 KEY ("Object3", "priority", "0");
195 KEY ("Object3", "supported-formats", "GES_TRACK_TYPE_UNKNOWN");
196 KEY ("Object3", "max-duration", "18446744073709551615");
197 KEY ("Object3", "mute", "false");
198 KEY ("Object3", "text", "\"the\\\\ quick\\\\ brown\\\\ fox\"");
199 KEY ("Object3", "font-desc", "\"Serif\\\\ 36\"");
200 KEY ("Object3", "halignment", "center");
201 KEY ("Object3", "valignment", "baseline");
202 KEY ("Object3", "color", "4294967295");
203 KEY ("Object3", "xpos", "0.5");
204 KEY ("Object3", "ypos", "0.5");
208 g_key_file_free (cmp);
211 GST_DEBUG ("Removing layer from the timeline");
212 fail_unless (ges_timeline_remove_layer (timeline, layer));
213 fail_unless (ges_timeline_remove_layer (timeline, layer2));
215 GST_DEBUG ("Removing track from the timeline");
216 g_object_ref (track);
217 fail_unless (ges_timeline_remove_track (timeline, track));
218 fail_unless (ges_track_get_timeline (track) == NULL);
219 ASSERT_OBJECT_REFCOUNT (track, "track", 1);
220 g_object_unref (track);
222 ASSERT_OBJECT_REFCOUNT (timeline, "timeline", 1);
224 g_object_unref (timeline);
225 g_object_unref (formatter);
230 /* do action for every item and then free the list */
232 #define g_list_free_all(list) \
234 g_list_foreach(list, (GFunc) g_object_unref, NULL); \
238 /* print out a helpful error message when a comparison fails. Works with the
239 * TIMELINE_COMPARE_*, LAYER*, SIMPLE_LAYER*, abd TRACK, macros below to give
240 * information about the source line where the failing object was created.
243 #define CMP_FAIL(a, ...) \
244 fail_unless (FALSE, __VA_ARGS__);
246 /* compare two GObjects for equality. pointer identity and GType short-circuit
247 * the comparison. If a and b are not identical pointers and of the same
248 * GType, compares every readable property for equality using
249 * g_param_values_cmp.
253 ges_objs_equal (GObject * a, GObject * b)
257 GParamSpec **props = NULL, **iter = NULL;
262 GST_DEBUG ("comparing %s (%p) and %s (%p)\n",
263 G_OBJECT_TYPE_NAME (a), a, G_OBJECT_TYPE_NAME (b), b);
268 at = G_TYPE_FROM_INSTANCE (a);
270 fail_unless (at == G_TYPE_FROM_INSTANCE (b));
272 typename = (gchar *) g_type_name (at);
274 /* compare every readable property */
276 klass = G_OBJECT_GET_CLASS (a);
277 props = g_object_class_list_properties (klass, &n_props);
279 for (i = 0, iter = props; i < n_props; i++, iter++) {
284 /* ignore name and layer properties */
285 if (!g_strcmp0 ("name", (*iter)->name) ||
286 !g_strcmp0 ("layer", (*iter)->name) ||
287 !g_strcmp0 ("parent", (*iter)->name))
290 /* special case caps property */
291 if (!g_strcmp0 ("caps", (*iter)->name)) {
292 GstCaps *acaps, *bcaps;
294 g_object_get (a, "caps", &acaps, NULL);
295 g_object_get (b, "caps", &bcaps, NULL);
296 if (gst_caps_is_equal (acaps, bcaps)) {
297 gst_caps_unref (acaps);
298 gst_caps_unref (bcaps);
301 gst_caps_unref (acaps);
302 gst_caps_unref (bcaps);
303 CMP_FAIL (b, "%s's %p and %p differ by property caps", a, b);
308 g_value_init (&av, (*iter)->value_type);
309 g_value_init (&bv, (*iter)->value_type);
311 if (!((*iter)->flags & G_PARAM_READABLE))
314 g_object_get_property (a, (*iter)->name, &av);
315 g_object_get_property (b, (*iter)->name, &bv);
317 if (g_param_values_cmp (*iter, &av, &bv) != 0) {
318 gchar *a_str, *b_str;
320 a_str = gst_value_serialize (&av);
321 b_str = gst_value_serialize (&bv);
323 CMP_FAIL (b, "%s's %p and %p differ by property %s (%s != %s)",
324 typename, a, b, (*iter)->name, a_str, b_str);
345 ges_tracks_equal (GESTrack * a, GESTrack * b)
347 return ges_objs_equal (G_OBJECT (a), G_OBJECT (b));
351 ges_layers_equal (GESTimelineLayer * a, GESTimelineLayer * b)
353 GList *a_objs = NULL, *b_objs = NULL, *a_iter, *b_iter;
354 gboolean ret = FALSE;
357 if (!ges_objs_equal (G_OBJECT (a), G_OBJECT (b)))
360 a_objs = ges_timeline_layer_get_objects (a);
361 b_objs = ges_timeline_layer_get_objects (b);
363 /* one shortcoming of this procedure is that the objects need to be stored
364 * in the same order. Not sure if this is a problem in practice */
366 for (i = 0, a_iter = a_objs, b_iter = b_objs; a_iter && b_iter; a_iter =
367 a_iter->next, b_iter = b_iter->next, i++) {
368 if (!ges_objs_equal (a_iter->data, b_iter->data)) {
369 CMP_FAIL (b, "layers %p and %p differ by obj at position %d", a, b, i);
374 if (a_iter || b_iter) {
375 CMP_FAIL (b, "layers %p and %p have differing number of objects", a, b);
383 g_list_free_all (a_objs);
384 g_list_free_all (b_objs);
390 ges_timelines_equal (GESTimeline * a, GESTimeline * b)
392 GList *a_tracks, *b_tracks, *a_iter, *b_iter, *a_layers, *b_layers;
394 gboolean ret = FALSE;
397 if (!ges_objs_equal (G_OBJECT (a), G_OBJECT (b))) {
398 CMP_FAIL (b, "%p and %p are not of the same type");
402 a_tracks = ges_timeline_get_tracks (a);
403 b_tracks = ges_timeline_get_tracks (b);
404 a_layers = ges_timeline_get_layers (a);
405 b_layers = ges_timeline_get_layers (b);
407 /* one shortcoming of this procedure is that the objects need to be stored
408 * in the same order. Not sure if this is a problem in practice */
410 for (i = 0, a_iter = a_tracks, b_iter = b_tracks; a_iter && b_iter; a_iter =
411 a_iter->next, b_iter = b_iter->next, i++) {
412 if (!ges_tracks_equal (a_iter->data, b_iter->data)) {
413 CMP_FAIL (b, "GESTimelines %p and %p differ by tracks at position %d", a,
419 if (a_iter || b_iter) {
420 CMP_FAIL (b, "GESTimelines %p and %p have differing number of tracks", a,
425 for (i = 0, a_iter = a_layers, b_iter = b_layers; a_iter && b_iter; a_iter =
426 a_iter->next, b_iter = b_iter->next, i++) {
427 if (!ges_layers_equal (a_iter->data, b_iter->data)) {
432 if (a_iter || b_iter) {
433 CMP_FAIL (b, "GESTimelines %p and %p have differing numbre of layers", a,
442 g_list_free_all (a_tracks);
443 g_list_free_all (b_tracks);
444 g_list_free_all (a_layers);
445 g_list_free_all (b_layers);
450 #define TIMELINE_BEGIN(location) \
452 GESTimeline **a, *b;\
454 if (*a) g_object_unref (*a);\
455 b = ges_timeline_new();\
458 #define TIMELINE_END }
460 #define TIMELINE_COMPARE(a, b)\
462 fail_unless (ges_timelines_equal(a, b));\
465 #define TRACK(type, caps) \
469 c = gst_caps_from_string(caps);\
470 trk = ges_track_new (type, c);\
471 ges_timeline_add_track (b, trk);\
472 g_object_set_data(G_OBJECT(trk),"file", (void *) __FILE__);\
473 g_object_set_data(G_OBJECT(trk),"line", (void *) __LINE__);\
474 g_object_set_data(G_OBJECT(trk),"function", (void *) GST_FUNCTION);\
477 #define LAYER_BEGIN(priority) \
479 GESTimelineLayer *l;\
480 l = ges_timeline_layer_new ();\
481 ges_timeline_add_layer (b, l);\
482 ges_timeline_layer_set_priority (l, priority);\
483 g_object_set_data(G_OBJECT(l),"file", (void *) __FILE__);\
484 g_object_set_data(G_OBJECT(l),"line", (void *) __LINE__);\
485 g_object_set_data(G_OBJECT(l),"function", (void *) GST_FUNCTION);
490 #define LAYER_OBJECT(type, ...) \
492 GESTimelineObject *obj;\
493 obj = GES_TIMELINE_OBJECT(\
494 g_object_new ((type), __VA_ARGS__, NULL));\
495 ges_timeline_layer_add_object (l, obj);\
496 g_object_set_data(G_OBJECT(obj),"file", (void *) __FILE__);\
497 g_object_set_data(G_OBJECT(obj),"line", (void *) __LINE__);\
498 g_object_set_data(G_OBJECT(obj),"function", (void *) GST_FUNCTION);\
501 #define SIMPLE_LAYER_BEGIN(priority) \
503 GESSimpleTimelineLayer *l;\
504 l = ges_simple_timeline_layer_new ();\
505 ges_timeline_add_layer (b, GES_TIMELINE_LAYER(l));\
506 ges_timeline_layer_set_priority(GES_TIMELINE_LAYER(l), priority);\
507 g_object_set_data(G_OBJECT(l),"file", (void *) __FILE__);\
508 g_object_set_data(G_OBJECT(l),"line", (void *) __LINE__);\
509 g_object_set_data(G_OBJECT(l),"function", (void *) GST_FUNCTION);
511 #define SIMPLE_LAYER_OBJECT(type, position, ...) \
513 GESTimelineObject *obj;\
514 obj = GES_TIMELINE_OBJECT(\
515 g_object_new ((type), __VA_ARGS__, NULL));\
516 ges_simple_timeline_layer_add_object (l, obj, position);\
517 g_object_set_data(G_OBJECT(obj),"file", (void *) __FILE__);\
518 g_object_set_data(G_OBJECT(obj),"line", (void *) __LINE__);\
519 g_object_set_data(G_OBJECT(obj),"function", (void *) GST_FUNCTION);\
523 static const gchar *data = "\n[General]\n"
525 "type=GES_TRACK_TYPE_AUDIO\n"
533 "type=GESTimelineTestSource\n"
536 "duration=2000000000\n"
539 "vpattern=100% Black\n"
544 "type=GESTimelineStandardTransition\n"
547 "duration=500000000\n"
549 "vtype=A bar moves from left to right\n"
552 "type=GESTimelineTestSource\n"
555 "duration=2000000000\n"
558 "vpattern=100% Black\n"
567 "type=GESTimelineTitleSource\n"
570 "duration=1000000000\n"
573 "text=\"the\\\\ quick\\\\ brown\\\\ fox\"\n"
574 "font-desc=\"Serif\\\\ 36\"\n"
575 "halignment=center\n" "valignment=baseline\n";
577 GST_START_TEST (test_keyfile_load)
579 GESTimeline *timeline = NULL, *expected = NULL;
580 GESFormatter *formatter;
586 GST_DEBUG ("Create a timeline");
587 timeline = ges_timeline_new ();
588 fail_unless (timeline != NULL);
590 /* create serialization object */
592 GST_DEBUG ("creating a default formatter");
593 formatter = GES_FORMATTER (ges_keyfile_formatter_new ());
595 ges_formatter_set_data (formatter, g_strdup (data), strlen (data));
597 fail_unless (ges_formatter_load (formatter, timeline));
599 TIMELINE_BEGIN (expected) {
601 TRACK (GES_TRACK_TYPE_AUDIO, "audio/x-raw");
603 SIMPLE_LAYER_BEGIN (0) {
605 SIMPLE_LAYER_OBJECT ((GES_TYPE_TIMELINE_TEST_SOURCE), -1,
606 "duration", (guint64) 2 * GST_SECOND);
608 SIMPLE_LAYER_OBJECT ((GES_TYPE_TIMELINE_STANDARD_TRANSITION), -1,
609 "duration", (guint64) GST_SECOND / 2,
610 "vtype", GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR);
612 SIMPLE_LAYER_OBJECT ((GES_TYPE_TIMELINE_TEST_SOURCE), -1,
613 "duration", (guint64) 2 * GST_SECOND);
619 LAYER_OBJECT (GES_TYPE_TIMELINE_TITLE_SOURCE,
620 "start", (guint64) 5 * GST_SECOND,
621 "duration", (guint64) GST_SECOND, "priority", 2, "text",
622 "the quick brown fox");
628 TIMELINE_COMPARE (timeline, expected);
631 g_object_unref (formatter);
632 g_object_unref (timeline);
633 g_object_unref (expected);
638 GST_START_TEST (test_pitivi_file_load)
640 GESFormatter *formatter;
641 GESTimeline *timeline, *expected;
643 gchar *uri, *save_uri, *cur_dir;
645 /*create the expected timeline */
646 timeline = ges_timeline_new ();
647 mainloop = g_main_loop_new (NULL, FALSE);
648 expected = ges_timeline_new ();
650 /* create the timeline from formatter */
651 formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
652 cur_dir = g_get_current_dir ();
653 uri = g_build_filename (cur_dir, "test.xptv", NULL);
654 save_uri = g_build_filename (cur_dir, "testsave.xptv", NULL);
657 if (g_file_test (uri, G_FILE_TEST_EXISTS) == FALSE) {
658 GST_ERROR ("Could not test GESPitiviFormatter as no project file found");
662 ges_formatter_load_from_uri (formatter, timeline, uri);
663 g_timeout_add (1000, (GSourceFunc) g_main_loop_quit, mainloop);
664 g_main_loop_run (mainloop);
666 formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
667 ges_formatter_save_to_uri (formatter, timeline, save_uri);
668 formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
669 ges_formatter_load_from_uri (formatter, expected, uri);
670 g_timeout_add (1000, (GSourceFunc) g_main_loop_quit, mainloop);
671 g_main_loop_run (mainloop);
673 /* compare the two timelines and fail test if they are different */
674 TIMELINE_COMPARE (expected, timeline);
677 g_main_loop_unref (mainloop);
678 g_object_unref (formatter);
679 g_object_unref (timeline);
680 g_object_unref (expected);
685 GST_START_TEST (test_keyfile_identity)
688 /* we will create several timelines. they will first be serialized, then
689 * deseriailzed and compared against the original. */
691 GESTimeline *orig = NULL, *serialized = NULL;
692 GESFormatter *formatter;
696 formatter = GES_FORMATTER (ges_keyfile_formatter_new ());
698 TIMELINE_BEGIN (orig) {
700 TRACK (GES_TRACK_TYPE_AUDIO, "audio/x-raw,"
701 "format=(string)" GST_AUDIO_NE (S32) ",rate=8000");
702 TRACK (GES_TRACK_TYPE_VIDEO, "video/x-raw,format=(string)RGB24");
706 LAYER_OBJECT (GES_TYPE_TIMELINE_TEXT_OVERLAY,
707 "start", (guint64) GST_SECOND,
708 "duration", (guint64) 2 * GST_SECOND,
710 "text", "Hello, world!",
711 "font-desc", "Sans 9",
712 "halignment", GES_TEXT_HALIGN_LEFT,
713 "valignment", GES_TEXT_VALIGN_TOP);
715 LAYER_OBJECT (GES_TYPE_TIMELINE_TEST_SOURCE,
716 "start", (guint64) 0,
717 "duration", (guint64) 5 * GST_SECOND,
719 "freq", (gdouble) 500,
720 "volume", 1.0, "vpattern", GES_VIDEO_TEST_PATTERN_WHITE);
722 LAYER_OBJECT (GES_TYPE_TIMELINE_TEXT_OVERLAY,
723 "start", (guint64) 7 * GST_SECOND,
724 "duration", (guint64) 2 * GST_SECOND,
726 "text", "Hello, world!",
727 "font-desc", "Sans 9",
728 "halignment", GES_TEXT_HALIGN_LEFT,
729 "valignment", GES_TEXT_VALIGN_TOP);
731 LAYER_OBJECT (GES_TYPE_TIMELINE_TEST_SOURCE,
732 "start", (guint64) 6 * GST_SECOND,
733 "duration", (guint64) 5 * GST_SECOND,
735 "freq", (gdouble) 600,
736 "volume", 1.0, "vpattern", GES_VIDEO_TEST_PATTERN_RED);
744 serialized = ges_timeline_new ();
746 ges_formatter_save (formatter, orig);
747 ges_formatter_load (formatter, serialized);
749 TIMELINE_COMPARE (serialized, orig);
751 g_object_unref (formatter);
752 g_object_unref (serialized);
753 g_object_unref (orig);
761 Suite *s = suite_create ("ges-save-load");
762 TCase *tc_chain = tcase_create ("basic");
764 suite_add_tcase (s, tc_chain);
766 tcase_add_test (tc_chain, test_keyfile_save);
767 tcase_add_test (tc_chain, test_keyfile_load);
768 tcase_add_test (tc_chain, test_keyfile_identity);
769 tcase_add_test (tc_chain, test_pitivi_file_load);
775 main (int argc, char **argv)
779 Suite *s = ges_suite ();
780 SRunner *sr = srunner_create (s);
782 gst_check_init (&argc, &argv);
784 srunner_run_all (sr, CK_NORMAL);
785 nf = srunner_ntests_failed (sr);