gnome-shell/src/st/test-theme.c
Florian Müllner f19c75d9c3 st: Test for :insensitive styling
Commit 473e77e2c5 fixed applying the :insensitive pseudo class to
initially unreactive widgets, and adjusted the style test to work
with that.

In hindsight, we can do better than just making the test work, and
include a test case for the :insensitive styling as well (namely
the issue the previous commit was fixing).

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1649>
2021-02-08 15:41:08 +01:00

630 lines
21 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* test-theme.c: test program for CSS styling code
*
* Copyright 2009, 2010 Red Hat, Inc.
*
* This program 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 program is distributed in the hope 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <clutter/clutter.h>
#include "st-theme.h"
#include "st-theme-context.h"
#include "st-label.h"
#include "st-button.h"
#include <math.h>
#include <string.h>
#include <meta/main.h>
#include <meta/meta-backend.h>
static ClutterActor *stage;
static StThemeNode *root;
static StThemeNode *group1;
static StThemeNode *text1;
static StThemeNode *text2;
static StThemeNode *group2;
static StThemeNode *text3;
static StThemeNode *text4;
static StThemeNode *group3;
static StThemeNode *group4;
static StThemeNode *group5;
static StThemeNode *group6;
static StThemeNode *button;
static gboolean fail;
static const char *test;
static void
assert_font (StThemeNode *node,
const char *node_description,
const char *expected)
{
char *value = pango_font_description_to_string (st_theme_node_get_font (node));
if (strcmp (expected, value) != 0)
{
g_print ("%s: %s.font: expected: %s, got: %s\n",
test, node_description, expected, value);
fail = TRUE;
}
g_free (value);
}
static void
assert_font_features (StThemeNode *node,
const char *node_description,
const char *expected)
{
char *value = st_theme_node_get_font_features (node);
if (g_strcmp0 (expected, value) != 0)
{
g_print ("%s: %s.font-feature-settings: expected: %s, got: %s\n",
test, node_description, expected, value);
fail = TRUE;
}
if (value)
g_free (value);
}
static char *
text_decoration_to_string (StTextDecoration decoration)
{
GString *result = g_string_new (NULL);
if (decoration & ST_TEXT_DECORATION_UNDERLINE)
g_string_append(result, " underline");
if (decoration & ST_TEXT_DECORATION_OVERLINE)
g_string_append(result, " overline");
if (decoration & ST_TEXT_DECORATION_LINE_THROUGH)
g_string_append(result, " line_through");
if (decoration & ST_TEXT_DECORATION_BLINK)
g_string_append(result, " blink");
if (result->len > 0)
g_string_erase (result, 0, 1);
else
g_string_append(result, "none");
return g_string_free (result, FALSE);
}
static void
assert_text_decoration (StThemeNode *node,
const char *node_description,
StTextDecoration expected)
{
StTextDecoration value = st_theme_node_get_text_decoration (node);
if (expected != value)
{
char *es = text_decoration_to_string (expected);
char *vs = text_decoration_to_string (value);
g_print ("%s: %s.text-decoration: expected: %s, got: %s\n",
test, node_description, es, vs);
fail = TRUE;
g_free (es);
g_free (vs);
}
}
static void
assert_foreground_color (StThemeNode *node,
const char *node_description,
guint32 expected)
{
ClutterColor color;
guint32 value;
st_theme_node_get_foreground_color (node, &color);
value = clutter_color_to_pixel (&color);
if (expected != value)
{
g_print ("%s: %s.color: expected: #%08x, got: #%08x\n",
test, node_description, expected, value);
fail = TRUE;
}
}
static void
assert_background_color (StThemeNode *node,
const char *node_description,
guint32 expected)
{
ClutterColor color;
guint32 value;
st_theme_node_get_background_color (node, &color);
value = clutter_color_to_pixel (&color);
if (expected != value)
{
g_print ("%s: %s.background-color: expected: #%08x, got: #%08x\n",
test, node_description, expected, value);
fail = TRUE;
}
}
static const char *
side_to_string (StSide side)
{
switch (side)
{
case ST_SIDE_TOP:
return "top";
case ST_SIDE_RIGHT:
return "right";
case ST_SIDE_BOTTOM:
return "bottom";
case ST_SIDE_LEFT:
return "left";
default:
return "<unknown>";
}
}
static void
assert_border_color (StThemeNode *node,
const char *node_description,
StSide side,
guint32 expected)
{
ClutterColor color;
guint32 value;
st_theme_node_get_border_color (node, side, &color);
value = clutter_color_to_pixel (&color);
if (expected != value)
{
g_print ("%s: %s.border-%s-color: expected: #%08x, got: #%08x\n",
test, node_description, side_to_string (side), expected, value);
fail = TRUE;
}
}
static void
assert_background_image (StThemeNode *node,
const char *node_description,
const char *expected)
{
GFile *value = st_theme_node_get_background_image (node);
GFile *expected_file;
if (expected != NULL && value != NULL)
{
expected_file = g_file_new_for_path (expected);
if (!g_file_equal (expected_file, value))
{
char *uri = g_file_get_uri (expected_file);
g_print ("%s: %s.background-image: expected: %s, got: %s\n",
test, node_description, expected, uri);
fail = TRUE;
g_free (uri);
}
}
}
#define LENGTH_EPSILON 0.001
static void
assert_length (const char *node_description,
const char *property_description,
double expected,
double value)
{
if (fabs (expected - value) > LENGTH_EPSILON)
{
g_print ("%s %s.%s: expected: %3f, got: %3f\n",
test, node_description, property_description, expected, value);
fail = TRUE;
}
}
static void
test_defaults (void)
{
test = "defaults";
/* font comes from context */
assert_font (root, "stage", "sans-serif 12");
/* black is the default foreground color */
assert_foreground_color (root, "stage", 0x00000ff);
}
static void
test_lengths (void)
{
test = "lengths";
/* 12pt == 16px at 96dpi */
assert_length ("group1", "padding-top", 16.,
st_theme_node_get_padding (group1, ST_SIDE_TOP));
/* 12px == 12px */
assert_length ("group1", "padding-right", 12.,
st_theme_node_get_padding (group1, ST_SIDE_RIGHT));
/* 2em == 32px (with a 12pt font) */
assert_length ("group1", "padding-bottom", 32.,
st_theme_node_get_padding (group1, ST_SIDE_BOTTOM));
/* 1in == 72pt == 96px, at 96dpi */
assert_length ("group1", "padding-left", 96.,
st_theme_node_get_padding (group1, ST_SIDE_LEFT));
/* 12pt == 16px at 96dpi */
assert_length ("group1", "margin-top", 16.,
st_theme_node_get_margin (group1, ST_SIDE_TOP));
/* 12px == 12px */
assert_length ("group1", "margin-right", 12.,
st_theme_node_get_margin (group1, ST_SIDE_RIGHT));
/* 2em == 32px (with a 12pt font) */
assert_length ("group1", "margin-bottom", 32.,
st_theme_node_get_margin (group1, ST_SIDE_BOTTOM));
/* 1in == 72pt == 96px, at 96dpi */
assert_length ("group1", "margin-left", 96.,
st_theme_node_get_margin (group1, ST_SIDE_LEFT));
}
static void
test_classes (void)
{
test = "classes";
/* .special-text class overrides size and style;
* the StBin.special-text selector doesn't match */
assert_font (text1, "text1", "sans-serif Italic 32px");
}
static void
test_type_inheritance (void)
{
test = "type_inheritance";
/* From StBin element selector */
assert_length ("button", "padding-top", 10.,
st_theme_node_get_padding (button, ST_SIDE_TOP));
/* From StButton element selector */
assert_length ("button", "padding-right", 20.,
st_theme_node_get_padding (button, ST_SIDE_RIGHT));
}
static void
test_adjacent_selector (void)
{
test = "adjacent_selector";
/* #group1 > #text1 matches text1 */
assert_foreground_color (text1, "text1", 0x00ff00ff);
/* stage > #text2 doesn't match text2 */
assert_foreground_color (text2, "text2", 0x000000ff);
}
static void
test_padding (void)
{
test = "padding";
/* Test that a 4-sided padding property assigns the right paddings to
* all sides */
assert_length ("group2", "padding-top", 1.,
st_theme_node_get_padding (group2, ST_SIDE_TOP));
assert_length ("group2", "padding-right", 2.,
st_theme_node_get_padding (group2, ST_SIDE_RIGHT));
assert_length ("group2", "padding-bottom", 3.,
st_theme_node_get_padding (group2, ST_SIDE_BOTTOM));
assert_length ("group2", "padding-left", 4.,
st_theme_node_get_padding (group2, ST_SIDE_LEFT));
}
static void
test_margin (void)
{
test = "margin";
/* Test that a 4-sided margin property assigns the right margin to
* all sides */
assert_length ("group2", "margin-top", 1.,
st_theme_node_get_margin (group2, ST_SIDE_TOP));
assert_length ("group2", "margin-right", 2.,
st_theme_node_get_margin (group2, ST_SIDE_RIGHT));
assert_length ("group2", "margin-bottom", 3.,
st_theme_node_get_margin (group2, ST_SIDE_BOTTOM));
assert_length ("group2", "margin-left", 4.,
st_theme_node_get_margin (group2, ST_SIDE_LEFT));
/* Test that a 3-sided margin property assigns the right margin to
* all sides */
assert_length ("group4", "margin-top", 1.,
st_theme_node_get_margin (group4, ST_SIDE_TOP));
assert_length ("group4", "margin-right", 2.,
st_theme_node_get_margin (group4, ST_SIDE_RIGHT));
assert_length ("group4", "margin-bottom", 3.,
st_theme_node_get_margin (group4, ST_SIDE_BOTTOM));
assert_length ("group4", "margin-left", 2.,
st_theme_node_get_margin (group4, ST_SIDE_LEFT));
/* Test that a 2-sided margin property assigns the right margin to
* all sides */
assert_length ("group5", "margin-top", 1.,
st_theme_node_get_margin (group5, ST_SIDE_TOP));
assert_length ("group5", "margin-right", 2.,
st_theme_node_get_margin (group5, ST_SIDE_RIGHT));
assert_length ("group5", "margin-bottom", 1.,
st_theme_node_get_margin (group5, ST_SIDE_BOTTOM));
assert_length ("group5", "margin-left", 2.,
st_theme_node_get_margin (group5, ST_SIDE_LEFT));
/* Test that all sides have a margin of 0 when not specified */
assert_length ("group6", "margin-top", 0.,
st_theme_node_get_margin (group6, ST_SIDE_TOP));
assert_length ("group6", "margin-right", 0.,
st_theme_node_get_margin (group6, ST_SIDE_RIGHT));
assert_length ("group6", "margin-bottom", 0.,
st_theme_node_get_margin (group6, ST_SIDE_BOTTOM));
assert_length ("group6", "margin-left", 0.,
st_theme_node_get_margin (group6, ST_SIDE_LEFT));
}
static void
test_border (void)
{
test = "border";
/* group2 is defined as having a thin black border along the top three
* sides with rounded joins, then a square-joined green border at the
* bottom
*/
assert_length ("group2", "border-top-width", 2.,
st_theme_node_get_border_width (group2, ST_SIDE_TOP));
assert_length ("group2", "border-right-width", 2.,
st_theme_node_get_border_width (group2, ST_SIDE_RIGHT));
assert_length ("group2", "border-bottom-width", 5.,
st_theme_node_get_border_width (group2, ST_SIDE_BOTTOM));
assert_length ("group2", "border-left-width", 2.,
st_theme_node_get_border_width (group2, ST_SIDE_LEFT));
assert_border_color (group2, "group2", ST_SIDE_TOP, 0x000000ff);
assert_border_color (group2, "group2", ST_SIDE_RIGHT, 0x000000ff);
assert_border_color (group2, "group2", ST_SIDE_BOTTOM, 0x0000ffff);
assert_border_color (group2, "group2", ST_SIDE_LEFT, 0x000000ff);
assert_length ("group2", "border-radius-topleft", 10.,
st_theme_node_get_border_radius (group2, ST_CORNER_TOPLEFT));
assert_length ("group2", "border-radius-topright", 10.,
st_theme_node_get_border_radius (group2, ST_CORNER_TOPRIGHT));
assert_length ("group2", "border-radius-bottomright", 0.,
st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMRIGHT));
assert_length ("group2", "border-radius-bottomleft", 0.,
st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMLEFT));
}
static void
test_background (void)
{
test = "background";
/* group1 has a background: shortcut property setting color and image */
assert_background_color (group1, "group1", 0xff0000ff);
assert_background_image (group1, "group1", "some-background.png");
/* text1 inherits the background image but not the color */
assert_background_color (text1, "text1", 0x00000000);
assert_background_image (text1, "text1", "some-background.png");
/* text2 inherits both, but then background: none overrides both */
assert_background_color (text2, "text2", 0x00000000);
assert_background_image (text2, "text2", NULL);
/* background-image property */
assert_background_image (group2, "group2", "other-background.png");
}
static void
test_font (void)
{
test = "font";
/* font specified with font: */
assert_font (group2, "group2", "serif Italic 12px");
/* text3 inherits and overrides individually properties */
assert_font (text3, "text3", "serif Bold Oblique Small-Caps 24px");
}
static void
test_font_features (void)
{
test = "font_features";
/* group1 has font-feature-settings: "tnum" */
assert_font_features (group1, "group1", "\"tnum\"");
/* text2 should inherit from group1 */
assert_font_features (text2, "text2", "\"tnum\"");
/* group2 has font-feature-settings: "tnum", "zero" */
assert_font_features (group2, "group2", "\"tnum\", \"zero\"");
/* text3 should inherit from group2 using the inherit keyword */
assert_font_features (text3, "text3", "\"tnum\", \"zero\"");
/* text4 has font-feature-settings: normal */
assert_font_features (text4, "text4", NULL);
}
static void
test_pseudo_class (void)
{
StWidget *label;
StThemeNode *labelNode;
test = "pseudo_class";
/* text4 has :visited and :hover pseudo-classes, so should pick up both of these */
assert_foreground_color (text4, "text4", 0x888888ff);
assert_text_decoration (text4, "text4", ST_TEXT_DECORATION_UNDERLINE);
/* :hover pseudo-class matches, but class doesn't match */
assert_text_decoration (group3, "group3", 0);
/* Test the StWidget add/remove pseudo_class interfaces */
label = st_label_new ("foo");
clutter_actor_add_child (stage, CLUTTER_ACTOR (label));
labelNode = st_widget_get_theme_node (label);
assert_foreground_color (labelNode, "label", 0x000000ff);
assert_text_decoration (labelNode, "label", 0);
assert_length ("label", "border-width", 0.,
st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
st_widget_add_style_pseudo_class (label, "visited");
g_assert (st_widget_has_style_pseudo_class (label, "visited"));
labelNode = st_widget_get_theme_node (label);
assert_foreground_color (labelNode, "label", 0x888888ff);
assert_text_decoration (labelNode, "label", 0);
assert_length ("label", "border-width", 0.,
st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
st_widget_add_style_pseudo_class (label, "hover");
g_assert (st_widget_has_style_pseudo_class (label, "hover"));
labelNode = st_widget_get_theme_node (label);
assert_foreground_color (labelNode, "label", 0x888888ff);
assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
assert_length ("label", "border-width", 0.,
st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
st_widget_remove_style_pseudo_class (label, "visited");
g_assert (!st_widget_has_style_pseudo_class (label, "visited"));
g_assert (st_widget_has_style_pseudo_class (label, "hover"));
labelNode = st_widget_get_theme_node (label);
assert_foreground_color (labelNode, "label", 0x000000ff);
assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
assert_length ("label", "border-width", 0.,
st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
st_widget_add_style_pseudo_class (label, "boxed");
labelNode = st_widget_get_theme_node (label);
assert_foreground_color (labelNode, "label", 0x000000ff);
assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
assert_length ("label", "border-width", 1.,
st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
st_widget_remove_style_pseudo_class (label, "hover");
labelNode = st_widget_get_theme_node (label);
assert_foreground_color (labelNode, "label", 0x000000ff);
assert_text_decoration (labelNode, "label", 0);
assert_length ("label", "border-width", 1.,
st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
st_widget_remove_style_pseudo_class (label, "boxed");
g_assert (!st_widget_has_style_pseudo_class (label, "boxed"));
g_assert (st_widget_has_style_pseudo_class (label, "insensitive"));
labelNode = st_widget_get_theme_node (label);
assert_foreground_color (labelNode, "label", 0x000000ff);
assert_text_decoration (labelNode, "label", 0);
assert_length ("label", "border-width", 0.,
st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
clutter_actor_set_reactive (CLUTTER_ACTOR (label), TRUE);
g_assert (st_widget_get_style_pseudo_class (label) == NULL);
}
static void
test_inline_style (void)
{
test = "inline_style";
/* These properties come from the inline-style specified when creating the node */
assert_foreground_color (text3, "text3", 0x00000ffff);
assert_length ("text3", "padding-bottom", 12.,
st_theme_node_get_padding (text3, ST_SIDE_BOTTOM));
}
int
main (int argc, char **argv)
{
MetaBackend *backend;
StTheme *theme;
StThemeContext *context;
PangoFontDescription *font_desc;
GFile *file;
g_autofree char *cwd = NULL;
gtk_init (&argc, &argv);
/* meta_init() cds to $HOME */
cwd = g_get_current_dir ();
meta_test_init ();
if (chdir (cwd) < 0)
g_error ("chdir('%s') failed: %s", cwd, g_strerror (errno));
/* Make sure our assumptions about resolution are correct */
g_object_set (clutter_settings_get_default (), "font-dpi", -1, NULL);
file = g_file_new_for_path ("test-theme.css");
theme = st_theme_new (file, NULL, NULL);
g_object_unref (file);
backend = meta_get_backend ();
stage = meta_backend_get_stage (backend);
context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage));
st_theme_context_set_theme (context, theme);
font_desc = pango_font_description_from_string ("sans-serif 12");
st_theme_context_set_font (context, font_desc);
pango_font_description_free (font_desc);
root = st_theme_context_get_root_node (context);
group1 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_ACTOR, "group1", NULL, NULL, NULL);
text1 = st_theme_node_new (context, group1, NULL,
CLUTTER_TYPE_TEXT, "text1", "special-text", NULL, NULL);
text2 = st_theme_node_new (context, group1, NULL,
CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
group2 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_ACTOR, "group2", NULL, NULL, NULL);
group4 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_ACTOR, "group4", NULL, NULL, NULL);
group5 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_ACTOR, "group5", NULL, NULL, NULL);
group6 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_ACTOR, "group6", NULL, NULL, NULL);
text3 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
"color: #0000ff; padding-bottom: 12px;");
text4 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover", NULL);
group3 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_ACTOR, "group3", NULL, "hover", NULL);
button = st_theme_node_new (context, root, NULL,
ST_TYPE_BUTTON, "button", NULL, NULL, NULL);
test_defaults ();
test_lengths ();
test_classes ();
test_type_inheritance ();
test_adjacent_selector ();
test_padding ();
test_margin ();
test_border ();
test_background ();
test_font ();
test_font_features ();
test_pseudo_class ();
test_inline_style ();
g_object_unref (button);
g_object_unref (group1);
g_object_unref (group2);
g_object_unref (group3);
g_object_unref (group4);
g_object_unref (group5);
g_object_unref (group6);
g_object_unref (text1);
g_object_unref (text2);
g_object_unref (text3);
g_object_unref (text4);
g_object_unref (theme);
clutter_actor_destroy (stage);
return fail ? 1 : 0;
}