gstreamer/subprojects/gst-devtools/validate/gst/validate/gst-validate-runner.c

991 lines
28 KiB
C

/* GStreamer
*
* Copyright (C) 2013-2016 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
* Author: Thibault Saunier <thibault.saunier@collabora.com>
*
* gst-validate-runner.c - Validate Runner class
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include "validate.h"
#include "gst-validate-internal.h"
#include "gst-validate-report.h"
#include "gst-validate-monitor-factory.h"
#include "gst-validate-override-registry.h"
#include "gst-validate-runner.h"
#include "gst-validate-reporter.h"
#include "gst-validate-mockdecryptor.h"
GST_DEBUG_CATEGORY_STATIC (gst_validate_runner_debug);
#undef GST_CAT_DEFAULT
#define GST_CAT_DEFAULT gst_validate_runner_debug
static gboolean element_created = FALSE;
/* We create a GstValidateRunner on _init ()
* so that we keep backward compatibility when
* the user create a Runner after creating the pipeline
* but the runner was actually already ready to be used.
*/
static GstValidateRunner *first_runner = NULL;
/**
* SECTION:gst-validate-runner
* @title: GstValidateRunner
* @short_description: Class that runs Gst Validate tests for a pipeline
*
* Allows you to test a pipeline within GstValidate. It is the object where
* all issue reporting is done.
*
* In the tools using GstValidate the only minimal code to be able to monitor
* your pipelines is:
*
* |[
* GstPipeline *pipeline = gst_pipeline_new ("monitored-pipeline");
* GstValidateRunner *runner = gst_validate_runner_new ();
* GstValidateMonitor *monitor = gst_validate_monitor_factory_create (
* GST_OBJECT (pipeline), runner, NULL);
*
* // Run the pipeline and do whatever you want with it
*
* // In that same order
* gst_object_unref (pipeline);
* gst_object_unref (runner);
* gst_object_unref (monitor);
* ]|
*
*/
struct _GstValidateRunnerPrivate
{
GMutex mutex;
GList *reports;
GstValidateReportingDetails default_level;
GHashTable *reports_by_type;
/* A list of PatternLevel */
GList *report_pattern_levels;
/* Whether the runner was create with GST_TRACERS=validate or not) */
gboolean user_created;
gchar *pipeline_names;
gchar **pipeline_names_strv;
GList *expected_issues;
};
/* Describes the reporting level to apply to a name pattern */
typedef struct _PatternLevel
{
GPatternSpec *pattern;
GstValidateReportingDetails level;
} PatternLevel;
#define GST_VALIDATE_RUNNER_LOCK(r) \
G_STMT_START { \
GST_LOG_OBJECT (r, "About to lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
(g_mutex_lock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \
GST_LOG_OBJECT (r, "Acquired lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
} G_STMT_END
#define GST_VALIDATE_RUNNER_UNLOCK(r) \
G_STMT_START { \
GST_LOG_OBJECT (r, "About to unlock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
(g_mutex_unlock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \
GST_LOG_OBJECT (r, "Released lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
} G_STMT_END
#define gst_validate_runner_parent_class parent_class
G_DEFINE_TYPE_WITH_PRIVATE (GstValidateRunner, gst_validate_runner,
GST_TYPE_TRACER);
/* signals */
enum
{
REPORT_ADDED_SIGNAL,
STOPPING_SIGNAL,
/* add more above */
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_PARAMS,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
static guint _signals[LAST_SIGNAL] = { 0, };
static gboolean
gst_validate_runner_should_monitor (GstValidateRunner * self,
GstElement * element)
{
gint i;
GstValidateMonitor *monitor;
if (!GST_IS_PIPELINE (element)) {
return FALSE;
}
if (self->priv->user_created)
return FALSE;
if (!self->priv->pipeline_names_strv)
return TRUE;
monitor = gst_validate_get_monitor (G_OBJECT (element));
if (monitor) {
GST_ERROR_OBJECT (self, "Pipeline %" GST_PTR_FORMAT " is already"
" monitored by %" GST_PTR_FORMAT " using runner: %" GST_PTR_FORMAT
" NOT monitoring again.",
element, monitor,
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
}
for (i = 0; self->priv->pipeline_names_strv[i]; i++) {
if (g_pattern_match_simple (self->priv->pipeline_names_strv[i],
GST_OBJECT_NAME (element)))
return TRUE;
}
return FALSE;
}
static void
do_element_new (GstValidateRunner * self, guint64 ts, GstElement * element)
{
element_created = TRUE;
if (gst_validate_runner_should_monitor (self, element)) {
/* the reference to the monitor is lost */
gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), self, NULL);
}
}
static gboolean
_parse_reporting_level (gchar * str, GstValidateReportingDetails * level)
{
if (!str)
return FALSE;
/* works in place */
g_strstrip (str);
if (g_ascii_isdigit (str[0])) {
unsigned long l;
char *endptr;
l = strtoul (str, &endptr, 10);
if (endptr > str && endptr[0] == 0) {
*level = (GstValidateReportingDetails) l;
} else {
return FALSE;
}
} else if (g_ascii_strcasecmp (str, "smart") == 0) {
*level = GST_VALIDATE_SHOW_SMART;
} else if (g_ascii_strcasecmp (str, "none") == 0) {
*level = GST_VALIDATE_SHOW_NONE;
} else if (g_ascii_strcasecmp (str, "synthetic") == 0) {
*level = GST_VALIDATE_SHOW_SYNTHETIC;
} else if (g_ascii_strcasecmp (str, "subchain") == 0) {
*level = GST_VALIDATE_SHOW_SUBCHAIN;
} else if (g_ascii_strcasecmp (str, "monitor") == 0) {
*level = GST_VALIDATE_SHOW_MONITOR;
} else if (g_ascii_strcasecmp (str, "all") == 0) {
*level = GST_VALIDATE_SHOW_ALL;
} else
return FALSE;
return TRUE;
}
static void
_free_report_pattern_level (PatternLevel * pattern_level)
{
g_pattern_spec_free (pattern_level->pattern);
g_free (pattern_level);
}
static void
_set_reporting_level_for_name (GstValidateRunner * runner,
const gchar * pattern, GstValidateReportingDetails level)
{
PatternLevel *pattern_level = g_new (PatternLevel, 1);
GPatternSpec *pattern_spec = g_pattern_spec_new (pattern);
pattern_level->pattern = pattern_spec;
pattern_level->level = level;
/* Allow the user to single out a pad with the "element-name__pad-name" syntax
*/
if (g_strrstr (pattern, "__"))
runner->priv->report_pattern_levels =
g_list_prepend (runner->priv->report_pattern_levels, pattern_level);
else
runner->priv->report_pattern_levels =
g_list_append (runner->priv->report_pattern_levels, pattern_level);
}
static void
_replace_double_colons (gchar * word)
{
while (word) {
word = strstr (word, "::");
if (word) {
word[0] = '_';
word[1] = '_';
}
}
}
static void
_set_report_levels_from_string (GstValidateRunner * self, const gchar * list)
{
gchar **split;
gchar **walk;
g_assert (list);
GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list);
split = g_strsplit (list, ",", 0);
for (walk = split; *walk; walk++) {
_replace_double_colons (*walk);
if (strchr (*walk, ':')) {
gchar **values = g_strsplit (*walk, ":", 2);
if (values[0] && values[1]) {
GstValidateReportingDetails level;
if (_parse_reporting_level (values[1], &level))
_set_reporting_level_for_name (self, values[0], level);
}
g_strfreev (values);
} else {
GstValidateReportingDetails level;
if (_parse_reporting_level (*walk, &level))
self->priv->default_level = level;
}
}
g_strfreev (split);
}
static void
_init_report_levels (GstValidateRunner * self)
{
const gchar *env;
env = g_getenv ("GST_VALIDATE_REPORTING_DETAILS");
if (env)
_set_report_levels_from_string (self, env);
}
static void
_unref_report_list (gpointer unused, GList * reports, gpointer unused_too)
{
g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
}
static void
gst_validate_runner_finalize (GObject * object)
{
GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
if (!runner->priv->user_created)
gst_validate_runner_exit (runner, TRUE);
g_list_free_full (runner->priv->reports,
(GDestroyNotify) gst_validate_report_unref);
g_list_free_full (runner->priv->report_pattern_levels,
(GDestroyNotify) _free_report_pattern_level);
g_mutex_clear (&runner->priv->mutex);
g_free (runner->priv->pipeline_names);
g_strfreev (runner->priv->pipeline_names_strv);
g_hash_table_foreach (runner->priv->reports_by_type, (GHFunc)
_unref_report_list, NULL);
g_hash_table_destroy (runner->priv->reports_by_type);
G_OBJECT_CLASS (parent_class)->finalize (object);
if (!runner->priv->user_created)
gst_validate_deinit ();
}
static GObject *
gst_validate_runner_constructor (GType type, guint n_construct_params,
GObjectConstructParam * construct_params)
{
GObject *runner = G_OBJECT_CLASS (parent_class)->constructor (type,
n_construct_params, construct_params);
if (!gst_validate_is_initialized ()) {
first_runner = GST_VALIDATE_RUNNER (runner);
gst_validate_init ();
first_runner = NULL;
return runner;
}
return runner;
}
static void
gst_validate_runner_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstValidateRunner *runner;
runner = GST_VALIDATE_RUNNER (object);
switch (prop_id) {
case PROP_PARAMS:
{
g_value_set_string (value, runner->priv->pipeline_names);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_runner_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstValidateRunner *runner;
runner = GST_VALIDATE_RUNNER (object);
switch (prop_id) {
case PROP_PARAMS:
{
g_free (runner->priv->pipeline_names);
g_strfreev (runner->priv->pipeline_names_strv);
runner->priv->pipeline_names = g_value_dup_string (value);
if (runner->priv->pipeline_names)
runner->priv->pipeline_names_strv =
g_strsplit (runner->priv->pipeline_names, ",", -1);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_runner_class_init (GstValidateRunnerClass * klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gst_validate_runner_finalize;
gobject_class->set_property = gst_validate_runner_set_property;
gobject_class->get_property = gst_validate_runner_get_property;
gobject_class->constructor = gst_validate_runner_constructor;
properties[PROP_PARAMS] =
g_param_spec_string ("params", "Params", "Extra configuration parameters",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
_signals[REPORT_ADDED_SIGNAL] =
g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
GST_TYPE_VALIDATE_REPORT);
_signals[STOPPING_SIGNAL] =
g_signal_new ("stopping", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
NULL, NULL, NULL, G_TYPE_NONE, 0);
GST_DEBUG_CATEGORY_INIT (gst_validate_runner_debug, "gstvalidaterunner",
GST_DEBUG_FG_YELLOW, "Gst validate runner");
}
static void
gst_validate_runner_init (GstValidateRunner * runner)
{
runner->priv = gst_validate_runner_get_instance_private (runner);
g_mutex_init (&runner->priv->mutex);
runner->priv->reports_by_type = g_hash_table_new (g_direct_hash,
g_direct_equal);
runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT;
_init_report_levels (runner);
runner->priv->expected_issues = gst_validate_get_test_file_expected_issues ();
gst_tracing_register_hook (GST_TRACER (runner), "element-new",
G_CALLBACK (do_element_new));
gst_element_register (NULL, GST_MOCKDECRYPTOR_NAME, GST_RANK_MARGINAL,
GST_TYPE_MOCKDECRYPTOR);
}
/**
* gst_validate_runner_new:
*
* Create a new #GstValidateRunner
*
* Returns: A newly created #GstValidateRunner
*/
GstValidateRunner *
gst_validate_runner_new (void)
{
GstValidateRunner *runner;
if (first_runner) {
runner = first_runner;
first_runner = NULL;
} else if (element_created) {
gst_validate_abort
("Should never create a GstValidateRunner after a GstElement "
"has been created in the same process.");
return NULL;
} else {
runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
runner->priv->user_created = TRUE;
}
{
GstValidateOverrideRegistry *registry =
gst_validate_override_registry_get ();
GList *all_overrides =
gst_validate_override_registry_get_override_list (registry);
GList *i;
for (i = all_overrides; i; i = i->next) {
GstValidateOverride *override = (GstValidateOverride *) i->data;
gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (override),
runner);
}
g_list_free (all_overrides);
}
return runner;
}
/*
* gst_validate_runner_get_default_reporting_level:
*
* Returns: the default #GstValidateReportingDetails used to output a report.
*/
GstValidateReportingDetails
gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner)
{
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
GST_VALIDATE_SHOW_UNKNOWN);
return runner->priv->default_level;
}
#if !GLIB_CHECK_VERSION(2,70,0)
#define g_pattern_spec_match_string g_pattern_match_string
#endif
/*
* gst_validate_runner_get_reporting_level_for_name:
*
* Returns: the #GstValidateReportingDetails that will be applied for a given name.
* If no pattern was set for such a name, this function will return
* #GST_VALIDATE_SHOW_UNKNOWN, and reporting for that name will
* default to the global reporting level.
*/
GstValidateReportingDetails
gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner,
const gchar * name)
{
GList *tmp;
gchar *fixed_name;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
GST_VALIDATE_SHOW_UNKNOWN);
fixed_name = g_strdup (name);
_replace_double_colons (fixed_name);
for (tmp = runner->priv->report_pattern_levels; tmp; tmp = tmp->next) {
PatternLevel *pattern_level = (PatternLevel *) tmp->data;
if (g_pattern_spec_match_string (pattern_level->pattern, fixed_name)) {
g_free (fixed_name);
return pattern_level->level;
}
}
g_free (fixed_name);
return GST_VALIDATE_SHOW_UNKNOWN;
}
static void
synthesize_reports (GstValidateRunner * runner, GstValidateReport * report)
{
GstValidateIssueId issue_id;
GList *reports;
issue_id = report->issue->issue_id;
GST_VALIDATE_RUNNER_LOCK (runner);
reports =
g_hash_table_lookup (runner->priv->reports_by_type,
GINT_TO_POINTER (issue_id));
reports = g_list_append (reports, gst_validate_report_ref (report));
g_hash_table_insert (runner->priv->reports_by_type,
GINT_TO_POINTER (issue_id), reports);
GST_VALIDATE_RUNNER_UNLOCK (runner);
}
static void
_dot_pipeline (GstValidateReport * report, GstStructure * config)
{
GstPipeline *pipeline = gst_validate_reporter_get_pipeline (report->reporter);
if (pipeline) {
gint details = GST_DEBUG_GRAPH_SHOW_ALL;
gchar *reporter_basename =
g_path_get_basename (gst_validate_reporter_get_name (report->reporter));
report->dotfile_name =
g_strdup_printf ("%" GST_TIME_FORMAT "-validate-report-%s-on-%s-%s",
GST_TIME_ARGS (GST_CLOCK_DIFF (_priv_start_time,
gst_util_get_timestamp ())),
gst_validate_report_level_get_name (report->level), reporter_basename,
g_quark_to_string (report->issue->issue_id));
g_free (reporter_basename);
if (config)
gst_structure_get_int (config, "details", &details);
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipeline),
GST_DEBUG_GRAPH_SHOW_ALL, report->dotfile_name);
gst_object_unref (pipeline);
}
}
static void
gst_validate_runner_maybe_dot_pipeline (GstValidateRunner * runner,
GstValidateReport * report)
{
GList *config;
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL ||
gst_validate_report_check_abort (report)) {
_dot_pipeline (report, NULL);
return;
}
for (config = gst_validate_plugin_get_config (NULL);
config; config = config->next) {
if (gst_structure_has_name (config->data, "core")) {
GstValidateReportLevel level;
const gchar *level_str,
*action = gst_structure_get_string (config->data, "action");
if (g_strcmp0 (action, "dot-pipeline"))
continue;
level_str = gst_structure_get_string (config->data, "report-level");
level = level_str ? gst_validate_report_level_from_name (level_str) :
GST_VALIDATE_REPORT_LEVEL_CRITICAL;
if (level >= report->level) {
_dot_pipeline (report, config->data);
return;
}
}
}
}
static gboolean
check_report_expected (GstValidateRunner * runner, GstValidateReport * report)
{
GList *tmp;
#define GET_STR(name) gst_structure_get_string (known_issue, name)
for (tmp = runner->priv->expected_issues; tmp; tmp = tmp->next) {
GstStructure *known_issue = tmp->data;
const gchar *id = GET_STR ("issue-id");
if (!id || g_quark_from_string (id) == report->issue->issue_id) {
const gchar *summary = GET_STR ("summary");
if (!summary || !g_strcmp0 (summary, report->issue->summary)) {
const gchar *details = GET_STR ("details");
if (!details || g_regex_match_simple (details, report->message, 0, 0)) {
const gchar *detected_on = GET_STR ("detected-on");
if (!detected_on || !g_strcmp0 (detected_on, report->reporter_name)) {
const gchar *level = GET_STR ("level");
const gchar *report_level =
gst_validate_report_level_get_name (report->level);
if (!detected_on || !g_strcmp0 (level, report_level)) {
gboolean is_sometimes;
if (!gst_structure_get_boolean (known_issue, "sometimes",
&is_sometimes) || !is_sometimes) {
runner->priv->expected_issues =
g_list_remove (runner->priv->expected_issues, known_issue);
gst_structure_free (known_issue);
}
return TRUE;
}
}
}
}
}
#undef GET_STR
}
return FALSE;
}
void
gst_validate_runner_add_report (GstValidateRunner * runner,
GstValidateReport * report)
{
GstValidateReportingDetails details, reporter_details, issue_type_details;
g_return_if_fail (GST_IS_VALIDATE_RUNNER (runner));
if (report->level == GST_VALIDATE_REPORT_LEVEL_IGNORE)
return;
if (check_report_expected (runner, report)) {
GST_INFO_OBJECT (runner, "Found expected issue: %p", report);
report->level = GST_VALIDATE_REPORT_LEVEL_EXPECTED;
}
gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE (report),
report));
gst_validate_runner_maybe_dot_pipeline (runner, report);
details = reporter_details =
gst_validate_reporter_get_reporting_level (report->reporter);
issue_type_details =
gst_validate_runner_get_reporting_level_for_name (runner,
g_quark_to_string (report->issue->issue_id));
if (reporter_details == GST_VALIDATE_SHOW_UNKNOWN)
details = issue_type_details;
/* Let's use our own reporting strategy */
if (details == GST_VALIDATE_SHOW_UNKNOWN) {
gst_validate_report_set_reporting_level (report,
runner->priv->default_level);
switch (runner->priv->default_level) {
case GST_VALIDATE_SHOW_NONE:
return;
case GST_VALIDATE_SHOW_SMART:
if (!gst_validate_report_check_abort (report) &&
report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
!report->trace) {
synthesize_reports (runner, report);
return;
}
break;
case GST_VALIDATE_SHOW_SYNTHETIC:
if (!report->trace) {
synthesize_reports (runner, report);
return;
}
default:
break;
}
} else if (details == GST_VALIDATE_SHOW_NONE) {
GST_DEBUG ("Not reporting.");
return;
}
GST_VALIDATE_RUNNER_LOCK (runner);
runner->priv->reports =
g_list_append (runner->priv->reports, gst_validate_report_ref (report));
GST_VALIDATE_RUNNER_UNLOCK (runner);
g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report);
}
/**
* gst_validate_runner_get_reports_count:
* @runner: The #GstValidateRunner to get the number of reports from
*
* Get the number of reports present in the runner:
*
* Returns: The number of reports present in the runner.
*/
guint
gst_validate_runner_get_reports_count (GstValidateRunner * runner)
{
GList *tmp;
guint l;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 0);
GST_VALIDATE_RUNNER_LOCK (runner);
l = g_list_length (runner->priv->reports);
for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
GstValidateReport *report = (GstValidateReport *) tmp->data;
l += g_list_length (report->repeated_reports);
}
l += g_hash_table_size (runner->priv->reports_by_type);
GST_VALIDATE_RUNNER_UNLOCK (runner);
return l;
}
/**
* gst_validate_runner_get_reports:
* @runner: The #GstValidateRunner
*
* Returns: (element-type GstValidateReport) (transfer full): The list of reports
*/
GList *
gst_validate_runner_get_reports (GstValidateRunner * runner)
{
GList *ret;
GST_VALIDATE_RUNNER_LOCK (runner);
ret =
g_list_copy_deep (runner->priv->reports,
(GCopyFunc) gst_validate_report_ref, NULL);
GST_VALIDATE_RUNNER_UNLOCK (runner);
return ret;
}
static GList *
_do_report_synthesis (GstValidateRunner * runner)
{
GHashTableIter iter;
GList *reports, *tmp;
gpointer key, value;
GList *criticals = NULL;
/* Take the lock so the hash table won't be modified while we are iterating
* over it */
GST_VALIDATE_RUNNER_LOCK (runner);
g_hash_table_iter_init (&iter, runner->priv->reports_by_type);
while (g_hash_table_iter_next (&iter, &key, &value)) {
GstValidateReport *report;
reports = (GList *) value;
if (!reports)
continue;
report = (GstValidateReport *) (reports->data);
gst_validate_report_print_level (report);
gst_validate_report_print_detected_on (report);
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
criticals = g_list_append (criticals, report);
gst_validate_report_print_details (report);
} else if (report->issue->flags & GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS)
gst_validate_report_print_details (report);
for (tmp = g_list_next (reports); tmp; tmp = tmp->next) {
report = (GstValidateReport *) tmp->data;
gst_validate_report_print_detected_on (report);
if ((report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) ||
(report->issue->flags & GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS)) {
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
criticals = g_list_append (criticals, report);
gst_validate_report_print_details (report);
}
}
report = (GstValidateReport *) (reports->data);
gst_validate_report_print_description (report);
gst_validate_printf (NULL, "\n");
}
GST_VALIDATE_RUNNER_UNLOCK (runner);
return criticals;
}
/**
* gst_validate_runner_printf:
* @runner: The #GstValidateRunner to print all the reports for
*
* Prints all the reports on the terminal or on wherever is set
* in the `GST_VALIDATE_FILE` env variable.
*
* Returns: 0 if no critical error has been found and 18 if a critical
* error has been detected. That return value is usually to be used as
* exit code of the application.
*/
int
gst_validate_runner_printf (GstValidateRunner * runner)
{
GList *reports, *tmp;
int ret = 0;
GList *criticals = NULL;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
criticals = _do_report_synthesis (runner);
reports = gst_validate_runner_get_reports (runner);
for (tmp = reports; tmp; tmp = tmp->next) {
GstValidateReport *report = (GstValidateReport *) tmp->data;
if (gst_validate_report_should_print (report))
gst_validate_report_printf (report);
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
criticals = g_list_append (criticals, report);
}
}
if (criticals) {
GList *iter;
g_printerr ("\n\n**Got criticals. Return value set to 18**:\n");
ret = 18;
for (iter = criticals; iter; iter = iter->next) {
g_printerr (" * critical error %s\n",
((GstValidateReport *) (iter->data))->message);
}
g_printerr ("\n");
}
g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
g_list_free (criticals);
gst_validate_printf (NULL, "Issues found: %u\n",
gst_validate_runner_get_reports_count (runner));
return ret;
}
int
gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
{
gint ret = 0;
GList *tmp, *configs;
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0);
if (print_result) {
ret = gst_validate_runner_printf (runner);
} else {
GList *tmp;
for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
GstValidateReport *report = (GstValidateReport *) tmp->data;
if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
ret = 18;
}
}
configs = gst_validate_get_config (NULL);
for (tmp = configs; tmp; tmp = tmp->next) {
if (!gst_structure_has_field (tmp->data, "__n_usages__")) {
gst_validate_error_structure (tmp->data,
"Unused config: '%" GST_PTR_FORMAT "'", tmp->data);
}
}
g_list_free (configs);
for (tmp = runner->priv->expected_issues; tmp; tmp = tmp->next) {
GstStructure *known_issue = tmp->data;
gboolean is_sometimes;
if (!gst_structure_get_boolean (known_issue, "sometimes", &is_sometimes)
|| !is_sometimes) {
GstStructure *tmpstruct = gst_structure_copy (known_issue);
gst_structure_remove_fields (tmpstruct, "__debug__", "__lineno__",
"__filename__", NULL);
/* Ideally we should report an issue here.. but we do not have a reporter */
gst_validate_error_structure (known_issue,
"Expected issue didn't happen: '%" GST_PTR_FORMAT "'", tmpstruct);
gst_structure_free (tmpstruct);
}
}
g_list_free_full (runner->priv->expected_issues,
(GDestroyNotify) gst_structure_free);
runner->priv->expected_issues = NULL;
return ret;
}
void
gst_validate_init_runner (void)
{
if (!first_runner) {
first_runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
first_runner->priv->user_created = TRUE;
} /* else the first runner has been created through the GST_TRACERS system */
}
void
gst_validate_deinit_runner (void)
{
g_clear_object (&first_runner);
}
GstValidateReportingDetails
gst_validate_runner_get_default_reporting_details (GstValidateRunner * runner)
{
g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
GST_VALIDATE_SHOW_UNKNOWN);
return runner->priv->default_level;
}
#ifdef __GST_VALIDATE_PLUGIN
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_tracer_register (plugin, "validate", GST_TYPE_VALIDATE_RUNNER))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, validatetracer,
"GStreamer Validate tracers", plugin_init, VERSION, GST_LICENSE,
GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
#endif /* __GST_VALIDATE_PLUGIN */