validate: Add action types to set/check various child properties at once
authorThibault Saunier <tsaunier@igalia.com>
Thu, 20 Feb 2020 15:27:37 +0000 (12:27 -0300)
committerThibault Saunier <tsaunier@igalia.com>
Wed, 26 Feb 2020 16:36:30 +0000 (13:36 -0300)
And add a way to take into account control bindings.

ges/ges-validate.c

index 2310ae1..80e0840 100644 (file)
@@ -449,6 +449,185 @@ _split_clip (GstValidateScenario * scenario, GstValidateAction * action)
   return (ges_clip_split (GES_CLIP (element), position) != NULL);
 }
 
+typedef struct
+{
+  GstValidateScenario *scenario;
+  GESTimelineElement *element;
+  GstValidateActionReturn res;
+  GstClockTime time;
+  gboolean check_children;
+  GstValidateAction *action;
+} PropertyData;
+
+static gboolean
+check_property (GQuark field_id, GValue * expected_value, PropertyData * data)
+{
+  GValue cvalue = G_VALUE_INIT, *tvalue = NULL, comparable_value = G_VALUE_INIT,
+      *observed_value;
+  const gchar *property = g_quark_to_string (field_id);
+  GstControlBinding *binding = NULL;
+
+  if (!data->check_children) {
+    g_object_get_property (G_OBJECT (data->element), property, &cvalue);
+    goto compare;
+  }
+
+  if (GST_CLOCK_TIME_IS_VALID (data->time)) {
+    if (!GES_IS_TRACK_ELEMENT (data->element)) {
+      GST_VALIDATE_REPORT_ACTION (data->scenario, data->action,
+          SCENARIO_ACTION_EXECUTION_ERROR,
+          "Could not get property at time for type %s - only GESTrackElement supported",
+          G_OBJECT_TYPE_NAME (data->element));
+      data->res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+
+      return FALSE;
+    }
+
+    binding =
+        ges_track_element_get_control_binding (GES_TRACK_ELEMENT
+        (data->element), property);
+    if (binding) {
+      tvalue = gst_control_binding_get_value (binding, data->time);
+
+      if (!tvalue) {
+        GST_VALIDATE_REPORT_ACTION (data->scenario, data->action,
+            SCENARIO_ACTION_EXECUTION_ERROR,
+            "Could not get property: %s at %" GST_TIME_FORMAT, property,
+            GST_TIME_ARGS (data->time));
+        data->res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+
+        return FALSE;
+      }
+    }
+  }
+
+  if (!tvalue
+      && !ges_timeline_element_get_child_property (data->element, property,
+          &cvalue)) {
+    GST_VALIDATE_REPORT_ACTION (data->scenario, data->action,
+        SCENARIO_ACTION_EXECUTION_ERROR, "Could not get property: %s:",
+        property);
+    data->res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+
+    return FALSE;
+  }
+
+compare:
+  observed_value = tvalue ? tvalue : &cvalue;
+
+  if (G_VALUE_TYPE (observed_value) != G_VALUE_TYPE (expected_value)) {
+    g_value_init (&comparable_value, G_VALUE_TYPE (observed_value));
+
+    if (G_VALUE_TYPE (observed_value) == GST_TYPE_CLOCK_TIME) {
+      GstClockTime t;
+
+      if (gst_validate_utils_get_clocktime (data->action->structure, property,
+              &t)) {
+        g_value_set_uint64 (&comparable_value, t);
+        expected_value = &comparable_value;
+      }
+    } else if (g_value_transform (expected_value, &comparable_value)) {
+      expected_value = &comparable_value;
+    }
+  }
+
+  if (gst_value_compare (observed_value, expected_value) != GST_VALUE_EQUAL) {
+    gchar *expected = gst_value_serialize (expected_value), *observed =
+        gst_value_serialize (observed_value);
+
+    GST_VALIDATE_REPORT_ACTION (data->scenario, data->action,
+        SCENARIO_ACTION_CHECK_ERROR,
+        "%s:%s expected value: '(%s)%s' different than observed: '(%s)%s'",
+        GES_TIMELINE_ELEMENT_NAME (data->element), property,
+        G_VALUE_TYPE_NAME (observed_value), expected,
+        G_VALUE_TYPE_NAME (expected_value), observed);
+
+    g_free (expected);
+    g_free (observed);
+    data->res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+  }
+
+  if (G_VALUE_TYPE (&comparable_value) != G_TYPE_NONE)
+    g_value_unset (&comparable_value);
+
+  if (tvalue) {
+    g_value_unset (tvalue);
+    g_free (tvalue);
+  } else
+    g_value_reset (&cvalue);
+  return TRUE;
+}
+
+static gboolean
+set_property (GQuark field_id, const GValue * value, PropertyData * data)
+{
+  const gchar *property = g_quark_to_string (field_id);
+
+  if (!ges_timeline_element_set_child_property (data->element, property, value)) {
+    gchar *v = gst_value_serialize (value);
+
+    GST_VALIDATE_REPORT_ACTION (data->scenario, data->action,
+        SCENARIO_ACTION_EXECUTION_ERROR,
+        "Could not set %s child property %s to %s",
+        GES_TIMELINE_ELEMENT_NAME (data->element), property, v);
+
+    data->res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+    g_free (v);
+
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+set_or_check_properties (GstValidateScenario * scenario,
+    GstValidateAction * action)
+{
+  GESTimelineElement *element;
+  GstStructure *structure;
+  const gchar *element_name;
+  PropertyData data = {
+    .scenario = scenario,
+    .element = NULL,
+    .res = GST_VALIDATE_EXECUTE_ACTION_OK,
+    .time = GST_CLOCK_TIME_NONE,
+    .check_children =
+        !gst_structure_has_name (action->structure, "check-ges-properties"),
+    .action = action,
+  };
+
+  DECLARE_AND_GET_TIMELINE (scenario, action);
+
+  gst_validate_action_get_clocktime (scenario, action, "at-time", &data.time);
+
+  structure = gst_structure_copy (action->structure);
+  element_name = gst_structure_get_string (structure, "element-name");
+  element = ges_timeline_get_element (timeline, element_name);
+  if (!element) {
+    GST_VALIDATE_REPORT_ACTION (scenario, action,
+        SCENARIO_ACTION_EXECUTION_ERROR,
+        "Can not find element: %s", element_name);
+
+    data.res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
+    goto done;
+  }
+  g_object_unref (timeline);
+
+  data.element = element;
+  gst_structure_remove_fields (structure, "element-name", "at-time", NULL);
+  gst_structure_foreach (structure,
+      gst_structure_has_name (action->structure,
+          "set-child-properties") ? (GstStructureForeachFunc) set_property
+      : (GstStructureForeachFunc) check_property, &data);
+  gst_object_unref (element);
+
+done:
+  gst_structure_free (structure);
+
+  return data.res;
+}
+
 static gboolean
 _set_track_restriction_caps (GstValidateScenario * scenario,
     GstValidateAction * action)
@@ -1126,6 +1305,52 @@ ges_validate_register_action_types (void)
         {NULL}
       }, "Allows to change child property of an object", GST_VALIDATE_ACTION_TYPE_NONE);
 
+  gst_validate_register_action_type ("check-ges-properties", "ges", set_or_check_properties,
+      (GstValidateActionParameter []) {
+        {
+          .name = "element-name",
+          .description = "The name of the element on which to check properties",
+          .types = "string",
+          .mandatory = TRUE,
+        },
+        {NULL}
+      }, "Check `element-name` properties values defined by the"
+         " fields in the following format: `property_name=expected-value`",
+        GST_VALIDATE_ACTION_TYPE_NONE);
+
+  gst_validate_register_action_type ("check-child-properties", "ges", set_or_check_properties,
+      (GstValidateActionParameter []) {
+        {
+          .name = "element-name",
+          .description = "The name of the element on which to check children properties",
+          .types = "string",
+          .mandatory = TRUE,
+        },
+        {
+          .name = "at-time",
+          .description = "The time at which to check the values, taking into"
+            " account the ControlBinding if any set.",
+          .types = "string",
+          .mandatory = FALSE,
+        },
+        {NULL}
+      }, "Check `element-name` children properties values defined by the"
+         " fields in the following format: `property_name=expected-value`",
+        GST_VALIDATE_ACTION_TYPE_NONE);
+
+  gst_validate_register_action_type ("set-child-properties", "ges", set_or_check_properties,
+      (GstValidateActionParameter []) {
+        {
+          .name = "element-name",
+          .description = "The name of the element on which to modify child properties",
+          .types = "string",
+          .mandatory = TRUE,
+        },
+        {NULL}
+      }, "Sets `element-name` children properties values defined by the"
+         " fields in the following format: `property-name=new-value`",
+        GST_VALIDATE_ACTION_TYPE_NONE);
+
   gst_validate_register_action_type ("split-clip", "ges", _split_clip,
       (GstValidateActionParameter []) {
         {