mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-28 14:31:10 +00:00
testsuite: Add a test that checks for property accessors
Make sure that every object property in GTK has accessors for getting its value (if the property is readable) or setting it (if it is writable). Since we are still missing accessors, the test is allowed to fail for now. Sadly, mesons xfail support is busted, so we just disable the test entirely :( Based on initial work by Benjamin Otte. Related: #2440
This commit is contained in:
parent
60b1b4669e
commit
d0b7a4a4ec
336
testsuite/gtk/accessor-apis.c
Normal file
336
testsuite/gtk/accessor-apis.c
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static struct
|
||||
{
|
||||
const char *expected;
|
||||
const char *alternative;
|
||||
} exceptions[] = {
|
||||
/* keep this sorted please */
|
||||
{ "gdk_device_get_tool", "gdk_device_get_device_tool" },
|
||||
{ "gdk_display_get_input_shapes", "gdk_display_supports_input_shapes" },
|
||||
{ "gtk_constraint_guide_get_max_height", "gtk_constraint_guide_get_max_size" },
|
||||
{ "gtk_constraint_guide_get_max_width", "gtk_constraint_guide_get_max_size" },
|
||||
{ "gtk_constraint_guide_get_min_height", "gtk_constraint_guide_get_min_size" },
|
||||
{ "gtk_constraint_guide_get_min_width", "gtk_constraint_guide_get_min_size" },
|
||||
{ "gtk_constraint_guide_get_nat_height", "gtk_constraint_guide_get_nat_size" },
|
||||
{ "gtk_constraint_guide_get_nat_width", "gtk_constraint_guide_get_nat_size" },
|
||||
{ "gtk_constraint_guide_set_max_height", "gtk_constraint_guide_set_max_size" },
|
||||
{ "gtk_constraint_guide_set_max_width", "gtk_constraint_guide_set_max_size" },
|
||||
{ "gtk_constraint_guide_set_min_height", "gtk_constraint_guide_set_min_size" },
|
||||
{ "gtk_constraint_guide_set_min_width", "gtk_constraint_guide_set_min_size" },
|
||||
{ "gtk_constraint_guide_set_nat_height", "gtk_constraint_guide_set_nat_size" },
|
||||
{ "gtk_constraint_guide_set_nat_width", "gtk_constraint_guide_set_nat_size" },
|
||||
{ "gtk_tree_view_get_enable_grid_lines", "gtk_tree_view_get_grid_lines" },
|
||||
{ "gtk_tree_view_set_enable_grid_lines", "gtk_tree_view_set_grid_lines" },
|
||||
{ "gtk_widget_get_height_request", "gtk_widget_get_size_request" },
|
||||
{ "gtk_widget_get_width_request", "gtk_widget_get_size_request" },
|
||||
{ "gtk_widget_set_height_request", "gtk_widget_set_size_request" },
|
||||
{ "gtk_widget_set_width_request", "gtk_widget_set_size_request" },
|
||||
{ "gtk_window_get_default_height", "gtk_window_get_default_size" },
|
||||
{ "gtk_window_get_default_width", "gtk_window_get_default_size" },
|
||||
{ "gtk_window_set_default_height", "gtk_window_set_default_size" },
|
||||
{ "gtk_window_set_default_width", "gtk_window_set_default_size" },
|
||||
{ "gtk_window_get_display", "gtk_widget_get_display" },
|
||||
{ "gtk_window_get_focus_widget", "gtk_window_get_focus" },
|
||||
{ "gtk_window_set_focus_widget", "gtk_window_set_focus" },
|
||||
};
|
||||
|
||||
static const char *type_exceptions[] = {
|
||||
"GtkCellRenderer",
|
||||
"GtkSettings",
|
||||
"GtkTextTag",
|
||||
};
|
||||
|
||||
static GModule *module;
|
||||
|
||||
static gboolean
|
||||
function_exists (const char *function_name)
|
||||
{
|
||||
gpointer func;
|
||||
guint i;
|
||||
|
||||
if (g_module_symbol (module, function_name, &func) && func)
|
||||
return TRUE;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(exceptions); i++)
|
||||
{
|
||||
if (g_str_equal (function_name, exceptions[i].expected))
|
||||
{
|
||||
if (exceptions[i].alternative)
|
||||
return function_exists (exceptions[i].alternative);
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Keep in sync with gtkbuilder.c ! */
|
||||
static char *
|
||||
type_name_mangle (const char *name,
|
||||
gboolean split_first_cap)
|
||||
{
|
||||
GString *symbol_name = g_string_new ("");
|
||||
gint i;
|
||||
|
||||
for (i = 0; name[i] != '\0'; i++)
|
||||
{
|
||||
/* skip if uppercase, first or previous is uppercase */
|
||||
if ((name[i] == g_ascii_toupper (name[i]) &&
|
||||
((i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
|
||||
(i == 1 && name[0] == g_ascii_toupper (name[0]) && split_first_cap))) ||
|
||||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
|
||||
name[i-1] == g_ascii_toupper (name[i-1]) &&
|
||||
name[i-2] == g_ascii_toupper (name[i-2])))
|
||||
g_string_append_c (symbol_name, '_');
|
||||
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
|
||||
}
|
||||
|
||||
return g_string_free (symbol_name, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
property_name_mangle (GString *symbol_name,
|
||||
const char *name)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; name[i] != '\0'; i++)
|
||||
{
|
||||
if (g_ascii_isalnum (name[i]))
|
||||
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
|
||||
else
|
||||
g_string_append_c (symbol_name, '_');
|
||||
}
|
||||
}
|
||||
|
||||
const char *getters[] = { "get", "is", "ref" };
|
||||
const char *setters[] = { "set" };
|
||||
|
||||
static char **
|
||||
get_potential_names (GType type,
|
||||
gboolean get,
|
||||
const char *property_name)
|
||||
{
|
||||
GPtrArray *options;
|
||||
char *type_name_options[2];
|
||||
guint n_type_name_options, n_verbs;
|
||||
const char **verbs;
|
||||
guint i, j;
|
||||
|
||||
if (get)
|
||||
{
|
||||
verbs = getters;
|
||||
n_verbs = G_N_ELEMENTS (getters);
|
||||
}
|
||||
else
|
||||
{
|
||||
verbs = setters;
|
||||
n_verbs = G_N_ELEMENTS (setters);
|
||||
}
|
||||
|
||||
type_name_options[0] = type_name_mangle (g_type_name (type), FALSE);
|
||||
type_name_options[1] = type_name_mangle (g_type_name (type), TRUE);
|
||||
if (g_str_equal (type_name_options[0], type_name_options[1]))
|
||||
n_type_name_options = 1;
|
||||
else
|
||||
n_type_name_options = 2;
|
||||
|
||||
options = g_ptr_array_new ();
|
||||
|
||||
for (i = 0; i < n_type_name_options; i++)
|
||||
{
|
||||
for (j = 0; j < n_verbs; j++)
|
||||
{
|
||||
GString *str;
|
||||
|
||||
str = g_string_new (type_name_options[i]);
|
||||
g_string_append_c (str, '_');
|
||||
g_string_append (str, verbs[j]);
|
||||
g_string_append_c (str, '_');
|
||||
property_name_mangle (str, property_name);
|
||||
|
||||
g_ptr_array_add (options, g_string_free (str, FALSE));
|
||||
}
|
||||
|
||||
if (g_str_has_prefix (property_name, "is-") ||
|
||||
g_str_has_prefix (property_name, "has-") ||
|
||||
g_str_has_prefix (property_name, "contains-"))
|
||||
{
|
||||
GString *str;
|
||||
|
||||
/* try without a verb */
|
||||
str = g_string_new (type_name_options[i]);
|
||||
g_string_append_c (str, '_');
|
||||
property_name_mangle (str, property_name);
|
||||
|
||||
g_ptr_array_add (options, g_string_free (str, FALSE));
|
||||
}
|
||||
}
|
||||
|
||||
g_free (type_name_options[0]);
|
||||
g_free (type_name_options[1]);
|
||||
|
||||
g_ptr_array_add (options, NULL);
|
||||
|
||||
return (char **) g_ptr_array_free (options, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
check_function_name (GType type,
|
||||
gboolean get,
|
||||
const char *property_name)
|
||||
{
|
||||
guint i;
|
||||
char **names;
|
||||
|
||||
names = get_potential_names (type, get, property_name);
|
||||
|
||||
for (i = 0; names[i] != NULL; i++)
|
||||
{
|
||||
if (function_exists (names[i]))
|
||||
{
|
||||
g_strfreev (names);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_test_message ("No %s for property %s::%s", get ? "getter" : "setter", g_type_name (type), property_name);
|
||||
for (i = 0; names[i] != NULL; i++)
|
||||
{
|
||||
g_test_message (" %s", names[i]);
|
||||
}
|
||||
|
||||
g_test_fail ();
|
||||
|
||||
g_strfreev (names);
|
||||
}
|
||||
|
||||
static void
|
||||
check_property (GParamSpec *pspec)
|
||||
{
|
||||
if (pspec->flags & G_PARAM_READABLE)
|
||||
{
|
||||
check_function_name (pspec->owner_type,
|
||||
TRUE,
|
||||
pspec->name);
|
||||
}
|
||||
if (pspec->flags & G_PARAM_WRITABLE &&
|
||||
!(pspec->flags & G_PARAM_CONSTRUCT_ONLY))
|
||||
{
|
||||
check_function_name (pspec->owner_type,
|
||||
FALSE,
|
||||
pspec->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_accessors (gconstpointer data)
|
||||
{
|
||||
GType type = GPOINTER_TO_SIZE (data);
|
||||
GObjectClass *klass;
|
||||
GParamSpec **pspecs;
|
||||
guint i, n_pspecs;
|
||||
|
||||
klass = g_type_class_ref (type);
|
||||
pspecs = g_object_class_list_properties (klass, &n_pspecs);
|
||||
|
||||
for (i = 0; i < n_pspecs; ++i)
|
||||
{
|
||||
GParamSpec *pspec = pspecs[i];
|
||||
|
||||
if (pspec->owner_type != type)
|
||||
continue;
|
||||
|
||||
check_property (pspec);
|
||||
}
|
||||
|
||||
g_free (pspecs);
|
||||
g_type_class_unref (klass);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
type_is_whitelisted (GType type)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (!G_TYPE_IS_INSTANTIATABLE (type) ||
|
||||
!g_type_is_a (type, G_TYPE_OBJECT))
|
||||
return TRUE;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (type_exceptions); i++)
|
||||
{
|
||||
GType exception = g_type_from_name (type_exceptions[i]);
|
||||
|
||||
/* type hasn't been registered yet */
|
||||
if (exception == G_TYPE_INVALID)
|
||||
continue;
|
||||
|
||||
if (g_type_is_a (type, exception))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
const GType *all_types;
|
||||
guint n_types = 0, i;
|
||||
gint result;
|
||||
|
||||
/* These must be set before before gtk_test_init */
|
||||
g_setenv ("GIO_USE_VFS", "local", TRUE);
|
||||
g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
|
||||
|
||||
/* initialize test program */
|
||||
gtk_test_init (&argc, &argv);
|
||||
gtk_test_register_all_types ();
|
||||
|
||||
module = g_module_open (NULL, G_MODULE_BIND_LAZY);
|
||||
|
||||
all_types = gtk_test_list_all_types (&n_types);
|
||||
|
||||
for (i = 0; i < n_types; i++)
|
||||
{
|
||||
char *test_path;
|
||||
|
||||
if (type_is_whitelisted (all_types[i]))
|
||||
continue;
|
||||
|
||||
test_path = g_strdup_printf ("/accessor-apis/%s", g_type_name (all_types[i]));
|
||||
|
||||
g_test_add_data_func (test_path, GSIZE_TO_POINTER (all_types[i]), test_accessors);
|
||||
|
||||
g_free (test_path);
|
||||
}
|
||||
|
||||
result = g_test_run();
|
||||
|
||||
g_module_close (module);
|
||||
|
||||
return result;
|
||||
}
|
@ -21,6 +21,8 @@ endif
|
||||
# - 'suites': (array): additional test suites
|
||||
tests = [
|
||||
{ 'name': 'accel' },
|
||||
# sadly, mesons xfail support seems busted
|
||||
# { 'name': 'accessor-apis' },
|
||||
{ 'name': 'action' },
|
||||
{ 'name': 'adjustment' },
|
||||
{ 'name': 'bitset' },
|
||||
@ -132,6 +134,8 @@ tests = [
|
||||
|
||||
# Tests that are expected to fail
|
||||
xfail = [
|
||||
# we are still missing some accessors
|
||||
'accessor-apis',
|
||||
# one of the window resizing tests fails after
|
||||
# the GdkToplevel refactoring, and needs a big
|
||||
# gtkwindow.c configure request cleanup
|
||||
|
Loading…
Reference in New Issue
Block a user