3 * Copyright (C) 2013 Collabora Ltd.
4 * Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
6 * validate.c - Validate generic functions
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
25 * @title: Initialization
26 * @short_description: Initialize GstValidate
31 #endif /* HAVE_CONFIG_H */
33 #include <locale.h> /* for LC_NUMERIC */
37 #include <glib/gstdio.h>
38 #include <sys/types.h>
44 #include "gst-validate-utils.h"
45 #include "gst-validate-internal.h"
48 #define WIN32_LEAN_AND_MEAN /* prevents from including too many things */
49 #include <windows.h> /* GetStdHandle, windows console */
50 HMODULE _priv_gstvalidate_dll_handle = NULL;
51 #endif /* G_OS_WIN32 */
53 GST_DEBUG_CATEGORY (gstvalidate_debug);
55 static GMutex _gst_validate_registry_mutex;
56 static GstRegistry *_gst_validate_registry_default = NULL;
58 G_LOCK_DEFINE_STATIC (all_configs_lock);
59 static GList *all_configs = NULL;
60 static gboolean got_configs = FALSE;
62 static GList *core_config = NULL;
63 static gboolean testfile_used = FALSE;
64 static GList *testfile_structs = NULL;
65 static gchar *global_testfile = NULL;
66 static gboolean validate_initialized = FALSE;
67 static gboolean loaded_globals = FALSE;
68 GstClockTime _priv_start_time;
70 GQuark _Q_VALIDATE_MONITOR;
73 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
75 DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
77 if (fdwReason == DLL_PROCESS_ATTACH)
78 _priv_gstvalidate_dll_handle = (HMODULE) hinstDLL;
82 #endif /* G_OS_WIN32 */
85 gst_validate_registry_get (void)
87 GstRegistry *registry;
89 g_mutex_lock (&_gst_validate_registry_mutex);
90 if (G_UNLIKELY (!_gst_validate_registry_default)) {
91 _gst_validate_registry_default = g_object_new (GST_TYPE_REGISTRY, NULL);
92 gst_object_ref_sink (GST_OBJECT_CAST (_gst_validate_registry_default));
94 registry = _gst_validate_registry_default;
95 g_mutex_unlock (&_gst_validate_registry_mutex);
100 #define GST_VALIDATE_PLUGIN_CONFIG "gst-validate-plugin-config"
103 _free_plugin_config (gpointer data)
108 /* Copied from gststructure.c to avoid assertion */
110 gst_structure_validate_name (const gchar * name)
114 g_return_val_if_fail (name != NULL, FALSE);
116 if (G_UNLIKELY (!g_ascii_isalpha (*name))) {
117 GST_INFO ("Invalid character '%c' at offset 0 in structure name: %s",
122 /* FIXME: test name string more */
124 while (*s && (g_ascii_isalnum (*s) || strchr ("/-_.:+", *s) != NULL))
129 if (G_UNLIKELY (*s != '\0')) {
130 GST_INFO ("Invalid character '%c' at offset %" G_GUINTPTR_FORMAT " in"
131 " structure name: %s", *s, ((guintptr) s - (guintptr) name), name);
139 _set_vars_func (GQuark field_id, const GValue * value, GstStructure * vars)
141 gst_structure_id_set_value (vars, field_id, value);
146 static GstStructure *
147 get_test_file_meta (void)
151 for (tmp = testfile_structs; tmp; tmp = tmp->next) {
152 if (gst_structure_has_name (tmp->data, "meta"))
160 create_config (const gchar * config)
162 GstStructure *local_vars;
163 GList *structures = NULL, *tmp;
164 gchar *config_file = NULL;
167 local_vars = gst_structure_new_empty ("vars");
168 f = g_file_new_for_path (config);
169 if (g_file_query_exists (f, NULL)) {
170 structures = gst_validate_utils_structs_parse_from_filename (config, NULL,
173 GstCaps *confs = NULL;
175 if (gst_structure_validate_name (config))
176 confs = gst_caps_from_string (config);
181 for (i = 0; i < gst_caps_get_size (confs); i++) {
182 GstStructure *structure = gst_caps_get_structure (confs, i);
184 structures = g_list_append (structures, gst_structure_copy (structure));
187 gst_caps_unref (confs);
191 gst_validate_structure_set_variables_from_struct_file (local_vars,
193 g_free (config_file);
195 for (tmp = structures; tmp; tmp = tmp->next) {
196 GstStructure *structure = tmp->data;
198 if (gst_structure_has_field (structure, "set-vars")) {
199 gst_structure_remove_field (structure, "set-vars");
200 gst_structure_foreach (structure,
201 (GstStructureForeachFunc) _set_vars_func, local_vars);
202 gst_structure_free (structure);
203 } else if (!loaded_globals
204 && gst_structure_has_name (structure, "set-globals")) {
205 gst_validate_structure_resolve_variables (NULL, structure, local_vars, 0);
206 gst_validate_set_globals (structure);
207 gst_structure_free (structure);
209 gst_validate_structure_resolve_variables (NULL, structure, local_vars, 0);
210 all_configs = g_list_append (all_configs, structure);
214 loaded_globals = TRUE;
215 gst_structure_free (local_vars);
216 g_list_free (structures);
220 get_structures_from_array (GstStructure * structure, const gchar * fieldname)
226 value = gst_structure_get_value (structure, fieldname);
230 if (GST_VALUE_HOLDS_STRUCTURE (value)) {
231 return g_list_append (res,
232 gst_structure_copy (gst_value_get_structure (value)));
235 if (!GST_VALUE_HOLDS_LIST (value)) {
239 size = gst_value_list_get_size (value);
240 for (i = 0; i < size; i++) {
241 const GValue *v1 = gst_value_list_get_value (value, i);
243 if (!GST_VALUE_HOLDS_STRUCTURE (v1))
247 g_list_append (res, gst_structure_copy (gst_value_get_structure (v1)));
255 get_structures_from_array_in_meta (const gchar * fieldname)
258 gchar **strs = NULL, *filename = NULL, *debug = NULL;
259 gint current_lineno = -1;
260 GstStructure *meta = get_test_file_meta ();
265 res = get_structures_from_array (meta, fieldname);
269 gst_structure_get (meta,
270 "__lineno__", G_TYPE_INT, ¤t_lineno,
271 "__debug__", G_TYPE_STRING, &debug,
272 "__filename__", G_TYPE_STRING, &filename, NULL);
273 strs = gst_validate_utils_get_strv (meta, fieldname);
278 for (i = 0; strs[i]; i++) {
279 GstStructure *tmpstruct = gst_structure_from_string (strs[i], NULL);
281 if (tmpstruct == NULL) {
282 gst_validate_abort ("%s:%d: Invalid structure\n %4d | %s\n%s",
283 filename, current_lineno, current_lineno, strs[i], debug);
286 gst_structure_set (tmpstruct,
287 "__lineno__", G_TYPE_INT, current_lineno,
288 "__filename__", G_TYPE_STRING, filename,
289 "__debug__", G_TYPE_STRING, debug, NULL);
290 res = g_list_append (res, tmpstruct);
302 * gst_validate_plugin_get_config:
303 * @plugin: a #GstPlugin, or #NULL
305 * Return the configuration specific to @plugin, or the "core" one if @plugin
308 * Returns: (transfer none) (element-type GstStructure): a list of #GstStructure
311 gst_validate_plugin_get_config (GstPlugin * plugin)
314 GList *plugin_conf = NULL;
318 g_object_get_data (G_OBJECT (plugin), GST_VALIDATE_PLUGIN_CONFIG)))
321 suffix = gst_plugin_get_name (plugin);
329 plugin_conf = gst_validate_get_config (suffix);
331 g_object_set_data_full (G_OBJECT (plugin), GST_VALIDATE_PLUGIN_CONFIG,
332 plugin_conf, _free_plugin_config);
334 core_config = plugin_conf;
340 gst_validate_ensure_all_configs (void)
350 all_configs = get_structures_from_array_in_meta ("configs");
351 config = g_getenv ("GST_VALIDATE_CONFIG");
355 tmp = g_strsplit (config, G_SEARCHPATH_SEPARATOR_S, -1);
356 for (i = 0; tmp[i] != NULL; i++) {
357 if (tmp[i][0] == '\0')
360 create_config (tmp[i]);
366 gst_validate_get_config (const gchar * structname)
368 GList *tmp, *res = NULL;
370 G_LOCK (all_configs_lock);
371 gst_validate_ensure_all_configs ();
373 for (tmp = all_configs; tmp; tmp = tmp->next) {
376 if (structname && !gst_structure_has_name (tmp->data, structname)) {
378 } else if (structname) {
379 gst_structure_get (tmp->data, "__n_usages__", G_TYPE_INT, &n_usages,
382 gst_structure_set (tmp->data, "__n_usages__", G_TYPE_INT, n_usages, NULL);
384 res = g_list_append (res, tmp->data);
386 G_UNLOCK (all_configs_lock);
392 gst_validate_init_plugins (void)
394 GstRegistry *registry;
395 const gchar *plugin_path;
397 gst_registry_fork_set_enabled (FALSE);
398 registry = gst_validate_registry_get ();
400 plugin_path = g_getenv ("GST_VALIDATE_PLUGIN_PATH");
405 GST_DEBUG ("GST_VALIDATE_PLUGIN_PATH set to %s", plugin_path);
406 list = g_strsplit (plugin_path, G_SEARCHPATH_SEPARATOR_S, 0);
407 for (i = 0; list[i]; i++) {
408 gst_registry_scan_path (registry, list[i]);
412 GST_DEBUG ("GST_VALIDATE_PLUGIN_PATH not set");
415 if (plugin_path == NULL) {
418 /* plugins in the user's home directory take precedence over
419 * system-installed ones */
420 home_plugins = g_build_filename (g_get_user_data_dir (),
421 "gstreamer-" GST_API_VERSION, "plugins", NULL);
423 GST_DEBUG ("scanning home plugins %s", home_plugins);
424 gst_registry_scan_path (registry, home_plugins);
425 g_free (home_plugins);
427 /* add the main (installed) library path */
435 g_win32_get_package_installation_directory_of_module
436 (_priv_gstvalidate_dll_handle);
438 dir = g_build_filename (base_dir,
439 "lib", "gstreamer-" GST_API_VERSION, "validate", NULL);
441 GST_DEBUG ("scanning DLL dir %s", dir);
443 gst_registry_scan_path (registry, dir);
449 gst_registry_scan_path (registry, VALIDATEPLUGINDIR);
452 gst_registry_fork_set_enabled (TRUE);
456 gst_validate_init_debug (void)
458 GST_DEBUG_CATEGORY_INIT (gstvalidate_debug, "validate", 0,
459 "Validation library");
465 * Initializes GstValidate. Call this before any usage of GstValidate.
466 * You should take care of initializing GStreamer before calling this
470 gst_validate_init (void)
472 if (validate_initialized) {
475 gst_validate_init_debug ();
476 _priv_start_time = gst_util_get_timestamp ();
477 _Q_VALIDATE_MONITOR = g_quark_from_static_string ("validate-monitor");
479 setlocale (LC_NUMERIC, "C");
481 /* init the report system (can be called multiple times) */
482 gst_validate_report_init ();
484 /* Init the scenario system */
487 /* Ensure we load overrides before any use of a monitor */
488 gst_validate_override_registry_preload ();
490 validate_initialized = TRUE;
492 gst_validate_extra_checks_init ();
493 gst_validate_flow_init ();
494 gst_validate_init_plugins ();
495 gst_validate_init_runner ();
499 gst_validate_deinit (void)
501 g_mutex_lock (&_gst_validate_registry_mutex);
503 g_list_free (core_config);
506 g_list_free_full (all_configs, (GDestroyNotify) gst_structure_free);
507 gst_validate_deinit_runner ();
509 gst_validate_scenario_deinit ();
511 g_clear_object (&_gst_validate_registry_default);
513 g_list_free_full (testfile_structs, (GDestroyNotify) gst_structure_free);
514 testfile_structs = NULL;
515 g_clear_pointer (&global_testfile, g_free);
517 _priv_validate_override_registry_deinit ();
518 validate_initialized = FALSE;
519 gst_validate_report_deinit ();
521 g_mutex_unlock (&_gst_validate_registry_mutex);
522 g_mutex_clear (&_gst_validate_registry_mutex);
526 gst_validate_is_initialized (void)
528 return validate_initialized;
532 gst_validate_get_test_file_expected_issues (void)
534 GList *res = get_structures_from_array_in_meta ("expected-issues"), *tmp;
536 for (tmp = res; tmp; tmp = tmp->next) {
537 GstStructure *known_issue = tmp->data;
538 const gchar *summary = gst_structure_get_string (known_issue, "summary");
539 const gchar *id = gst_structure_get_string (known_issue, "issue-id");
542 gst_validate_error_structure (known_issue,
543 "Missing 'summary' or 'issue-id' fields.");
550 gst_validate_get_test_file_scenario (GList ** structs,
551 const gchar ** scenario_name, gchar ** original_name)
553 GList *res = NULL, *tmp;
554 GstStructure *meta = get_test_file_meta ();
556 if (!testfile_structs || testfile_used)
559 if (meta && gst_structure_has_field (meta, "scenario")) {
560 *scenario_name = gst_structure_get_string (meta, "scenario");
565 for (tmp = testfile_structs; tmp; tmp = tmp->next) {
566 GstStructure *structure = NULL;
568 if (gst_structure_has_name (tmp->data, "set-globals"))
571 structure = gst_structure_copy (tmp->data);
572 if (gst_structure_has_name (structure, "meta"))
573 gst_structure_remove_fields (structure, "configs", "gst-validate-args",
575 res = g_list_append (res, structure);
579 *original_name = global_testfile;
580 testfile_used = TRUE;
585 /* Only the first monitor pipeline will be used */
587 gst_validate_setup_test_file (const gchar * testfile, gboolean use_fakesinks)
590 GstStructure *res = NULL;
592 g_assert (!got_configs);
594 gst_validate_abort ("A testfile was already loaded: %s", global_testfile);
596 global_testfile = g_canonicalize_filename (testfile, NULL);
598 gst_validate_set_globals (NULL);
599 gst_validate_structure_set_variables_from_struct_file (NULL, global_testfile);
601 gst_validate_utils_structs_parse_from_filename (global_testfile, NULL,
604 if (!testfile_structs)
605 gst_validate_abort ("Could not load test file: %s", global_testfile);
607 res = testfile_structs->data;
608 if (gst_structure_has_name (testfile_structs->data, "set-globals")) {
609 GstStructure *globals = testfile_structs->data;
610 gst_validate_set_globals (globals);
611 if (!testfile_structs->next)
613 ("Only one `set-globals` structure in %s, nothing to test here.",
615 res = testfile_structs->next->data;
618 if (!gst_structure_has_name (res, "meta"))
620 ("First structure of a .validatetest file should be a `meta` or "
621 "`set-gobals` then `meta`, got: %s", gst_structure_to_string (res));
623 register_action_types ();
624 gst_validate_scenario_check_and_set_needs_clock_sync (testfile_structs, &res);
626 gst_validate_set_test_file_globals (res, global_testfile, use_fakesinks);
627 gst_validate_structure_resolve_variables (NULL, res, NULL, 0);
629 tool = gst_structure_get_string (res, "tool");
631 tool = "gst-validate-" GST_API_VERSION;
633 if (g_strcmp0 (tool, g_get_prgname ()))
635 ("Validate test file: '%s' was made to be run with '%s' not '%s'",
636 global_testfile, tool, g_get_prgname ());