From b669bb0327527c23dc481dec0c803ebc4ab53239 Mon Sep 17 00:00:00 2001 From: Thibault Saunier <tsaunier@igalia.com> Date: Thu, 7 May 2020 00:23:07 -0400 Subject: [PATCH] validate: Add support for known-issues in the .validatetest And add some tests about remaining actions failures Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-devtools/-/merge_requests/189> --- docs/gst-validate-test-file.md | 38 +++++++++- validate/gst/validate/gst-validate-internal.h | 1 + validate/gst/validate/gst-validate-report.c | 2 + validate/gst/validate/gst-validate-report.h | 1 + validate/gst/validate/gst-validate-runner.c | 74 +++++++++++++++++++ validate/gst/validate/validate.c | 44 ++++++++--- validate/launcher/baseclasses.py | 6 ++ ...k_set_prop_never_called_error.validatetest | 9 +++ ...egotiated.accept_caps_failure.validatetest | 7 ++ .../tests/launcher_tests/test_validate.py | 9 +++ validate/tools/gst-validate.c | 3 +- 11 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 validate/tests/launcher_tests/check_set_prop_never_called_error.validatetest create mode 100644 validate/tests/launcher_tests/not_negotiated.accept_caps_failure.validatetest diff --git a/docs/gst-validate-test-file.md b/docs/gst-validate-test-file.md index 6328aa0ab7..045b78233a 100644 --- a/docs/gst-validate-test-file.md +++ b/docs/gst-validate-test-file.md @@ -44,8 +44,8 @@ args = { ## configs -The `config` field is an array of string containing the same content as -usual [config](gst-validate-config.md) files contain. +The `configs` field is an array of structures containing the same content as +usual [configs](gst-validate-config.md) files. For example: @@ -57,6 +57,40 @@ configs = { } ``` +Note: Since this is GstStructure synthax, we need to have the structures in the +array as strings/within quotes. + +## expected-issues + +The `expected-issues` field is an array of `expected-issue` structures containing +information about issues to expect (which can be known bugs or not). + +Use `gst-validate-1.0 --print-issue-types` to print information about all issue types. + +For example: + +``` yaml +expected-issues = { + "expected-issue, issue-id=scenario::not-ended", +} +``` + +Note: Since this is GstStructure synthax, we need to have the structures in the +array as strings/within quotes. + +### Fields: + +* `issue-id`: (string): Issue ID - Mandatory if `summary` is not provided. +* `summary`: (string): Summary - Mandatory if `issue-id` is not provided. +* `details`: Regex string to match the issue details `detected-on`: (string): + The name of the element the issue happened on `level`: (string): + Issue level +* `sometimes`: (boolean): Default: `false` - Wheteher the issue happens only + sometimes if `false` and the issue doesn't happen, an error will + be issued. +* `issue-url`: (string): The url of the issue in the bug tracker if the issue is + a bug. + # Variables The same way diff --git a/validate/gst/validate/gst-validate-internal.h b/validate/gst/validate/gst-validate-internal.h index 07912a2164..8fc4880494 100644 --- a/validate/gst/validate/gst-validate-internal.h +++ b/validate/gst/validate/gst-validate-internal.h @@ -63,4 +63,5 @@ G_GNUC_INTERNAL gboolean gst_validate_get_test_file_scenario (GList** structs, c G_GNUC_INTERNAL GstValidateScenario* gst_validate_scenario_from_structs (GstValidateRunner* runner, GstElement* pipeline, GList* structures, gchar* origin_file); G_GNUC_INTERNAL GList* gst_validate_get_config(const gchar *structname); +G_GNUC_INTERNAL GList * gst_validate_get_test_file_expected_issues (void); #endif diff --git a/validate/gst/validate/gst-validate-report.c b/validate/gst/validate/gst-validate-report.c index 2372c7e0bd..b8a4be2a16 100644 --- a/validate/gst/validate/gst-validate-report.c +++ b/validate/gst/validate/gst-validate-report.c @@ -677,6 +677,8 @@ gst_validate_report_level_get_name (GstValidateReportLevel level) return "issue"; case GST_VALIDATE_REPORT_LEVEL_IGNORE: return "ignore"; + case GST_VALIDATE_REPORT_LEVEL_EXPECTED: + return "expected"; default: return "unknown"; } diff --git a/validate/gst/validate/gst-validate-report.h b/validate/gst/validate/gst-validate-report.h index 1643b1b911..f19e8f2ccc 100644 --- a/validate/gst/validate/gst-validate-report.h +++ b/validate/gst/validate/gst-validate-report.h @@ -67,6 +67,7 @@ typedef enum { GST_VALIDATE_REPORT_LEVEL_ISSUE, GST_VALIDATE_REPORT_LEVEL_IGNORE, GST_VALIDATE_REPORT_LEVEL_UNKNOWN, + GST_VALIDATE_REPORT_LEVEL_EXPECTED, GST_VALIDATE_REPORT_LEVEL_NUM_ENTRIES, } GstValidateReportLevel; diff --git a/validate/gst/validate/gst-validate-runner.c b/validate/gst/validate/gst-validate-runner.c index 06d0a0f31d..53ab4c566b 100644 --- a/validate/gst/validate/gst-validate-runner.c +++ b/validate/gst/validate/gst-validate-runner.c @@ -93,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 */ @@ -448,6 +450,8 @@ 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)); @@ -629,6 +633,50 @@ gst_validate_runner_maybe_dot_pipeline (GstValidateRunner * runner, } } +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); + return TRUE; + } + } + } + } + } +#undef GET_STR + } + + return FALSE; +} + void gst_validate_runner_add_report (GstValidateRunner * runner, GstValidateReport * report) @@ -640,6 +688,9 @@ gst_validate_runner_add_report (GstValidateRunner * runner, if (report->level == GST_VALIDATE_REPORT_LEVEL_IGNORE) return; + if (check_report_expected (runner, 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); @@ -841,7 +892,10 @@ int gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result) { gint ret = 0; + GList *tmp; + 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); @@ -854,6 +908,26 @@ gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result) } } + 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 *tmp = gst_structure_copy (known_issue); + gst_structure_remove_fields (tmp, "__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 "'", tmp); + gst_structure_free (tmp); + } + } + + g_list_free_full (runner->priv->expected_issues, + (GDestroyNotify) gst_structure_free); + runner->priv->expected_issues = NULL; + return ret; } diff --git a/validate/gst/validate/validate.c b/validate/gst/validate/validate.c index fa4e437fd7..434275dcef 100644 --- a/validate/gst/validate/validate.c +++ b/validate/gst/validate/validate.c @@ -236,10 +236,10 @@ create_config (const gchar * config, const gchar * suffix) } static GList * -gst_validate_get_testfile_configs (const gchar * suffix) +get_structures_from_array_in_meta (const gchar * fieldname) { GList *res = NULL; - gchar **config_strs = NULL, *filename = NULL, *debug = NULL; + gchar **strs = NULL, *filename = NULL, *debug = NULL; gint current_lineno = -1; GstStructure *meta = get_test_file_meta (); @@ -250,18 +250,17 @@ gst_validate_get_testfile_configs (const gchar * suffix) "__lineno__", G_TYPE_INT, ¤t_lineno, "__debug__", G_TYPE_STRING, &debug, "__filename__", G_TYPE_STRING, &filename, NULL); - config_strs = gst_validate_utils_get_strv (meta, "configs"); + strs = gst_validate_utils_get_strv (meta, fieldname); - if (config_strs) { + if (strs) { gint i; - for (i = 0; config_strs[i]; i++) { - GstStructure *tmpstruct = - gst_structure_from_string (config_strs[i], NULL); + for (i = 0; strs[i]; i++) { + GstStructure *tmpstruct = gst_structure_from_string (strs[i], NULL); if (tmpstruct == NULL) { gst_validate_abort ("%s:%d: Invalid structure\n %4d | %s\n%s", - filename, current_lineno, current_lineno, config_strs[i], debug); + filename, current_lineno, current_lineno, strs[i], debug); } gst_structure_set (tmpstruct, @@ -274,7 +273,15 @@ gst_validate_get_testfile_configs (const gchar * suffix) g_free (filename); g_free (debug); - g_strfreev (config_strs); + g_strfreev (strs); + + return res; +} + +static GList * +gst_validate_get_testfile_configs (const gchar * suffix) +{ + GList *res = get_structures_from_array_in_meta ("configs"); return get_config_from_structures (res, NULL, suffix); } @@ -481,6 +488,24 @@ gst_validate_is_initialized (void) return validate_initialized; } +GList * +gst_validate_get_test_file_expected_issues (void) +{ + GList *res = get_structures_from_array_in_meta ("expected-issues"), *tmp; + + for (tmp = res; tmp; tmp = tmp->next) { + GstStructure *known_issue = tmp->data; + const gchar *summary = gst_structure_get_string (known_issue, "summary"); + const gchar *id = gst_structure_get_string (known_issue, "issue-id"); + + if (!id && !summary) + gst_validate_error_structure (known_issue, + "Missing 'summary' or 'issue-id' fields."); + } + + return res; +} + gboolean gst_validate_get_test_file_scenario (GList ** structs, const gchar ** scenario_name, gchar ** original_name) @@ -553,7 +578,6 @@ gst_validate_setup_test_file (const gchar * testfile, gboolean use_fakesinks) gst_validate_scenario_check_and_set_needs_clock_sync (testfile_structs, &res); gst_validate_set_test_file_globals (res, testfile, use_fakesinks); - gst_validate_structure_resolve_variables (res, NULL); tool = gst_structure_get_string (res, "tool"); diff --git a/validate/launcher/baseclasses.py b/validate/launcher/baseclasses.py index 3bf73cc0e0..bea38bdd21 100644 --- a/validate/launcher/baseclasses.py +++ b/validate/launcher/baseclasses.py @@ -1091,6 +1091,12 @@ class GstValidateTest(Test): Colors.ENDC) result = Result.KNOWN_ERROR + if result == Result.PASSED: + for report in self.reports: + if report["level"] == "expected": + result = Result.KNOWN_ERROR + break + self.set_result(result, msg.strip()) def _generate_expected_issues(self): diff --git a/validate/tests/launcher_tests/check_set_prop_never_called_error.validatetest b/validate/tests/launcher_tests/check_set_prop_never_called_error.validatetest new file mode 100644 index 0000000000..8bb73e14f1 --- /dev/null +++ b/validate/tests/launcher_tests/check_set_prop_never_called_error.validatetest @@ -0,0 +1,9 @@ +meta, + args = { + "fakesrc num-buffers=1 ! fakesink", + }, + expected-issues = { + "expected-issue, issue-id=scenario::not-ended", + } + +set-property, target-element-factory-name=capsfilter, property-name=caps, property-value="video/x-raw,framerate=30/1,format=I420" \ No newline at end of file diff --git a/validate/tests/launcher_tests/not_negotiated.accept_caps_failure.validatetest b/validate/tests/launcher_tests/not_negotiated.accept_caps_failure.validatetest new file mode 100644 index 0000000000..aa8777cabf --- /dev/null +++ b/validate/tests/launcher_tests/not_negotiated.accept_caps_failure.validatetest @@ -0,0 +1,7 @@ +meta, + args = { + "audiotestsrc ! capsfilter caps=\"audio/x-raw,channels=2,channel-mask=(bitmask)0x67\" ! audioconvert ! capsfilter caps=\"audio/x-raw,channels=6,channel-mask=(bitmask)0x32\" name=capsfilter ! fakesink", + }, + expected-issues = { + "expected-issue, level=critical, summary=\"a NOT NEGOTIATED message has been posted on the bus.\", details=\".*Caps negotiation failed at pad.*capsfilter:sink.*as it refused caps:.*\"", + } diff --git a/validate/tests/launcher_tests/test_validate.py b/validate/tests/launcher_tests/test_validate.py index 2591918baf..bad3275344 100644 --- a/validate/tests/launcher_tests/test_validate.py +++ b/validate/tests/launcher_tests/test_validate.py @@ -20,6 +20,8 @@ """ The GstValidate default testsuite """ +import os +from launcher.apps.gstvalidate import GstValidateSimpleTestsGenerator TEST_MANAGER = "validate" @@ -43,6 +45,8 @@ def get_pipelines(test_manager): def setup_tests(test_manager, options): + testsuite_dir = os.path.realpath(os.path.join(os.path.dirname(__file__))) + print("Setting up tests to test GstValidate") # No restriction about scenarios that are potentially used valid_scenarios = ["play_15s"] @@ -52,4 +56,9 @@ def setup_tests(test_manager, options): pipelines_descriptions=get_pipelines(test_manager), valid_scenarios=valid_scenarios)) + test_manager.add_generators( + GstValidateSimpleTestsGenerator("simple", test_manager, + os.path.join(testsuite_dir)) + ) + return True diff --git a/validate/tools/gst-validate.c b/validate/tools/gst-validate.c index 968af6dd5e..e3b4373dcd 100644 --- a/validate/tools/gst-validate.c +++ b/validate/tools/gst-validate.c @@ -408,8 +408,7 @@ main (int argc, gchar ** argv) "as gstreamer debugging"); if (argc == 1) { - gst_validate_printf (NULL, "%s", g_option_context_get_help (ctx, FALSE, - NULL)); + g_print ("%s", g_option_context_get_help (ctx, FALSE, NULL)); exit (1); }