1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * test-theme.c: test program for CSS styling code
4 *
5 * Copyright 2009, 2010 Red Hat, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <clutter/clutter.h>
22 #include "st-theme.h"
23 #include "st-theme-context.h"
24 #include "st-label.h"
25 #include <math.h>
26 #include <string.h>
27
28 static ClutterActor *stage;
29 static StThemeNode *root;
30 static StThemeNode *group1;
31 static StThemeNode *text1;
32 static StThemeNode *text2;
33 static StThemeNode *group2;
34 static StThemeNode *text3;
35 static StThemeNode *text4;
36 static StThemeNode *group3;
37 static StThemeNode *cairo_texture;
38 static gboolean fail;
39
40 static const char *test;
41
42 static void
43 assert_font (StThemeNode *node,
44 const char *node_description,
45 const char *expected)
46 {
47 char *value = pango_font_description_to_string (st_theme_node_get_font (node));
48
49 if (strcmp (expected, value) != 0)
50 {
51 g_print ("%s: %s.font: expected: %s, got: %s\n",
52 test, node_description, expected, value);
53 fail = TRUE;
54 }
55
56 g_free (value);
57 }
58
59 static char *
60 text_decoration_to_string (StTextDecoration decoration)
61 {
62 GString *result = g_string_new (NULL);
63
64 if (decoration & ST_TEXT_DECORATION_UNDERLINE)
65 g_string_append(result, " underline");
66 if (decoration & ST_TEXT_DECORATION_OVERLINE)
67 g_string_append(result, " overline");
68 if (decoration & ST_TEXT_DECORATION_LINE_THROUGH)
69 g_string_append(result, " line_through");
70 if (decoration & ST_TEXT_DECORATION_BLINK)
71 g_string_append(result, " blink");
72
73 if (result->len > 0)
74 g_string_erase (result, 0, 1);
75 else
76 g_string_append(result, "none");
77
78 return g_string_free (result, FALSE);
79 }
80
81 static void
82 assert_text_decoration (StThemeNode *node,
83 const char *node_description,
84 StTextDecoration expected)
85 {
86 StTextDecoration value = st_theme_node_get_text_decoration (node);
87 if (expected != value)
88 {
89 char *es = text_decoration_to_string (expected);
90 char *vs = text_decoration_to_string (value);
91
92 g_print ("%s: %s.text-decoration: expected: %s, got: %s\n",
93 test, node_description, es, vs);
94 fail = TRUE;
95
96 g_free (es);
97 g_free (vs);
98 }
99 }
100
101 static void
102 assert_foreground_color (StThemeNode *node,
103 const char *node_description,
104 guint32 expected)
105 {
106 ClutterColor color;
107 st_theme_node_get_foreground_color (node, &color);
108 guint32 value = clutter_color_to_pixel (&color);
109
110 if (expected != value)
111 {
112 g_print ("%s: %s.color: expected: #%08x, got: #%08x\n",
113 test, node_description, expected, value);
114 fail = TRUE;
115 }
116 }
117
118 static void
119 assert_background_color (StThemeNode *node,
120 const char *node_description,
121 guint32 expected)
122 {
123 ClutterColor color;
124 st_theme_node_get_background_color (node, &color);
125 guint32 value = clutter_color_to_pixel (&color);
126
127 if (expected != value)
128 {
129 g_print ("%s: %s.background-color: expected: #%08x, got: #%08x\n",
130 test, node_description, expected, value);
131 fail = TRUE;
132 }
133 }
134
135 static const char *
136 side_to_string (StSide side)
137 {
138 switch (side)
139 {
140 case ST_SIDE_TOP:
141 return "top";
142 case ST_SIDE_RIGHT:
143 return "right";
144 case ST_SIDE_BOTTOM:
145 return "bottom";
146 case ST_SIDE_LEFT:
147 return "left";
148 }
149
150 return "<unknown>";
151 }
152
153 static void
154 assert_border_color (StThemeNode *node,
155 const char *node_description,
156 StSide side,
157 guint32 expected)
158 {
159 ClutterColor color;
160 st_theme_node_get_border_color (node, side, &color);
161 guint32 value = clutter_color_to_pixel (&color);
162
163 if (expected != value)
164 {
165 g_print ("%s: %s.border-%s-color: expected: #%08x, got: #%08x\n",
166 test, node_description, side_to_string (side), expected, value);
167 fail = TRUE;
168 }
169 }
170
171 static void
172 assert_background_image (StThemeNode *node,
173 const char *node_description,
174 const char *expected)
175 {
176 const char *value = st_theme_node_get_background_image (node);
177 if (expected == NULL)
178 expected = "(null)";
179 if (value == NULL)
180 value = "(null)";
181
182 if (strcmp (expected, value) != 0)
183 {
184 g_print ("%s: %s.background-image: expected: %s, got: %s\n",
185 test, node_description, expected, value);
186 fail = TRUE;
187 }
188 }
189
190 #define LENGTH_EPSILON 0.001
191
192 static void
193 assert_length (const char *node_description,
194 const char *property_description,
195 double expected,
196 double value)
197 {
198 if (fabs (expected - value) > LENGTH_EPSILON)
199 {
200 g_print ("%s %s.%s: expected: %3f, got: %3f\n",
201 test, node_description, property_description, expected, value);
202 fail = TRUE;
203 }
204 }
205
206 static void
207 test_defaults (void)
208 {
209 test = "defaults";
210 /* font comes from context */
211 assert_font (root, "stage", "sans-serif 12");
212 /* black is the default foreground color */
213 assert_foreground_color (root, "stage", 0x00000ff);
214 }
215
216 static void
217 test_lengths (void)
218 {
219 test = "lengths";
220 /* 12pt == 16px at 96dpi */
221 assert_length ("group1", "padding-top", 16.,
222 st_theme_node_get_padding (group1, ST_SIDE_TOP));
223 /* 12px == 12px */
224 assert_length ("group1", "padding-right", 12.,
225 st_theme_node_get_padding (group1, ST_SIDE_RIGHT));
226 /* 2em == 32px (with a 12pt font) */
227 assert_length ("group1", "padding-bottom", 32.,
228 st_theme_node_get_padding (group1, ST_SIDE_BOTTOM));
229 /* 1in == 72pt == 96px, at 96dpi */
230 assert_length ("group1", "padding-left", 96.,
231 st_theme_node_get_padding (group1, ST_SIDE_LEFT));
232 }
233
234 static void
235 test_classes (void)
236 {
237 test = "classes";
238 /* .special-text class overrides size and style;
239 * the ClutterTexture.special-text selector doesn't match */
240 assert_font (text1, "text1", "sans-serif Italic 32px");
241 }
242
243 static void
244 test_type_inheritance (void)
245 {
246 test = "type_inheritance";
247 /* From ClutterTexture element selector */
248 assert_length ("cairoTexture", "padding-top", 10.,
249 st_theme_node_get_padding (cairo_texture, ST_SIDE_TOP));
250 /* From ClutterCairoTexture element selector */
251 assert_length ("cairoTexture", "padding-right", 20.,
252 st_theme_node_get_padding (cairo_texture, ST_SIDE_RIGHT));
253 }
254
255 static void
256 test_adjacent_selector (void)
257 {
258 test = "adjacent_selector";
259 /* #group1 > #text1 matches text1 */
260 assert_foreground_color (text1, "text1", 0x00ff00ff);
261 /* stage > #text2 doesn't match text2 */
262 assert_foreground_color (text2, "text2", 0x000000ff);
263 }
264
265 static void
266 test_padding (void)
267 {
268 test = "padding";
269 /* Test that a 4-sided padding property assigns the right paddings to
270 * all sides */
271 assert_length ("group2", "padding-top", 1.,
272 st_theme_node_get_padding (group2, ST_SIDE_TOP));
273 assert_length ("group2", "padding-right", 2.,
274 st_theme_node_get_padding (group2, ST_SIDE_RIGHT));
275 assert_length ("group2", "padding-bottom", 3.,
276 st_theme_node_get_padding (group2, ST_SIDE_BOTTOM));
277 assert_length ("group2", "padding-left", 4.,
278 st_theme_node_get_padding (group2, ST_SIDE_LEFT));
279 }
280
281 static void
282 test_border (void)
283 {
284 test = "border";
285
286 /* group2 is defined as having a thin black border along the top three
287 * sides with rounded joins, then a square-joined green border at the
288 * botttom
289 */
290
291 assert_length ("group2", "border-top-width", 2.,
292 st_theme_node_get_border_width (group2, ST_SIDE_TOP));
293 assert_length ("group2", "border-right-width", 2.,
294 st_theme_node_get_border_width (group2, ST_SIDE_RIGHT));
295 assert_length ("group2", "border-bottom-width", 5.,
296 st_theme_node_get_border_width (group2, ST_SIDE_BOTTOM));
297 assert_length ("group2", "border-left-width", 2.,
298 st_theme_node_get_border_width (group2, ST_SIDE_LEFT));
299
300 assert_border_color (group2, "group2", ST_SIDE_TOP, 0x000000ff);
301 assert_border_color (group2, "group2", ST_SIDE_RIGHT, 0x000000ff);
302 assert_border_color (group2, "group2", ST_SIDE_BOTTOM, 0x0000ffff);
303 assert_border_color (group2, "group2", ST_SIDE_LEFT, 0x000000ff);
304
305 assert_length ("group2", "border-radius-topleft", 10.,
306 st_theme_node_get_border_radius (group2, ST_CORNER_TOPLEFT));
307 assert_length ("group2", "border-radius-topright", 10.,
308 st_theme_node_get_border_radius (group2, ST_CORNER_TOPRIGHT));
309 assert_length ("group2", "border-radius-bottomright", 0.,
310 st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMRIGHT));
311 assert_length ("group2", "border-radius-bottomleft", 0.,
312 st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMLEFT));
313 }
314
315 static void
316 test_background (void)
317 {
318 test = "background";
319 /* group1 has a background: shortcut property setting color and image */
320 assert_background_color (group1, "group1", 0xff0000ff);
321 assert_background_image (group1, "group1", "st/some-background.png");
322 /* text1 inherits the background image but not the color */
323 assert_background_color (text1, "text1", 0x00000000);
324 assert_background_image (text1, "text1", "st/some-background.png");
325 /* text1 inherits inherits both, but then background: none overrides both */
326 assert_background_color (text2, "text2", 0x00000000);
327 assert_background_image (text2, "text2", NULL);
328 /* background-image property */
329 assert_background_image (group2, "group2", "st/other-background.png");
330 }
331
332 static void
333 test_font (void)
334 {
335 test = "font";
336 /* font specified with font: */
337 assert_font (group2, "group2", "serif Italic 12px");
338 /* text3 inherits and overrides individually properties */
339 assert_font (text3, "text3", "serif Bold Oblique Small-Caps 24px");
340 }
341
342 static void
343 test_pseudo_class (void)
344 {
345 StWidget *label;
346 StThemeNode *labelNode;
347
348 test = "pseudo_class";
349 /* text4 has :visited and :hover pseudo-classes, so should pick up both of these */
350 assert_foreground_color (text4, "text4", 0x888888ff);
351 assert_text_decoration (text4, "text4", ST_TEXT_DECORATION_UNDERLINE);
352 /* :hover pseudo-class matches, but class doesn't match */
353 assert_text_decoration (group3, "group3", 0);
354
355 /* Test the StWidget add/remove pseudo_class interfaces */
356 label = st_label_new ("foo");
357 clutter_actor_add_child (stage, CLUTTER_ACTOR (label));
358
359 labelNode = st_widget_get_theme_node (label);
360 assert_foreground_color (labelNode, "label", 0x000000ff);
361 assert_text_decoration (labelNode, "label", 0);
362 assert_length ("label", "border-width", 0.,
363 st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
364
365 st_widget_add_style_pseudo_class (label, "visited");
366 g_assert (st_widget_has_style_pseudo_class (label, "visited"));
367 labelNode = st_widget_get_theme_node (label);
368 assert_foreground_color (labelNode, "label", 0x888888ff);
369 assert_text_decoration (labelNode, "label", 0);
370 assert_length ("label", "border-width", 0.,
371 st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
372
373 st_widget_add_style_pseudo_class (label, "hover");
374 g_assert (st_widget_has_style_pseudo_class (label, "hover"));
375 labelNode = st_widget_get_theme_node (label);
376 assert_foreground_color (labelNode, "label", 0x888888ff);
377 assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
378 assert_length ("label", "border-width", 0.,
379 st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
380
381 st_widget_remove_style_pseudo_class (label, "visited");
382 g_assert (!st_widget_has_style_pseudo_class (label, "visited"));
383 g_assert (st_widget_has_style_pseudo_class (label, "hover"));
384 labelNode = st_widget_get_theme_node (label);
385 assert_foreground_color (labelNode, "label", 0x000000ff);
386 assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
387 assert_length ("label", "border-width", 0.,
388 st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
389
390 st_widget_add_style_pseudo_class (label, "boxed");
391 labelNode = st_widget_get_theme_node (label);
392 assert_foreground_color (labelNode, "label", 0x000000ff);
393 assert_text_decoration (labelNode, "label", ST_TEXT_DECORATION_UNDERLINE);
394 assert_length ("label", "border-width", 1.,
395 st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
396
397 st_widget_remove_style_pseudo_class (label, "hover");
398 labelNode = st_widget_get_theme_node (label);
399 assert_foreground_color (labelNode, "label", 0x000000ff);
400 assert_text_decoration (labelNode, "label", 0);
401 assert_length ("label", "border-width", 1.,
402 st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
403
404 st_widget_remove_style_pseudo_class (label, "boxed");
405 g_assert (st_widget_get_style_pseudo_class (label) == NULL);
406 labelNode = st_widget_get_theme_node (label);
407 assert_foreground_color (labelNode, "label", 0x000000ff);
408 assert_text_decoration (labelNode, "label", 0);
409 assert_length ("label", "border-width", 0.,
410 st_theme_node_get_border_width (labelNode, ST_SIDE_TOP));
411 }
412
413 static void
414 test_inline_style (void)
415 {
416 test = "inline_style";
417 /* These properties come from the inline-style specified when creating the node */
418 assert_foreground_color (text3, "text3", 0x00000ffff);
419 assert_length ("text3", "padding-bottom", 12.,
420 st_theme_node_get_padding (text3, ST_SIDE_BOTTOM));
421 }
422
423 int
424 main (int argc, char **argv)
425 {
426 StTheme *theme;
427 StThemeContext *context;
428 PangoFontDescription *font_desc;
429
430 gtk_init (&argc, &argv);
431
432 if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
433 return 1;
434
435 theme = st_theme_new ("st/test-theme.css",
436 NULL, NULL);
437
438 stage = clutter_stage_new ();
439 context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage));
440 st_theme_context_set_theme (context, theme);
441
442 font_desc = pango_font_description_from_string ("sans-serif 12");
443 st_theme_context_set_font (context, font_desc);
444 pango_font_description_free (font_desc);
445
446 root = st_theme_context_get_root_node (context);
447 group1 = st_theme_node_new (context, root, NULL,
448 CLUTTER_TYPE_GROUP, "group1", NULL, NULL, NULL);
449 text1 = st_theme_node_new (context, group1, NULL,
450 CLUTTER_TYPE_TEXT, "text1", "special-text", NULL, NULL);
451 text2 = st_theme_node_new (context, group1, NULL,
452 CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
453 group2 = st_theme_node_new (context, root, NULL,
454 CLUTTER_TYPE_GROUP, "group2", NULL, NULL, NULL);
455 text3 = st_theme_node_new (context, group2, NULL,
456 CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
457 "color: #0000ff; padding-bottom: 12px;");
458 text4 = st_theme_node_new (context, group2, NULL,
459 CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover", NULL);
460 group3 = st_theme_node_new (context, group2, NULL,
461 CLUTTER_TYPE_GROUP, "group3", NULL, "hover", NULL);
462 cairo_texture = st_theme_node_new (context, root, NULL,
463 CLUTTER_TYPE_CAIRO_TEXTURE, "cairoTexture", NULL, NULL, NULL);
'clutter_cairo_texture_get_type' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-cairo-texture.h:98): Use 'clutter_canvas_get_type' instead
(emitted by gcc)
464
465 test_defaults ();
466 test_lengths ();
467 test_classes ();
468 test_type_inheritance ();
469 test_adjacent_selector ();
470 test_padding ();
471 test_border ();
472 test_background ();
473 test_font ();
474 test_pseudo_class ();
475 test_inline_style ();
476
477 g_object_unref (cairo_texture);
478 g_object_unref (group1);
479 g_object_unref (group2);
480 g_object_unref (group3);
481 g_object_unref (text1);
482 g_object_unref (text2);
483 g_object_unref (text3);
484 g_object_unref (text4);
485 g_object_unref (theme);
486
487 clutter_actor_destroy (stage);
488
489 return fail ? 1 : 0;
490 }