validate: cleanup the use of GST_VALIDATE_API on Windows
[platform/upstream/gstreamer.git] / subprojects / gst-devtools / validate / gst / validate / gst-validate-runner.c
1 /* GStreamer
2  *
3  * Copyright (C) 2013-2016 Collabora Ltd.
4  *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
5  *  Author: Thibault Saunier <thibault.saunier@collabora.com>
6  *
7  * gst-validate-runner.c - Validate Runner class
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include "validate.h"
34 #include "gst-validate-internal.h"
35 #include "gst-validate-report.h"
36 #include "gst-validate-monitor-factory.h"
37 #include "gst-validate-override-registry.h"
38 #include "gst-validate-runner.h"
39 #include "gst-validate-reporter.h"
40 #include "gst-validate-mockdecryptor.h"
41
42 GST_DEBUG_CATEGORY_STATIC (gst_validate_runner_debug);
43 #undef GST_CAT_DEFAULT
44 #define GST_CAT_DEFAULT gst_validate_runner_debug
45
46 static gboolean element_created = FALSE;
47
48 /* We create a GstValidateRunner on _init ()
49  * so that we keep backward compatibility when
50  * the user create a Runner after creating the pipeline
51  * but the runner was actually already ready to be used.
52  */
53 static GstValidateRunner *first_runner = NULL;
54
55 /**
56  * SECTION:gst-validate-runner
57  * @title: GstValidateRunner
58  * @short_description: Class that runs Gst Validate tests for a pipeline
59  *
60  * Allows you to test a pipeline within GstValidate. It is the object where
61  * all issue reporting is done.
62  *
63  * In the tools using GstValidate the only minimal code to be able to monitor
64  * your pipelines is:
65  *
66  * |[
67  *  GstPipeline *pipeline = gst_pipeline_new ("monitored-pipeline");
68  *  GstValidateRunner *runner = gst_validate_runner_new ();
69  *  GstValidateMonitor *monitor = gst_validate_monitor_factory_create (
70  *          GST_OBJECT (pipeline), runner, NULL);
71  *
72  *  // Run the pipeline and do whatever you want with it
73  *
74  *  // In that same order
75  *  gst_object_unref (pipeline);
76  *  gst_object_unref (runner);
77  *  gst_object_unref (monitor);
78  * ]|
79  *
80  */
81
82 struct _GstValidateRunnerPrivate
83 {
84   GMutex mutex;
85   GList *reports;
86   GstValidateReportingDetails default_level;
87   GHashTable *reports_by_type;
88
89   /* A list of PatternLevel */
90   GList *report_pattern_levels;
91
92   /* Whether the runner was create with GST_TRACERS=validate or not) */
93   gboolean user_created;
94
95   gchar *pipeline_names;
96   gchar **pipeline_names_strv;
97
98   GList *expected_issues;
99 };
100
101 /* Describes the reporting level to apply to a name pattern */
102 typedef struct _PatternLevel
103 {
104   GPatternSpec *pattern;
105   GstValidateReportingDetails level;
106 } PatternLevel;
107
108 #define GST_VALIDATE_RUNNER_LOCK(r)                     \
109   G_STMT_START {                                        \
110   GST_LOG_OBJECT (r, "About to lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
111   (g_mutex_lock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex));           \
112   GST_LOG_OBJECT (r, "Acquired lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
113   } G_STMT_END
114
115 #define GST_VALIDATE_RUNNER_UNLOCK(r)                   \
116   G_STMT_START {                                        \
117   GST_LOG_OBJECT (r, "About to unlock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
118   (g_mutex_unlock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex));         \
119   GST_LOG_OBJECT (r, "Released lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
120   } G_STMT_END
121
122 #define gst_validate_runner_parent_class parent_class
123 G_DEFINE_TYPE_WITH_PRIVATE (GstValidateRunner, gst_validate_runner,
124     GST_TYPE_TRACER);
125
126 /* signals */
127 enum
128 {
129   REPORT_ADDED_SIGNAL,
130   STOPPING_SIGNAL,
131   /* add more above */
132   LAST_SIGNAL
133 };
134
135 enum
136 {
137   PROP_0,
138   PROP_PARAMS,
139   PROP_LAST
140 };
141
142 static GParamSpec *properties[PROP_LAST];
143
144 static guint _signals[LAST_SIGNAL] = { 0, };
145
146 static gboolean
147 gst_validate_runner_should_monitor (GstValidateRunner * self,
148     GstElement * element)
149 {
150   gint i;
151   GstValidateMonitor *monitor;
152
153   if (!GST_IS_PIPELINE (element)) {
154     return FALSE;
155   }
156
157   if (self->priv->user_created)
158     return FALSE;
159
160   if (!self->priv->pipeline_names_strv)
161     return TRUE;
162
163   monitor = gst_validate_get_monitor (G_OBJECT (element));
164
165   if (monitor) {
166     GST_ERROR_OBJECT (self, "Pipeline %" GST_PTR_FORMAT " is already"
167         " monitored by %" GST_PTR_FORMAT " using runner: %" GST_PTR_FORMAT
168         " NOT monitoring again.",
169         element, monitor,
170         gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
171   }
172
173   for (i = 0; self->priv->pipeline_names_strv[i]; i++) {
174     if (g_pattern_match_simple (self->priv->pipeline_names_strv[i],
175             GST_OBJECT_NAME (element)))
176       return TRUE;
177   }
178
179   return FALSE;
180 }
181
182 static void
183 do_element_new (GstValidateRunner * self, guint64 ts, GstElement * element)
184 {
185   element_created = TRUE;
186   if (gst_validate_runner_should_monitor (self, element)) {
187     /* the reference to the monitor is lost */
188     gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), self, NULL);
189   }
190 }
191
192 static gboolean
193 _parse_reporting_level (gchar * str, GstValidateReportingDetails * level)
194 {
195   if (!str)
196     return FALSE;
197
198   /* works in place */
199   g_strstrip (str);
200
201   if (g_ascii_isdigit (str[0])) {
202     unsigned long l;
203     char *endptr;
204     l = strtoul (str, &endptr, 10);
205     if (endptr > str && endptr[0] == 0) {
206       *level = (GstValidateReportingDetails) l;
207     } else {
208       return FALSE;
209     }
210   } else if (g_ascii_strcasecmp (str, "smart") == 0) {
211     *level = GST_VALIDATE_SHOW_SMART;
212   } else if (g_ascii_strcasecmp (str, "none") == 0) {
213     *level = GST_VALIDATE_SHOW_NONE;
214   } else if (g_ascii_strcasecmp (str, "synthetic") == 0) {
215     *level = GST_VALIDATE_SHOW_SYNTHETIC;
216   } else if (g_ascii_strcasecmp (str, "subchain") == 0) {
217     *level = GST_VALIDATE_SHOW_SUBCHAIN;
218   } else if (g_ascii_strcasecmp (str, "monitor") == 0) {
219     *level = GST_VALIDATE_SHOW_MONITOR;
220   } else if (g_ascii_strcasecmp (str, "all") == 0) {
221     *level = GST_VALIDATE_SHOW_ALL;
222   } else
223     return FALSE;
224
225   return TRUE;
226 }
227
228 static void
229 _free_report_pattern_level (PatternLevel * pattern_level)
230 {
231   g_pattern_spec_free (pattern_level->pattern);
232   g_free (pattern_level);
233 }
234
235 static void
236 _set_reporting_level_for_name (GstValidateRunner * runner,
237     const gchar * pattern, GstValidateReportingDetails level)
238 {
239   PatternLevel *pattern_level = g_malloc (sizeof (PatternLevel));
240   GPatternSpec *pattern_spec = g_pattern_spec_new (pattern);
241
242   pattern_level->pattern = pattern_spec;
243   pattern_level->level = level;
244
245   /* Allow the user to single out a pad with the "element-name__pad-name" syntax
246    */
247   if (g_strrstr (pattern, "__"))
248     runner->priv->report_pattern_levels =
249         g_list_prepend (runner->priv->report_pattern_levels, pattern_level);
250   else
251     runner->priv->report_pattern_levels =
252         g_list_append (runner->priv->report_pattern_levels, pattern_level);
253 }
254
255 static void
256 _replace_double_colons (gchar * word)
257 {
258   while (word) {
259     word = strstr (word, "::");
260     if (word) {
261       word[0] = '_';
262       word[1] = '_';
263     }
264   }
265 }
266
267 static void
268 _set_report_levels_from_string (GstValidateRunner * self, const gchar * list)
269 {
270   gchar **split;
271   gchar **walk;
272
273   g_assert (list);
274
275   GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list);
276
277   split = g_strsplit (list, ",", 0);
278
279   for (walk = split; *walk; walk++) {
280     _replace_double_colons (*walk);
281     if (strchr (*walk, ':')) {
282       gchar **values = g_strsplit (*walk, ":", 2);
283
284       if (values[0] && values[1]) {
285         GstValidateReportingDetails level;
286
287         if (_parse_reporting_level (values[1], &level))
288           _set_reporting_level_for_name (self, values[0], level);
289       }
290
291       g_strfreev (values);
292     } else {
293       GstValidateReportingDetails level;
294
295       if (_parse_reporting_level (*walk, &level))
296         self->priv->default_level = level;
297     }
298   }
299
300   g_strfreev (split);
301 }
302
303 static void
304 _init_report_levels (GstValidateRunner * self)
305 {
306   const gchar *env;
307
308   env = g_getenv ("GST_VALIDATE_REPORTING_DETAILS");
309   if (env)
310     _set_report_levels_from_string (self, env);
311 }
312
313 static void
314 _unref_report_list (gpointer unused, GList * reports, gpointer unused_too)
315 {
316   g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
317 }
318
319 static void
320 gst_validate_runner_finalize (GObject * object)
321 {
322   GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
323
324   if (!runner->priv->user_created)
325     gst_validate_runner_exit (runner, TRUE);
326
327   g_list_free_full (runner->priv->reports,
328       (GDestroyNotify) gst_validate_report_unref);
329
330   g_list_free_full (runner->priv->report_pattern_levels,
331       (GDestroyNotify) _free_report_pattern_level);
332
333   g_mutex_clear (&runner->priv->mutex);
334
335   g_free (runner->priv->pipeline_names);
336   g_strfreev (runner->priv->pipeline_names_strv);
337
338   g_hash_table_foreach (runner->priv->reports_by_type, (GHFunc)
339       _unref_report_list, NULL);
340   g_hash_table_destroy (runner->priv->reports_by_type);
341
342   G_OBJECT_CLASS (parent_class)->finalize (object);
343
344   if (!runner->priv->user_created)
345     gst_validate_deinit ();
346 }
347
348 static GObject *
349 gst_validate_runner_constructor (GType type, guint n_construct_params,
350     GObjectConstructParam * construct_params)
351 {
352   GObject *runner = G_OBJECT_CLASS (parent_class)->constructor (type,
353       n_construct_params, construct_params);
354
355   if (!gst_validate_is_initialized ()) {
356     first_runner = GST_VALIDATE_RUNNER (runner);
357     gst_validate_init ();
358     first_runner = NULL;
359
360     return runner;
361   }
362
363   return runner;
364 }
365
366
367 static void
368 gst_validate_runner_get_property (GObject * object, guint prop_id,
369     GValue * value, GParamSpec * pspec)
370 {
371   GstValidateRunner *runner;
372
373   runner = GST_VALIDATE_RUNNER (object);
374   switch (prop_id) {
375     case PROP_PARAMS:
376     {
377       g_value_set_string (value, runner->priv->pipeline_names);
378       break;
379     }
380     default:
381       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382       break;
383   }
384 }
385
386 static void
387 gst_validate_runner_set_property (GObject * object, guint prop_id,
388     const GValue * value, GParamSpec * pspec)
389 {
390   GstValidateRunner *runner;
391
392   runner = GST_VALIDATE_RUNNER (object);
393   switch (prop_id) {
394     case PROP_PARAMS:
395     {
396       g_free (runner->priv->pipeline_names);
397       g_strfreev (runner->priv->pipeline_names_strv);
398
399       runner->priv->pipeline_names = g_value_dup_string (value);
400       if (runner->priv->pipeline_names)
401         runner->priv->pipeline_names_strv =
402             g_strsplit (runner->priv->pipeline_names, ",", -1);
403       break;
404     }
405     default:
406       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
407       break;
408   }
409 }
410
411 static void
412 gst_validate_runner_class_init (GstValidateRunnerClass * klass)
413 {
414   GObjectClass *gobject_class;
415
416   gobject_class = G_OBJECT_CLASS (klass);
417
418   gobject_class->finalize = gst_validate_runner_finalize;
419
420   gobject_class->set_property = gst_validate_runner_set_property;
421   gobject_class->get_property = gst_validate_runner_get_property;
422   gobject_class->constructor = gst_validate_runner_constructor;
423
424   properties[PROP_PARAMS] =
425       g_param_spec_string ("params", "Params", "Extra configuration parameters",
426       NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
427
428   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
429
430   _signals[REPORT_ADDED_SIGNAL] =
431       g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass),
432       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
433       GST_TYPE_VALIDATE_REPORT);
434
435   _signals[STOPPING_SIGNAL] =
436       g_signal_new ("stopping", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
437       NULL, NULL, NULL, G_TYPE_NONE, 0);
438
439   GST_DEBUG_CATEGORY_INIT (gst_validate_runner_debug, "gstvalidaterunner",
440       GST_DEBUG_FG_YELLOW, "Gst validate runner");
441 }
442
443 static void
444 gst_validate_runner_init (GstValidateRunner * runner)
445 {
446   runner->priv = gst_validate_runner_get_instance_private (runner);
447
448   runner->priv->reports_by_type = g_hash_table_new (g_direct_hash,
449       g_direct_equal);
450
451   runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT;
452   _init_report_levels (runner);
453
454   runner->priv->expected_issues = gst_validate_get_test_file_expected_issues ();
455
456   gst_tracing_register_hook (GST_TRACER (runner), "element-new",
457       G_CALLBACK (do_element_new));
458
459   gst_element_register (NULL, GST_MOCKDECRYPTOR_NAME, GST_RANK_MARGINAL,
460       GST_TYPE_MOCKDECRYPTOR);
461 }
462
463 /**
464  * gst_validate_runner_new:
465  *
466  * Create a new #GstValidateRunner
467  *
468  * Returns: A newly created #GstValidateRunner
469  */
470 GstValidateRunner *
471 gst_validate_runner_new (void)
472 {
473   GstValidateRunner *runner;
474
475   if (first_runner) {
476     runner = first_runner;
477     first_runner = NULL;
478   } else if (element_created) {
479     gst_validate_abort
480         ("Should never create a GstValidateRunner after a GstElement "
481         "has been created in the same process.");
482
483     return NULL;
484   } else {
485     runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
486     runner->priv->user_created = TRUE;
487   }
488
489   {
490     GstValidateOverrideRegistry *registry =
491         gst_validate_override_registry_get ();
492     GList *all_overrides =
493         gst_validate_override_registry_get_override_list (registry);
494     GList *i;
495     for (i = all_overrides; i; i = i->next) {
496       GstValidateOverride *override = (GstValidateOverride *) i->data;
497       gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (override),
498           runner);
499     }
500     g_list_free (all_overrides);
501   }
502
503   return runner;
504 }
505
506 /*
507  * gst_validate_runner_get_default_reporting_level:
508  *
509  * Returns: the default #GstValidateReportingDetails used to output a report.
510  */
511 GstValidateReportingDetails
512 gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner)
513 {
514   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
515       GST_VALIDATE_SHOW_UNKNOWN);
516
517   return runner->priv->default_level;
518 }
519
520 #if !GLIB_CHECK_VERSION(2,70,0)
521 #define g_pattern_spec_match_string g_pattern_match_string
522 #endif
523
524 /*
525  * gst_validate_runner_get_reporting_level_for_name:
526  *
527  * Returns: the #GstValidateReportingDetails that will be applied for a given name.
528  * If no pattern was set for such a name, this function will return
529  * #GST_VALIDATE_SHOW_UNKNOWN, and reporting for that name will
530  * default to the global reporting level.
531  */
532 GstValidateReportingDetails
533 gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner,
534     const gchar * name)
535 {
536   GList *tmp;
537   gchar *fixed_name;
538
539   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
540       GST_VALIDATE_SHOW_UNKNOWN);
541
542   fixed_name = g_strdup (name);
543   _replace_double_colons (fixed_name);
544   for (tmp = runner->priv->report_pattern_levels; tmp; tmp = tmp->next) {
545     PatternLevel *pattern_level = (PatternLevel *) tmp->data;
546     if (g_pattern_spec_match_string (pattern_level->pattern, fixed_name)) {
547       g_free (fixed_name);
548
549       return pattern_level->level;
550     }
551   }
552
553   g_free (fixed_name);
554   return GST_VALIDATE_SHOW_UNKNOWN;
555 }
556
557 static void
558 synthesize_reports (GstValidateRunner * runner, GstValidateReport * report)
559 {
560   GstValidateIssueId issue_id;
561   GList *reports;
562
563   issue_id = report->issue->issue_id;
564
565   GST_VALIDATE_RUNNER_LOCK (runner);
566   reports =
567       g_hash_table_lookup (runner->priv->reports_by_type,
568       GINT_TO_POINTER (issue_id));
569   reports = g_list_append (reports, gst_validate_report_ref (report));
570   g_hash_table_insert (runner->priv->reports_by_type,
571       GINT_TO_POINTER (issue_id), reports);
572   GST_VALIDATE_RUNNER_UNLOCK (runner);
573 }
574
575 static void
576 _dot_pipeline (GstValidateReport * report, GstStructure * config)
577 {
578   GstPipeline *pipeline = gst_validate_reporter_get_pipeline (report->reporter);
579
580   if (pipeline) {
581     gint details = GST_DEBUG_GRAPH_SHOW_ALL;
582     gchar *reporter_basename =
583         g_path_get_basename (gst_validate_reporter_get_name (report->reporter));
584     report->dotfile_name =
585         g_strdup_printf ("%" GST_TIME_FORMAT "-validate-report-%s-on-%s-%s",
586         GST_TIME_ARGS (GST_CLOCK_DIFF (_priv_start_time,
587                 gst_util_get_timestamp ())),
588         gst_validate_report_level_get_name (report->level), reporter_basename,
589         g_quark_to_string (report->issue->issue_id));
590
591     g_free (reporter_basename);
592
593     if (config)
594       gst_structure_get_int (config, "details", &details);
595
596     GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipeline),
597         GST_DEBUG_GRAPH_SHOW_ALL, report->dotfile_name);
598
599     gst_object_unref (pipeline);
600   }
601
602 }
603
604 static void
605 gst_validate_runner_maybe_dot_pipeline (GstValidateRunner * runner,
606     GstValidateReport * report)
607 {
608   GList *config;
609
610   if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL ||
611       gst_validate_report_check_abort (report)) {
612
613     _dot_pipeline (report, NULL);
614     return;
615   }
616
617   for (config = gst_validate_plugin_get_config (NULL);
618       config; config = config->next) {
619
620     if (gst_structure_has_name (config->data, "core")) {
621       GstValidateReportLevel level;
622       const gchar *level_str,
623           *action = gst_structure_get_string (config->data, "action");
624
625       if (g_strcmp0 (action, "dot-pipeline"))
626         continue;
627
628       level_str = gst_structure_get_string (config->data, "report-level");
629       level = level_str ? gst_validate_report_level_from_name (level_str) :
630           GST_VALIDATE_REPORT_LEVEL_CRITICAL;
631
632       if (level >= report->level) {
633         _dot_pipeline (report, config->data);
634
635         return;
636       }
637     }
638   }
639 }
640
641 static gboolean
642 check_report_expected (GstValidateRunner * runner, GstValidateReport * report)
643 {
644   GList *tmp;
645
646 #define GET_STR(name) gst_structure_get_string (known_issue, name)
647
648   for (tmp = runner->priv->expected_issues; tmp; tmp = tmp->next) {
649     GstStructure *known_issue = tmp->data;
650     const gchar *id = GET_STR ("issue-id");
651
652     if (!id || g_quark_from_string (id) == report->issue->issue_id) {
653       const gchar *summary = GET_STR ("summary");
654
655       if (!summary || !g_strcmp0 (summary, report->issue->summary)) {
656         const gchar *details = GET_STR ("details");
657
658         if (!details || g_regex_match_simple (details, report->message, 0, 0)) {
659           const gchar *detected_on = GET_STR ("detected-on");
660
661           if (!detected_on || !g_strcmp0 (detected_on, report->reporter_name)) {
662             const gchar *level = GET_STR ("level");
663             const gchar *report_level =
664                 gst_validate_report_level_get_name (report->level);
665
666             if (!detected_on || !g_strcmp0 (level, report_level)) {
667               gboolean is_sometimes;
668
669               if (!gst_structure_get_boolean (known_issue, "sometimes",
670                       &is_sometimes) || !is_sometimes) {
671                 runner->priv->expected_issues =
672                     g_list_remove (runner->priv->expected_issues, known_issue);
673                 gst_structure_free (known_issue);
674               }
675               return TRUE;
676             }
677           }
678         }
679       }
680     }
681 #undef GET_STR
682   }
683
684   return FALSE;
685 }
686
687 void
688 gst_validate_runner_add_report (GstValidateRunner * runner,
689     GstValidateReport * report)
690 {
691   GstValidateReportingDetails details, reporter_details, issue_type_details;
692
693   g_return_if_fail (GST_IS_VALIDATE_RUNNER (runner));
694
695   if (report->level == GST_VALIDATE_REPORT_LEVEL_IGNORE)
696     return;
697
698   if (check_report_expected (runner, report)) {
699     GST_INFO_OBJECT (runner, "Found expected issue: %p", report);
700     report->level = GST_VALIDATE_REPORT_LEVEL_EXPECTED;
701   }
702
703   gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE (report),
704           report));
705   gst_validate_runner_maybe_dot_pipeline (runner, report);
706
707   details = reporter_details =
708       gst_validate_reporter_get_reporting_level (report->reporter);
709   issue_type_details =
710       gst_validate_runner_get_reporting_level_for_name (runner,
711       g_quark_to_string (report->issue->issue_id));
712
713   if (reporter_details == GST_VALIDATE_SHOW_UNKNOWN)
714     details = issue_type_details;
715
716   /* Let's use our own reporting strategy */
717   if (details == GST_VALIDATE_SHOW_UNKNOWN) {
718     gst_validate_report_set_reporting_level (report,
719         runner->priv->default_level);
720     switch (runner->priv->default_level) {
721       case GST_VALIDATE_SHOW_NONE:
722         return;
723       case GST_VALIDATE_SHOW_SMART:
724         if (!gst_validate_report_check_abort (report) &&
725             report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
726             !report->trace) {
727           synthesize_reports (runner, report);
728           return;
729         }
730         break;
731       case GST_VALIDATE_SHOW_SYNTHETIC:
732         if (!report->trace) {
733           synthesize_reports (runner, report);
734           return;
735         }
736       default:
737         break;
738     }
739   } else if (details == GST_VALIDATE_SHOW_NONE) {
740     GST_DEBUG ("Not reporting.");
741     return;
742   }
743
744   GST_VALIDATE_RUNNER_LOCK (runner);
745   runner->priv->reports =
746       g_list_append (runner->priv->reports, gst_validate_report_ref (report));
747   GST_VALIDATE_RUNNER_UNLOCK (runner);
748
749   g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report);
750 }
751
752 /**
753  * gst_validate_runner_get_reports_count:
754  * @runner: The #GstValidateRunner to get the number of reports from
755  *
756  * Get the number of reports present in the runner:
757  *
758  * Returns: The number of reports present in the runner.
759  */
760 guint
761 gst_validate_runner_get_reports_count (GstValidateRunner * runner)
762 {
763   GList *tmp;
764   guint l;
765
766   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 0);
767
768   GST_VALIDATE_RUNNER_LOCK (runner);
769   l = g_list_length (runner->priv->reports);
770   for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
771     GstValidateReport *report = (GstValidateReport *) tmp->data;
772     l += g_list_length (report->repeated_reports);
773   }
774   l += g_hash_table_size (runner->priv->reports_by_type);
775   GST_VALIDATE_RUNNER_UNLOCK (runner);
776
777   return l;
778 }
779
780 /**
781  * gst_validate_runner_get_reports:
782  * @runner: The #GstValidateRunner
783  *
784  * Returns: (element-type GstValidateReport) (transfer full): The list of reports
785  */
786 GList *
787 gst_validate_runner_get_reports (GstValidateRunner * runner)
788 {
789   GList *ret;
790
791   GST_VALIDATE_RUNNER_LOCK (runner);
792   ret =
793       g_list_copy_deep (runner->priv->reports,
794       (GCopyFunc) gst_validate_report_ref, NULL);
795   GST_VALIDATE_RUNNER_UNLOCK (runner);
796
797   return ret;
798 }
799
800 static GList *
801 _do_report_synthesis (GstValidateRunner * runner)
802 {
803   GHashTableIter iter;
804   GList *reports, *tmp;
805   gpointer key, value;
806   GList *criticals = NULL;
807
808   /* Take the lock so the hash table won't be modified while we are iterating
809    * over it */
810   GST_VALIDATE_RUNNER_LOCK (runner);
811   g_hash_table_iter_init (&iter, runner->priv->reports_by_type);
812   while (g_hash_table_iter_next (&iter, &key, &value)) {
813     GstValidateReport *report;
814     reports = (GList *) value;
815
816     if (!reports)
817       continue;
818
819     report = (GstValidateReport *) (reports->data);
820
821     gst_validate_report_print_level (report);
822     gst_validate_report_print_detected_on (report);
823
824     if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
825       criticals = g_list_append (criticals, report);
826       gst_validate_report_print_details (report);
827     } else if (report->issue->flags & GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS)
828       gst_validate_report_print_details (report);
829
830     for (tmp = g_list_next (reports); tmp; tmp = tmp->next) {
831       report = (GstValidateReport *) tmp->data;
832       gst_validate_report_print_detected_on (report);
833
834       if ((report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) ||
835           (report->issue->flags & GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS)) {
836         if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
837           criticals = g_list_append (criticals, report);
838         gst_validate_report_print_details (report);
839       }
840     }
841     report = (GstValidateReport *) (reports->data);
842     gst_validate_report_print_description (report);
843     gst_validate_printf (NULL, "\n");
844   }
845   GST_VALIDATE_RUNNER_UNLOCK (runner);
846
847   return criticals;
848 }
849
850 /**
851  * gst_validate_runner_printf:
852  * @runner: The #GstValidateRunner to print all the reports for
853  *
854  * Prints all the reports on the terminal or on wherever is set
855  * in the `GST_VALIDATE_FILE` env variable.
856  *
857  * Returns: 0 if no critical error has been found and 18 if a critical
858  * error has been detected. That return value is usually to be used as
859  * exit code of the application.
860  */
861 int
862 gst_validate_runner_printf (GstValidateRunner * runner)
863 {
864   GList *reports, *tmp;
865   int ret = 0;
866   GList *criticals = NULL;
867
868   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
869
870   criticals = _do_report_synthesis (runner);
871   reports = gst_validate_runner_get_reports (runner);
872   for (tmp = reports; tmp; tmp = tmp->next) {
873     GstValidateReport *report = (GstValidateReport *) tmp->data;
874
875     if (gst_validate_report_should_print (report))
876       gst_validate_report_printf (report);
877
878     if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
879       criticals = g_list_append (criticals, report);
880     }
881   }
882
883   if (criticals) {
884     GList *iter;
885     g_printerr ("\n\n**Got criticals. Return value set to 18**:\n");
886     ret = 18;
887     for (iter = criticals; iter; iter = iter->next) {
888       g_printerr ("  * critical error %s\n",
889           ((GstValidateReport *) (iter->data))->message);
890     }
891     g_printerr ("\n");
892   }
893
894   g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
895   g_list_free (criticals);
896   gst_validate_printf (NULL, "Issues found: %u\n",
897       gst_validate_runner_get_reports_count (runner));
898   return ret;
899 }
900
901 int
902 gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
903 {
904   gint ret = 0;
905   GList *tmp, *configs;
906
907   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
908
909   g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0);
910   if (print_result) {
911     ret = gst_validate_runner_printf (runner);
912   } else {
913     GList *tmp;
914     for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
915       GstValidateReport *report = (GstValidateReport *) tmp->data;
916       if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
917         ret = 18;
918     }
919   }
920
921   configs = gst_validate_get_config (NULL);
922   for (tmp = configs; tmp; tmp = tmp->next) {
923     if (!gst_structure_has_field (tmp->data, "__n_usages__")) {
924       gst_validate_error_structure (tmp->data,
925           "Unused config: '%" GST_PTR_FORMAT "'", tmp->data);
926     }
927   }
928   g_list_free (configs);
929
930   for (tmp = runner->priv->expected_issues; tmp; tmp = tmp->next) {
931     GstStructure *known_issue = tmp->data;
932     gboolean is_sometimes;
933
934     if (!gst_structure_get_boolean (known_issue, "sometimes", &is_sometimes)
935         || !is_sometimes) {
936       GstStructure *tmpstruct = gst_structure_copy (known_issue);
937       gst_structure_remove_fields (tmpstruct, "__debug__", "__lineno__",
938           "__filename__", NULL);
939       /* Ideally we should report an issue here.. but we do not have a reporter */
940       gst_validate_error_structure (known_issue,
941           "Expected issue didn't happen: '%" GST_PTR_FORMAT "'", tmpstruct);
942       gst_structure_free (tmpstruct);
943     }
944   }
945
946   g_list_free_full (runner->priv->expected_issues,
947       (GDestroyNotify) gst_structure_free);
948   runner->priv->expected_issues = NULL;
949
950   return ret;
951 }
952
953 void
954 gst_validate_init_runner (void)
955 {
956   if (!first_runner) {
957     first_runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
958     first_runner->priv->user_created = TRUE;
959   }                             /* else the first runner has been created through the GST_TRACERS system */
960 }
961
962 void
963 gst_validate_deinit_runner (void)
964 {
965   g_clear_object (&first_runner);
966 }
967
968 GstValidateReportingDetails
969 gst_validate_runner_get_default_reporting_details (GstValidateRunner * runner)
970 {
971   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
972       GST_VALIDATE_SHOW_UNKNOWN);
973
974   return runner->priv->default_level;
975 }
976
977 #ifdef __GST_VALIDATE_PLUGIN
978 static gboolean
979 plugin_init (GstPlugin * plugin)
980 {
981   if (!gst_tracer_register (plugin, "validate", GST_TYPE_VALIDATE_RUNNER))
982     return FALSE;
983
984   return TRUE;
985 }
986
987 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, validatetracer,
988     "GStreamer Validate tracers", plugin_init, VERSION, GST_LICENSE,
989     GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
990 #endif /* __GST_VALIDATE_PLUGIN */