report: Add a way to force backtraces on reports
[platform/upstream/gstreamer.git] / validate / gst / validate / gst-validate-report.c
1 /* GStreamer
2  *
3  * Copyright (C) 2013 Collabora Ltd.
4  *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
5  *
6  * gst-validate-monitor-report.c - Validate report/issues functions
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 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28
29 #include <stdlib.h>             /* exit */
30 #include <stdio.h>              /* fprintf */
31 #include <glib/gstdio.h>
32 #include <errno.h>
33
34 #include <string.h>
35 #include "gst-validate-i18n-lib.h"
36 #include "gst-validate-internal.h"
37
38 #include "gst-validate-report.h"
39 #include "gst-validate-reporter.h"
40 #include "gst-validate-monitor.h"
41 #include "gst-validate-scenario.h"
42
43 static GstClockTime _gst_validate_report_start_time = 0;
44 static GstValidateDebugFlags _gst_validate_flags = 0;
45 static GHashTable *_gst_validate_issues = NULL;
46 static FILE **log_files = NULL;
47 static gboolean output_is_tty = TRUE;
48
49 /* Tcp server for communications with gst-validate-launcher */
50 GSocketClient *socket_client = NULL;
51 GSocketConnection *server_connection = NULL;
52 GOutputStream *server_ostream = NULL;
53
54 static GType _gst_validate_report_type = 0;
55
56 static JsonNode *
57 gst_validate_report_serialize (GstValidateReport * report)
58 {
59   JsonNode *node = json_node_alloc ();
60   JsonObject *jreport = json_object_new ();
61
62   json_object_set_string_member (jreport, "type", "report");
63   json_object_set_string_member (jreport, "issue-id",
64       g_quark_to_string (report->issue->issue_id));
65   json_object_set_string_member (jreport, "summary", report->issue->summary);
66   json_object_set_string_member (jreport, "level",
67       gst_validate_report_level_get_name (report->level));
68   json_object_set_string_member (jreport, "detected-on", report->reporter_name);
69   json_object_set_string_member (jreport, "details", report->message);
70
71   node = json_node_init_object (node, jreport);
72   json_object_unref (jreport);
73
74   return node;
75 }
76
77 GType
78 gst_validate_report_get_type (void)
79 {
80   if (_gst_validate_report_type == 0) {
81     _gst_validate_report_type =
82         g_boxed_type_register_static (g_intern_static_string
83         ("GstValidateReport"), (GBoxedCopyFunc) gst_mini_object_ref,
84         (GBoxedFreeFunc) gst_mini_object_unref);
85
86     json_boxed_register_serialize_func (_gst_validate_report_type,
87         JSON_NODE_OBJECT,
88         (JsonBoxedSerializeFunc) gst_validate_report_serialize);
89   }
90
91   return _gst_validate_report_type;
92 }
93
94
95 GRegex *newline_regex = NULL;
96
97 GST_DEBUG_CATEGORY_STATIC (gst_validate_report_debug);
98 #undef GST_CAT_DEFAULT
99 #define GST_CAT_DEFAULT gst_validate_report_debug
100
101 #define GST_VALIDATE_REPORT_SHADOW_REPORTS_LOCK(r)                      \
102   G_STMT_START {                                        \
103   (g_mutex_lock (&((GstValidateReport *) r)->shadow_reports_lock));             \
104   } G_STMT_END
105
106 #define GST_VALIDATE_REPORT_SHADOW_REPORTS_UNLOCK(r)                    \
107   G_STMT_START {                                        \
108   (g_mutex_unlock (&((GstValidateReport *) r)->shadow_reports_lock));           \
109   } G_STMT_END
110
111 static GstValidateIssue *
112 gst_validate_issue_ref (GstValidateIssue * issue)
113 {
114   g_return_val_if_fail (issue != NULL, NULL);
115
116   g_atomic_int_inc (&issue->refcount);
117
118   return issue;
119 }
120
121 static void
122 gst_validate_issue_unref (GstValidateIssue * issue)
123 {
124   if (G_UNLIKELY (g_atomic_int_dec_and_test (&issue->refcount))) {
125     g_free (issue->summary);
126     g_free (issue->description);
127
128     /* We are using an string array for area and name */
129     g_strfreev (&issue->area);
130
131     g_slice_free (GstValidateIssue, issue);
132   }
133 }
134
135
136 G_DEFINE_BOXED_TYPE (GstValidateIssue, gst_validate_issue,
137     (GBoxedCopyFunc) gst_validate_issue_ref,
138     (GBoxedFreeFunc) gst_validate_issue_unref);
139
140 GstValidateIssueId
141 gst_validate_issue_get_id (GstValidateIssue * issue)
142 {
143   return issue->issue_id;
144 }
145
146 /**
147  * gst_validate_issue_new_full:
148  * @issue_id: The ID of the issue, should be a GQuark
149  * @summary: A summary of the issue
150  * @description: A more complete description of the issue
151  * @default_level: The level at which the issue will be reported by default
152  * @flags: The flags to determine behaviour of the issue
153  *
154  * Returns: (transfer full): The newly created #GstValidateIssue
155  */
156 GstValidateIssue *
157 gst_validate_issue_new_full (GstValidateIssueId issue_id, const gchar * summary,
158     const gchar * description, GstValidateReportLevel default_level,
159     GstValidateIssueFlags flags)
160 {
161   GstValidateIssue *issue;
162   gchar **area_name = g_strsplit (g_quark_to_string (issue_id), "::", 2);
163
164   if (!(area_name[0] != NULL && area_name[1] != NULL && area_name[2] == NULL)) {
165     g_warning ("Wrong issue ID: %s (should be in the form: area::name)",
166         g_quark_to_string (issue_id));
167     g_strfreev (area_name);
168
169     return NULL;
170   }
171
172   issue = g_slice_new (GstValidateIssue);
173   issue->issue_id = issue_id;
174   issue->summary = g_strdup (summary);
175   issue->description = g_strdup (description);
176   issue->default_level = default_level;
177   issue->area = area_name[0];
178   issue->name = area_name[1];
179   issue->flags = flags;
180
181   g_free (area_name);
182   return issue;
183 }
184
185 /**
186  * gst_validate_issue_new:
187  * @issue_id: The ID of the issue, should be a GQuark
188  * @summary: A summary of the issue
189  * @description: A more complete description of the issue
190  * @default_level: The level at which the issue will be reported by default
191  *
192  * Returns: (transfer full): The newly created #GstValidateIssue
193  */
194 GstValidateIssue *
195 gst_validate_issue_new (GstValidateIssueId issue_id, const gchar * summary,
196     const gchar * description, GstValidateReportLevel default_level)
197 {
198   GstValidateIssue *issue;
199   gchar **area_name = g_strsplit (g_quark_to_string (issue_id), "::", 2);
200
201   if (!(area_name[0] != NULL && area_name[1] != NULL && area_name[2] == NULL)) {
202     g_warning ("Wrong issue ID: %s (should be in the form: area::name)",
203         g_quark_to_string (issue_id));
204     g_strfreev (area_name);
205
206     return NULL;
207   }
208
209   issue = g_slice_new (GstValidateIssue);
210   issue->issue_id = issue_id;
211   issue->summary = g_strdup (summary);
212   issue->description = g_strdup (description);
213   issue->default_level = default_level;
214   issue->area = area_name[0];
215   issue->name = area_name[1];
216   issue->flags = GST_VALIDATE_ISSUE_FLAGS_NONE;
217
218   g_free (area_name);
219   return issue;
220 }
221
222 void
223 gst_validate_issue_set_default_level (GstValidateIssue * issue,
224     GstValidateReportLevel default_level)
225 {
226   GST_INFO ("Setting issue %s::%s default level to %s",
227       issue->area, issue->name,
228       gst_validate_report_level_get_name (default_level));
229
230   issue->default_level = default_level;
231 }
232
233 /**
234  * gst_validate_issue_register:
235  * @issue: (transfer none): The #GstValidateIssue to register
236  *
237  * Registers @issue in the issue type system
238  */
239 void
240 gst_validate_issue_register (GstValidateIssue * issue)
241 {
242   g_return_if_fail (g_hash_table_lookup (_gst_validate_issues,
243           (gpointer) gst_validate_issue_get_id (issue)) == NULL);
244
245   g_hash_table_insert (_gst_validate_issues,
246       (gpointer) gst_validate_issue_get_id (issue), issue);
247 }
248
249 #define REGISTER_VALIDATE_ISSUE(lvl,id,sum,desc)                        \
250   gst_validate_issue_register (gst_validate_issue_new (id, \
251                                                        sum, desc, GST_VALIDATE_REPORT_LEVEL_##lvl))
252
253 #define REGISTER_VALIDATE_ISSUE_FULL(lvl,id,sum,desc,flags)                     \
254   gst_validate_issue_register (gst_validate_issue_new_full (id, \
255                                                        sum, desc, GST_VALIDATE_REPORT_LEVEL_##lvl, flags))
256 static void
257 gst_validate_report_load_issues (void)
258 {
259   g_return_if_fail (_gst_validate_issues == NULL);
260
261   _gst_validate_issues = g_hash_table_new_full (g_direct_hash, g_direct_equal,
262       NULL, (GDestroyNotify) gst_validate_issue_unref);
263
264   /* **
265    * WARNING: The `summary` is used to define known issues in the testsuites.
266    * Avoid changing them or **make sure** to at least update the validate test
267    * suite if you do so.
268    * **/
269   REGISTER_VALIDATE_ISSUE (WARNING, BUFFER_BEFORE_SEGMENT,
270       "buffer was received before a segment",
271       _("in push mode, a segment event must be received before a buffer"));
272   REGISTER_VALIDATE_ISSUE (ISSUE, BUFFER_IS_OUT_OF_SEGMENT,
273       "buffer is out of the segment range",
274       _("buffer being pushed is out of the current segment's start-stop "
275           "range. Meaning it is going to be discarded downstream without "
276           "any use"));
277   REGISTER_VALIDATE_ISSUE (WARNING, BUFFER_TIMESTAMP_OUT_OF_RECEIVED_RANGE,
278       "buffer timestamp is out of the received buffer timestamps' range",
279       _("a buffer leaving an element should have its timestamps in the range "
280           "of the received buffers timestamps. i.e. If an element received "
281           "buffers with timestamps from 0s to 10s, it can't push a buffer with "
282           "a 11s timestamp, because it doesn't have data for that"));
283   REGISTER_VALIDATE_ISSUE (WARNING, WRONG_BUFFER,
284       "Received buffer does not correspond to wanted one.",
285       _("When checking playback of a file against a MediaInfo file"
286           " all buffers coming into the decoders might be checked"
287           " and should have the exact expected metadatas and hash of the"
288           " content"));
289   REGISTER_VALIDATE_ISSUE (CRITICAL, WRONG_FLOW_RETURN,
290       "flow return from pad push doesn't match expected value",
291       _("flow return from a 1:1 sink/src pad element is as simple as "
292           "returning what downstream returned. For elements that have multiple "
293           "src pads, flow returns should be properly combined"));
294   REGISTER_VALIDATE_ISSUE (ISSUE, BUFFER_AFTER_EOS,
295       "buffer was received after EOS",
296       _("a pad shouldn't receive any more buffers after it gets EOS"));
297   REGISTER_VALIDATE_ISSUE (WARNING, FLOW_ERROR_WITHOUT_ERROR_MESSAGE,
298       "GST_FLOW_ERROR returned without posting an ERROR on the bus",
299       _("Element MUST post a GST_MESSAGE_ERROR with GST_ELEMENT_ERROR before"
300           " returning GST_FLOW_ERROR"));
301   REGISTER_VALIDATE_ISSUE (WARNING, BUFFER_MISSING_DISCONT,
302       _("Buffer didn't have expected DISCONT flag"),
303       _("Buffers after SEGMENT and FLUSH must have a DISCONT flag"));
304
305   REGISTER_VALIDATE_ISSUE (ISSUE, CAPS_IS_MISSING_FIELD,
306       "caps is missing a required field for its type",
307       _("some caps types are expected to contain a set of basic fields. "
308           "For example, raw video should have 'width', 'height', 'framerate' "
309           "and 'pixel-aspect-ratio'"));
310   REGISTER_VALIDATE_ISSUE (WARNING, CAPS_FIELD_HAS_BAD_TYPE,
311       "caps field has an unexpected type",
312       _("some common caps fields should always use the same expected types"));
313   REGISTER_VALIDATE_ISSUE (WARNING, CAPS_EXPECTED_FIELD_NOT_FOUND,
314       "caps expected field wasn't present",
315       _("a field that should be present in the caps wasn't found. "
316           "Fields sets on a sink pad caps should be propagated downstream "
317           "when it makes sense to do so"));
318   REGISTER_VALIDATE_ISSUE (CRITICAL, GET_CAPS_NOT_PROXYING_FIELDS,
319       "getcaps function isn't proxying downstream fields correctly",
320       _("elements should set downstream caps restrictions on its caps when "
321           "replying upstream's getcaps queries to avoid upstream sending data"
322           " in an unsupported format"));
323   REGISTER_VALIDATE_ISSUE (CRITICAL, CAPS_FIELD_UNEXPECTED_VALUE,
324       "a field in caps has an unexpected value",
325       _("fields set on a sink pad should be propagated downstream via "
326           "set caps"));
327
328   REGISTER_VALIDATE_ISSUE (WARNING, EVENT_NEWSEGMENT_NOT_PUSHED,
329       "new segment event wasn't propagated downstream",
330       _("segments received from upstream should be pushed downstream"));
331   REGISTER_VALIDATE_ISSUE (WARNING, SERIALIZED_EVENT_WASNT_PUSHED_IN_TIME,
332       "a serialized event received should be pushed in the same 'time' "
333       "as it was received",
334       _("serialized events should be pushed in the same order they are "
335           "received and serialized with buffers. If an event is received after"
336           " a buffer with timestamp end 'X', it should be pushed right after "
337           "buffers with timestamp end 'X'"));
338   REGISTER_VALIDATE_ISSUE (ISSUE, EOS_HAS_WRONG_SEQNUM,
339       "EOS events that are part of the same pipeline 'operation' should "
340       "have the same seqnum",
341       _("when events/messages are created from another event/message, "
342           "they should have their seqnums set to the original event/message "
343           "seqnum"));
344   REGISTER_VALIDATE_ISSUE (ISSUE, FLUSH_START_HAS_WRONG_SEQNUM,
345       "FLUSH_START events that are part of the same pipeline 'operation' should "
346       "have the same seqnum",
347       _("when events/messages are created from another event/message, "
348           "they should have their seqnums set to the original event/message "
349           "seqnum"));
350   REGISTER_VALIDATE_ISSUE (ISSUE, FLUSH_STOP_HAS_WRONG_SEQNUM,
351       "FLUSH_STOP events that are part of the same pipeline 'operation' should "
352       "have the same seqnum",
353       _("when events/messages are created from another event/message, "
354           "they should have their seqnums set to the original event/message "
355           "seqnum"));
356   REGISTER_VALIDATE_ISSUE (ISSUE, SEGMENT_HAS_WRONG_SEQNUM,
357       "SEGMENT events that are part of the same pipeline 'operation' should "
358       "have the same seqnum",
359       _("when events/messages are created from another event/message, "
360           "they should have their seqnums set to the original event/message "
361           "seqnum"));
362   REGISTER_VALIDATE_ISSUE (CRITICAL, SEGMENT_HAS_WRONG_START,
363       "A segment doesn't have the proper time value after an ACCURATE seek",
364       _("If a seek with the ACCURATE flag was accepted, the following segment "
365           "should have a time value corresponding exactly to the requested start "
366           "seek time"));
367   REGISTER_VALIDATE_ISSUE (WARNING, EVENT_SERIALIZED_OUT_OF_ORDER,
368       "a serialized event received should be pushed in the same order "
369       "as it was received",
370       _("serialized events should be pushed in the same order they are "
371           "received."));
372   REGISTER_VALIDATE_ISSUE (WARNING, EVENT_NEW_SEGMENT_MISMATCH,
373       "a new segment event has different value than the received one",
374       _("when receiving a new segment, an element should push an equivalent "
375           "segment downstream"));
376   REGISTER_VALIDATE_ISSUE (WARNING, EVENT_FLUSH_START_UNEXPECTED,
377       "received an unexpected flush start event", NULL);
378   REGISTER_VALIDATE_ISSUE (WARNING, EVENT_FLUSH_STOP_UNEXPECTED,
379       "received an unexpected flush stop event", NULL);
380   REGISTER_VALIDATE_ISSUE (WARNING, EVENT_CAPS_DUPLICATE,
381       "received the same caps twice", NULL);
382
383   REGISTER_VALIDATE_ISSUE (CRITICAL, EVENT_SEEK_NOT_HANDLED,
384       "seek event wasn't handled", NULL);
385   REGISTER_VALIDATE_ISSUE (CRITICAL, EVENT_SEEK_RESULT_POSITION_WRONG,
386       "position after a seek is wrong", NULL);
387   REGISTER_VALIDATE_ISSUE (CRITICAL, EVENT_SEEK_INVALID_SEQNUM,
388       "segments after a seek don't have the same seqnum", NULL);
389
390   REGISTER_VALIDATE_ISSUE (WARNING, EVENT_EOS_WITHOUT_SEGMENT,
391       "EOS received without segment event before",
392       _("A segment event should always be sent before data flow"
393           " EOS being some kind of data flow, there is no exception"
394           " in that regard"));
395
396   REGISTER_VALIDATE_ISSUE (CRITICAL, EVENT_INVALID_SEQNUM,
397       "Event has an invalid seqnum",
398       _("An event is using GST_SEQNUM_INVALID. This should never happen"));
399
400   REGISTER_VALIDATE_ISSUE (CRITICAL, STATE_CHANGE_FAILURE,
401       "state change failed", NULL);
402
403   REGISTER_VALIDATE_ISSUE (WARNING, FILE_SIZE_INCORRECT,
404       "resulting file size wasn't within the expected values", NULL);
405   REGISTER_VALIDATE_ISSUE (WARNING, FILE_DURATION_INCORRECT,
406       "resulting file duration wasn't within the expected values", NULL);
407   REGISTER_VALIDATE_ISSUE (WARNING, FILE_SEEKABLE_INCORRECT,
408       "resulting file wasn't seekable or not seekable as expected", NULL);
409   REGISTER_VALIDATE_ISSUE (CRITICAL, FILE_PROFILE_INCORRECT,
410       "resulting file stream profiles didn't match expected values", NULL);
411   REGISTER_VALIDATE_ISSUE (ISSUE, FILE_TAG_DETECTION_INCORRECT,
412       "detected tags are different than expected ones", NULL);
413   REGISTER_VALIDATE_ISSUE (CRITICAL, FILE_FRAMES_INCORRECT,
414       "resulting file frames are not as expected", NULL);
415   REGISTER_VALIDATE_ISSUE (CRITICAL, FILE_SEGMENT_INCORRECT,
416       "resulting segment is not as expected", NULL);
417   REGISTER_VALIDATE_ISSUE (WARNING, FILE_NO_STREAM_INFO,
418       "the discoverer could not determine the stream info", NULL);
419   REGISTER_VALIDATE_ISSUE (WARNING, FILE_NO_STREAM_ID,
420       "the discoverer found a stream that had no stream ID", NULL);
421
422
423   REGISTER_VALIDATE_ISSUE (CRITICAL, ALLOCATION_FAILURE,
424       "a memory allocation failed during Validate run", NULL);
425   REGISTER_VALIDATE_ISSUE (CRITICAL, MISSING_PLUGIN,
426       "a gstreamer plugin is missing and prevented Validate from running",
427       NULL);
428   REGISTER_VALIDATE_ISSUE (CRITICAL, NOT_NEGOTIATED,
429       "a NOT NEGOTIATED message has been posted on the bus.", NULL);
430   REGISTER_VALIDATE_ISSUE (WARNING, WARNING_ON_BUS,
431       "We got a WARNING message on the bus", NULL);
432   REGISTER_VALIDATE_ISSUE (CRITICAL, ERROR_ON_BUS,
433       "We got an ERROR message on the bus", NULL);
434   REGISTER_VALIDATE_ISSUE (WARNING, QUERY_POSITION_SUPERIOR_DURATION,
435       "Query position reported a value superior than what query duration "
436       "returned", NULL);
437   REGISTER_VALIDATE_ISSUE (WARNING, QUERY_POSITION_OUT_OF_SEGMENT,
438       "Query position reported a value outside of the current expected "
439       "segment", NULL);
440   REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_NOT_ENDED,
441       "The program stopped before some actions were executed", NULL);
442   REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_ACTION_TIMEOUT,
443       "The execution of an action timed out", NULL);
444   REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_FILE_MALFORMED,
445       "The scenario file was malformed", NULL);
446   REGISTER_VALIDATE_ISSUE_FULL (CRITICAL, SCENARIO_ACTION_EXECUTION_ERROR,
447       "The execution of an action did not properly happen", NULL,
448       GST_VALIDATE_ISSUE_FLAGS_NO_BACKTRACE |
449       GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS);
450   REGISTER_VALIDATE_ISSUE_FULL (CRITICAL, SCENARIO_ACTION_CHECK_ERROR,
451       "A check action failed", NULL,
452       GST_VALIDATE_ISSUE_FLAGS_NO_BACKTRACE |
453       GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS);
454   REGISTER_VALIDATE_ISSUE (ISSUE, SCENARIO_ACTION_EXECUTION_ISSUE,
455       "An issue happened during the execution of a scenario", NULL);
456   REGISTER_VALIDATE_ISSUE (CRITICAL, CONFIG_LATENCY_TOO_HIGH,
457       "The pipeline latency is higher than the maximum allowed by the scenario",
458       NULL);
459   REGISTER_VALIDATE_ISSUE (CRITICAL, CONFIG_TOO_MANY_BUFFERS_DROPPED,
460       "The number of dropped buffers is higher than the maximum allowed by the scenario",
461       NULL);
462   REGISTER_VALIDATE_ISSUE (CRITICAL, CONFIG_BUFFER_FREQUENCY_TOO_LOW,
463       _
464       ("Pad buffers push frequency is lower than the minimum required by the config"),
465       NULL);
466   REGISTER_VALIDATE_ISSUE_FULL (WARNING, G_LOG_WARNING,
467       _("We got a g_log warning"), NULL,
468       GST_VALIDATE_ISSUE_FLAGS_FORCE_BACKTRACE |
469       GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS);
470   REGISTER_VALIDATE_ISSUE_FULL (CRITICAL, G_LOG_CRITICAL,
471       "We got a g_log critical issue", NULL,
472       GST_VALIDATE_ISSUE_FLAGS_FORCE_BACKTRACE |
473       GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS);
474   REGISTER_VALIDATE_ISSUE_FULL (ISSUE, G_LOG_ISSUE, "We got a g_log issue",
475       NULL,
476       GST_VALIDATE_ISSUE_FLAGS_FORCE_BACKTRACE |
477       GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS);
478
479   REGISTER_VALIDATE_ISSUE (CRITICAL, PULL_RANGE_FROM_WRONG_THREAD,
480       "gst_pad_pull_range called from wrong thread",
481       _("gst_pad_pull_range has to be called from the sinkpad task thread."));
482 }
483
484 gboolean
485 gst_validate_send (JsonNode * root)
486 {
487   gboolean res = FALSE;
488   JsonGenerator *jgen;
489   gsize message_length;
490   gchar *object, *message;
491   GError *error = NULL;
492
493   if (!server_ostream)
494     goto done;
495
496   jgen = json_generator_new ();
497   json_generator_set_root (jgen, root);
498
499   object = json_generator_to_data (jgen, &message_length);
500   message = g_malloc0 (message_length + 5);
501   GST_WRITE_UINT32_BE (message, message_length);
502   strcpy (&message[4], object);
503   g_free (object);
504
505   res = g_output_stream_write_all (server_ostream, message, message_length + 4,
506       NULL, NULL, &error);
507
508   if (!res) {
509     if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING)) {
510       GST_DEBUG ("Stream was busy, trying again later.");
511
512       g_free (message);
513       g_object_unref (jgen);
514       if (error)
515         g_error_free (error);
516       g_idle_add ((GSourceFunc) gst_validate_send, root);
517       return G_SOURCE_REMOVE;
518     }
519
520     GST_ERROR ("ERROR: Can't write to remote: %s", error->message);
521   } else if (!g_output_stream_flush (server_ostream, NULL, &error)) {
522     GST_ERROR ("ERROR: Can't flush stream: %s", error->message);
523   }
524
525   g_free (message);
526   g_object_unref (jgen);
527   if (error)
528     g_error_free (error);
529
530 done:
531   json_node_free (root);
532
533   return G_SOURCE_REMOVE;
534 }
535
536 void
537 gst_validate_report_init (void)
538 {
539   const gchar *var, *file_env, *server_env, *uuid;
540   const GDebugKey keys[] = {
541     {"fatal_criticals", GST_VALIDATE_FATAL_CRITICALS},
542     {"fatal_warnings", GST_VALIDATE_FATAL_WARNINGS},
543     {"fatal_issues", GST_VALIDATE_FATAL_ISSUES},
544     {"print_issues", GST_VALIDATE_PRINT_ISSUES},
545     {"print_warnings", GST_VALIDATE_PRINT_WARNINGS},
546     {"print_criticals", GST_VALIDATE_PRINT_CRITICALS}
547   };
548
549   GST_DEBUG_CATEGORY_INIT (gst_validate_report_debug, "gstvalidatereport",
550       GST_DEBUG_FG_YELLOW, "Gst validate reporting");
551
552   _gst_validate_report_type = gst_validate_report_get_type ();
553
554   if (_gst_validate_report_start_time == 0) {
555     _gst_validate_report_start_time = gst_util_get_timestamp ();
556
557     /* init the debug flags */
558     var = g_getenv ("GST_VALIDATE");
559     if (var && strlen (var) > 0) {
560       _gst_validate_flags =
561           g_parse_debug_string (var, keys, G_N_ELEMENTS (keys));
562     }
563
564     gst_validate_report_load_issues ();
565   }
566 #ifdef HAVE_UNISTD_H
567   output_is_tty = isatty (1);
568 #endif
569
570   server_env = g_getenv ("GST_VALIDATE_SERVER");
571   uuid = g_getenv ("GST_VALIDATE_UUID");
572
573   if (server_env && !uuid) {
574     GST_INFO ("No GST_VALIDATE_UUID specified !");
575   } else if (server_env) {
576     GstUri *server_uri = gst_uri_from_string (server_env);
577
578     if (server_uri && !g_strcmp0 (gst_uri_get_scheme (server_uri), "tcp")) {
579       JsonBuilder *jbuilder;
580       GError *err = NULL;
581       socket_client = g_socket_client_new ();
582
583       server_connection = g_socket_client_connect_to_host (socket_client,
584           gst_uri_get_host (server_uri), gst_uri_get_port (server_uri),
585           NULL, &err);
586
587       if (!server_connection) {
588         g_clear_error (&err);
589         g_clear_object (&socket_client);
590
591       } else {
592         server_ostream =
593             g_io_stream_get_output_stream (G_IO_STREAM (server_connection));
594         jbuilder = json_builder_new ();
595         json_builder_begin_object (jbuilder);
596         json_builder_set_member_name (jbuilder, "uuid");
597         json_builder_add_string_value (jbuilder, uuid);
598         json_builder_set_member_name (jbuilder, "started");
599         json_builder_add_boolean_value (jbuilder, TRUE);
600         json_builder_end_object (jbuilder);
601
602         gst_validate_send (json_builder_get_root (jbuilder));
603         g_object_unref (jbuilder);
604       }
605
606       gst_uri_unref (server_uri);
607     } else {
608       GST_ERROR ("Server URI not valid: %s", server_env);
609     }
610   }
611
612   file_env = g_getenv ("GST_VALIDATE_FILE");
613   if (file_env != NULL && *file_env != '\0') {
614     gint i;
615     gchar **wanted_files;
616     wanted_files = g_strsplit (file_env, G_SEARCHPATH_SEPARATOR_S, 0);
617
618     /* FIXME: Make sure it is freed in the deinit function when that is
619      * implemented */
620     log_files =
621         g_malloc0 (sizeof (FILE *) * (g_strv_length (wanted_files) + 1));
622     for (i = 0; i < g_strv_length (wanted_files); i++) {
623       FILE *log_file;
624       if (g_strcmp0 (wanted_files[i], "stderr") == 0) {
625         log_file = stderr;
626       } else if (g_strcmp0 (wanted_files[i], "stdout") == 0) {
627         log_file = stdout;
628       } else {
629         log_file = g_fopen (wanted_files[i], "w");
630       }
631
632       if (log_file == NULL) {
633         g_printerr ("Could not open log file '%s' for writing: %s\n", file_env,
634             g_strerror (errno));
635         log_file = stderr;
636       }
637
638       log_files[i] = log_file;
639     }
640
641     g_strfreev (wanted_files);
642   } else {
643     log_files = g_malloc0 (sizeof (FILE *) * 2);
644     log_files[0] = stdout;
645   }
646
647 #ifndef GST_DISABLE_GST_DEBUG
648   if (!newline_regex)
649     newline_regex =
650         g_regex_new ("\n", G_REGEX_OPTIMIZE | G_REGEX_MULTILINE, 0, NULL);
651 #endif
652 }
653
654 void
655 gst_validate_report_deinit (void)
656 {
657   if (server_ostream) {
658     g_output_stream_close (server_ostream, NULL, NULL);
659     server_ostream = NULL;
660   }
661
662   g_clear_object (&socket_client);
663   g_clear_object (&server_connection);
664 }
665
666 GstValidateIssue *
667 gst_validate_issue_from_id (GstValidateIssueId issue_id)
668 {
669   return g_hash_table_lookup (_gst_validate_issues, (gpointer) issue_id);
670 }
671
672 /* TODO how are these functions going to work with extensions */
673 const gchar *
674 gst_validate_report_level_get_name (GstValidateReportLevel level)
675 {
676   switch (level) {
677     case GST_VALIDATE_REPORT_LEVEL_CRITICAL:
678       return "critical";
679     case GST_VALIDATE_REPORT_LEVEL_WARNING:
680       return "warning";
681     case GST_VALIDATE_REPORT_LEVEL_ISSUE:
682       return "issue";
683     case GST_VALIDATE_REPORT_LEVEL_IGNORE:
684       return "ignore";
685     case GST_VALIDATE_REPORT_LEVEL_EXPECTED:
686       return "expected";
687     default:
688       return "unknown";
689   }
690
691   return NULL;
692 }
693
694 GstValidateReportLevel
695 gst_validate_report_level_from_name (const gchar * level_name)
696 {
697   if (g_strcmp0 (level_name, "critical") == 0)
698     return GST_VALIDATE_REPORT_LEVEL_CRITICAL;
699
700   else if (g_strcmp0 (level_name, "warning") == 0)
701     return GST_VALIDATE_REPORT_LEVEL_WARNING;
702
703   else if (g_strcmp0 (level_name, "issue") == 0)
704     return GST_VALIDATE_REPORT_LEVEL_ISSUE;
705
706   else if (g_strcmp0 (level_name, "ignore") == 0)
707     return GST_VALIDATE_REPORT_LEVEL_IGNORE;
708
709   return GST_VALIDATE_REPORT_LEVEL_UNKNOWN;
710 }
711
712 gboolean
713 gst_validate_report_should_print (GstValidateReport * report)
714 {
715   if ((!(_gst_validate_flags & GST_VALIDATE_PRINT_ISSUES) &&
716           !(_gst_validate_flags & GST_VALIDATE_PRINT_WARNINGS) &&
717           !(_gst_validate_flags & GST_VALIDATE_PRINT_CRITICALS))) {
718     return TRUE;
719   }
720
721   if ((report->level <= GST_VALIDATE_REPORT_LEVEL_ISSUE &&
722           _gst_validate_flags & GST_VALIDATE_PRINT_ISSUES) ||
723       (report->level <= GST_VALIDATE_REPORT_LEVEL_WARNING &&
724           _gst_validate_flags & GST_VALIDATE_PRINT_WARNINGS) ||
725       (report->level <= GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
726           _gst_validate_flags & GST_VALIDATE_PRINT_CRITICALS)) {
727
728     return TRUE;
729   }
730
731   return FALSE;
732 }
733
734 gboolean
735 gst_validate_report_check_abort (GstValidateReport * report)
736 {
737   if ((report->level <= GST_VALIDATE_REPORT_LEVEL_ISSUE &&
738           _gst_validate_flags & GST_VALIDATE_FATAL_ISSUES) ||
739       (report->level <= GST_VALIDATE_REPORT_LEVEL_WARNING &&
740           _gst_validate_flags & GST_VALIDATE_FATAL_WARNINGS) ||
741       (report->level <= GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
742           _gst_validate_flags & GST_VALIDATE_FATAL_CRITICALS)) {
743
744     return TRUE;
745   }
746
747   return FALSE;
748 }
749
750 GstValidateIssueId
751 gst_validate_report_get_issue_id (GstValidateReport * report)
752 {
753   return gst_validate_issue_get_id (report->issue);
754 }
755
756 static void
757 _report_free (GstValidateReport * report)
758 {
759   g_free (report->message);
760   g_free (report->reporter_name);
761   g_free (report->trace);
762   g_free (report->dotfile_name);
763   g_list_free_full (report->shadow_reports,
764       (GDestroyNotify) gst_validate_report_unref);
765   g_list_free_full (report->repeated_reports,
766       (GDestroyNotify) gst_validate_report_unref);
767   g_mutex_clear (&report->shadow_reports_lock);
768   g_slice_free (GstValidateReport, report);
769 }
770
771 static gboolean
772 gst_validate_report_should_generate_backtrace (GstValidateIssue * issue,
773     GstValidateReport * report,
774     GstValidateReportingDetails default_details,
775     GstValidateReportingDetails issue_type_details,
776     GstValidateReportingDetails reporter_details)
777 {
778   if (issue->flags & GST_VALIDATE_ISSUE_FLAGS_FORCE_BACKTRACE)
779     return TRUE;
780
781   if (issue->flags & GST_VALIDATE_ISSUE_FLAGS_NO_BACKTRACE)
782     return FALSE;
783
784   if (default_details == GST_VALIDATE_SHOW_ALL)
785     return TRUE;
786
787   if (issue_type_details == GST_VALIDATE_SHOW_ALL)
788     return TRUE;
789
790   if (gst_validate_report_check_abort (report))
791     return TRUE;
792
793   if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
794     return TRUE;
795
796   return FALSE;
797 }
798
799 GstValidateReport *
800 gst_validate_report_new (GstValidateIssue * issue,
801     GstValidateReporter * reporter, const gchar * message)
802 {
803   GstValidateReport *report = g_slice_new0 (GstValidateReport);
804   GstValidateReportingDetails reporter_details, default_details,
805       issue_type_details;
806   GstValidateRunner *runner = gst_validate_reporter_get_runner (reporter);
807
808   gst_mini_object_init (((GstMiniObject *) report), 0,
809       _gst_validate_report_type, NULL, NULL,
810       (GstMiniObjectFreeFunction) _report_free);
811   GST_MINI_OBJECT_FLAG_SET (report, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
812
813   report->issue = issue;
814   /* The reporter is owning a ref on the report so it doesn't keep a ref to
815    * avoid reference cycles. But the report can also be used by
816    * GstValidateRunner *after* that the reporter has been destroyed, so we
817    * cache the reporter name to avoid crashing in
818    * gst_validate_report_print_detected_on if the reporter has been destroyed.
819    */
820   report->reporter = reporter;
821   report->reporter_name = g_strdup (gst_validate_reporter_get_name (reporter));
822   report->message = g_strdup (message);
823   g_mutex_init (&report->shadow_reports_lock);
824   report->timestamp =
825       gst_util_get_timestamp () - _gst_validate_report_start_time;
826   report->level = issue->default_level;
827   report->reporting_level = GST_VALIDATE_SHOW_UNKNOWN;
828
829   reporter_details = gst_validate_reporter_get_reporting_level (reporter);
830   issue_type_details = gst_validate_runner_get_reporting_level_for_name (runner,
831       g_quark_to_string (issue->issue_id));
832   default_details = gst_validate_runner_get_default_reporting_details (runner);
833   gst_object_unref (runner);
834   if (reporter_details != GST_VALIDATE_SHOW_ALL &&
835       reporter_details != GST_VALIDATE_SHOW_UNKNOWN)
836     return report;
837
838   if (gst_validate_report_should_generate_backtrace (issue, report,
839           default_details, issue_type_details, reporter_details))
840     report->trace = gst_debug_get_stack_trace (GST_STACK_TRACE_SHOW_FULL);
841
842   return report;
843 }
844
845 void
846 gst_validate_report_unref (GstValidateReport * report)
847 {
848   gst_mini_object_unref (GST_MINI_OBJECT (report));
849 }
850
851 GstValidateReport *
852 gst_validate_report_ref (GstValidateReport * report)
853 {
854   return (GstValidateReport *) gst_mini_object_ref (GST_MINI_OBJECT (report));
855 }
856
857 void
858 gst_validate_printf (gpointer source, const gchar * format, ...)
859 {
860   va_list var_args;
861
862   va_start (var_args, format);
863   gst_validate_printf_valist (source, format, var_args);
864   va_end (var_args);
865 }
866
867 typedef struct
868 {
869   GString *str;
870   gint indent;
871   gint printed;
872 } PrintActionFieldData;
873
874 static gboolean
875 _append_value (GQuark field_id, const GValue * value, PrintActionFieldData * d)
876 {
877   gchar *val_str = NULL;
878   const gchar *fieldname = g_quark_to_string (field_id);
879
880   if (g_str_has_prefix (fieldname, "__") && g_str_has_suffix (fieldname, "__"))
881     return TRUE;
882
883   if (g_strcmp0 (fieldname, "repeat") == 0)
884     return TRUE;
885
886   d->printed++;
887   if (G_VALUE_TYPE (value) == GST_TYPE_CLOCK_TIME)
888     val_str = g_strdup_printf ("%" GST_TIME_FORMAT,
889         GST_TIME_ARGS (g_value_get_uint64 (value)));
890   else
891     val_str = gst_value_serialize (value);
892
893   g_string_append_printf (d->str, "\n%*c   - ", d->indent, ' ');
894   g_string_append (d->str, fieldname);
895   g_string_append_len (d->str, "=", 1);
896   g_string_append (d->str, val_str);
897
898   g_free (val_str);
899
900   return TRUE;
901 }
902
903 /**
904  * gst_validate_print_action:
905  * @action: (allow-none): The source object to log
906  * @message: The message to print out in the GstValidate logging system
907  *
908  * Print @message to the GstValidate logging system
909  */
910 void
911 gst_validate_print_action (GstValidateAction * action, const gchar * message)
912 {
913   GString *string = NULL;
914
915   if (message == NULL) {
916     gint indent = (gst_validate_action_get_level (action) * 2);
917     PrintActionFieldData d = { NULL, indent, 0 };
918     d.str = string = g_string_new (NULL);
919
920     g_string_append_printf (string, "%s",
921         gst_structure_get_name (action->structure));
922
923     if (GST_VALIDATE_ACTION_N_REPEATS (action))
924       g_string_append_printf (string, " [%s=%d/%d]",
925           GST_VALIDATE_ACTION_RANGE_NAME (action) ?
926           GST_VALIDATE_ACTION_RANGE_NAME (action) : "repeat", action->repeat,
927           GST_VALIDATE_ACTION_N_REPEATS (action));
928
929     g_string_append (string, " ( ");
930     gst_structure_foreach (action->structure,
931         (GstStructureForeachFunc) _append_value, &d);
932     if (d.printed)
933       g_string_append_printf (string, "\n%*c)\n", indent, ' ');
934     else
935       g_string_append (string, ")\n");
936     message = string->str;
937   }
938
939   gst_validate_printf (action, "%s", message);
940
941   if (string)
942     g_string_free (string, TRUE);
943 }
944
945 static void
946 print_action_parameter (GString * string, GstValidateActionType * type,
947     GstValidateActionParameter * param)
948 {
949   gchar *desc;
950   g_string_append_printf (string, "\n\n* `%s`:(%s): ", param->name,
951       param->mandatory ? "mandatory" : "optional");
952
953   if (g_strcmp0 (param->description, "")) {
954     desc = g_strdup (param->description);
955   } else {
956     desc = g_strdup ("__No description__");
957   }
958
959   g_string_append (string, desc);
960   g_free (desc);
961
962   if (param->possible_variables) {
963     desc =
964         g_regex_replace (newline_regex,
965         param->possible_variables, -1, 0, "\n\n  * ", 0, NULL);
966     g_string_append_printf (string, "\n\n  Possible variables:\n\n  * %s",
967         desc);
968   }
969
970   if (param->types)
971     g_string_append_printf (string, "\n\n  Possible types: `%s`", param->types);
972
973   if (!param->mandatory)
974     g_string_append_printf (string, "\n\n  Default: %s", param->def);
975
976 }
977
978 static void
979 print_action_parameter_prototype (GString * string,
980     GstValidateActionParameter * param, gboolean is_first)
981 {
982   if (!is_first)
983     g_string_append (string, ",");
984   g_string_append (string, "\n    ");
985
986   if (!param->mandatory)
987     g_string_append (string, "[");
988
989   g_string_append (string, param->name);
990   if (param->types)
991     g_string_append_printf (string, "=(%s)", param->types);
992
993   if (!param->mandatory)
994     g_string_append (string, "]");
995 }
996
997 static int
998 sort_parameters (const GstValidateActionParameter * param1,
999     const GstValidateActionParameter * param2)
1000 {
1001   if (param1->mandatory && !param2->mandatory)
1002     return -1;
1003
1004   if (!param1->mandatory && param2->mandatory)
1005     return 1;
1006
1007   return g_strcmp0 (param1->name, param2->name);
1008 }
1009
1010 void
1011 gst_validate_printf_valist (gpointer source, const gchar * format, va_list args)
1012 {
1013   gint i;
1014   gchar *tmp;
1015   GString *string = g_string_new (NULL);
1016
1017   if (source) {
1018     if (*(GType *) source == GST_TYPE_VALIDATE_ACTION) {
1019       GstValidateAction *action = (GstValidateAction *) source;
1020       gint indent = gst_validate_action_get_level (action) * 2;
1021
1022       if (_action_check_and_set_printed (action))
1023         goto out;
1024
1025       if (!indent)
1026         g_string_assign (string, "Executing ");
1027       else
1028         g_string_append_printf (string, "%*c↳ Executing ", indent - 2, ' ');
1029     } else if (*(GType *) source == GST_TYPE_VALIDATE_ACTION_TYPE) {
1030       gint i;
1031       gint n_params;
1032       gboolean has_parameters = FALSE;
1033       gboolean is_first = TRUE;
1034
1035       GstValidateActionParameter playback_time_param = {
1036         .name = "playback-time",
1037         .description = "The playback time at which the action will be executed",
1038         .mandatory = FALSE,
1039         .types = "double,string",
1040         .possible_variables =
1041             "`position`: The current position in the stream\n"
1042             "`duration`: The duration of the stream",
1043         .def = "0.0"
1044       };
1045
1046       GstValidateActionParameter on_message_param = {
1047         .name = "on-message",
1048         .description =
1049             "Specify on what message type the action will be executed.\n"
1050             " If both 'playback-time' and 'on-message' is specified, the action will be executed\n"
1051             " on whatever happens first.",
1052         .mandatory = FALSE,
1053         .types = "string",
1054         .possible_variables = NULL,
1055         .def = NULL
1056       };
1057
1058
1059       GstValidateActionType *type = GST_VALIDATE_ACTION_TYPE (source);
1060
1061       /* Ignore private action types */
1062       if (g_str_has_prefix (type->name, "priv_"))
1063         return;
1064
1065       g_string_append_printf (string, "\n## %s\n\n", type->name);
1066
1067       g_string_append_printf (string, "\n``` validate-scenario\n%s,",
1068           type->name);
1069
1070       for (n_params = 0; type->parameters[n_params].name != NULL; n_params++);
1071       qsort (type->parameters, n_params, sizeof (GstValidateActionParameter),
1072           (GCompareFunc) sort_parameters);
1073       for (i = 0; type->parameters[i].name; i++) {
1074         print_action_parameter_prototype (string, &type->parameters[i],
1075             is_first);
1076         is_first = FALSE;
1077       }
1078
1079       if (!IS_CONFIG_ACTION_TYPE (type->flags)) {
1080         print_action_parameter_prototype (string, &playback_time_param,
1081             is_first);
1082         is_first = FALSE;
1083       }
1084
1085       g_string_append (string, ";\n```\n");
1086
1087       g_string_append_printf (string, "\n%s", type->description);
1088       g_string_append_printf (string,
1089           "\n * Implementer namespace: %s", type->implementer_namespace);
1090
1091       if (IS_CONFIG_ACTION_TYPE (type->flags))
1092         g_string_append_printf (string,
1093             "\n * Is config action (meaning it will be executing right "
1094             "at the beginning of the execution of the pipeline)");
1095
1096
1097       if (type->parameters || !IS_CONFIG_ACTION_TYPE (type->flags))
1098         g_string_append_printf (string, "\n\n### Parameters");
1099
1100       if (type->parameters) {
1101         has_parameters = TRUE;
1102         for (i = 0; type->parameters[i].name; i++) {
1103           print_action_parameter (string, type, &type->parameters[i]);
1104         }
1105       }
1106
1107       if (!IS_CONFIG_ACTION_TYPE (type->flags)) {
1108         print_action_parameter (string, type, &playback_time_param);
1109         print_action_parameter (string, type, &on_message_param);
1110       }
1111
1112
1113       if ((type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL)) {
1114         has_parameters = TRUE;
1115         g_string_append_printf (string,
1116             "\n     optional                   : "
1117             "Don't raise an error if this action hasn't been executed or failed"
1118             "\n%-32s  ### Possible types:"
1119             "\n%-32s    boolean" "\n%-32s  Default: false", "", "", "");
1120       }
1121
1122       if (!has_parameters)
1123         g_string_append_printf (string, "\n\n  ### No Parameters");
1124     } else if (GST_IS_VALIDATE_REPORTER (source) &&
1125         gst_validate_reporter_get_name (source)) {
1126       g_string_printf (string, "\n%s --> ",
1127           gst_validate_reporter_get_name (source));
1128     } else if (GST_IS_OBJECT (source)) {
1129       g_string_printf (string, "\n%s --> ", GST_OBJECT_NAME (source));
1130     } else if (G_IS_OBJECT (source)) {
1131       g_string_printf (string, "\n<%s@%p> --> ", G_OBJECT_TYPE_NAME (source),
1132           source);
1133     }
1134   }
1135
1136   tmp = gst_info_strdup_vprintf (format, args);
1137   g_string_append (string, tmp);
1138   g_free (tmp);
1139
1140   if (!newline_regex)
1141     newline_regex =
1142         g_regex_new ("\n", G_REGEX_OPTIMIZE | G_REGEX_MULTILINE, 0, NULL);
1143
1144 #ifndef GST_DISABLE_GST_DEBUG
1145   {
1146     gchar *str;
1147
1148     str = g_regex_replace (newline_regex, string->str, string->len, 0,
1149         "", 0, NULL);
1150
1151     if (source)
1152       GST_INFO ("%s", str);
1153     else
1154       GST_DEBUG ("%s", str);
1155
1156     g_free (str);
1157   }
1158 #endif
1159
1160   for (i = 0; log_files[i]; i++) {
1161     fprintf (log_files[i], "%s", string->str);
1162     fflush (log_files[i]);
1163   }
1164
1165 out:
1166   g_string_free (string, TRUE);
1167 }
1168
1169 gboolean
1170 gst_validate_report_set_master_report (GstValidateReport * report,
1171     GstValidateReport * master_report)
1172 {
1173   GList *tmp;
1174   gboolean add_shadow_report = TRUE;
1175
1176   if (master_report->reporting_level >= GST_VALIDATE_SHOW_MONITOR &&
1177       master_report->reporting_level != GST_VALIDATE_SHOW_SMART) {
1178     return FALSE;
1179   }
1180
1181   report->master_report = master_report;
1182
1183   GST_VALIDATE_REPORT_SHADOW_REPORTS_LOCK (master_report);
1184   for (tmp = master_report->shadow_reports; tmp; tmp = tmp->next) {
1185     GstValidateReport *shadow_report = (GstValidateReport *) tmp->data;
1186     if (report->reporter == shadow_report->reporter) {
1187       add_shadow_report = FALSE;
1188       break;
1189     }
1190   }
1191   if (add_shadow_report)
1192     master_report->shadow_reports =
1193         g_list_append (master_report->shadow_reports,
1194         gst_validate_report_ref (report));
1195   GST_VALIDATE_REPORT_SHADOW_REPORTS_UNLOCK (master_report);
1196
1197   return TRUE;
1198 }
1199
1200 void
1201 gst_validate_report_print_level (GstValidateReport * report)
1202 {
1203   gst_validate_printf (NULL, "%10s : %s\n",
1204       gst_validate_report_level_get_name (report->level),
1205       report->issue->summary);
1206 }
1207
1208 void
1209 gst_validate_report_print_detected_on (GstValidateReport * report)
1210 {
1211   GList *tmp;
1212
1213   gst_validate_printf (NULL, "%*s Detected on <%s",
1214       12, "", report->reporter_name);
1215   for (tmp = report->shadow_reports; tmp; tmp = tmp->next) {
1216     GstValidateReport *shadow_report = (GstValidateReport *) tmp->data;
1217     gst_validate_printf (NULL, ", %s", shadow_report->reporter_name);
1218   }
1219   gst_validate_printf (NULL, ">\n");
1220 }
1221
1222 void
1223 gst_validate_report_print_details (GstValidateReport * report)
1224 {
1225   if (report->message) {
1226     gint i;
1227     gchar **lines = g_strsplit (report->message, "\n", -1);
1228
1229     gst_validate_printf (NULL, "%*s Details : %s\n", 12, "", lines[0]);
1230     for (i = 1; lines[i]; i++)
1231       gst_validate_printf (NULL, "%*s%s\n", 21, "", lines[i]);
1232     g_strfreev (lines);
1233   }
1234 }
1235
1236 static void
1237 gst_validate_report_print_trace (GstValidateReport * report)
1238 {
1239   if (report->trace) {
1240     gint i;
1241     gchar **lines = g_strsplit (report->trace, "\n", -1);
1242
1243     gst_validate_printf (NULL, "%*s backtrace :\n", 12, "");
1244     for (i = 0; lines[i]; i++)
1245       gst_validate_printf (NULL, "%*s%s\n", 15, "", lines[i]);
1246     g_strfreev (lines);
1247   }
1248 }
1249
1250 static void
1251 gst_validate_report_print_dotfile (GstValidateReport * report)
1252 {
1253   const gchar *dotdir = g_getenv ("GST_DEBUG_DUMP_DOT_DIR");
1254   const gchar *doturl = g_getenv ("GST_VALIDATE_DEBUG_DUMP_DOT_URL");
1255
1256   if (!report->dotfile_name)
1257     return;
1258
1259   if (doturl)
1260     gst_validate_printf (NULL, "%*s dotfile : %s%s%s.dot\n", 12, "",
1261         doturl, G_DIR_SEPARATOR_S, report->dotfile_name);
1262   else if (dotdir)
1263     gst_validate_printf (NULL, "%*s dotfile : %s%s%s.dot\n", 12, "",
1264         dotdir, G_DIR_SEPARATOR_S, report->dotfile_name);
1265   else
1266     gst_validate_printf (NULL,
1267         "%*s dotfile : no dotfile produced as GST_DEBUG_DUMP_DOT_DIR is not set.\n",
1268         12, "");
1269 }
1270
1271 void
1272 gst_validate_report_print_description (GstValidateReport * report)
1273 {
1274   if (report->issue->description)
1275     gst_validate_printf (NULL, "%*s Description : %s\n", 12, "",
1276         report->issue->description);
1277 }
1278
1279 void
1280 gst_validate_report_printf (GstValidateReport * report)
1281 {
1282   GList *tmp;
1283
1284   gst_validate_report_print_level (report);
1285   gst_validate_report_print_detected_on (report);
1286   gst_validate_report_print_details (report);
1287   for (tmp = report->repeated_reports; tmp; tmp = tmp->next) {
1288     gst_validate_report_print_details (tmp->data);
1289   }
1290   gst_validate_report_print_dotfile (report);
1291   gst_validate_report_print_trace (report);
1292
1293   gst_validate_report_print_description (report);
1294   gst_validate_printf (NULL, "\n");
1295 }
1296
1297 void
1298 gst_validate_report_set_reporting_level (GstValidateReport * report,
1299     GstValidateReportingDetails level)
1300 {
1301   report->reporting_level = level;
1302 }
1303
1304 void
1305 gst_validate_report_add_repeated_report (GstValidateReport * report,
1306     GstValidateReport * repeated_report)
1307 {
1308   report->repeated_reports =
1309       g_list_append (report->repeated_reports,
1310       gst_validate_report_ref (repeated_report));
1311 }
1312
1313
1314 void
1315 gst_validate_print_position (GstClockTime position, GstClockTime duration,
1316     gdouble rate, gchar * extra_info)
1317 {
1318   JsonBuilder *jbuilder;
1319
1320   gst_validate_printf (NULL,
1321       "<position: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
1322       " speed: %f %s/>%c", GST_TIME_ARGS (position), GST_TIME_ARGS (duration),
1323       rate, extra_info ? extra_info : "", output_is_tty ? '\r' : '\n');
1324
1325   if (!server_ostream)
1326     return;
1327
1328   jbuilder = json_builder_new ();
1329   json_builder_begin_object (jbuilder);
1330   json_builder_set_member_name (jbuilder, "type");
1331   json_builder_add_string_value (jbuilder, "position");
1332   json_builder_set_member_name (jbuilder, "position");
1333   json_builder_add_int_value (jbuilder, position);
1334   json_builder_set_member_name (jbuilder, "duration");
1335   json_builder_add_int_value (jbuilder, duration);
1336   json_builder_set_member_name (jbuilder, "speed");
1337   json_builder_add_double_value (jbuilder, rate);
1338   json_builder_end_object (jbuilder);
1339
1340   gst_validate_send (json_builder_get_root (jbuilder));
1341   g_object_unref (jbuilder);
1342
1343   g_free (extra_info);
1344 }
1345
1346 void
1347 gst_validate_skip_test (const gchar * format, ...)
1348 {
1349   JsonBuilder *jbuilder;
1350   va_list va_args;
1351   gchar *tmp;
1352
1353   va_start (va_args, format);
1354   tmp = gst_info_strdup_vprintf (format, va_args);
1355   va_end (va_args);
1356
1357   if (!server_ostream) {
1358     gchar *f = g_strconcat ("ok 1 # SKIP ", tmp, NULL);
1359
1360     g_free (tmp);
1361     gst_validate_printf (NULL, "%s", f);
1362     return;
1363   }
1364
1365   jbuilder = json_builder_new ();
1366   json_builder_begin_object (jbuilder);
1367   json_builder_set_member_name (jbuilder, "type");
1368   json_builder_add_string_value (jbuilder, "skip-test");
1369   json_builder_set_member_name (jbuilder, "details");
1370   json_builder_add_string_value (jbuilder, tmp);
1371   json_builder_end_object (jbuilder);
1372   g_free (tmp);
1373
1374   gst_validate_send (json_builder_get_root (jbuilder));
1375   g_object_unref (jbuilder);
1376 }
1377
1378 static void
1379 print_issue (gpointer key, GstValidateIssue * issue, gpointer user_data)
1380 {
1381   gst_validate_printf (NULL, "\n# `%s` (%" G_GUINTPTR_FORMAT ")\n\n",
1382       g_quark_to_string (issue->issue_id), issue->issue_id);
1383   gst_validate_printf (NULL, "%c%s\n\n", g_ascii_toupper (issue->summary[0]),
1384       &issue->summary[1]);
1385   if (issue->description)
1386     gst_validate_printf (NULL, "%c%s\n\n",
1387         g_ascii_toupper (issue->description[0]), &issue->description[1]);
1388   gst_validate_printf (NULL, "Area: %s\n", issue->area);
1389   gst_validate_printf (NULL, "Name: %s\n", issue->name);
1390   gst_validate_printf (NULL, "Default severity: %s\n\n",
1391       gst_validate_report_level_get_name (issue->default_level));
1392 }
1393
1394 void
1395 gst_validate_print_issues (void)
1396 {
1397   g_return_if_fail (_gst_validate_issues);
1398
1399   g_hash_table_foreach (_gst_validate_issues, (GHFunc) print_issue, NULL);
1400 }
1401
1402 void
1403 gst_validate_error_structure (gpointer structure, const gchar * format, ...)
1404 {
1405   gchar *filename = NULL;
1406   gint lineno = -1;
1407   gchar *tmp, *debug = NULL;
1408   GString *f = g_string_new (NULL);
1409   va_list var_args;
1410   gchar *color = NULL;
1411
1412   const gchar *endcolor = "";
1413
1414 #if GLIB_CHECK_VERSION(2,50,0)
1415   if (g_log_writer_supports_color (fileno (stderr))) {
1416     color = gst_debug_construct_term_color (GST_DEBUG_FG_RED);
1417     endcolor = "\033[0m";
1418   }
1419 #endif
1420
1421   if (structure) {
1422     if (GST_IS_STRUCTURE (structure)) {
1423       filename =
1424           g_strdup (gst_structure_get_string (structure, "__filename__"));
1425       debug = g_strdup (gst_structure_get_string (structure, "__debug__"));
1426       gst_structure_get_int (structure, "__lineno__", &lineno);
1427       /* We are going to assert... we can boutcher the struct! */
1428       gst_structure_remove_fields (structure, "__filename__", "__lineno__",
1429           "__debug__", NULL);
1430     } else {
1431       filename = g_strdup (GST_VALIDATE_ACTION_FILENAME (structure));
1432       debug = g_strdup (GST_VALIDATE_ACTION_DEBUG (structure));
1433       lineno = GST_VALIDATE_ACTION_LINENO (structure);
1434     }
1435   }
1436
1437   va_start (var_args, format);
1438   tmp = gst_info_strdup_vprintf (format, var_args);
1439   va_end (var_args);
1440
1441   g_string_append_printf (f, "%s:%d: %s\n",
1442       filename ? filename : "Unknown", lineno, tmp);
1443
1444   if (debug)
1445     g_string_append (f, debug);
1446
1447   g_print ("Bail out! %sERROR%s: %s\n\n", color ? color : "", endcolor, f->str);
1448   g_string_free (f, TRUE);
1449   g_free (debug);
1450   g_free (color);
1451   g_free (filename);
1452   g_free (tmp);
1453
1454   exit (-18);
1455 }
1456
1457 void
1458 gst_validate_abort (const gchar * format, ...)
1459 {
1460   va_list var_args;
1461   gchar *tmp;
1462
1463   va_start (var_args, format);
1464   tmp = gst_info_strdup_vprintf (format, var_args);
1465   va_end (var_args);
1466
1467   g_print ("Bail out! %s\n", tmp);
1468   exit (-18);
1469 }