validate-runner: Add code to parse GST_VALIDATE_REPORT_LEVEL.
[platform/upstream/gstreamer.git] / validate / gst / validate / gst-validate-runner.c
1 /* GStreamer
2  *
3  * Copyright (C) 2013 Collabora Ltd.
4  *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
5  *
6  * gst-validate-runner.c - Validate Runner class
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <string.h>
25 #include <stdlib.h>
26
27 #ifdef HAVE_CONFIG_H
28 #  include "config.h"
29 #endif
30
31 #include "gst-validate-internal.h"
32 #include "gst-validate-report.h"
33 #include "gst-validate-monitor-factory.h"
34 #include "gst-validate-override-registry.h"
35 #include "gst-validate-runner.h"
36
37 /**
38  * SECTION:gst-validate-runner
39  * @short_description: Class that runs Gst Validate tests for a pipeline
40  *
41  * Allows you to test a pipeline within GstValidate. It is the object where
42  * all issue reporting is done.
43  *
44  * In the tools using GstValidate the only minimal code to be able to monitor
45  * your pipelines is:
46  *
47  * |[
48  *  GstPipeline *pipeline = gst_pipeline_new ("monitored-pipeline");
49  *  GstValidateRunner *runner = gst_validate_runner_new ();
50  *  GstValidateMonitor *monitor = gst_validate_monitor_factory_create (
51  *          GST_OBJECT (pipeline), runner, NULL);
52  *
53  *  // Run the pipeline and do whatever you want with it
54  *
55  *  // In that same order
56  *  gst_object_unref (pipeline);
57  *  gst_object_unref (runner);
58  *  gst_object_unref (monitor);
59  * ]|
60  */
61
62 struct _GstValidateRunnerPrivate
63 {
64   GMutex mutex;
65   GList *reports;
66   GstValidateReportingLevel default_level;
67
68   /* A list of PatternLevel */
69   GList *report_pattern_levels;
70 };
71
72 /* Describes the reporting level to apply to a name pattern */
73 typedef struct _PatternLevel
74 {
75   GPatternSpec *pattern;
76   GstValidateReportingLevel level;
77 } PatternLevel;
78
79 #define GST_VALIDATE_RUNNER_LOCK(r)                     \
80   G_STMT_START {                                        \
81   GST_LOG_OBJECT (r, "About to lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
82   (g_mutex_lock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex));           \
83   GST_LOG_OBJECT (r, "Acquired lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
84   } G_STMT_END
85
86 #define GST_VALIDATE_RUNNER_UNLOCK(r)                   \
87   G_STMT_START {                                        \
88   GST_LOG_OBJECT (r, "About to unlock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
89   (g_mutex_unlock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex));         \
90   GST_LOG_OBJECT (r, "Released lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
91   } G_STMT_END
92
93 #define gst_validate_runner_parent_class parent_class
94 G_DEFINE_TYPE (GstValidateRunner, gst_validate_runner, G_TYPE_OBJECT);
95
96 /* signals */
97 enum
98 {
99   REPORT_ADDED_SIGNAL,
100   /* add more above */
101   LAST_SIGNAL
102 };
103
104 static guint _signals[LAST_SIGNAL] = { 0 };
105
106 static gboolean
107 _parse_reporting_level (gchar * str, GstValidateReportingLevel * level)
108 {
109   if (!str)
110     return FALSE;
111
112   /* works in place */
113   g_strstrip (str);
114
115   if (g_ascii_isdigit (str[0])) {
116     unsigned long l;
117     char *endptr;
118     l = strtoul (str, &endptr, 10);
119     if (endptr > str && endptr[0] == 0) {
120       *level = (GstValidateReportingLevel) l;
121     } else {
122       return FALSE;
123     }
124   } else if (g_ascii_strcasecmp (str, "none") == 0) {
125     *level = GST_VALIDATE_REPORTING_LEVEL_NONE;
126   } else if (g_ascii_strcasecmp (str, "synthetic") == 0) {
127     *level = GST_VALIDATE_REPORTING_LEVEL_SYNTHETIC;
128   } else if (g_ascii_strcasecmp (str, "subchain") == 0) {
129     *level = GST_VALIDATE_REPORTING_LEVEL_SUBCHAIN;
130   } else if (g_ascii_strcasecmp (str, "monitor") == 0) {
131     *level = GST_VALIDATE_REPORTING_LEVEL_MONITOR;
132   } else if (g_ascii_strcasecmp (str, "all") == 0) {
133     *level = GST_VALIDATE_REPORTING_LEVEL_ALL;
134   } else
135     return FALSE;
136
137   return TRUE;
138 }
139
140 static void
141 _free_report_pattern_level (PatternLevel * pattern_level)
142 {
143   g_pattern_spec_free (pattern_level->pattern);
144   g_free (pattern_level);
145 }
146
147 static void
148 _set_reporting_level_for_name (GstValidateRunner * runner,
149     const gchar * pattern, GstValidateReportingLevel level)
150 {
151   PatternLevel *pattern_level = g_malloc (sizeof (PatternLevel));
152   GPatternSpec *pattern_spec = g_pattern_spec_new (pattern);
153
154   pattern_level->pattern = pattern_spec;
155   pattern_level->level = level;
156
157   runner->priv->report_pattern_levels =
158       g_list_append (runner->priv->report_pattern_levels, pattern_level);
159 }
160
161 static void
162 _set_report_levels_from_string (GstValidateRunner * self, const gchar * list)
163 {
164   gchar **split;
165   gchar **walk;
166
167   g_assert (list);
168
169   GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list);
170
171   split = g_strsplit (list, ",", 0);
172
173   for (walk = split; *walk; walk++) {
174     if (strchr (*walk, ':')) {
175       gchar **values = g_strsplit (*walk, ":", 2);
176
177       if (values[0] && values[1]) {
178         GstValidateReportingLevel level;
179
180         if (_parse_reporting_level (values[1], &level))
181           _set_reporting_level_for_name (self, values[0], level);
182       }
183
184       g_strfreev (values);
185     } else {
186       GstValidateReportingLevel level;
187
188       if (_parse_reporting_level (*walk, &level))
189         self->priv->default_level = level;
190     }
191   }
192
193   g_strfreev (split);
194 }
195
196 static void
197 _init_report_levels (GstValidateRunner * self)
198 {
199   const gchar *env;
200
201   env = g_getenv ("GST_VALIDATE_REPORT_LEVEL");
202   if (env)
203     _set_report_levels_from_string (self, env);
204 }
205
206 static void
207 gst_validate_runner_dispose (GObject * object)
208 {
209   GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
210
211   g_list_free_full (runner->priv->reports,
212       (GDestroyNotify) gst_validate_report_unref);
213   g_list_free_full (runner->priv->report_pattern_levels,
214       (GDestroyNotify) _free_report_pattern_level);
215
216   g_mutex_clear (&runner->priv->mutex);
217
218   G_OBJECT_CLASS (parent_class)->dispose (object);
219 }
220
221 static void
222 gst_validate_runner_class_init (GstValidateRunnerClass * klass)
223 {
224   GObjectClass *gobject_class;
225
226   gobject_class = G_OBJECT_CLASS (klass);
227
228   gobject_class->dispose = gst_validate_runner_dispose;
229
230   g_type_class_add_private (klass, sizeof (GstValidateRunnerPrivate));
231
232   _signals[REPORT_ADDED_SIGNAL] =
233       g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass),
234       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
235       GST_TYPE_VALIDATE_REPORT);
236 }
237
238 static void
239 gst_validate_runner_init (GstValidateRunner * runner)
240 {
241   runner->priv = G_TYPE_INSTANCE_GET_PRIVATE (runner, GST_TYPE_VALIDATE_RUNNER,
242       GstValidateRunnerPrivate);
243   g_mutex_init (&runner->priv->mutex);
244
245   runner->priv->default_level = GST_VALIDATE_REPORTING_LEVEL_DEFAULT;
246   _init_report_levels (runner);
247 }
248
249 /**
250  * gst_validate_runner_new:
251  *
252  * Create a new #GstValidateRunner
253  *
254  * Returns: A newly created #GstValidateRunner
255  */
256 GstValidateRunner *
257 gst_validate_runner_new (void)
258 {
259   return g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
260 }
261
262 /*
263  * gst_validate_runner_get_default_reporting_level:
264  *
265  * Returns: the default #GstValidateReportingLevel used to output a report.
266  */
267 GstValidateReportingLevel
268 gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner)
269 {
270   return runner->priv->default_level;
271 }
272
273 /*
274  * gst_validate_runner_get_reporting_level_for_name:
275  *
276  * Returns: the #GstValidateReportingLevel that will be applied for a given name.
277  * If no pattern was set for such a name, this function will return
278  * #GST_VALIDATE_REPORTING_LEVEL_UNKNOWN, and reporting for that name will
279  * default to the global reporting level.
280  */
281 GstValidateReportingLevel
282 gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner,
283     const gchar * name)
284 {
285   GList *tmp;
286
287   for (tmp = runner->priv->report_pattern_levels; tmp; tmp = tmp->next) {
288     PatternLevel *pattern_level = (PatternLevel *) tmp->data;
289     if (g_pattern_match_string (pattern_level->pattern, name))
290       return pattern_level->level;
291   }
292
293   return GST_VALIDATE_REPORTING_LEVEL_UNKNOWN;
294 }
295
296 void
297 gst_validate_runner_add_report (GstValidateRunner * runner,
298     GstValidateReport * report)
299 {
300   GST_VALIDATE_RUNNER_LOCK (runner);
301   runner->priv->reports = g_list_append (runner->priv->reports, report);
302   GST_VALIDATE_RUNNER_UNLOCK (runner);
303
304   g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report);
305 }
306
307 /**
308  * gst_validate_runner_get_reports_count:
309  * @runner: The $GstValidateRunner to get the number of report from
310  *
311  * Get the number of reports present in the runner:
312  *
313  * Returns: The number of report present in the runner.
314  */
315 guint
316 gst_validate_runner_get_reports_count (GstValidateRunner * runner)
317 {
318   guint l;
319
320   g_return_val_if_fail (runner != NULL, 0);
321
322   GST_VALIDATE_RUNNER_LOCK (runner);
323   l = g_list_length (runner->priv->reports);
324   GST_VALIDATE_RUNNER_UNLOCK (runner);
325
326   return l;
327 }
328
329 GList *
330 gst_validate_runner_get_reports (GstValidateRunner * runner)
331 {
332   GList *ret;
333
334   GST_VALIDATE_RUNNER_LOCK (runner);
335   ret =
336       g_list_copy_deep (runner->priv->reports,
337       (GCopyFunc) gst_validate_report_ref, NULL);
338   GST_VALIDATE_RUNNER_UNLOCK (runner);
339
340   return ret;
341 }
342
343 /**
344  * gst_validate_runner_printf:
345  * @runner: The #GstValidateRunner to print all the reports for
346  *
347  * Prints all the report on the terminal or on wherever set
348  * in the #GST_VALIDATE_FILE env variable.
349  *
350  * Returns: 0 if no critical error has been found and 18 if a critical
351  * error has been detected. That return value is usually to be used as
352  * exit code of the application.
353  * */
354 int
355 gst_validate_runner_printf (GstValidateRunner * runner)
356 {
357   GList *reports, *tmp;
358   guint count = 0;
359   int ret = 0;
360   GList *criticals = NULL;
361
362   reports = gst_validate_runner_get_reports (runner);
363   for (tmp = reports; tmp; tmp = tmp->next) {
364     GstValidateReport *report = tmp->data;
365
366     if (gst_validate_report_should_print (report))
367       gst_validate_report_printf (report);
368
369     if (ret == 0 && report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
370       criticals = g_list_append (criticals, tmp->data);
371       ret = 18;
372     }
373     count++;
374   }
375
376   if (criticals) {
377     GList *iter;
378
379     g_printerr ("\n\n==== Got criticals, Return value set to 18 ====\n");
380
381     for (iter = criticals; iter; iter = iter->next) {
382       g_printerr ("     Critical error %s\n",
383           ((GstValidateReport *) (iter->data))->message);
384     }
385     g_printerr ("\n");
386   }
387
388   g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
389   gst_validate_printf (NULL, "Issues found: %u\n", count);
390   g_list_free (criticals);
391   return ret;
392 }