Merging gst-devtools
[platform/upstream/gstreamer.git] / validate / gst / validate / gst-validate-runner.c
index f99a674..13c132e 100644 (file)
@@ -35,6 +35,8 @@
 #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
@@ -51,6 +53,7 @@ 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
@@ -72,6 +75,7 @@ static GstValidateRunner *first_runner = NULL;
  *  gst_object_unref (runner);
  *  gst_object_unref (monitor);
  * ]|
+ *
  */
 
 struct _GstValidateRunnerPrivate
@@ -89,6 +93,8 @@ struct _GstValidateRunnerPrivate
 
   gchar *pipeline_names;
   gchar **pipeline_names_strv;
+
+  GList *expected_issues;
 };
 
 /* Describes the reporting level to apply to a name pattern */
@@ -113,7 +119,8 @@ typedef struct _PatternLevel
   } G_STMT_END
 
 #define gst_validate_runner_parent_class parent_class
-G_DEFINE_TYPE (GstValidateRunner, gst_validate_runner, GST_TYPE_TRACER);
+G_DEFINE_TYPE_WITH_PRIVATE (GstValidateRunner, gst_validate_runner,
+    GST_TYPE_TRACER);
 
 /* signals */
 enum
@@ -413,8 +420,6 @@ gst_validate_runner_class_init (GstValidateRunnerClass * klass)
   gobject_class->get_property = gst_validate_runner_get_property;
   gobject_class->constructor = gst_validate_runner_constructor;
 
-  g_type_class_add_private (klass, sizeof (GstValidateRunnerPrivate));
-
   properties[PROP_PARAMS] =
       g_param_spec_string ("params", "Params", "Extra configuration parameters",
       NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
@@ -437,9 +442,7 @@ gst_validate_runner_class_init (GstValidateRunnerClass * klass)
 static void
 gst_validate_runner_init (GstValidateRunner * runner)
 {
-  runner->priv = G_TYPE_INSTANCE_GET_PRIVATE (runner, GST_TYPE_VALIDATE_RUNNER,
-      GstValidateRunnerPrivate);
-  g_mutex_init (&runner->priv->mutex);
+  runner->priv = gst_validate_runner_get_instance_private (runner);
 
   runner->priv->reports_by_type = g_hash_table_new (g_direct_hash,
       g_direct_equal);
@@ -447,8 +450,13 @@ gst_validate_runner_init (GstValidateRunner * runner)
   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);
 }
 
 /**
@@ -467,7 +475,8 @@ gst_validate_runner_new (void)
     runner = first_runner;
     first_runner = NULL;
   } else if (element_created) {
-    g_error ("Should never create a GstValidateRunner after a GstElement"
+    gst_validate_abort
+        ("Should never create a GstValidateRunner after a GstElement "
         "has been created in the same process.");
 
     return NULL;
@@ -476,6 +485,20 @@ gst_validate_runner_new (void)
     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;
 }
 
@@ -487,6 +510,9 @@ gst_validate_runner_new (void)
 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;
 }
 
@@ -503,13 +529,23 @@ 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_match_string (pattern_level->pattern, name))
+    if (g_pattern_match_string (pattern_level->pattern, fixed_name)) {
+      g_free (fixed_name);
+
       return pattern_level->level;
+    }
   }
 
+  g_free (fixed_name);
   return GST_VALIDATE_SHOW_UNKNOWN;
 }
 
@@ -531,18 +567,149 @@ synthesize_reports (GstValidateRunner * runner, GstValidateReport * report)
   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 reporter_level =
-      gst_validate_reporter_get_reporting_level (report->reporter);
+  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 (reporter_level == GST_VALIDATE_SHOW_UNKNOWN) {
+  if (details == GST_VALIDATE_SHOW_UNKNOWN) {
     gst_validate_report_set_reporting_level (report,
         runner->priv->default_level);
     switch (runner->priv->default_level) {
@@ -550,16 +717,23 @@ gst_validate_runner_add_report (GstValidateRunner * runner,
         return;
       case GST_VALIDATE_SHOW_SMART:
         if (!gst_validate_report_check_abort (report) &&
-            report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
+            report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
+            !report->trace) {
           synthesize_reports (runner, report);
           return;
         }
+        break;
       case GST_VALIDATE_SHOW_SYNTHETIC:
-        synthesize_reports (runner, report);
-        return;
+        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);
@@ -572,11 +746,11 @@ gst_validate_runner_add_report (GstValidateRunner * runner,
 
 /**
  * gst_validate_runner_get_reports_count:
- * @runner: The $GstValidateRunner to get the number of report from
+ * @runner: The $GstValidateRunner to get the number of reports from
  *
  * Get the number of reports present in the runner:
  *
- * Returns: The number of report present in the runner.
+ * Returns: The number of reports present in the runner.
  */
 guint
 gst_validate_runner_get_reports_count (GstValidateRunner * runner)
@@ -584,12 +758,14 @@ gst_validate_runner_get_reports_count (GstValidateRunner * runner)
   GList *tmp;
   guint l;
 
-  g_return_val_if_fail (runner != NULL, 0);
+  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)
-    l += g_list_length (((GstValidateReport *) tmp->data)->repeated_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);
 
@@ -600,7 +776,7 @@ gst_validate_runner_get_reports_count (GstValidateRunner * runner)
  * gst_validate_runner_get_reports:
  * @runner: The #GstValidateRunner
  *
- * Return: (element-type GstValidate.Report)(transfer full): all the reports
+ * Return: (element-type GstValidateReport)(transfer full): all the reports
  */
 GList *
 gst_validate_runner_get_reports (GstValidateRunner * runner)
@@ -643,14 +819,17 @@ _do_report_synthesis (GstValidateRunner * runner)
     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);
+      report = (GstValidateReport *) tmp->data;
       gst_validate_report_print_detected_on (report);
 
-      if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
-        criticals = g_list_append (criticals, 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);
       }
     }
@@ -667,8 +846,8 @@ _do_report_synthesis (GstValidateRunner * runner)
  * gst_validate_runner_printf:
  * @runner: The #GstValidateRunner to print all the reports for
  *
- * Prints all the report on the terminal or on wherever set
- * in the #GST_VALIDATE_FILE env variable.
+ * 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
@@ -681,27 +860,27 @@ gst_validate_runner_printf (GstValidateRunner * runner)
   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 = tmp->data;
+    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, tmp->data);
+      criticals = g_list_append (criticals, report);
     }
   }
 
   if (criticals) {
     GList *iter;
-
-    g_printerr ("\n\n==== Got criticals, Return value set to 18 ====\n");
+    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",
+      g_printerr ("  * critical error %s\n",
           ((GstValidateReport *) (iter->data))->message);
     }
     g_printerr ("\n");
@@ -718,22 +897,51 @@ int
 gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
 {
   gint ret = 0;
+  GList *tmp, *configs;
 
-  g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0);
+  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 = tmp->data;
-
+      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;
 }
 
@@ -755,6 +963,9 @@ gst_validate_deinit_runner (void)
 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;
 }