From 41623071731d5b442608ec493ecb6a85977e8e94 Mon Sep 17 00:00:00 2001 From: Zipdox Date: Wed, 21 Dec 2022 23:55:07 +0100 Subject: [PATCH] GSettings: New schema source from path, bytes and resource --- gio/gsettingsschema.c | 327 ++++++++++++++-- gio/gsettingsschema.h | 20 + gio/tests/gsettings.c | 354 +++++++++++++++++- gio/tests/meson.build | 3 +- .../org.gtk.schemasourcecheck.gschema.xml | 7 + .../schema_from_resource_test.gresource.xml | 14 + subprojects/gvdb | 1 - 7 files changed, 689 insertions(+), 37 deletions(-) create mode 100644 gio/tests/schema_from_resource_test.gresource.xml delete mode 160000 subprojects/gvdb diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c index e8ccc8c1f..3223c20b4 100644 --- a/gio/gsettingsschema.c +++ b/gio/gsettingsschema.c @@ -21,6 +21,7 @@ #include "config.h" #include "glib-private.h" +#include "gresource.h" #include "gsettingsschema-internal.h" #include "gsettings.h" @@ -156,6 +157,20 @@ struct _GSettingsSchema gint ref_count; }; +/* + * @G_SETTINGS_SCHEMA_SOURCE_FILE: The schema source was created from a path or directory + * @G_SETTINGS_SCHEMA_SOURCE_RESOURCE: The schema source was created from a resource + * @G_SETTINGS_SCHEMA_SOURCE_BYTES: The schema source was created from #GBytes + * + * Type of a #GSettingsSchemaSource. It indicates how the source was created. + */ +typedef enum +{ + G_SETTINGS_SCHEMA_SOURCE_FILE, + G_SETTINGS_SCHEMA_SOURCE_RESOURCE, + G_SETTINGS_SCHEMA_SOURCE_BYTES +} GSettingsSchemaSourceType; + /** * G_TYPE_SETTINGS_SCHEMA_SOURCE: * @@ -184,7 +199,18 @@ G_DEFINE_BOXED_TYPE (GSettingsSchema, g_settings_schema, g_settings_schema_ref, struct _GSettingsSchemaSource { GSettingsSchemaSource *parent; - gchar *directory; + + GSettingsSchemaSourceType type; + union { + struct { + char *directory; + } file; + struct { + char *path; + GResourceLookupFlags lookup_flags; + } resource; + } type_data; + GvdbTable *table; GHashTable **text_tables; @@ -230,7 +256,18 @@ g_settings_schema_source_unref (GSettingsSchemaSource *source) if (source->parent) g_settings_schema_source_unref (source->parent); gvdb_table_free (source->table); - g_free (source->directory); + + switch (source->type) + { + case G_SETTINGS_SCHEMA_SOURCE_FILE: + g_free (source->type_data.file.directory); + break; + case G_SETTINGS_SCHEMA_SOURCE_RESOURCE: + g_free(source->type_data.resource.path); + break; + default: + break; + } if (source->text_tables) { @@ -282,6 +319,8 @@ g_settings_schema_source_unref (GSettingsSchemaSource *source) * @parent should probably be given as the default schema source, as * returned by g_settings_schema_source_get_default(). * + * Returns: (transfer full): a new #GSettingsSchemaSource, or %NULL + * * Since: 2.32 **/ GSettingsSchemaSource * @@ -291,22 +330,158 @@ g_settings_schema_source_new_from_directory (const gchar *directory, GError **error) { GSettingsSchemaSource *source; - GvdbTable *table; gchar *filename; filename = g_build_filename (directory, "gschemas.compiled", NULL); - table = gvdb_table_new (filename, trusted, error); + source = g_settings_schema_source_new_from_path (filename, parent, trusted, error); g_free (filename); + return source; +} + +/** + * g_settings_schema_source_new_from_path: + * @path: (type filename): the filename of a compiled schema source + * @parent: (nullable): a #GSettingsSchemaSource, or %NULL + * @trusted: %TRUE, if the directory is trusted + * @error: a pointer to a #GError pointer set to %NULL, or %NULL + * + * Almost identical to g_settings_schema_source_new_from_directory() but + * takes a path to a compiled schema file directly instead of to the + * directory it is in. + * + * Returns: (transfer full): a new #GSettingsSchemaSource, or %NULL + * + * Since: 2.78 + **/ +GSettingsSchemaSource * +g_settings_schema_source_new_from_path (const gchar *path, + GSettingsSchemaSource *parent, + gboolean trusted, + GError **error) +{ + GSettingsSchemaSource *source; + GvdbTable *table; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + table = gvdb_table_new (path, trusted, error); if (table == NULL) return NULL; - source = g_slice_new (GSettingsSchemaSource); - source->directory = g_strdup (directory); + source = g_slice_new0 (GSettingsSchemaSource); source->parent = parent ? g_settings_schema_source_ref (parent) : NULL; - source->text_tables = NULL; source->table = table; source->ref_count = 1; + source->type = G_SETTINGS_SCHEMA_SOURCE_FILE; + source->type_data.file.directory = g_path_get_dirname (path); + + return source; +} + +/** + * g_settings_schema_source_new_from_bytes: + * @bytes: a #GBytes + * @parent: (nullable): a #GSettingsSchemaSource, or %NULL + * @trusted: %TRUE, if the data is trusted + * @error: a pointer to a #GError pointer set to %NULL, or %NULL + * + * Attempts to create a new schema source corresponding to the contents + * of @bytes, which should contain the data as produced by the + * [glib-compile-schemas][glib-compile-schemas] tool. + * + * This should only be used in standalone applications and should not + * be used in situations where settings are shared with other applications. + * + * Note that g_settings_schema_key_get_summary() and + * g_settings_schema_key_get_description() will always return %NULL for + * a #GSettingsSchemaKey belonging to a #GSettingsSchema created from a + * schema source returned by this function. + * + * See g_settings_schema_source_new_from_directory() for more information. + * + * Returns: (transfer full) (nullable): a new #GSettingsSchemaSource, or %NULL + * + * Since: 2.78 + **/ +GSettingsSchemaSource * +g_settings_schema_source_new_from_bytes (GBytes *bytes, + GSettingsSchemaSource *parent, + gboolean trusted, + GError **error) +{ + GSettingsSchemaSource *source; + GvdbTable *table; + + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + table = gvdb_table_new_from_bytes (bytes, trusted, error); + if (table == NULL) + return NULL; + + source = g_slice_new0 (GSettingsSchemaSource); + source->parent = parent ? g_settings_schema_source_ref (parent) : NULL; + source->table = table; + source->ref_count = 1; + source->type = G_SETTINGS_SCHEMA_SOURCE_BYTES; + + return source; +} + +/** + * g_settings_schema_source_new_from_resource: + * @path: the resource path + * @lookup_flags: A #GResourceLookupFlags + * @parent: (nullable): a #GSettingsSchemaSource, or %NULL + * @trusted: %TRUE, if the resource is trusted + * @error: a pointer to a #GError pointer set to %NULL, or %NULL + * + * Attempts to create a new schema source corresponding to the contents + * of the given resource, which should contain the data as produced by the + * [glib-compile-schemas][glib-compile-schemas] tool. + * + * This should only be used in standalone applications and should not + * be used in situations where settings are shared with other applications. + * + * Note that for g_settings_schema_key_get_summary() and + * g_settings_schema_key_get_description() to work, an XML schema resource + * $path.xml must be present. + * + * See g_settings_schema_source_new_from_directory() for more information. + * + * Returns: (transfer full): a new #GSettingsSchemaSource, or %NULL + * + * Since: 2.78 + **/ +GSettingsSchemaSource * +g_settings_schema_source_new_from_resource (const gchar *path, + GResourceLookupFlags lookup_flags, + GSettingsSchemaSource *parent, + gboolean trusted, + GError **error) +{ + GSettingsSchemaSource *source; + GBytes *bytes; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + bytes = g_resources_lookup_data (path, lookup_flags, error); + if (bytes == NULL) + return NULL; + + source = g_settings_schema_source_new_from_bytes (bytes, parent, trusted, error); + if (source == NULL) { + g_bytes_unref (bytes); + return NULL; + } + g_bytes_unref (bytes); + + source->type = G_SETTINGS_SCHEMA_SOURCE_RESOURCE; + source->type_data.resource.path = g_strdup (path); + source->type_data.resource.lookup_flags = lookup_flags; return source; } @@ -680,12 +855,35 @@ text (GMarkupParseContext *context, } static void -parse_into_text_tables (const gchar *directory, +parse_into_text_tables (const char *data, + gsize size, GHashTable *summaries, GHashTable *descriptions) { GMarkupParser parser = { start_element, end_element, text, NULL, NULL }; TextTableParseInfo info = { summaries, descriptions, NULL, NULL, NULL, NULL }; + GMarkupParseContext *context; + + context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &info, NULL); + /* Ignore errors here, this is best effort only. */ + if (g_markup_parse_context_parse (context, data, size, NULL)) + (void) g_markup_parse_context_end_parse (context, NULL); + g_markup_parse_context_free (context); + + /* Clean up dangling stuff in case there was an error. */ + g_slist_free_full (info.gettext_domain, g_free); + g_slist_free_full (info.schema_id, g_free); + g_slist_free_full (info.key_name, g_free); + + if (info.string) + g_string_free (info.string, TRUE); +} + +static void +parse_into_text_tables_directory (const gchar *directory, + GHashTable *summaries, + GHashTable *descriptions) +{ const gchar *basename; GDir *dir; @@ -699,29 +897,7 @@ parse_into_text_tables (const gchar *directory, filename = g_build_filename (directory, basename, NULL); if (g_file_get_contents (filename, &contents, &size, NULL)) { - GMarkupParseContext *context; - - context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &info, NULL); - /* Ignore errors here, this is best effort only. */ - if (g_markup_parse_context_parse (context, contents, size, NULL)) - (void) g_markup_parse_context_end_parse (context, NULL); - g_markup_parse_context_free (context); - - /* Clean up dangling stuff in case there was an error. */ - g_slist_free_full (info.gettext_domain, g_free); - g_slist_free_full (info.schema_id, g_free); - g_slist_free_full (info.key_name, g_free); - - info.gettext_domain = NULL; - info.schema_id = NULL; - info.key_name = NULL; - - if (info.string) - { - g_string_free (info.string, TRUE); - info.string = NULL; - } - + parse_into_text_tables (contents, size, summaries, descriptions); g_free (contents); } @@ -731,6 +907,68 @@ parse_into_text_tables (const gchar *directory, g_dir_close (dir); } +static void +parse_into_text_tables_resource (const char *path, + GResourceLookupFlags lookup_flags, + GHashTable *summaries, + GHashTable *descriptions) +{ + char *xml_path; + GError *error = NULL; + GBytes *bytes; + const char *data; + gsize size, i; + char **resources; + + /* First try loading the XML schema data from @path + '.xml'. */ + xml_path = g_strdup_printf ("%s.xml", path); + bytes = g_resources_lookup_data (xml_path, lookup_flags, &error); + g_free (xml_path); + if (bytes) + { + data = g_bytes_get_data (bytes, &size); + parse_into_text_tables (data, size, summaries, descriptions); + g_bytes_unref (bytes); + return; + } + + if (!g_error_matches (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND)) + { + g_error_free (error); + return; + } + g_error_free (error); + + /* If there is no singular @path + '.xml' resource, try to load all + * resources from under the @path + '.xml/' path. + */ + xml_path = g_strdup_printf ("%s.xml/", path); + resources = g_resources_enumerate_children (xml_path, lookup_flags, NULL); + if (!resources) + { + g_free (xml_path); + return; + } + + for (i = 0; resources[i] != NULL; i++) + { + char *child_path; + + child_path = g_strconcat (xml_path, resources[i], NULL); + bytes = g_resources_lookup_data (child_path, lookup_flags, NULL); + g_free (child_path); + if (!bytes) + continue; + + data = g_bytes_get_data (bytes, &size); + parse_into_text_tables (data, size, summaries, descriptions); + g_bytes_unref (bytes); + } + + g_free (xml_path); + g_strfreev (resources); +} + static GHashTable ** g_settings_schema_source_get_text_tables (GSettingsSchemaSource *source) { @@ -742,8 +980,19 @@ g_settings_schema_source_get_text_tables (GSettingsSchemaSource *source) text_tables[0] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref); text_tables[1] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref); - if (source->directory) - parse_into_text_tables (source->directory, text_tables[0], text_tables[1]); + if (source->type == G_SETTINGS_SCHEMA_SOURCE_FILE) + { + parse_into_text_tables_directory (source->type_data.file.directory, text_tables[0], text_tables[1]); + } + else if (source->type == G_SETTINGS_SCHEMA_SOURCE_RESOURCE) + { + parse_into_text_tables_resource (source->type_data.resource.path, source->type_data.resource.lookup_flags, + text_tables[0], text_tables[1]); + } + else if (source->type == G_SETTINGS_SCHEMA_SOURCE_BYTES) + { + /* Nothing to do in this case. */ + } g_once_init_leave_pointer (&source->text_tables, text_tables); } @@ -1704,6 +1953,12 @@ g_settings_schema_key_get_name (GSettingsSchemaKey *key) * If no summary has been provided in the schema for @key, returns * %NULL. * + * This function only works on keys belonging to a #GSettingsSchema + * created from a #GSettingsSchemaSource created with + * g_settings_schema_source_new_from_directory() or + * g_settings_schema_source_new_from_path(), and requires the source + * XML file to be present in the same directory as the compiled schema. + * * The summary is a short description of the purpose of the key; usually * one short sentence. Summaries can be translated and the value * returned from this function is is the current locale. @@ -1738,6 +1993,12 @@ g_settings_schema_key_get_summary (GSettingsSchemaKey *key) * If no description has been provided in the schema for @key, returns * %NULL. * + * This function only works on keys belonging to a #GSettingsSchema + * created from a #GSettingsSchemaSource created with + * g_settings_schema_source_new_from_directory() or + * g_settings_schema_source_new_from_path(), and requires the source + * XML file to be present in the same directory as the compiled schema. + * * The description can be one sentence to several paragraphs in length. * Paragraphs are delimited with a double newline. Descriptions can be * translated and the value returned from this function is is the diff --git a/gio/gsettingsschema.h b/gio/gsettingsschema.h index a3bb7df80..927844574 100644 --- a/gio/gsettingsschema.h +++ b/gio/gsettingsschema.h @@ -23,6 +23,7 @@ #include #include +#include G_BEGIN_DECLS @@ -47,6 +48,25 @@ GSettingsSchemaSource * g_settings_schema_source_new_from_directory (const g gboolean trusted, GError **error); +GIO_AVAILABLE_IN_2_78 +GSettingsSchemaSource * g_settings_schema_source_new_from_path (const gchar *path, + GSettingsSchemaSource *parent, + gboolean trusted, + GError **error); + +GIO_AVAILABLE_IN_2_78 +GSettingsSchemaSource * g_settings_schema_source_new_from_bytes (GBytes *bytes, + GSettingsSchemaSource *parent, + gboolean trusted, + GError **error); + +GIO_AVAILABLE_IN_2_78 +GSettingsSchemaSource * g_settings_schema_source_new_from_resource (const gchar *path, + GResourceLookupFlags lookup_flags, + GSettingsSchemaSource *parent, + gboolean trusted, + GError **error); + GIO_AVAILABLE_IN_2_32 GSettingsSchema * g_settings_schema_source_lookup (GSettingsSchemaSource *source, const gchar *schema_id, diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c index 60f2aebae..00600ac0f 100644 --- a/gio/tests/gsettings.c +++ b/gio/tests/gsettings.c @@ -2811,7 +2811,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS } static void -test_schema_source (void) +test_schema_source_directory (void) { GSettingsSchemaSource *parent; GSettingsSchemaSource *source; @@ -2910,6 +2910,325 @@ test_schema_source (void) g_object_unref (backend); } +static void +test_schema_source_path (void) +{ + GSettingsSchemaSource *parent; + GSettingsSchemaSource *source; + GSettingsBackend *backend; + GSettingsSchema *schema; + GError *error = NULL; + GSettings *settings, *child; + gboolean enabled; + + backend = g_settings_backend_get_default (); + + /* make sure it fails properly */ + parent = g_settings_schema_source_get_default (); + source = g_settings_schema_source_new_from_path ("/path/that/does/not/exist/gschemas.compiled", parent, TRUE, &error); + g_assert_null (source); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT); + g_clear_error (&error); + + /* Test error handling of corrupt compiled files. */ + source = g_settings_schema_source_new_from_path ("schema-source-corrupt/gschemas.compiled", parent, TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert_null (source); + g_clear_error (&error); + + /* Test error handling of empty compiled files. */ + source = g_settings_schema_source_new_from_path ("schema-source-empty/gschemas.compiled", parent, TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert_null (source); + g_clear_error (&error); + + /* create a source with the parent */ + source = g_settings_schema_source_new_from_path ("schema-source/gschemas.compiled", parent, TRUE, &error); + g_assert_no_error (error); + g_assert_nonnull (source); + + /* check recursive lookups are working */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", TRUE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + /* check recursive lookups for non-existent schemas */ + schema = g_settings_schema_source_lookup (source, "org.gtk.doesnotexist", TRUE); + g_assert_null (schema); + + /* check non-recursive for schema that only exists in lower layers */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", FALSE); + g_assert_null (schema); + + /* check non-recursive lookup for non-existent */ + schema = g_settings_schema_source_lookup (source, "org.gtk.doesnotexist", FALSE); + g_assert_null (schema); + + /* check non-recursive for schema that exists in toplevel */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", FALSE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + /* check recursive for schema that exists in toplevel */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", TRUE); + g_assert_nonnull (schema); + + /* try to use it for something */ + settings = g_settings_new_full (schema, backend, "/test/"); + g_settings_schema_unref (schema); + enabled = FALSE; + g_settings_get (settings, "enabled", "b", &enabled); + g_assert_true (enabled); + + /* Check that child schemas are resolved from the correct schema source, see glib#1884 */ + child = g_settings_get_child (settings, "child"); + g_settings_get (settings, "enabled", "b", &enabled); + + g_object_unref (child); + g_object_unref (settings); + g_settings_schema_source_unref (source); + + /* try again, but with no parent */ + source = g_settings_schema_source_new_from_path ("schema-source/gschemas.compiled", NULL, FALSE, NULL); + g_assert_nonnull (source); + + /* should not find it this time, even if recursive... */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", FALSE); + g_assert_null (schema); + schema = g_settings_schema_source_lookup (source, "org.gtk.test", TRUE); + g_assert_null (schema); + + /* should still find our own... */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", TRUE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", FALSE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + g_settings_schema_source_unref (source); + g_object_unref (backend); +} + +static void +test_schema_source_bytes (void) +{ + GSettingsSchemaSource *parent; + GSettingsSchemaSource *source; + GSettingsBackend *backend; + GSettingsSchema *schema; + GError *error = NULL; + GSettings *settings, *child; + gboolean enabled; + GFile *corrupt_schema_file, *empty_schema_file, *normal_schema_file; + GBytes *corrupt_schema_bytes, *empty_schema_bytes, *normal_schema_bytes; + + backend = g_settings_backend_get_default (); + + parent = g_settings_schema_source_get_default (); + + /* Read the schemas into bytes */ + corrupt_schema_file = g_file_new_for_path("schema-source-corrupt/gschemas.compiled"); + corrupt_schema_bytes = g_file_load_bytes(corrupt_schema_file, NULL, NULL, NULL); + empty_schema_file = g_file_new_for_path("schema-source-empty/gschemas.compiled"); + empty_schema_bytes = g_file_load_bytes(empty_schema_file, NULL, NULL, NULL); + normal_schema_file = g_file_new_for_path("schema-source/gschemas.compiled"); + normal_schema_bytes = g_file_load_bytes(normal_schema_file, NULL, NULL, NULL); + + /* Test error handling of corrupt compiled files. */ + source = g_settings_schema_source_new_from_bytes (corrupt_schema_bytes, parent, TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert_null (source); + g_clear_error (&error); + + /* Test error handling of empty compiled files. */ + source = g_settings_schema_source_new_from_bytes (empty_schema_bytes, parent, TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert_null (source); + g_clear_error (&error); + + /* create a source with the parent */ + source = g_settings_schema_source_new_from_bytes (normal_schema_bytes, parent, TRUE, &error); + g_assert_no_error (error); + g_assert_nonnull (source); + + /* check recursive lookups are working */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", TRUE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + /* check recursive lookups for non-existent schemas */ + schema = g_settings_schema_source_lookup (source, "org.gtk.doesnotexist", TRUE); + g_assert_null (schema); + + /* check non-recursive for schema that only exists in lower layers */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", FALSE); + g_assert_null (schema); + + /* check non-recursive lookup for non-existent */ + schema = g_settings_schema_source_lookup (source, "org.gtk.doesnotexist", FALSE); + g_assert_null (schema); + + /* check non-recursive for schema that exists in toplevel */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", FALSE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + /* check recursive for schema that exists in toplevel */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", TRUE); + g_assert_nonnull (schema); + + /* try to use it for something */ + settings = g_settings_new_full (schema, backend, "/test/"); + g_settings_schema_unref (schema); + enabled = FALSE; + g_settings_get (settings, "enabled", "b", &enabled); + g_assert_true (enabled); + + /* Check that child schemas are resolved from the correct schema source, see glib#1884 */ + child = g_settings_get_child (settings, "child"); + g_settings_get (settings, "enabled", "b", &enabled); + + g_object_unref (child); + g_object_unref (settings); + g_settings_schema_source_unref (source); + g_bytes_unref(empty_schema_bytes); + g_object_unref(empty_schema_file); + g_bytes_unref(corrupt_schema_bytes); + g_object_unref(corrupt_schema_file); + + /* try again, but with no parent */ + source = g_settings_schema_source_new_from_bytes (normal_schema_bytes, NULL, FALSE, NULL); + g_assert_nonnull (source); + + /* should not find it this time, even if recursive... */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", FALSE); + g_assert_null (schema); + schema = g_settings_schema_source_lookup (source, "org.gtk.test", TRUE); + g_assert_null (schema); + + /* should still find our own... */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", TRUE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", FALSE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + g_settings_schema_source_unref (source); + g_bytes_unref(normal_schema_bytes); + g_object_unref(normal_schema_file); + + g_object_unref (backend); +} + +static void +test_schema_source_resource (void) +{ + GSettingsSchemaSource *parent; + GSettingsSchemaSource *source; + GSettingsBackend *backend; + GSettingsSchema *schema; + GSettingsSchemaKey *key; + GError *error = NULL; + GSettings *settings, *child; + gboolean enabled; + + backend = g_settings_backend_get_default (); + + parent = g_settings_schema_source_get_default (); + + /* Test error handling of corrupt compiled files. */ + source = g_settings_schema_source_new_from_resource ("/schema-source-corrupt/gschemas.compiled", G_RESOURCE_LOOKUP_FLAGS_NONE, parent, TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert_null (source); + g_clear_error (&error); + + /* Test error handling of empty compiled files. */ + source = g_settings_schema_source_new_from_resource ("/schema-source-empty/gschemas.compiled", G_RESOURCE_LOOKUP_FLAGS_NONE, parent, TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert_null (source); + g_clear_error (&error); + + /* create a source with the parent */ + source = g_settings_schema_source_new_from_resource ("/schema-source/gschemas.compiled", G_RESOURCE_LOOKUP_FLAGS_NONE, parent, TRUE, &error); + g_assert_no_error (error); + g_assert_nonnull (source); + + /* check recursive lookups are working */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", TRUE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + /* check recursive lookups for non-existent schemas */ + schema = g_settings_schema_source_lookup (source, "org.gtk.doesnotexist", TRUE); + g_assert_null (schema); + + /* check non-recursive for schema that only exists in lower layers */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", FALSE); + g_assert_null (schema); + + /* check non-recursive lookup for non-existent */ + schema = g_settings_schema_source_lookup (source, "org.gtk.doesnotexist", FALSE); + g_assert_null (schema); + + /* check non-recursive for schema that exists in toplevel */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", FALSE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + /* check recursive for schema that exists in toplevel */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", TRUE); + g_assert_nonnull (schema); + + /* check summary and description lookup */ + key = g_settings_schema_get_key (schema, "enabled"); + g_assert_cmpstr (g_settings_schema_key_get_summary (key), ==, "Boolean setting test"); + g_assert_cmpstr (g_settings_schema_key_get_description (key), ==, "Setting to test boolean settings"); + g_settings_schema_key_unref (key); + key = g_settings_schema_get_key (schema, "nosummary"); + g_assert_null (g_settings_schema_key_get_summary (key)); + g_assert_null (g_settings_schema_key_get_description (key)); + g_settings_schema_key_unref (key); + + /* try to use it for something */ + settings = g_settings_new_full (schema, backend, "/test/"); + g_settings_schema_unref (schema); + enabled = FALSE; + g_settings_get (settings, "enabled", "b", &enabled); + g_assert_true (enabled); + + /* Check that child schemas are resolved from the correct schema source, see glib#1884 */ + child = g_settings_get_child (settings, "child"); + g_settings_get (settings, "enabled", "b", &enabled); + + g_object_unref (child); + g_object_unref (settings); + g_settings_schema_source_unref (source); + + /* try again, but with no parent */ + source = g_settings_schema_source_new_from_resource ("/schema-source/gschemas.compiled", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL, TRUE, &error); + g_assert_nonnull (source); + + /* should not find it this time, even if recursive... */ + schema = g_settings_schema_source_lookup (source, "org.gtk.test", FALSE); + g_assert_null (schema); + schema = g_settings_schema_source_lookup (source, "org.gtk.test", TRUE); + g_assert_null (schema); + + /* should still find our own... */ + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", TRUE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + schema = g_settings_schema_source_lookup (source, "org.gtk.schemasourcecheck", FALSE); + g_assert_nonnull (schema); + g_settings_schema_unref (schema); + + g_settings_schema_source_unref (source); + + g_object_unref (backend); +} + static void test_schema_list_keys (void) { @@ -3269,6 +3588,7 @@ main (int argc, char *argv[]) gchar *schema_text; gchar *override_text; gchar *enums; + GResource *loaded_resource; gint result; const KeyfileTestData keyfile_test_data_explicit_path = { "/tests/", "root", "tests", "/" }; const KeyfileTestData keyfile_test_data_empty_path = { "/", "root", "root", "/" }; @@ -3296,6 +3616,7 @@ main (int argc, char *argv[]) GError *local_error = NULL; char *subprocess_stdout = NULL; + gboolean rv; /* A GVDB header is 6 guint32s, and requires a magic number in the first * two guint32s. A set of zero bytes of a greater length is considered * corrupt. */ @@ -3329,6 +3650,10 @@ main (int argc, char *argv[]) g_assert_true (g_file_set_contents ("org.gtk.test.gschema.override", override_text, -1, NULL)); g_free (override_text); + g_assert_true (g_file_get_contents (SRCDIR "/schema_from_resource_test.gresource.xml", &override_text, NULL, NULL)); + g_assert_true (g_file_set_contents ("schema_from_resource_test.gresource.xml", override_text, -1, NULL)); + g_free (override_text); + g_remove ("gschemas.compiled"); /* #GLIB_COMPILE_SCHEMAS is defined in meson.build */ g_assert_true (g_spawn_command_line_sync (GLIB_COMPILE_SCHEMAS " --targetdir=. " @@ -3342,6 +3667,7 @@ main (int argc, char *argv[]) g_assert_cmpint (result, ==, 0); g_remove ("schema-source/gschemas.compiled"); + g_remove ("schema-source/org.gtk.schemasourcecheck.gschema.xml"); g_mkdir ("schema-source", 0777); g_assert_true (g_spawn_command_line_sync (GLIB_COMPILE_SCHEMAS " --targetdir=schema-source " "--schema-file=" SRCDIR "/org.gtk.schemasourcecheck.gschema.xml", @@ -3351,6 +3677,10 @@ main (int argc, char *argv[]) g_clear_pointer (&subprocess_stdout, g_free); g_assert_cmpint (result, ==, 0); + g_assert_true (g_file_get_contents (SRCDIR "/org.gtk.schemasourcecheck.gschema.xml", &override_text, NULL, NULL)); + g_assert_true (g_file_set_contents ("schema-source/org.gtk.schemasourcecheck.gschema.xml", override_text, -1, NULL)); + g_free (override_text); + g_remove ("schema-source-corrupt/gschemas.compiled"); g_mkdir ("schema-source-corrupt", 0777); g_file_set_contents ("schema-source-corrupt/gschemas.compiled", @@ -3365,6 +3695,23 @@ main (int argc, char *argv[]) "", 0, &local_error); g_assert_no_error (local_error); + + /* #GLIB_COMPILE_RESOURCES is defined in meson.build */ + rv = g_spawn_command_line_sync (GLIB_COMPILE_RESOURCES + " schema_from_resource_test.gresource.xml " + "--target=schema_from_resource_test.gresource", + NULL, NULL, &result, &local_error); + g_assert_no_error (local_error); + g_assert_true (rv); + rv = g_spawn_check_wait_status (result, &local_error); + g_assert_no_error (local_error); + g_assert_true (rv); + g_remove ("schema_from_resource_test.gresource.xml"); + + loaded_resource = g_resource_load ("schema_from_resource_test.gresource", &local_error); + g_remove ("schema_from_resource_test.gresource"); + g_assert_no_error (local_error); + g_resources_register (loaded_resource); } g_test_add_func ("/gsettings/basic", test_basic); @@ -3437,7 +3784,10 @@ main (int argc, char *argv[]) g_test_add_func ("/gsettings/list-schemas", test_list_schemas); g_test_add_func ("/gsettings/mapped", test_get_mapped); g_test_add_func ("/gsettings/get-range", test_get_range); - g_test_add_func ("/gsettings/schema-source", test_schema_source); + g_test_add_func ("/gsettings/schema-source/directory", test_schema_source_directory); + g_test_add_func ("/gsettings/schema-source/path", test_schema_source_path); + g_test_add_func ("/gsettings/schema-source/bytes", test_schema_source_bytes); + g_test_add_func ("/gsettings/schema-source/resource", test_schema_source_resource); g_test_add_func ("/gsettings/schema-list-keys", test_schema_list_keys); g_test_add_func ("/gsettings/actions", test_actions); g_test_add_func ("/gsettings/null-backend", test_null_backend); diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 26091b6e7..783807965 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -9,6 +9,7 @@ test_c_args = [ '-DG_LOG_DOMAIN="GLib-GIO"', '-DGLIB_MKENUMS="@0@"'.format(glib_mkenums.full_path()), '-DGLIB_COMPILE_SCHEMAS="@0@"'.format(glib_compile_schemas.full_path()), + '-DGLIB_COMPILE_RESOURCES="@0@"'.format(glib_compile_resources.full_path()), '-UG_DISABLE_ASSERT', ] @@ -631,7 +632,7 @@ if host_machine.system() != 'windows' 'c_args' : ['-DSRCDIR="@0@"'.format(meson.current_source_dir()), '-DTEST_LOCALE_PATH="@0@"'.format(test_mo_dir)], 'install' : false, - 'depends' : glib_compile_schemas, + 'depends' : [glib_compile_schemas, glib_compile_resources], # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/3148 'can_fail' : host_system == 'gnu', }, diff --git a/gio/tests/org.gtk.schemasourcecheck.gschema.xml b/gio/tests/org.gtk.schemasourcecheck.gschema.xml index b484da1a3..e5d37270e 100644 --- a/gio/tests/org.gtk.schemasourcecheck.gschema.xml +++ b/gio/tests/org.gtk.schemasourcecheck.gschema.xml @@ -2,6 +2,13 @@ true + Boolean setting test + + Setting to test boolean settings + + + + true diff --git a/gio/tests/schema_from_resource_test.gresource.xml b/gio/tests/schema_from_resource_test.gresource.xml new file mode 100644 index 000000000..7d7822692 --- /dev/null +++ b/gio/tests/schema_from_resource_test.gresource.xml @@ -0,0 +1,14 @@ + + + schema-source-corrupt/gschemas.compiled + + + schema-source-empty/gschemas.compiled + + + schema-source/gschemas.compiled + + + schema-source/org.gtk.schemasourcecheck.gschema.xml + + diff --git a/subprojects/gvdb b/subprojects/gvdb deleted file mode 160000 index 2b42fc75f..000000000 --- a/subprojects/gvdb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b42fc75f09dbe1cd1057580b5782b08f2dcb400