/* -*- 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 . */ #include #include "st-theme.h" #include "st-theme-context.h" #include "st-label.h" #include #include 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 *cairo_texture; 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 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; st_theme_node_get_foreground_color (node, &color); guint32 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; st_theme_node_get_background_color (node, &color); guint32 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"; } return ""; } static void assert_border_color (StThemeNode *node, const char *node_description, StSide side, guint32 expected) { ClutterColor color; st_theme_node_get_border_color (node, side, &color); guint32 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) { const char *value = st_theme_node_get_background_image (node); if (expected == NULL) expected = "(null)"; if (value == NULL) value = "(null)"; if (strcmp (expected, value) != 0) { g_print ("%s: %s.background-image: expected: %s, got: %s\n", test, node_description, expected, value); fail = TRUE; } } #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)); } static void test_classes (void) { test = "classes"; /* .special-text class overrides size and style; * the ClutterTexture.special-text selector doesn't match */ assert_font (text1, "text1", "sans-serif Italic 32px"); } static void test_type_inheritance (void) { test = "type_inheritance"; /* From ClutterTexture element selector */ assert_length ("cairoTexture", "padding-top", 10., st_theme_node_get_padding (cairo_texture, ST_SIDE_TOP)); /* From ClutterCairoTexture element selector */ assert_length ("cairoTexture", "padding-right", 20., st_theme_node_get_padding (cairo_texture, 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_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 * botttom */ 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", "st/some-background.png"); /* text1 inherits the background image but not the color */ assert_background_color (text1, "text1", 0x00000000); assert_background_image (text1, "text1", "st/some-background.png"); /* text1 inherits 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", "st/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_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_get_style_pseudo_class (label) == NULL); 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)); } 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) { StTheme *theme; StThemeContext *context; PangoFontDescription *font_desc; gtk_init (&argc, &argv); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; theme = st_theme_new ("st/test-theme.css", NULL, NULL); stage = clutter_stage_new (); 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_GROUP, "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_GROUP, "group2", 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_GROUP, "group3", NULL, "hover", NULL); cairo_texture = st_theme_node_new (context, root, NULL, CLUTTER_TYPE_CAIRO_TEXTURE, "cairoTexture", NULL, NULL, NULL); test_defaults (); test_lengths (); test_classes (); test_type_inheritance (); test_adjacent_selector (); test_padding (); test_border (); test_background (); test_font (); test_pseudo_class (); test_inline_style (); g_object_unref (cairo_texture); g_object_unref (group1); g_object_unref (group2); g_object_unref (group3); 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; }