summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Saunier <tsaunier@igalia.com>2020-06-15 10:50:14 -0400
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>2020-06-22 17:20:32 +0000
commit9c08bfcaca4ee622dff67c4d890d43ed10dbcc03 (patch)
tree0fd723969d1f485bc25ab009d20c9bded154dc48
parente7355ea039491fbba49edac0be4f7d31722bdf9e (diff)
validate:scenario: Replace the `sub-action` with a `foreach` action type
Sub-actions were really hard to use and conceptually weird. The implementation was ugly and made the code complex for nothing. Instead this commit introduces a `foreach` action type which allows repeating actions passed in an `actions` array the number of time specified by any `GstIntRange` value defined in the structure or its `repeat` field. This commit also makes sure that all action got through gst_validate_action_set_done upon finalization. + Cleanup surrounding code + Add tests Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-devtools/-/merge_requests/207>
-rw-r--r--validate/data/scenarios/change_state_intensive.scenario7
-rw-r--r--validate/gst/validate/gst-validate-internal.h2
-rw-r--r--validate/gst/validate/gst-validate-report.c58
-rw-r--r--validate/gst/validate/gst-validate-reporter.c45
-rw-r--r--validate/gst/validate/gst-validate-scenario.c466
-rw-r--r--validate/gst/validate/gst-validate-scenario.h5
-rw-r--r--validate/tests/launcher_tests/foreach.validatetest47
-rw-r--r--validate/tests/launcher_tests/foreach/flow-expectations/log-sink-sink-expected24
-rw-r--r--validate/tests/launcher_tests/foreach_deep.validatetest51
-rw-r--r--validate/tests/launcher_tests/foreach_repeat.validatetest31
10 files changed, 529 insertions, 207 deletions
diff --git a/validate/data/scenarios/change_state_intensive.scenario b/validate/data/scenarios/change_state_intensive.scenario
index 06ac60e..042d6fb 100644
--- a/validate/data/scenarios/change_state_intensive.scenario
+++ b/validate/data/scenarios/change_state_intensive.scenario
@@ -1,3 +1,8 @@
description, duration=0, summary="Set state to NULL->PLAYING->NULL 20 times", need-clock-sync=true, min-media-duration=1.0, live_content_compatible=True, handles-states=true, ignore-eos=true
-set-state, state="playing", sub-action="set-state, state=null", repeat=40
+
+foreach, i=[0, 40],
+ actions = {
+ "set-state, state=playing",
+ "set-state, state=null",
+ }
stop;
diff --git a/validate/gst/validate/gst-validate-internal.h b/validate/gst/validate/gst-validate-internal.h
index d454b4d..e5b26e5 100644
--- a/validate/gst/validate/gst-validate-internal.h
+++ b/validate/gst/validate/gst-validate-internal.h
@@ -47,7 +47,7 @@ void register_action_types (void);
* as we used to have to print actions in the action execution function
* and this is done by the scenario itself now */
G_GNUC_INTERNAL gboolean _action_check_and_set_printed (GstValidateAction *action);
-G_GNUC_INTERNAL gboolean gst_validate_action_is_subaction (GstValidateAction *action);
+G_GNUC_INTERNAL gboolean gst_validate_action_get_level (GstValidateAction *action);
G_GNUC_INTERNAL gboolean gst_validate_scenario_check_and_set_needs_clock_sync (GList *structures, GstStructure **meta);
#define GST_VALIDATE_SCENARIO_SUFFIX ".scenario"
diff --git a/validate/gst/validate/gst-validate-report.c b/validate/gst/validate/gst-validate-report.c
index 25b15b5..95c55e8 100644
--- a/validate/gst/validate/gst-validate-report.c
+++ b/validate/gst/validate/gst-validate-report.c
@@ -834,27 +834,36 @@ gst_validate_printf (gpointer source, const gchar * format, ...)
va_end (var_args);
}
+typedef struct
+{
+ GString *str;
+ gint indent;
+ gint printed;
+} PrintActionFieldData;
+
static gboolean
-_append_value (GQuark field_id, const GValue * value, GString * string)
+_append_value (GQuark field_id, const GValue * value, PrintActionFieldData * d)
{
gchar *val_str = NULL;
+ const gchar *fieldname = g_quark_to_string (field_id);
- if (g_strcmp0 (g_quark_to_string (field_id), "sub-action") == 0)
+ if (g_str_has_prefix (fieldname, "__") && g_str_has_suffix (fieldname, "__"))
return TRUE;
- if (g_strcmp0 (g_quark_to_string (field_id), "repeat") == 0)
+ if (g_strcmp0 (fieldname, "repeat") == 0)
return TRUE;
+ d->printed++;
if (G_VALUE_TYPE (value) == GST_TYPE_CLOCK_TIME)
val_str = g_strdup_printf ("%" GST_TIME_FORMAT,
GST_TIME_ARGS (g_value_get_uint64 (value)));
else
val_str = gst_value_serialize (value);
- g_string_append (string, "\n - ");
- g_string_append (string, g_quark_to_string (field_id));
- g_string_append_len (string, "=", 1);
- g_string_append (string, val_str);
+ g_string_append_printf (d->str, "\n%*c - ", d->indent, ' ');
+ g_string_append (d->str, fieldname);
+ g_string_append_len (d->str, "=", 1);
+ g_string_append (d->str, val_str);
g_free (val_str);
@@ -874,26 +883,24 @@ gst_validate_print_action (GstValidateAction * action, const gchar * message)
GString *string = NULL;
if (message == NULL) {
- gint nrepeats;
-
- string = g_string_new (NULL);
-
- if (gst_validate_action_is_subaction (action))
- g_string_append_printf (string, "(subaction)");
-
- if (gst_structure_get_int (action->structure, "repeat", &nrepeats))
- g_string_append_printf (string, "(%d/%d)", nrepeats - action->repeat + 1,
- nrepeats);
+ gint indent = (gst_validate_action_get_level (action) * 2);
+ PrintActionFieldData d = { NULL, indent, 0 };
+ d.str = string = g_string_new (NULL);
g_string_append_printf (string, "%s",
gst_structure_get_name (action->structure));
- g_string_append_len (string, " ( ", 3);
- gst_structure_foreach (action->structure,
- (GstStructureForeachFunc) _append_value, string);
+ if (GST_VALIDATE_ACTION_N_REPEATS (action))
+ g_string_append_printf (string, " [%s=%d/%d]",
+ GST_VALIDATE_ACTION_RANGE_NAME (action) ?
+ GST_VALIDATE_ACTION_RANGE_NAME (action) : "repeat", action->repeat,
+ GST_VALIDATE_ACTION_N_REPEATS (action));
- if (gst_structure_n_fields (action->structure))
- g_string_append (string, "\n)\n");
+ g_string_append (string, " ( ");
+ gst_structure_foreach (action->structure,
+ (GstStructureForeachFunc) _append_value, &d);
+ if (d.printed)
+ g_string_append_printf (string, "\n%*c)\n", indent, ' ');
else
g_string_append (string, ")\n");
message = string->str;
@@ -980,12 +987,15 @@ gst_validate_printf_valist (gpointer source, const gchar * format, va_list args)
if (source) {
if (*(GType *) source == GST_TYPE_VALIDATE_ACTION) {
GstValidateAction *action = (GstValidateAction *) source;
+ gint indent = gst_validate_action_get_level (action) * 2;
if (_action_check_and_set_printed (action))
goto out;
- g_string_assign (string, "\nExecuting ");
-
+ if (!indent)
+ g_string_assign (string, "Executing ");
+ else
+ g_string_append_printf (string, "%*c↳ Executing ", indent - 2, ' ');
} else if (*(GType *) source == GST_TYPE_VALIDATE_ACTION_TYPE) {
gint i;
gint n_params;
diff --git a/validate/gst/validate/gst-validate-reporter.c b/validate/gst/validate/gst-validate-reporter.c
index 52c5e1d..f23626a 100644
--- a/validate/gst/validate/gst-validate-reporter.c
+++ b/validate/gst/validate/gst-validate-reporter.c
@@ -345,28 +345,37 @@ gst_validate_report_action (GstValidateReporter * reporter,
const gchar * format, ...)
{
va_list var_args;
- gint nrepeats;
- gchar *f, *repeat = NULL;
-
- if (action && gst_structure_get_int (action->structure, "repeat", &nrepeats))
- repeat =
- g_strdup_printf (" (repeat: %d/%d)", nrepeats - action->repeat + 1,
- nrepeats);
-
- f = action ? g_strdup_printf ("\n> %s:%d%s\n> %d | %s\n> %*c|\n",
- GST_VALIDATE_ACTION_FILENAME (action),
- GST_VALIDATE_ACTION_LINENO (action), repeat ? repeat : "",
- GST_VALIDATE_ACTION_LINENO (action), format,
- (gint) floor (log10 (abs ((GST_VALIDATE_ACTION_LINENO (action))))) + 1,
- ' ')
- : g_strdup (format);
+ GString *f;
+ if (!action) {
+ f = g_string_new (format);
+ goto done;
+ }
+
+ f = g_string_new (NULL);
+ g_string_append_printf (f, "\n> %s:%d", GST_VALIDATE_ACTION_FILENAME (action),
+ GST_VALIDATE_ACTION_LINENO (action));
+
+ if (GST_VALIDATE_ACTION_N_REPEATS (action))
+ g_string_append_printf (f, " (repeat: %d/%d)",
+ action->repeat, GST_VALIDATE_ACTION_N_REPEATS (action));
+
+ g_string_append_printf (f, "\n%s", GST_VALIDATE_ACTION_DEBUG (action));
+ if (gst_validate_action_get_level (action)) {
+ gchar *subaction_str = gst_structure_to_string (action->structure);
+
+ g_string_append_printf (f, "\n |-> %s", subaction_str);
+ g_free (subaction_str);
+ }
+
+ g_string_append_printf (f, "\n >\n > %s", format);
+
+done:
va_start (var_args, format);
- gst_validate_report_valist (reporter, issue_id, f, var_args);
+ gst_validate_report_valist (reporter, issue_id, f->str, var_args);
va_end (var_args);
- g_free (f);
- g_free (repeat);
+ g_string_free (f, TRUE);
}
void
diff --git a/validate/gst/validate/gst-validate-scenario.c b/validate/gst/validate/gst-validate-scenario.c
index 992e5c6..8218b1b 100644
--- a/validate/gst/validate/gst-validate-scenario.c
+++ b/validate/gst/validate/gst-validate-scenario.c
@@ -374,9 +374,11 @@ struct _GstValidateActionPrivate
GstValidateExecuteActionReturn state; /* Actually ActionState */
gboolean printed;
gboolean executing_last_subaction;
+ gboolean subaction_level;
gboolean optional;
GstClockTime execution_time;
+ GstClockTime execution_duration;
GstClockTime timeout;
GWeakRef scenario;
@@ -457,6 +459,31 @@ _action_copy (GstValidateAction * act)
return copy;
}
+const gchar *
+gst_validate_action_return_get_name (GstValidateActionReturn r)
+{
+ switch (r) {
+ case GST_VALIDATE_EXECUTE_ACTION_ERROR:
+ return "ERROR";
+ case GST_VALIDATE_EXECUTE_ACTION_OK:
+ return "OK";
+ case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
+ return "ASYNC";
+ case GST_VALIDATE_EXECUTE_ACTION_INTERLACED:
+ return "INTERLACED";
+ case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
+ return "ERROR(reported)";
+ case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
+ return "IN_PROGRESS";
+ case GST_VALIDATE_EXECUTE_ACTION_NONE:
+ return "NONE";
+ case GST_VALIDATE_EXECUTE_ACTION_SKIP:
+ return "SKIP";
+ }
+ g_assert_not_reached ();
+ return "???";
+}
+
static void
_action_free (GstValidateAction * action)
{
@@ -508,6 +535,7 @@ gst_validate_action_new (GstValidateScenario * scenario,
gst_validate_action_init (action);
action->playback_time = GST_CLOCK_TIME_NONE;
action->priv->timeout = GST_CLOCK_TIME_NONE;
+ action->priv->state = GST_VALIDATE_EXECUTE_ACTION_NONE;
action->type = action_type->name;
action->repeat = -1;
@@ -541,11 +569,10 @@ _action_check_and_set_printed (GstValidateAction * action)
return TRUE;
}
-gboolean
-gst_validate_action_is_subaction (GstValidateAction * action)
+gint
+gst_validate_action_get_level (GstValidateAction * action)
{
- return !gst_structure_is_equal (action->structure,
- action->priv->main_structure);
+ return action->priv->subaction_level;
}
/* GstValidateActionType implementation */
@@ -1186,7 +1213,7 @@ _set_timed_value (GQuark field_id, const GValue * gvalue,
const gchar *unused_fields[] =
{ "binding-type", "source-type", "interpolation-mode",
"timestamp", "__scenario__", "__action__", "__res__", "repeat",
- "sub-action", "playback-time", NULL
+ "playback-time", NULL
};
if (g_strv_contains (unused_fields, field))
@@ -1348,7 +1375,7 @@ _set_or_check_properties (GQuark field_id, const GValue * value,
GParamSpec *paramspec = NULL;
const gchar *field = g_quark_to_string (field_id);
const gchar *unused_fields[] = { "__scenario__", "__action__", "__res__",
- "sub-action", "playback-time", "repeat", NULL
+ "playback-time", "repeat", NULL
};
if (g_strv_contains (unused_fields, field))
@@ -2381,6 +2408,27 @@ gst_validate_parse_next_action_playback_time (GstValidateScenario * self)
return TRUE;
}
+static gboolean
+_foreach_find_iterator (GQuark field_id, GValue * value,
+ GstValidateAction * action)
+{
+ if (!g_strcmp0 (g_quark_to_string (field_id), "actions"))
+ return TRUE;
+
+ if (!GST_VALUE_HOLDS_INT_RANGE (value))
+ return TRUE;
+
+ if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
+ gst_validate_error_structure (action, "Found several ranges in structure, "
+ "it is not supported");
+ return FALSE;
+ }
+
+ GST_VALIDATE_ACTION_RANGE_NAME (action) = g_quark_to_string (field_id);
+ return TRUE;
+}
+
+
GstValidateExecuteActionReturn
gst_validate_execute_action (GstValidateActionType * action_type,
GstValidateAction * action)
@@ -2395,6 +2443,11 @@ gst_validate_execute_action (GstValidateActionType * action_type,
if (action_type->prepare) {
res = action_type->prepare (action);
+ if (res == GST_VALIDATE_EXECUTE_ACTION_SKIP) {
+ gst_validate_print_action (action, NULL);
+ return GST_VALIDATE_EXECUTE_ACTION_OK;
+ }
+
if (res != GST_VALIDATE_EXECUTE_ACTION_OK) {
GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
" could not be prepared", action->structure);
@@ -2412,18 +2465,6 @@ gst_validate_execute_action (GstValidateActionType * action_type,
res = action_type->execute (scenario, action);
gst_object_unref (scenario);
- if (!gst_structure_has_field (action->structure, "sub-action")) {
- gst_structure_free (action->structure);
- action->priv->printed = FALSE;
- action->structure = gst_structure_copy (action->priv->main_structure);
-
- if (!(action->name = gst_structure_get_string (action->structure, "name")))
- action->name = "";
-
- if (res == GST_VALIDATE_EXECUTE_ACTION_ASYNC)
- action->priv->executing_last_subaction = TRUE;
- }
-
return res;
}
@@ -2540,75 +2581,24 @@ _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
return res;
}
-static GstValidateExecuteActionReturn
-_execute_sub_action_action (GstValidateAction * action)
+static gboolean
+gst_validate_scenario_execute_next_or_restart_looping (GstValidateScenario *
+ scenario)
{
- const gchar *subaction_str;
- GstStructure *subaction_struct = NULL;
- GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
- GstValidateScenario *scenario = NULL;
-
- if (action->priv->executing_last_subaction) {
- action->priv->executing_last_subaction = FALSE;
-
- goto done;
- }
-
- scenario = gst_validate_action_get_scenario (action);
- g_assert (scenario);
- subaction_str = gst_structure_get_string (action->structure, "sub-action");
- if (subaction_str) {
- subaction_struct = gst_structure_from_string (subaction_str, NULL);
-
- if (subaction_struct == NULL) {
- GST_VALIDATE_REPORT_ACTION (scenario, action, SCENARIO_FILE_MALFORMED,
- "Sub action %s could not be parsed", subaction_str);
-
- res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
- goto done;
- }
+ /* Recurse to the next action if it is possible
+ * to execute right away */
+ if (!scenario->priv->execute_on_idle) {
+ GST_DEBUG_OBJECT (scenario, "linking next action execution");
+ return execute_next_action (scenario);
} else {
- gst_structure_get (action->structure, "sub-action", GST_TYPE_STRUCTURE,
- &subaction_struct, NULL);
- }
-
- if (subaction_struct) {
- if (action->structure) {
- GST_INFO_OBJECT (scenario, "Clearing old action structure");
- gst_structure_free (action->structure);
- }
-
- res = _fill_action (scenario, action, subaction_struct, FALSE);
- if (res == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
- GST_VALIDATE_REPORT_ACTION (scenario, action,
- SCENARIO_ACTION_EXECUTION_ERROR,
- "Sub action %" GST_PTR_FORMAT " could not be filled",
- subaction_struct);
-
- goto done;
- }
-
- if (!GST_CLOCK_TIME_IS_VALID (action->playback_time)) {
- GstValidateActionType *action_type = _find_action_type (action->type);
-
- action->priv->printed = FALSE;
- res = gst_validate_execute_action (action_type, action);
-
- goto done;
- }
-
+ _add_execute_actions_gsource (scenario);
+ GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
+ " next dispatch");
}
-
-done:
- if (scenario)
- gst_object_unref (scenario);
- if (subaction_struct)
- gst_structure_free (subaction_struct);
- return res;
+ return G_SOURCE_CONTINUE;
}
-
/* This is the main action execution function
* it checks whether it is time to run the next action
* and if it is the case executes it.
@@ -2620,7 +2610,6 @@ done:
static gboolean
execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
{
- GList *tmp;
gdouble rate = 1.0;
GstClockTime position = -1;
GstValidateAction *act = NULL;
@@ -2642,13 +2631,39 @@ execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
if (scenario->priv->actions)
act = scenario->priv->actions->data;
- if (act) {
+ if (!act)
+ return G_SOURCE_CONTINUE;
- if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS) {
+ switch (act->priv->state) {
+ case GST_VALIDATE_EXECUTE_ACTION_NONE:
+ break;
+ case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
+ GST_INFO_OBJECT (scenario, "Action %s:%d still running",
+ GST_VALIDATE_ACTION_FILENAME (act), GST_VALIDATE_ACTION_LINENO (act));
return G_SOURCE_CONTINUE;
- } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
- tmp = priv->actions;
- priv->actions = g_list_remove_link (priv->actions, tmp);
+ case GST_VALIDATE_EXECUTE_ACTION_ERROR:
+ GST_VALIDATE_REPORT_ACTION (scenario, act,
+ SCENARIO_ACTION_EXECUTION_ERROR, "Action %s failed", act->type);
+ case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
+ case GST_VALIDATE_EXECUTE_ACTION_OK:
+ {
+ gchar *repeat = NULL;
+
+ if (GST_VALIDATE_ACTION_N_REPEATS (act))
+ repeat =
+ g_strdup_printf ("[%d/%d]", act->repeat,
+ GST_VALIDATE_ACTION_N_REPEATS (act));
+
+ gst_validate_printf (NULL,
+ "%*c⇨ Action %s '%s' %s (duration: %" GST_TIME_FORMAT ")\n\n",
+ (act->priv->subaction_level * 2) - 1, ' ',
+ gst_structure_get_name (act->priv->main_structure),
+ gst_validate_action_return_get_name (act->priv->state),
+ repeat ? repeat : "", GST_TIME_ARGS (act->priv->execution_duration));
+ g_free (repeat);
+
+ priv->actions = g_list_remove (priv->actions, act);
+ gst_validate_action_unref (act);
if (!gst_validate_parse_next_action_playback_time (scenario)) {
gst_validate_error_structure (priv->actions ? priv->
@@ -2662,16 +2677,15 @@ execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
" executing next", act->structure);
- gst_validate_action_unref (act);
- g_list_free (tmp);
-
if (scenario->priv->actions) {
act = scenario->priv->actions->data;
} else {
_check_scenario_is_done (scenario);
act = NULL;
}
- } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
+ break;
+ }
+ case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
GstClockTime etime =
gst_util_get_timestamp () - act->priv->execution_time;
@@ -2691,7 +2705,8 @@ execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
act->structure);
return G_SOURCE_CONTINUE;
- }
+ default:
+ g_assert_not_reached ();
}
if (message) {
@@ -2721,68 +2736,25 @@ execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
gst_structure_remove_field (act->structure, "on-message");
act->priv->state = gst_validate_execute_action (type, act);
- if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
- gchar *str = gst_structure_to_string (act->structure);
-
- GST_VALIDATE_REPORT_ACTION (scenario, act,
- SCENARIO_ACTION_EXECUTION_ERROR, "Could not execute %s", str);
-
- g_free (str);
- }
+ switch (act->priv->state) {
+ case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
+ GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
+ " to be done.");
- if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
- act->priv->state = _execute_sub_action_action (act);
- }
-
- if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
- tmp = priv->actions;
- priv->actions = g_list_remove_link (priv->actions, tmp);
-
- if (!gst_validate_parse_next_action_playback_time (scenario)) {
- gst_validate_error_structure (priv->actions ? priv->actions->data : NULL,
- "Could not determine next action playback time!");
-
- return G_SOURCE_REMOVE;
- }
+ SCENARIO_LOCK (scenario);
+ priv->execute_actions_source_id = 0;
+ SCENARIO_UNLOCK (scenario);
- if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_INTERLACED)
- gst_validate_action_unref (act);
- else {
+ return G_SOURCE_CONTINUE;
+ case GST_VALIDATE_EXECUTE_ACTION_INTERLACED:
SCENARIO_LOCK (scenario);
priv->interlaced_actions = g_list_append (priv->interlaced_actions, act);
SCENARIO_UNLOCK (scenario);
- }
-
- if (priv->actions == NULL)
- _check_scenario_is_done (scenario);
-
- g_list_free (tmp);
-
- /* Recurse to the next action if it is possible
- * to execute right away */
- if (!scenario->priv->execute_on_idle) {
- GST_DEBUG_OBJECT (scenario, "linking next action execution");
-
- return execute_next_action (scenario);
- } else {
- _add_execute_actions_gsource (scenario);
- GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
- " next dispatch");
-
+ return gst_validate_scenario_execute_next_or_restart_looping (scenario);
+ default:
+ gst_validate_action_set_done (act);
return G_SOURCE_CONTINUE;
- }
- } else {
- GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
- " to be done.");
-
- SCENARIO_LOCK (scenario);
- priv->execute_actions_source_id = 0;
- SCENARIO_UNLOCK (scenario);
-
- return G_SOURCE_CONTINUE;
}
-
- return G_SOURCE_CONTINUE;
}
static gboolean
@@ -3162,6 +3134,24 @@ done:
}
static GstValidateExecuteActionReturn
+_execute_check_subaction_level (GstValidateScenario * scenario,
+ GstValidateAction * action)
+{
+ GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
+ gint n;
+
+ REPORT_UNLESS (gst_structure_get_int (action->structure, "level", &n),
+ done, "No `n`!");
+ REPORT_UNLESS (gst_validate_action_get_level (action) == n, done,
+ "Expected subaction level %d, got %d", n,
+ gst_validate_action_get_level (action));
+
+
+done:
+ return res;
+}
+
+static GstValidateExecuteActionReturn
_execute_check_position (GstValidateScenario * scenario,
GstValidateAction * action)
{
@@ -3679,7 +3669,9 @@ gst_validate_action_default_prepare_func (GstValidateAction * action)
if (GST_VALIDATE_ACTION_N_REPEATS (action))
gst_structure_set (scenario->priv->vars,
- "repeat", G_TYPE_INT, action->repeat, NULL);
+ GST_VALIDATE_ACTION_RANGE_NAME (action) ?
+ GST_VALIDATE_ACTION_RANGE_NAME (action) : "repeat", G_TYPE_INT,
+ action->repeat, NULL);
gst_validate_structure_resolve_variables (action, action->structure,
scenario->priv->vars);
for (i = 0; type->parameters[i].name; i++) {
@@ -3710,6 +3702,136 @@ gst_validate_set_property_prepare_func (GstValidateAction * action)
return gst_validate_action_default_prepare_func (action);
}
+static GList *
+add_gvalue_to_list_as_struct (gpointer source, GList * list, const GValue * v)
+{
+ if (G_VALUE_HOLDS_STRING (v)) {
+ GstStructure *structure =
+ gst_structure_new_from_string (g_value_get_string (v));
+
+ if (!structure)
+ gst_validate_error_structure (source, "Invalid structure: %s",
+ g_value_get_string (v));
+
+ return g_list_append (list, structure);
+ }
+
+ if (GST_VALUE_HOLDS_STRUCTURE (v))
+ return g_list_append (list,
+ gst_structure_copy (gst_value_get_structure (v)));
+
+
+ gst_validate_error_structure (source, "Expected a string or a structure,"
+ " got %s instead", gst_value_serialize (v));
+ return NULL;
+}
+
+static GList *
+gst_validate_utils_get_structures (gpointer source,
+ GstStructure * str, const gchar * fieldname)
+{
+ guint i, size;
+ GList *res = NULL;
+ const GValue *value = gst_structure_get_value (str, fieldname);
+
+ if (!value)
+ return NULL;
+
+ if (G_VALUE_HOLDS_STRING (value) || GST_VALUE_HOLDS_STRUCTURE (value))
+ return add_gvalue_to_list_as_struct (source, res, value);
+
+ if (!GST_VALUE_HOLDS_LIST (value)) {
+ g_error ("%s must have type list of structure/string (or a string), "
+ "e.g. %s={ [struct1, a=val1], [struct2, a=val2] }, got: \"%s\" in %s",
+ fieldname, fieldname, gst_value_serialize (value),
+ gst_structure_to_string (str));
+ return NULL;
+ }
+
+ size = gst_value_list_get_size (value);
+ for (i = 0; i < size; i++)
+ res =
+ add_gvalue_to_list_as_struct (source, res,
+ gst_value_list_get_value (value, i));
+
+ return res;
+}
+
+static GstValidateExecuteActionReturn
+gst_validate_foreach_prepare (GstValidateAction * action)
+{
+ gint it, i;
+ gint min = 0, max = 1, step = 1;
+ GstValidateScenario *scenario;
+ GList *actions, *tmp;
+
+ scenario = gst_validate_action_get_scenario (action);
+ g_assert (scenario);
+ _update_well_known_vars (scenario);
+ gst_validate_action_setup_repeat (scenario, action);
+
+ GST_VALIDATE_ACTION_RANGE_NAME (action) = NULL;
+ gst_structure_foreach (action->structure,
+ (GstStructureForeachFunc) _foreach_find_iterator, action);
+
+ /* Allow using the repeat field here too */
+ if (!GST_VALIDATE_ACTION_RANGE_NAME (action)
+ && !GST_VALIDATE_ACTION_N_REPEATS (action))
+ gst_validate_error_structure (action, "Missing range specifier field.");
+
+ if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
+ const GValue *range = gst_structure_get_value (action->structure,
+ GST_VALIDATE_ACTION_RANGE_NAME (action));
+ min = gst_value_get_int_range_min (range);
+ max = gst_value_get_int_range_max (range);
+ step = gst_value_get_int_range_step (range);
+
+ if (min % step != 0)
+ gst_validate_error_structure (action,
+ "Range min[%d] must be a multiple of step[%d].", min, step);
+
+ if (max % step != 0)
+ gst_validate_error_structure (action,
+ "Range max[%d] must be a multiple of step[%d].", max, step);
+ } else {
+ min = action->repeat;
+ max = action->repeat + 1;
+ }
+
+ actions = gst_validate_utils_get_structures (action, action->structure,
+ "actions");
+ i = g_list_index (scenario->priv->actions, action);
+ for (it = min; it < max; it = it + step) {
+ for (tmp = actions; tmp; tmp = tmp->next) {
+ GstValidateAction *subaction;
+ GstStructure *nstruct = gst_structure_copy (tmp->data);
+
+ subaction = gst_validate_action_new (scenario,
+ _find_action_type (gst_structure_get_name (nstruct)), nstruct, FALSE);
+ GST_VALIDATE_ACTION_RANGE_NAME (subaction) =
+ GST_VALIDATE_ACTION_RANGE_NAME (action);
+ GST_VALIDATE_ACTION_FILENAME (subaction) =
+ g_strdup (GST_VALIDATE_ACTION_FILENAME (action));
+ GST_VALIDATE_ACTION_DEBUG (subaction) =
+ g_strdup (GST_VALIDATE_ACTION_DEBUG (action));
+ GST_VALIDATE_ACTION_LINENO (subaction) =
+ GST_VALIDATE_ACTION_LINENO (action);
+ subaction->repeat = it;
+ subaction->priv->subaction_level = action->priv->subaction_level + 1;
+ GST_VALIDATE_ACTION_N_REPEATS (subaction) = max;
+ scenario->priv->actions =
+ g_list_insert (scenario->priv->actions, subaction, i++);
+ }
+ }
+ g_list_free_full (actions, (GDestroyNotify) gst_structure_free);
+
+ scenario->priv->actions = g_list_remove (scenario->priv->actions, action);
+ gst_structure_remove_field (action->structure, "actions");
+
+ gst_object_unref (scenario);
+ return GST_VALIDATE_EXECUTE_ACTION_SKIP;
+}
+
static void
_check_waiting_for_message (GstValidateScenario * scenario,
GstMessage * message)
@@ -4138,7 +4260,7 @@ gst_validate_scenario_load_structures (GstValidateScenario * scenario,
}
gst_validate_error_structure (structure,
- "We do not handle action types %s", type);
+ "Unknown action type: '%s'", type);
goto failed;
}
@@ -5635,13 +5757,13 @@ static gboolean
_action_set_done (GstValidateAction * action)
{
JsonBuilder *jbuild;
- GstClockTime execution_duration;
GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
if (scenario == NULL || !action->priv->pending_set_done)
return G_SOURCE_REMOVE;
- execution_duration = gst_util_get_timestamp () - action->priv->execution_time;
+ action->priv->execution_duration =
+ gst_util_get_timestamp () - action->priv->execution_time;
jbuild = json_builder_new ();
json_builder_begin_object (jbuild);
@@ -5651,25 +5773,27 @@ _action_set_done (GstValidateAction * action)
json_builder_add_string_value (jbuild, action->type);
json_builder_set_member_name (jbuild, "execution-duration");
json_builder_add_double_value (jbuild,
- ((gdouble) execution_duration / GST_SECOND));
+ ((gdouble) action->priv->execution_duration / GST_SECOND));
json_builder_end_object (jbuild);
gst_validate_send (json_builder_get_root (jbuild));
g_object_unref (jbuild);
- gst_validate_printf (NULL, " -> Action %s done (duration: %" GST_TIME_FORMAT
- ")\n", action->type, GST_TIME_ARGS (execution_duration));
- action->priv->execution_time = GST_CLOCK_TIME_NONE;
- action->priv->state = _execute_sub_action_action (action);
-
- if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
-
- GST_DEBUG_OBJECT (scenario, "Sub action executed ASYNC");
- execute_next_action (scenario);
+ action->priv->pending_set_done = FALSE;
+ switch (action->priv->state) {
+ case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
+ case GST_VALIDATE_EXECUTE_ACTION_INTERLACED:
+ case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
+ case GST_VALIDATE_EXECUTE_ACTION_NONE:
+ action->priv->state = GST_VALIDATE_EXECUTE_ACTION_OK;
+ break;
+ default:
+ break;
}
+ gst_structure_free (action->structure);
+ action->structure = gst_structure_copy (action->priv->main_structure);
+ gst_validate_scenario_execute_next_or_restart_looping (scenario);
gst_object_unref (scenario);
-
- action->priv->pending_set_done = FALSE;
return G_SOURCE_REMOVE;
}
@@ -6844,9 +6968,25 @@ register_action_types (void)
}),
"Check current pipeline position.\n", GST_VALIDATE_ACTION_TYPE_NONE);
+ REGISTER_ACTION_TYPE("foreach", NULL,
+ ((GstValidateActionParameter[]) {
+ { .name = "actions",
+ .description = "The array of actions to repeat",
+ .mandatory = TRUE,
+ .types = "strv",
+ NULL },
+ { NULL } }),
+ "Run actions defined in the `actions` array the number of times specified\n"
+ " with a GstIntRange `i=[start, end, step]` parameter passed in, one and only\n"
+ " range is required as parameter.",
+ GST_VALIDATE_ACTION_TYPE_NONE);
+ type->prepare = gst_validate_foreach_prepare;
+
/* Internal actions types to test the validate scenario implementation */
REGISTER_ACTION_TYPE("priv_check-action-type-calls",
_execute_check_action_type_calls, NULL, NULL, 0);
+ REGISTER_ACTION_TYPE("priv_check-subaction-level",
+ _execute_check_subaction_level, NULL, NULL, 0);
/* *INDENT-ON* */
}
diff --git a/validate/gst/validate/gst-validate-scenario.h b/validate/gst/validate/gst-validate-scenario.h
index 628c0be..aeec38d 100644
--- a/validate/gst/validate/gst-validate-scenario.h
+++ b/validate/gst/validate/gst-validate-scenario.h
@@ -57,8 +57,11 @@ typedef enum
GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED,
GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS,
GST_VALIDATE_EXECUTE_ACTION_NONE,
+ GST_VALIDATE_EXECUTE_ACTION_SKIP,
} GstValidateActionReturn;
+const gchar *gst_validate_action_return_get_name (GstValidateActionReturn r);
+
/* TODO 2.0 -- Make it an actual enum type */
#define GstValidateExecuteActionReturn gint
@@ -93,6 +96,7 @@ typedef struct _GstValidateActionPrivate GstValidateActionPrivate;
#define GST_VALIDATE_ACTION_FILENAME(action) (((GstValidateAction*) action)->ABI.abi.filename)
#define GST_VALIDATE_ACTION_DEBUG(action) (((GstValidateAction*) action)->ABI.abi.debug)
#define GST_VALIDATE_ACTION_N_REPEATS(action) (((GstValidateAction*) action)->ABI.abi.n_repeats)
+#define GST_VALIDATE_ACTION_RANGE_NAME(action) (((GstValidateAction*) action)->ABI.abi.rangename)
/**
* GstValidateAction:
@@ -133,6 +137,7 @@ struct _GstValidateAction
gchar *filename;
gchar *debug;
gint n_repeats;
+ const gchar *rangename;
} abi;
} ABI;
};
diff --git a/validate/tests/launcher_tests/foreach.validatetest b/validate/tests/launcher_tests/foreach.validatetest
new file mode 100644
index 0000000..1ecc9f3
--- /dev/null
+++ b/validate/tests/launcher_tests/foreach.validatetest
@@ -0,0 +1,47 @@
+meta,
+ handles-states=true,
+ args = {
+ "videotestsrc pattern=ball animation-mode=frames num-buffers=30 ! video/x-raw,framerate=10/1 ! $(videosink) name=sink sync=true",
+ },
+ expected-issues = {
+ "expected-issue,
+ level=critical,
+ issue-id=scenario::execution-error,
+ details=\"Pipeline position doesn.t match expectations got 0:00:00.100000000 instead of.*\"",
+ "expected-issue,
+ level=critical,
+ issue-id=scenario::execution-error,
+ details=\"Pipeline position doesn.t match expectations got 0:00:00.200000000 instead of.*\"",
+ }
+
+pause;
+
+foreach, n=[0, 2],
+ actions = {
+ "seek, start=\"$(position)+0.1\", flags=\"accurate+flush\"",
+ "check-position, expected-position=\"expr($(n)*0.01)\"", # expected to fail
+ }
+
+priv_check-action-type-calls, type=seek, n=2
+priv_check-action-type-calls, type=check-position, n=2
+
+foreach, n=[0, 6],
+ actions = {
+ "seek, start=\"$(position)+0.1\", flags=\"accurate+flush\"",
+ "check-position, expected-position=\"expr((3 + $(n)) * 0.1)\"",
+ }
+
+priv_check-action-type-calls, type=seek, n=8
+priv_check-action-type-calls, type=check-position, n=8
+check-position, expected-position=0.8
+
+foreach, n=[9, 11],
+ actions = {
+ "seek, start=\"$(position)+0.1\", flags=\"accurate+flush\"",
+ "check-position, expected-position=\"expr($(n)*0.1)\"",
+ }
+priv_check-action-type-calls, type=seek, n=10
+# We called it once manually
+priv_check-action-type-calls, type=check-position, n=11
+check-position, expected-position=1.0
+stop
diff --git a/validate/tests/launcher_tests/foreach/flow-expectations/log-sink-sink-expected b/validate/tests/launcher_tests/foreach/flow-expectations/log-sink-sink-expected
new file mode 100644
index 0000000..f525c3c
--- /dev/null
+++ b/validate/tests/launcher_tests/foreach/flow-expectations/log-sink-sink-expected
@@ -0,0 +1,24 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event caps: video/x-raw, format=(string)AYUV64, width=(int)320, height=(int)240, framerate=(fraction)10/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive;
+event caps: video/x-raw, format=(string)AYUV64, width=(int)320, height=(int)240, framerate=(fraction)10/1, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: checksum=5d4a9a9aa2038170a66bb2c675a16672fe70efbe, pts=0:00:00.000000000, dur=0:00:00.100000000, flags=discont, meta=GstVideoMeta
+buffer: checksum=5d4a9a9aa2038170a66bb2c675a16672fe70efbe, pts=0:00:00.000000000, dur=0:00:00.100000000, flags=discont, meta=GstVideoMeta
+event flush-start: (no structure)
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.100000000, offset=0:00:00.000000000, stop=none, flags=0x01, time=0:00:00.100000000, base=0:00:00.000000000, position=0:00:00.100000000
+event segment: format=TIME, start=0:00:00.100000000, offset=0:00:00.000000000, stop=none, flags=0x01, time=0:00:00.100000000, base=0:00:00.000000000, position=0:00:00.100000000
+buffer: checksum=ace920a5c387c5d216c7bf4fdc83df6ac9d2656e, pts=0:00:00.100000000, dur=0:00:00.100000000, flags=discont, meta=GstVideoMeta
+buffer: checksum=ace920a5c387c5d216c7bf4fdc83df6ac9d2656e, pts=0:00:00.100000000, dur=0:00:00.100000000, flags=discont, meta=GstVideoMeta
+event flush-start: (no structure)
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.200000000, offset=0:00:00.000000000, stop=none, flags=0x01, time=0:00:00.200000000, base=0:00:00.000000000, position=0:00:00.200000000
+event segment: format=TIME, start=0:00:00.200000000, offset=0:00:00.000000000, stop=none, flags=0x01, time=0:00:00.200000000, base=0:00:00.000000000, position=0:00:00.200000000
+buffer: checksum=b4a5b43f70ad1a1adb1f5e414b39d6bfb5718373, pts=0:00:00.200000000, dur=0:00:00.100000000, flags=discont, meta=GstVideoMeta
+buffer: checksum=b4a5b43f70ad1a1adb1f5e414b39d6bfb5718373, pts=0:00:00.200000000, dur=0:00:00.100000000, flags=discont, meta=GstVideoMeta
diff --git a/validate/tests/launcher_tests/foreach_deep.validatetest b/validate/tests/launcher_tests/foreach_deep.validatetest
new file mode 100644
index 0000000..3fa9f52
--- /dev/null
+++ b/validate/tests/launcher_tests/foreach_deep.validatetest
@@ -0,0 +1,51 @@
+meta,
+ handles-states=true,
+ args = {
+ "videotestsrc pattern=ball animation-mode=frames num-buffers=30 ! video/x-raw,framerate=10/1 ! $(videosink) name=sink sync=true",
+ },
+ expected-issues = {
+ "expected-issue, level=critical, issue-id=scenario::execution-error,
+ details=\"Pipeline position doesn.t match expectations got 0:00:00.100000000 instead of.*\"",
+ "expected-issue, level=critical, issue-id=scenario::execution-error,
+ details=\"Pipeline position doesn.t match expectations got 0:00:00.200000000 instead of.*\"",
+ "expected-issue, level=critical, issue-id=scenario::execution-error,
+ details=\"Expected subaction level 4, got 3\"",
+ "expected-issue, level=critical, issue-id=scenario::execution-error,
+ details=\"Expected subaction level 4, got 3\"",
+ "expected-issue, level=critical, issue-id=scenario::execution-error,
+ details=\"Expected subaction level 5, got 4\"",
+ "expected-issue, level=critical, issue-id=scenario::execution-error,
+ details=\"Expected subaction level 5, got 4\"",
+ }
+
+pause;
+
+
+foreach, n=[0, 2],
+ actions = {
+ "seek, start=\"$(position)+0.1\", flags=\"accurate+flush\"",
+ "check-position, expected-position=\"expr($(n)*0.01)\"", # Expected failling subaction!
+ }
+
+priv_check-action-type-calls, type=seek, n=2
+priv_check-action-type-calls, type=check-position, n=2
+
+foreach, n=[0, 2],
+ actions = {
+ "seek, start=\"$(position)+0.1\", flags=\"accurate+flush\"",
+ "priv_check-subaction-level, level=1",
+ "foreach, n=[0, 1],
+ actions={
+ \"priv_check-subaction-level, level=2\",
+ \"foreach, j=[0, 1], actions={
+ \\\"priv_check-subaction-level, level=4\\\", # Failling... twice
+ \\\"priv_check-subaction-level, level=3\\\",
+ \\\"foreach, j=[0, 1], actions={
+ \\\\\\\"priv_check-subaction-level, level=4\\\\\\\",
+ \\\\\\\"priv_check-subaction-level, level=5\\\\\\\", # Failling... twice
+ }\\\",
+ }\",
+ }",
+ }
+priv_check-action-type-calls, type=seek, n=4
+stop
diff --git a/validate/tests/launcher_tests/foreach_repeat.validatetest b/validate/tests/launcher_tests/foreach_repeat.validatetest
new file mode 100644
index 0000000..94ccc92
--- /dev/null
+++ b/validate/tests/launcher_tests/foreach_repeat.validatetest
@@ -0,0 +1,31 @@
+meta,
+ handles-states=true,
+ args = {
+ "videotestsrc name=src pattern=ball animation-mode=frames num-buffers=30 ! video/x-raw,framerate=10/1 ! $(videosink) name=sink sync=true",
+ }
+
+pause;
+
+foreach, repeat="max(1, 2)",
+ actions = {
+ "seek, start=\"$(position)+0.1\", flags=\"accurate+flush\"",
+ "check-position, expected-position=\"expr((1+$(repeat))*0.1)\"",
+ }
+
+priv_check-action-type-calls, type=seek, n=2
+priv_check-action-type-calls, type=check-position, n=2
+
+foreach,
+ repeat=2,
+ pattern=[0, 10, 5],
+ actions = {
+ "set-properties, src::horizontal-speed=\"$(pattern)\"",
+ "check-properties, src::horizontal-speed=\"$(pattern)\"",
+ }
+
+check-properties, src::horizontal-speed=5
+priv_check-action-type-calls, type=set-properties, n=4
+priv_check-action-type-calls, type=check-properties, n=5
+priv_check-action-type-calls, type=seek, n=2
+priv_check-action-type-calls, type=check-position, n=2
+stop