1 #include <cassert>
2 #include <cmath>
3 #include "utest/utest.h"
4 #include "streq.h"
5 #include "strneq.h"
6 #include "style.h"
8 /// Dummy functions to keep linker happy
9 int sp_main_gui (int, char const**) { return 0; }
10 int sp_main_console (int, char const**) { return 0; }
12 /* Extracted mechanically from http://www.w3.org/TR/SVG11/types.html#ColorKeywords:
13 *
14 * tidy -wrap 999 < types.html 2> /dev/null |
15 * egrep '(prop|color-keyword)-value' |
16 * sed 's,<td><span class="prop-value">, {",;s/<td><span class="color-keyword-value">rgb(/", {/;s%).*%}},@%;s%</span></td>%%' |
17 * tr -d \\n |
18 * tr @ \\n
19 */
20 static struct {
21 char const *color_keyword;
22 struct {unsigned r; unsigned g; unsigned b; } rgb;
23 } const color_keywords[] = {
24 {"aliceblue", {240, 248, 255}},
25 {"antiquewhite", {250, 235, 215}},
26 {"aqua", { 0, 255, 255}},
27 {"aquamarine", {127, 255, 212}},
28 {"azure", {240, 255, 255}},
29 {"beige", {245, 245, 220}},
30 {"bisque", {255, 228, 196}},
31 {"black", { 0, 0, 0}},
32 {"blanchedalmond", {255, 235, 205}},
33 {"blue", { 0, 0, 255}},
34 {"blueviolet", {138, 43, 226}},
35 {"brown", {165, 42, 42}},
36 {"burlywood", {222, 184, 135}},
37 {"cadetblue", { 95, 158, 160}},
38 {"chartreuse", {127, 255, 0}},
39 {"chocolate", {210, 105, 30}},
40 {"coral", {255, 127, 80}},
41 {"cornflowerblue", {100, 149, 237}},
42 {"cornsilk", {255, 248, 220}},
43 {"crimson", {220, 20, 60}},
44 {"cyan", { 0, 255, 255}},
45 {"darkblue", { 0, 0, 139}},
46 {"darkcyan", { 0, 139, 139}},
47 {"darkgoldenrod", {184, 134, 11}},
48 {"darkgray", {169, 169, 169}},
49 {"darkgreen", { 0, 100, 0}},
50 {"darkgrey", {169, 169, 169}},
51 {"darkkhaki", {189, 183, 107}},
52 {"darkmagenta", {139, 0, 139}},
53 {"darkolivegreen", { 85, 107, 47}},
54 {"darkorange", {255, 140, 0}},
55 {"darkorchid", {153, 50, 204}},
56 {"darkred", {139, 0, 0}},
57 {"darksalmon", {233, 150, 122}},
58 {"darkseagreen", {143, 188, 143}},
59 {"darkslateblue", { 72, 61, 139}},
60 {"darkslategray", { 47, 79, 79}},
61 {"darkslategrey", { 47, 79, 79}},
62 {"darkturquoise", { 0, 206, 209}},
63 {"darkviolet", {148, 0, 211}},
64 {"deeppink", {255, 20, 147}},
65 {"deepskyblue", { 0, 191, 255}},
66 {"dimgray", {105, 105, 105}},
67 {"dimgrey", {105, 105, 105}},
68 {"dodgerblue", { 30, 144, 255}},
69 {"firebrick", {178, 34, 34}},
70 {"floralwhite", {255, 250, 240}},
71 {"forestgreen", { 34, 139, 34}},
72 {"fuchsia", {255, 0, 255}},
73 {"gainsboro", {220, 220, 220}},
74 {"ghostwhite", {248, 248, 255}},
75 {"gold", {255, 215, 0}},
76 {"goldenrod", {218, 165, 32}},
77 {"gray", {128, 128, 128}},
78 {"grey", {128, 128, 128}},
79 {"green", { 0, 128, 0}},
80 {"greenyellow", {173, 255, 47}},
81 {"honeydew", {240, 255, 240}},
82 {"hotpink", {255, 105, 180}},
83 {"indianred", {205, 92, 92}},
84 {"indigo", { 75, 0, 130}},
85 {"ivory", {255, 255, 240}},
86 {"khaki", {240, 230, 140}},
87 {"lavender", {230, 230, 250}},
88 {"lavenderblush", {255, 240, 245}},
89 {"lawngreen", {124, 252, 0}},
90 {"lemonchiffon", {255, 250, 205}},
91 {"lightblue", {173, 216, 230}},
92 {"lightcoral", {240, 128, 128}},
93 {"lightcyan", {224, 255, 255}},
94 {"lightgoldenrodyellow", {250, 250, 210}},
95 {"lightgray", {211, 211, 211}},
96 {"lightgreen", {144, 238, 144}},
97 {"lightgrey", {211, 211, 211}},
98 {"lightpink", {255, 182, 193}},
99 {"lightsalmon", {255, 160, 122}},
100 {"lightseagreen", { 32, 178, 170}},
101 {"lightskyblue", {135, 206, 250}},
102 {"lightslategray", {119, 136, 153}},
103 {"lightslategrey", {119, 136, 153}},
104 {"lightsteelblue", {176, 196, 222}},
105 {"lightyellow", {255, 255, 224}},
106 {"lime", { 0, 255, 0}},
107 {"limegreen", { 50, 205, 50}},
108 {"linen", {250, 240, 230}},
109 {"magenta", {255, 0, 255}},
110 {"maroon", {128, 0, 0}},
111 {"mediumaquamarine", {102, 205, 170}},
112 {"mediumblue", { 0, 0, 205}},
113 {"mediumorchid", {186, 85, 211}},
114 {"mediumpurple", {147, 112, 219}},
115 {"mediumseagreen", { 60, 179, 113}},
116 {"mediumslateblue", {123, 104, 238}},
117 {"mediumspringgreen", { 0, 250, 154}},
118 {"mediumturquoise", { 72, 209, 204}},
119 {"mediumvioletred", {199, 21, 133}},
120 {"midnightblue", { 25, 25, 112}},
121 {"mintcream", {245, 255, 250}},
122 {"mistyrose", {255, 228, 225}},
123 {"moccasin", {255, 228, 181}},
124 {"navajowhite", {255, 222, 173}},
125 {"navy", { 0, 0, 128}},
126 {"oldlace", {253, 245, 230}},
127 {"olive", {128, 128, 0}},
128 {"olivedrab", {107, 142, 35}},
129 {"orange", {255, 165, 0}},
130 {"orangered", {255, 69, 0}},
131 {"orchid", {218, 112, 214}},
132 {"palegoldenrod", {238, 232, 170}},
133 {"palegreen", {152, 251, 152}},
134 {"paleturquoise", {175, 238, 238}},
135 {"palevioletred", {219, 112, 147}},
136 {"papayawhip", {255, 239, 213}},
137 {"peachpuff", {255, 218, 185}},
138 {"peru", {205, 133, 63}},
139 {"pink", {255, 192, 203}},
140 {"plum", {221, 160, 221}},
141 {"powderblue", {176, 224, 230}},
142 {"purple", {128, 0, 128}},
143 {"red", {255, 0, 0}},
144 {"rosybrown", {188, 143, 143}},
145 {"royalblue", { 65, 105, 225}},
146 {"saddlebrown", {139, 69, 19}},
147 {"salmon", {250, 128, 114}},
148 {"sandybrown", {244, 164, 96}},
149 {"seagreen", { 46, 139, 87}},
150 {"seashell", {255, 245, 238}},
151 {"sienna", {160, 82, 45}},
152 {"silver", {192, 192, 192}},
153 {"skyblue", {135, 206, 235}},
154 {"slateblue", {106, 90, 205}},
155 {"slategray", {112, 128, 144}},
156 {"slategrey", {112, 128, 144}},
157 {"snow", {255, 250, 250}},
158 {"springgreen", { 0, 255, 127}},
159 {"steelblue", { 70, 130, 180}},
160 {"tan", {210, 180, 140}},
161 {"teal", { 0, 128, 128}},
162 {"thistle", {216, 191, 216}},
163 {"tomato", {255, 99, 71}},
164 {"turquoise", { 64, 224, 208}},
165 {"violet", {238, 130, 238}},
166 {"wheat", {245, 222, 179}},
167 {"white", {255, 255, 255}},
168 {"whitesmoke", {245, 245, 245}},
169 {"yellow", {255, 255, 0}},
170 {"yellowgreen", {154, 205, 50}},
171 {NULL, {0, 0, 0}}
172 };
174 static char const *const display_vals[] = {
175 "inline", "block", "list-item", "run-in", "compact", "marker", "table", "inline-table",
176 "table-row-group", "table-header-group", "table-footer-group", "table-row",
177 "table-column-group", "table-column", "table-cell", "table-caption", "none", NULL
178 };
180 static char const *const font_stretch_vals[] = {
181 "normal",
182 "wider",
183 "narrower",
184 "ultra-condensed",
185 "extra-condensed",
186 "condensed",
187 "semi-condensed",
188 "semi-expanded",
189 "expanded",
190 "extra-expanded",
191 "ultra-expanded",
192 NULL
193 };
195 static char const *const font_style_vals[] = {"normal", "italic", "oblique", NULL};
197 static char const *const font_variant_vals[] = {"normal", "small-caps", NULL};
199 static char const *const font_weight_vals[] = {"normal", "bold", "bolder", "lighter",
200 "100", "200", "300", "400", "500",
201 "600", "700", "800", "900", NULL};
203 static char const *const normal_val[] = {"normal", NULL};
205 static char const *const none_val[] = {"none", NULL};
207 static char const *const linecap_vals[] = {"butt", "round", "square", NULL};
209 static char const *const linejoin_vals[] = {"miter", "round", "bevel", NULL};
211 static char const *const text_anchor_vals[] = {"start", "middle", "end", NULL};
213 static char const *const visibility_vals[] = {"visible", "hidden", "collapse", NULL};
215 static char const *const writing_mode_vals[] = {"lr-tb", "rl-tb", "tb-rl", /*"lr", "rl", "tb",*/ NULL};
216 /* TODO: Inkscape correctly accepts lr,rl,tb, but reports them as lr-tb etc.
217 Either change inkscape or write custom test. */
219 static char const *const fill_rule_vals[] = {"nonzero", "evenodd", NULL};
221 static char const *const paint_enum_vals[] = {"none", "currentColor", NULL};
223 static gchar *
224 merge_then_write_string(gchar const *const str, guint const flags)
225 {
226 SPStyle *const style = sp_style_new();
227 sp_style_merge_from_style_string(style, str);
228 gchar *const ret = sp_style_write_string(style, flags);
229 sp_style_unref(style);
230 return ret;
231 }
233 static void
234 enum_val(char const prop[], char const *const vals[])
235 {
236 assert(vals);
237 assert(vals[0]);
239 for (unsigned i = 0; vals[i]; ++i) {
240 char *prop_eq_val = g_strdup_printf("%s:%s", prop, vals[i]);
241 gchar *ifset_str = merge_then_write_string(prop_eq_val, SP_STYLE_FLAG_IFSET);
242 UTEST_ASSERT(streq(ifset_str, prop_eq_val));
243 g_free(ifset_str);
244 g_free(prop_eq_val);
245 }
246 }
248 static void
249 color_val(char const prop[], char const *const dummy_vals[])
250 {
251 assert(dummy_vals == NULL);
252 char const *const extra_vals[] = {"blue", "#234", "#0100ff", NULL};
253 enum_val(prop, extra_vals);
254 #if 0
255 char const *color_vals[G_N_ELEMENTS(color_keywords) + G_N_ELEMENTS(extra_vals)];
256 for (unsigned i = 0; i < G_N_ELEMENTS(extra_vals); ++i) {
257 color_vals[i] = extra_vals[i];
258 }
259 for (unsigned i = 0; i < G_N_ELEMENTS(color_keywords); ++i) {
260 color_vals[G_N_ELEMENTS(extra_vals) + i] = color_keywords[i].color_keyword;
261 }
262 enum_val(prop, color_vals);
263 /* todo: other color stuff (rgb(), #123) */
264 #endif
265 }
267 static void
268 paint_val(char const prop[], char const *const dummy_vals[])
269 {
270 /* Ref: http://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint */
271 assert(dummy_vals == NULL);
272 color_val(prop, NULL);
273 enum_val(prop, paint_enum_vals);
274 /* todo: uri, `<uri> none' etc. */
275 }
277 static void
278 number_val(char const prop[], double const min, double const max, double const eps)
279 {
280 assert(min <= max);
281 /* TODO */
282 double const propns[] = {0., .5, .25, .125,
283 (1.0 / (1 << 7)),
284 (1.0 / (1 << 8)),
285 (1.0 / (1 << 15)),
286 (1.0 / (1 << 23)),
287 (1.0 / (1 << 24)),
288 (1.0 / (1 << 25)),
289 (1.0 / (1 << 26)),
290 1e-5, 0.13, 1/7., 2/3., 10/11., 1.0};
291 size_t const prop_len = std::strlen(prop);
292 for (unsigned i = 0; i < G_N_ELEMENTS(propns); ++i) {
293 double const propn = propns[i];
294 double const val = ( propn == 0
295 ? min
296 : ( propn == 1
297 ? max
298 : min + (propns[i] * (max - min)) ) );
299 char val_str[35];
300 g_ascii_formatd(val_str, sizeof(val_str), "%.17f", val);
301 char *prop_eq_val = g_strdup_printf("%s:%s", prop, val_str);
302 gchar *ifset_str = merge_then_write_string(prop_eq_val, SP_STYLE_FLAG_IFSET);
303 UTEST_ASSERT(strneq(ifset_str, prop_eq_val, prop_len + 1));
304 char *endptr;
305 double const found_val = g_strtod(ifset_str + prop_len + 1, &endptr);
306 UTEST_ASSERT(*endptr == '\0');
307 if (fabs(val) < 1.) {
308 UTEST_ASSERT(std::fabs(found_val - val) < eps);
309 } else {
310 UTEST_ASSERT(std::fabs(found_val / val - 1.) < eps);
311 }
312 g_free(ifset_str);
313 g_free(prop_eq_val);
314 }
315 }
317 static void
318 miterlimit_val(char const prop[], char const *const dummy_vals[])
319 {
320 // Ref: http://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty
321 assert(dummy_vals == NULL);
322 number_val(prop, 1., 20., 2e-7);
323 #if 0
324 static char const *const miterlimit_vals[] = {
325 "1", "1.0", "1.5", "2", "4", "8", "1e1", NULL
326 };
327 // bad values: <1, percentages, strings (none etc.).
328 #endif
329 }
331 static void
332 font_family_val(char const prop[], char const *const dummy_vals[])
333 {
334 assert(dummy_vals == NULL);
335 static char const *const generic_font_family_vals[] = {"serif", "sans-serif", "cursive", "fantasy", "monospace", NULL};
336 enum_val(prop, generic_font_family_vals);
337 /* todo: unrecognized fonts, comma-separated lists. */
338 }
340 /**
341 * "unitful length" is used for font-size, which we always write with an explicit unit (typically
342 * `px') for better CSS interoperability.
343 */
344 static void
345 unitful_length_val(char const prop[], char const *const dummy_vals[])
346 {
347 /* todo */
348 assert(dummy_vals == NULL);
349 }
351 static void
352 length_val(char const prop[], char const *const dummy_vals[])
353 {
354 assert(dummy_vals == NULL);
355 number_val(prop, 0., 6., 1e-7);
356 unitful_length_val(prop, dummy_vals);
357 /* todo: exponential notation. See http://www.w3.org/TR/SVG11/types.html#DataTypeNumber for
358 where exponential notation is/isn't allowed. */
359 }
361 static void
362 length_or_enum_val(char const prop[], char const *const vals[])
363 {
364 length_val(prop, NULL);
365 enum_val(prop, vals);
366 }
368 static void
369 opacity_val(char const prop[], char const *const dummy_vals[])
370 {
371 assert(dummy_vals == NULL);
372 number_val(prop, 0., 1., 1.0 / (1 << 24));
373 /* todo: exponential notation */
374 }
376 static void
377 uri_or_enum_val(char const prop[], char const *const vals[])
378 {
379 enum_val(prop, vals);
380 /* todo: uri's */
381 }
383 static void
384 suppress_warning_log_handler(gchar const *log_domain, GLogLevelFlags /*log_level*/,
385 gchar const *message,
386 gpointer /*user_data*/)
387 {
388 /* todo: We could strncpy message to a static buffer for later testing with
389 * UTEST_ASSERT(streq(prev_message, exp_message)). */
390 }
392 static void
393 test_mul24(unsigned const a24, unsigned const b24)
394 {
395 assert(a24 <= SP_SCALE24_MAX);
396 assert(b24 <= SP_SCALE24_MAX);
397 unsigned const manual_prod24 = SP_SCALE24_FROM_FLOAT(SP_SCALE24_TO_FLOAT(a24) *
398 SP_SCALE24_TO_FLOAT(b24) );
399 unsigned const prod24 = SP_SCALE24_MUL(a24, b24);
400 UTEST_ASSERT_SHOW(prod24 == manual_prod24,
401 ("MUL gives 0x%06x=%g, whereas explicit conversions give 0x%06x=%g;\n"
402 "multiplicands: 0x%06x * 0x%06x (i.e. %g * %g)",
403 prod24, SP_SCALE24_TO_FLOAT(prod24),
404 manual_prod24, SP_SCALE24_TO_FLOAT(manual_prod24),
405 a24, b24, SP_SCALE24_TO_FLOAT(a24), SP_SCALE24_TO_FLOAT(b24)));
406 }
408 static void
409 test_scale24_mul()
410 {
411 UTEST_TEST("SP_SCALE24_MUL") {
412 UTEST_ASSERT(0x1000 < SP_SCALE24_MAX);
413 UTEST_ASSERT(SP_SCALE24_MAX < SP_SCALE24_MAX + 1); // i.e. no overflow.
415 for (unsigned i = 0; i <= 10; ++i) {
416 unsigned const i24 = SP_SCALE24_MAX * i / 10;
417 UTEST_ASSERT(i24 == SP_SCALE24_FROM_FLOAT(i / 10.0));
418 for (unsigned j = 0; j <= 10; ++j) {
419 unsigned const j24 = SP_SCALE24_MAX * j / 10;
420 test_mul24(i24, j24);
421 }
422 }
424 for (unsigned i = 0; i < 10000; ++i) {
425 unsigned const a24 = rand() % (SP_SCALE24_MAX + 1);
426 unsigned const b24 = rand() % (SP_SCALE24_MAX + 1);
427 test_mul24(a24, b24);
428 }
429 }
430 }
432 static void
433 test_merge_opacity()
434 {
435 SPStyle &parent = *sp_style_new();
436 SPStyle &child = *sp_style_new();
438 unsigned const either = 2;
439 struct {
440 bool parent_set;
441 bool parent_inherit;
442 float parent_float_val;
443 bool child_set;
444 bool child_inherit;
445 float child_float_val;
446 unsigned exp_set;
447 unsigned exp_inherit;
448 double exp_float_val;
449 } const cases[] = {
450 {false, false, 1.0, false, false, 1.0, false, false, 1.0},
451 {false, false, 1.0, true, true, 1.0, false, false, 1.0},
452 {false, false, 1.0, true, false, 1.0, false, false, 1.0},
453 {false, false, 1.0, true, false, 0.5, true, false, 0.5},
454 {false, false, 1.0, true, false, 0.0, true, false, 0.0},
456 {true, true, 1.0, false, false, 1.0, true, true, 1.0},
457 {true, true, 0.7, false, false, 1.0, true, true, 0.7},
458 {true, true, 0.0, false, false, 1.0, true, true, 0.0},
459 {true, true, 1.0, true, true, 1.0, true, true, 1.0},
460 /* child computed value isn't required to be up-to-date, so test what happens when it
461 * isn't up-to-date. */
462 {true, true, 1.0, true, true, 0.7, true, true, 1.0},
463 {true, true, 0.6, true, true, 0.3, true, false, 0.36},
464 {true, true, 0.0, true, true, 0.0, true, true, 0.0},
465 {true, true, 0.0, true, true, 0.9, true, true, 0.0},
467 /* parent inherit, child set to number */
468 {true, true, 1.0, true, false, 1.0, true, true, 1.0},
469 {true, true, 1.0, true, false, 0.8, true, false, 0.8},
470 {true, true, 1.0, true, false, 0.0, true, false, 0.0},
471 {true, true, 0.9, true, false, 1.0, true, true, 0.9},
472 {true, true, 0.9, true, false, 0.8, true, false, 0.72},
473 {true, true, 0.9, true, false, 0.0, true, false, 0.0},
474 {true, true, 0.0, true, false, 1.0, true, true, 0.0},
475 {true, true, 0.0, true, false, 0.6, true, either, 0.0},
476 {true, true, 0.0, true, false, 0.0, true, false, 0.0},
478 /* parent set to number. */
479 {true, false, 1.0, false, false, 1.0, either, false, 1.0},
480 {true, false, 0.3, false, false, 1.0, true, false, 0.3},
481 {true, false, 0.3, false, false, 1.0, true, false, 0.3},
482 {true, false, 0.0, false, false, 1.0, true, false, 0.0},
484 {true, false, 1.0, true, true, 1.0, either, false, 1.0},
485 {true, false, 1.0, true, true, 0.8, either, false, 1.0},
486 {true, false, 0.8, true, true, 0.8, true, false, 0.64},
487 {true, false, 0.8, true, true, 0.5, true, false, 0.64},
488 {true, false, 0.0, true, true, 0.0, true, false, 0.0},
489 {true, false, 0.0, true, true, 0.4, true, false, 0.0},
491 {true, false, 1.0, true, false, 1.0, either, false, 1.0},
492 {true, false, 1.0, true, false, 0.4, true, false, 0.4},
493 {true, false, 1.0, true, false, 0.0, true, false, 0.0},
494 {true, false, 0.7, true, false, 1.0, true, false, 0.7},
495 {true, false, 0.7, true, false, 0.4, true, false, 0.28},
496 {true, false, 0.7, true, false, 0.0, true, false, 0.0},
497 {true, false, 0.0, true, false, 1.0, true, false, 0.0},
498 {true, false, 0.0, true, false, 0.6, true, false, 0.0},
499 {true, false, 0.0, true, false, 0.0, true, false, 0.0}
500 };
501 UTEST_TEST("sp_style_merge_from_dying_parent: opacity") {
502 for (unsigned i = 0; i < G_N_ELEMENTS(cases); ++i) {
503 parent.opacity.set = cases[i].parent_set;
504 parent.opacity.inherit = cases[i].parent_inherit;
505 parent.opacity.value = SP_SCALE24_FROM_FLOAT(cases[i].parent_float_val);
506 unsigned const parent24 = parent.opacity.value;
508 child.opacity.set = cases[i].child_set;
509 child.opacity.inherit = cases[i].child_inherit;
510 child.opacity.value = SP_SCALE24_FROM_FLOAT(cases[i].child_float_val);
511 unsigned const child24 = child.opacity.value;
513 sp_style_merge_from_dying_parent(&child, &parent);
514 if (cases[i].exp_set != either) {
515 UTEST_ASSERT(child.opacity.set == cases[i].exp_set);
516 }
517 if (cases[i].exp_inherit != either) {
518 UTEST_ASSERT(child.opacity.inherit == cases[i].exp_inherit);
519 }
520 unsigned const exp24 = SP_SCALE24_FROM_FLOAT(cases[i].exp_float_val);
521 UTEST_ASSERT_SHOW(child.opacity.value == exp24,
522 ("i=%u, expected 0x%06x=%g but found 0x%06x=%g; "
523 "parent 0x%06x=%g, child 0x%06x=%g",
524 i, exp24, cases[i].exp_float_val,
525 child.opacity.value, SP_SCALE24_TO_FLOAT(child.opacity.value),
526 parent24, SP_SCALE24_TO_FLOAT(parent24),
527 child24, SP_SCALE24_TO_FLOAT(child24)));
528 }
529 }
530 }
532 static bool
533 test_style()
534 {
535 struct {
536 char const *property;
537 char const *ink_dfl;
538 char const *spec_dfl;
539 void (*tst_fn)(char const[], char const *const[]);
540 char const *const *tst_fn_arg;
541 bool can_explicitly_inherit;
542 } const props[] = {
543 {"color", "black", "black", color_val, NULL, true},
544 // initial value "depends on user agent"
545 {"display", "inline", "inline", enum_val, display_vals, true},
546 {"fill", "black", "black", paint_val, NULL, true},
547 {"fill-opacity", "1", "1", opacity_val, NULL, true},
548 {"fill-rule", "nonzero", "nonzero", enum_val, fill_rule_vals, true},
549 {"font-family", "Bitstream Vera Sans", "Bitstream Vera Sans", font_family_val, NULL, true},
550 // initial value depends on user agent
551 {"font-size", "medium", "medium", unitful_length_val, NULL, true},
552 // TODO: abs, rel, pcnt.
553 {"font-stretch", "normal", "normal", enum_val, font_stretch_vals, true},
554 {"font-style", "normal", "normal", enum_val, font_style_vals, true},
555 {"font-variant", "normal", "normal", enum_val, font_variant_vals, true},
556 {"font-weight", "normal", "normal", enum_val, font_weight_vals, true},
557 {"letter-spacing", "normal", "normal", length_or_enum_val, normal_val, true},
558 {"marker", "none", "none", uri_or_enum_val, none_val, true},
559 {"marker-end", "none", "none", uri_or_enum_val, none_val, true},
560 {"marker-mid", "none", "none", uri_or_enum_val, none_val, true},
561 {"marker-start", "none", "none", uri_or_enum_val, none_val, true},
562 {"opacity", "1", "1", opacity_val, NULL, true},
563 {"stroke", "none", "none", paint_val, NULL, true},
564 {"stroke-dasharray", "none", "none", enum_val, none_val, true},
565 // TODO: http://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
566 //{"stroke-dashoffset", "0", "0", length_val, NULL, true},
567 // fixme: dashoffset currently fails a number of tests, but the relevant code
568 // is being worked on for something else at the time of writing, so I'm
569 // delaying fixing it. It should be changed to SPILength.
570 {"stroke-linecap", "butt", "butt", enum_val, linecap_vals, true},
571 {"stroke-linejoin", "miter", "miter", enum_val, linejoin_vals, true},
572 {"stroke-miterlimit", "4", "4", miterlimit_val, NULL, true},
573 {"stroke-opacity", "1", "1", opacity_val, NULL, true},
574 {"stroke-width", "1", "1", length_val, NULL, true},
575 {"text-anchor", "start", "start", enum_val, text_anchor_vals, true},
576 {"visibility", "visible", "visible", enum_val, visibility_vals, true},
577 {"writing-mode", "lr-tb", "lr-tb", enum_val, writing_mode_vals, true}
578 };
580 char const str0_all_exp[] =
581 "font-size:medium;"
582 "font-style:normal;"
583 "font-variant:normal;"
584 "font-weight:normal;"
585 "font-stretch:normal;"
586 "text-indent:0;"
587 "text-align:start;"
588 "text-decoration:none;"
589 "line-height:normal;"
590 "letter-spacing:normal;"
591 "word-spacing:normal;"
592 "text-transform:none;"
593 "direction:ltr;"
594 "block-progression:tb;"
595 "writing-mode:lr-tb;"
596 "text-anchor:start;"
597 "opacity:1;"
598 "color:black;"
599 "fill:black;"
600 "fill-opacity:1;"
601 "fill-rule:nonzero;"
602 "stroke:none;"
603 "stroke-width:1;"
604 "stroke-linecap:butt;"
605 "stroke-linejoin:miter;"
606 "marker:none;"
607 "marker-start:none;"
608 "marker-mid:none;"
609 "marker-end:none;"
610 "stroke-miterlimit:4;"
611 "stroke-dasharray:none;"
612 "stroke-dashoffset:0;"
613 "stroke-opacity:1;"
614 "visibility:visible;"
615 "display:inline;"
616 "overflow:visible;"
617 "font-family:Bitstream Vera Sans";
619 utest_start("style");
620 UTEST_TEST("sp_style_new, sp_style_write_string") {
621 SPStyle *style = sp_style_new();
622 gchar *str0_all = sp_style_write_string(style, SP_STYLE_FLAG_ALWAYS);
623 gchar *str0_set = sp_style_write_string(style, SP_STYLE_FLAG_IFSET);
624 UTEST_ASSERT(*str0_set == '\0');
625 UTEST_ASSERT(streq(str0_all, str0_all_exp));
626 printf(str0_all);
627 g_free(str0_all);
628 g_free(str0_set);
629 sp_style_unref(style);
630 }
632 UTEST_TEST("sp_style_merge_from_style_string(whitespace, ifset") {
633 gchar *str0_set = merge_then_write_string(" \t \t\t", SP_STYLE_FLAG_IFSET);
634 UTEST_ASSERT(*str0_set == '\0');
635 g_free(str0_set);
636 }
638 UTEST_TEST("sp_style_merge_from_style_string(whitespace, always") {
639 gchar *str0_all = merge_then_write_string(" \t \t\t", SP_STYLE_FLAG_ALWAYS);
640 UTEST_ASSERT(streq(str0_all, str0_all_exp));
641 g_free(str0_all);
642 }
644 /* Some tests for invalid style strings. We temporarily suppress all g_warning's. (The
645 current code uses g_warning instead of proper SVG error handling.) */
646 guint const log_handler_id = g_log_set_handler(NULL, G_LOG_LEVEL_WARNING,
647 suppress_warning_log_handler, NULL);
648 UTEST_TEST("sp_style_merge_from_style_string(\"fill:\", ifset)") {
649 gchar *str0_set = merge_then_write_string("fill:", SP_STYLE_FLAG_IFSET);
650 UTEST_ASSERT(*str0_set == '\0');
651 g_free(str0_set);
652 }
654 UTEST_TEST("sp_style_merge_from_style_string(\"font-family:\", always)") {
655 gchar *str0_all = merge_then_write_string("font-family:", SP_STYLE_FLAG_ALWAYS);
656 UTEST_ASSERT(streq(str0_all, str0_all_exp));
657 g_free(str0_all);
658 }
660 UTEST_TEST("sp_style_merge_from_style_string(\"fill: \", ifset)") {
661 gchar *str0_set = merge_then_write_string("fill: ", SP_STYLE_FLAG_IFSET);
662 UTEST_ASSERT(*str0_set == '\0');
663 g_free(str0_set);
664 }
666 UTEST_TEST("sp_style_merge_from_style_string(\"font-family: \", always)") {
667 gchar *str0_all = merge_then_write_string("font-family: ", SP_STYLE_FLAG_ALWAYS);
668 UTEST_ASSERT(streq(str0_all, str0_all_exp));
669 g_free(str0_all);
670 }
672 UTEST_TEST("sp_style_merge_from_style_string(\":none\", ifset)") {
673 gchar *str0_set = merge_then_write_string(":none", SP_STYLE_FLAG_IFSET);
674 UTEST_ASSERT(*str0_set == '\0');
675 g_free(str0_set);
676 }
677 g_log_remove_handler(NULL, log_handler_id);
679 /* The following tests involve invalid style, but aren't expected to trigger
680 g_log calls. */
681 /* invalid color setting */
682 {
683 char const *bad[] = {"#4321", "currentColor", "#87654321", "#42", "aquam"};
684 for (unsigned i = 0; i < G_N_ELEMENTS(bad); ++i) {
685 gchar *tst_name = g_strdup_printf("invalid color setting: %s", bad[i]);
686 gchar *style_str = g_strdup_printf("color:%s;color:#123;color:%s",
687 bad[i], bad[i]);
688 UTEST_TEST(tst_name) {
689 gchar *str0_set = merge_then_write_string(style_str, SP_STYLE_FLAG_IFSET);
690 UTEST_ASSERT(streq(str0_set, "color:#123"));
691 g_free(str0_set);
692 }
693 g_free(style_str);
694 g_free(tst_name);
695 }
696 }
698 /* End of invalid style string examples. */
701 #if 1 /* previously failed because of dashoffset:0 vs dashoffset:0.00000000 */
702 UTEST_TEST("sp_style_merge_from_style_string(default): ifset") {
703 gchar *ifset_str = merge_then_write_string(str0_all_exp, SP_STYLE_FLAG_IFSET);
704 UTEST_ASSERT(streq(ifset_str, str0_all_exp));
705 g_free(ifset_str);
706 }
708 UTEST_TEST("sp_style_merge_from_style_string(default): always") {
709 gchar *ifset_str = merge_then_write_string(str0_all_exp, SP_STYLE_FLAG_ALWAYS);
710 UTEST_ASSERT(streq(ifset_str, str0_all_exp));
711 g_free(ifset_str);
712 }
713 #endif
715 UTEST_TEST("sp_style_merge_from_style_string") {
716 /* Try setting default values, check that the all string is unaffected
717 but that the ifset string is affected. */
718 for (unsigned i = 0; i < G_N_ELEMENTS(props); ++i) {
719 char *prop_eq_val = g_strdup_printf("%s:%s", props[i].property, props[i].spec_dfl);
720 char *exp_set_str = g_strdup_printf("%s:%s", props[i].property, props[i].ink_dfl);
721 gchar *str0_all = merge_then_write_string(prop_eq_val, SP_STYLE_FLAG_ALWAYS);
722 gchar *str0_set = merge_then_write_string(prop_eq_val, SP_STYLE_FLAG_IFSET);
723 UTEST_ASSERT(streq(str0_all, str0_all_exp));
724 UTEST_ASSERT(streq(str0_set, exp_set_str));
725 g_free(str0_set);
726 g_free(str0_all);
727 g_free(exp_set_str);
728 g_free(prop_eq_val);
729 }
731 /* Check that explicit `inherit' is correctly preserved by write(ifset). */
732 for (unsigned i = 0; i < G_N_ELEMENTS(props); ++i) {
733 if (!props[i].can_explicitly_inherit) {
734 continue;
735 }
736 char *prop_eq_val = g_strdup_printf("%s:inherit", props[i].property);
737 gchar *ifset_str = merge_then_write_string(prop_eq_val, SP_STYLE_FLAG_IFSET);
738 UTEST_ASSERT(streq(ifset_str, prop_eq_val));
739 g_free(ifset_str);
740 g_free(prop_eq_val);
741 }
743 for (unsigned i = 0; i < G_N_ELEMENTS(props); ++i) {
744 props[i].tst_fn(props[i].property, props[i].tst_fn_arg);
745 }
746 }
748 test_scale24_mul();
749 test_merge_opacity();
751 return utest_end();
752 }
754 int main()
755 {
756 return ( test_style()
757 ? EXIT_SUCCESS
758 : EXIT_FAILURE );
759 }
761 /*
762 Local Variables:
763 mode:c++
764 c-file-style:"stroustrup"
765 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
766 indent-tabs-mode:nil
767 fill-column:99
768 End:
769 */
770 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :