1 #include <clutter/clutter.h>
6 #include "test-conform-common.h"
10 #define FLOAT_FUZZ_AMOUNT 5.0f
12 typedef struct _CallbackData CallbackData;
14 typedef gboolean (* PathTestFunc) (CallbackData *data);
16 static void compare_node (const ClutterPathNode *node, gpointer data_p);
23 ClutterPathNode nodes[MAX_NODES];
25 gboolean nodes_different;
29 static const char path_desc[] =
32 "C 29 30 31 32 33 34 "
35 "c 35 36 37 38 39 40 "
37 static const ClutterPathNode path_nodes[] =
38 { { CLUTTER_PATH_MOVE_TO, { { 21, 22 }, { 0, 0 }, { 0, 0 } } },
39 { CLUTTER_PATH_LINE_TO, { { 25, 26 }, { 0, 0 }, { 0, 0 } } },
40 { CLUTTER_PATH_CURVE_TO, { { 29, 30 }, { 31, 32 }, { 33, 34 } } },
41 { CLUTTER_PATH_REL_MOVE_TO, { { 23, 24 }, { 0, 0 }, { 0, 0 } } },
42 { CLUTTER_PATH_REL_LINE_TO, { { 27, 28 }, { 0, 0 }, { 0, 0 } } },
43 { CLUTTER_PATH_REL_CURVE_TO, { { 35, 36 }, { 37, 38 }, { 39, 40 } } },
44 { CLUTTER_PATH_CLOSE, { { 0, 0 }, { 0, 0 }, { 0, 0 } } } };
47 path_test_add_move_to (CallbackData *data)
49 ClutterPathNode node = { 0, };
51 node.type = CLUTTER_PATH_MOVE_TO;
55 clutter_path_add_move_to (data->path, node.points[0].x, node.points[0].y);
57 data->nodes[data->n_nodes++] = node;
63 path_test_add_line_to (CallbackData *data)
65 ClutterPathNode node = { 0, };
67 node.type = CLUTTER_PATH_LINE_TO;
71 clutter_path_add_line_to (data->path, node.points[0].x, node.points[0].y);
73 data->nodes[data->n_nodes++] = node;
79 path_test_add_curve_to (CallbackData *data)
81 ClutterPathNode node = { 0, };
83 node.type = CLUTTER_PATH_CURVE_TO;
89 node.points[2].y = 10;
91 clutter_path_add_curve_to (data->path,
92 node.points[0].x, node.points[0].y,
93 node.points[1].x, node.points[1].y,
94 node.points[2].x, node.points[2].y);
96 data->nodes[data->n_nodes++] = node;
102 path_test_add_close (CallbackData *data)
104 ClutterPathNode node = { 0, };
106 node.type = CLUTTER_PATH_CLOSE;
108 clutter_path_add_close (data->path);
110 data->nodes[data->n_nodes++] = node;
116 path_test_add_rel_move_to (CallbackData *data)
118 ClutterPathNode node = { 0, };
120 node.type = CLUTTER_PATH_REL_MOVE_TO;
121 node.points[0].x = 11;
122 node.points[0].y = 12;
124 clutter_path_add_rel_move_to (data->path, node.points[0].x, node.points[0].y);
126 data->nodes[data->n_nodes++] = node;
132 path_test_add_rel_line_to (CallbackData *data)
134 ClutterPathNode node = { 0, };
136 node.type = CLUTTER_PATH_REL_LINE_TO;
137 node.points[0].x = 13;
138 node.points[0].y = 14;
140 clutter_path_add_rel_line_to (data->path, node.points[0].x, node.points[0].y);
142 data->nodes[data->n_nodes++] = node;
148 path_test_add_rel_curve_to (CallbackData *data)
150 ClutterPathNode node = { 0, };
152 node.type = CLUTTER_PATH_REL_CURVE_TO;
153 node.points[0].x = 15;
154 node.points[0].y = 16;
155 node.points[1].x = 17;
156 node.points[1].y = 18;
157 node.points[2].x = 19;
158 node.points[2].y = 20;
160 clutter_path_add_rel_curve_to (data->path,
161 node.points[0].x, node.points[0].y,
162 node.points[1].x, node.points[1].y,
163 node.points[2].x, node.points[2].y);
165 data->nodes[data->n_nodes++] = node;
171 path_test_add_string (CallbackData *data)
175 for (i = 0; i < G_N_ELEMENTS (path_nodes); i++)
176 data->nodes[data->n_nodes++] = path_nodes[i];
178 clutter_path_add_string (data->path, path_desc);
184 path_test_add_node_by_struct (CallbackData *data)
188 for (i = 0; i < G_N_ELEMENTS (path_nodes); i++)
190 data->nodes[data->n_nodes++] = path_nodes[i];
191 clutter_path_add_node (data->path, path_nodes + i);
198 path_test_get_n_nodes (CallbackData *data)
200 return clutter_path_get_n_nodes (data->path) == data->n_nodes;
204 path_test_get_node (CallbackData *data)
208 data->nodes_found = 0;
209 data->nodes_different = FALSE;
211 for (i = 0; i < data->n_nodes; i++)
213 ClutterPathNode node;
215 clutter_path_get_node (data->path, i, &node);
217 compare_node (&node, data);
220 return !data->nodes_different;
224 path_test_get_nodes (CallbackData *data)
228 data->nodes_found = 0;
229 data->nodes_different = FALSE;
231 list = clutter_path_get_nodes (data->path);
233 for (node = list; node; node = node->next)
234 compare_node (node->data, data);
238 return !data->nodes_different && data->nodes_found == data->n_nodes;
242 path_test_insert_beginning (CallbackData *data)
244 ClutterPathNode node;
246 node.type = CLUTTER_PATH_LINE_TO;
247 node.points[0].x = 41;
248 node.points[0].y = 42;
250 memmove (data->nodes + 1, data->nodes,
251 data->n_nodes++ * sizeof (ClutterPathNode));
252 data->nodes[0] = node;
254 clutter_path_insert_node (data->path, 0, &node);
260 path_test_insert_end (CallbackData *data)
262 ClutterPathNode node;
264 node.type = CLUTTER_PATH_LINE_TO;
265 node.points[0].x = 43;
266 node.points[0].y = 44;
268 data->nodes[data->n_nodes++] = node;
270 clutter_path_insert_node (data->path, -1, &node);
276 path_test_insert_middle (CallbackData *data)
278 ClutterPathNode node;
279 int pos = data->n_nodes / 2;
281 node.type = CLUTTER_PATH_LINE_TO;
282 node.points[0].x = 45;
283 node.points[0].y = 46;
285 memmove (data->nodes + pos + 1, data->nodes + pos,
286 (data->n_nodes - pos) * sizeof (ClutterPathNode));
287 data->nodes[pos] = node;
290 clutter_path_insert_node (data->path, pos, &node);
296 path_test_clear (CallbackData *data)
298 clutter_path_clear (data->path);
306 path_test_clear_insert (CallbackData *data)
308 return path_test_clear (data) && path_test_insert_middle (data);
312 path_test_remove_beginning (CallbackData *data)
314 memmove (data->nodes, data->nodes + 1,
315 --data->n_nodes * sizeof (ClutterPathNode));
317 clutter_path_remove_node (data->path, 0);
323 path_test_remove_end (CallbackData *data)
325 clutter_path_remove_node (data->path, --data->n_nodes);
331 path_test_remove_middle (CallbackData *data)
333 int pos = data->n_nodes / 2;
335 memmove (data->nodes + pos, data->nodes + pos + 1,
336 (--data->n_nodes - pos) * sizeof (ClutterPathNode));
338 clutter_path_remove_node (data->path, pos);
344 path_test_remove_only (CallbackData *data)
346 return path_test_clear (data)
347 && path_test_add_line_to (data)
348 && path_test_remove_beginning (data);
352 path_test_replace (CallbackData *data)
354 ClutterPathNode node;
355 int pos = data->n_nodes / 2;
357 node.type = CLUTTER_PATH_LINE_TO;
358 node.points[0].x = 47;
359 node.points[0].y = 48;
361 data->nodes[pos] = node;
363 clutter_path_replace_node (data->path, pos, &node);
369 path_test_set_description (CallbackData *data)
371 data->n_nodes = G_N_ELEMENTS (path_nodes);
372 memcpy (data->nodes, path_nodes, sizeof (path_nodes));
374 return clutter_path_set_description (data->path, path_desc);
378 path_test_get_description (CallbackData *data)
383 desc1 = clutter_path_get_description (data->path);
384 clutter_path_clear (data->path);
385 if (!clutter_path_set_description (data->path, desc1))
387 desc2 = clutter_path_get_description (data->path);
389 if (strcmp (desc1, desc2))
399 path_test_convert_to_cairo_path (CallbackData *data)
401 cairo_surface_t *surface;
405 ClutterKnot path_start = { 0, 0 }, last_point = { 0, 0 };
407 /* Create a temporary image surface and context to hold the cairo
409 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 10, 10);
410 cr = cairo_create (surface);
412 /* Convert to a cairo path */
413 clutter_path_to_cairo_path (data->path, cr);
415 /* Get a copy of the cairo path data */
416 cpath = cairo_copy_path (cr);
418 /* Convert back to a clutter path */
419 clutter_path_clear (data->path);
420 clutter_path_add_cairo_path (data->path, cpath);
422 /* The relative nodes will have been converted to absolute so we
423 need to reflect this in the node array for comparison */
424 for (i = 0; i < data->n_nodes; i++)
426 switch (data->nodes[i].type)
428 case CLUTTER_PATH_MOVE_TO:
429 path_start = last_point = data->nodes[i].points[0];
432 case CLUTTER_PATH_LINE_TO:
433 last_point = data->nodes[i].points[0];
436 case CLUTTER_PATH_CURVE_TO:
437 last_point = data->nodes[i].points[2];
440 case CLUTTER_PATH_REL_MOVE_TO:
441 last_point.x += data->nodes[i].points[0].x;
442 last_point.y += data->nodes[i].points[0].y;
443 data->nodes[i].points[0] = last_point;
444 data->nodes[i].type = CLUTTER_PATH_MOVE_TO;
445 path_start = last_point;
448 case CLUTTER_PATH_REL_LINE_TO:
449 last_point.x += data->nodes[i].points[0].x;
450 last_point.y += data->nodes[i].points[0].y;
451 data->nodes[i].points[0] = last_point;
452 data->nodes[i].type = CLUTTER_PATH_LINE_TO;
455 case CLUTTER_PATH_REL_CURVE_TO:
456 for (j = 0; j < 3; j++)
458 data->nodes[i].points[j].x += last_point.x;
459 data->nodes[i].points[j].y += last_point.y;
461 last_point = data->nodes[i].points[2];
462 data->nodes[i].type = CLUTTER_PATH_CURVE_TO;
465 case CLUTTER_PATH_CLOSE:
466 last_point = path_start;
468 /* Cairo always adds a move to after every close so we need
469 to insert one here. Since Cairo commit 166453c1abf2 it
470 doesn't seem to do this anymore so will assume that if
471 Cairo's minor version is >= 11 then it includes that
473 if (cairo_version () < CAIRO_VERSION_ENCODE (1, 11, 0))
475 memmove (data->nodes + i + 2, data->nodes + i + 1,
476 (data->n_nodes - i - 1) * sizeof (ClutterPathNode));
477 data->nodes[i + 1].type = CLUTTER_PATH_MOVE_TO;
478 data->nodes[i + 1].points[0] = last_point;
485 /* Free the cairo resources */
486 cairo_path_destroy (cpath);
488 cairo_surface_destroy (surface);
494 float_fuzzy_equals (float fa, float fb)
496 return fabs (fa - fb) <= FLOAT_FUZZ_AMOUNT;
500 set_triangle_path (CallbackData *data)
502 /* Triangular shaped path hitting (0,0), (64,64) and (128,0) in four
503 parts. The two curves are actually straight lines */
504 static const ClutterPathNode nodes[] =
505 { { CLUTTER_PATH_MOVE_TO, { { 0, 0 } } },
506 { CLUTTER_PATH_LINE_TO, { { 32, 32 } } },
507 { CLUTTER_PATH_CURVE_TO, { { 40, 40 }, { 56, 56 }, { 64, 64 } } },
508 { CLUTTER_PATH_REL_CURVE_TO, { { 8, -8 }, { 24, -24 }, { 32, -32 } } },
509 { CLUTTER_PATH_REL_LINE_TO, { { 32, -32 } } } };
512 clutter_path_clear (data->path);
514 for (i = 0; i < G_N_ELEMENTS (nodes); i++)
515 clutter_path_add_node (data->path, nodes + i);
517 memcpy (data->nodes, nodes, sizeof (nodes));
518 data->n_nodes = G_N_ELEMENTS (nodes);
522 path_test_get_position (CallbackData *data)
524 static const float values[] = { 0.125f, 16.0f, 16.0f,
525 0.375f, 48.0f, 48.0f,
526 0.625f, 80.0f, 48.0f,
527 0.875f, 112.0f, 16.0f };
530 set_triangle_path (data);
532 for (i = 0; i < G_N_ELEMENTS (values); i += 3)
536 clutter_path_get_position (data->path,
540 if (!float_fuzzy_equals (values[i + 1], pos.x)
541 || !float_fuzzy_equals (values[i + 2], pos.y))
549 path_test_get_length (CallbackData *data)
551 const float actual_length /* sqrt(64**2 + 64**2) * 2 */ = 181.019336f;
554 clutter_path_set_description (data->path, "M 0 0 L 46340 0");
555 g_object_get (data->path, "length", &approx_length, NULL);
557 if (!(fabs (approx_length - 46340.f) / 46340.f <= 0.15f))
559 if (g_test_verbose ())
560 g_print ("M 0 0 L 46340 0 - Expected 46340, got %d instead.", approx_length);
565 clutter_path_set_description (data->path, "M 0 0 L 46341 0");
566 g_object_get (data->path, "length", &approx_length, NULL);
568 if (!(fabs (approx_length - 46341.f) / 46341.f <= 0.15f))
570 if (g_test_verbose ())
571 g_print ("M 0 0 L 46341 0 - Expected 46341, got %d instead.", approx_length);
576 set_triangle_path (data);
578 g_object_get (data->path, "length", &approx_length, NULL);
580 /* Allow 15% margin of error */
581 if (!(fabs (approx_length - actual_length) / (float) actual_length <= 0.15f))
583 if (g_test_verbose ())
584 g_print ("Expected %g, got %d instead.\n", actual_length, approx_length);
593 path_test_boxed_type (CallbackData *data)
599 nodes = clutter_path_get_nodes (data->path);
601 memset (&value, 0, sizeof (value));
603 for (l = nodes; l; l = l->next)
605 g_value_init (&value, CLUTTER_TYPE_PATH_NODE);
607 g_value_set_boxed (&value, l->data);
609 if (!clutter_path_node_equal (l->data,
610 g_value_get_boxed (&value)))
613 g_value_unset (&value);
616 g_slist_free (nodes);
628 { "Add line to", path_test_add_line_to },
629 { "Add move to", path_test_add_move_to },
630 { "Add curve to", path_test_add_curve_to },
631 { "Add close", path_test_add_close },
632 { "Add relative line to", path_test_add_rel_line_to },
633 { "Add relative move to", path_test_add_rel_move_to },
634 { "Add relative curve to", path_test_add_rel_curve_to },
635 { "Add string", path_test_add_string },
636 { "Add node by struct", path_test_add_node_by_struct },
637 { "Get number of nodes", path_test_get_n_nodes },
638 { "Get a node", path_test_get_node },
639 { "Get all nodes", path_test_get_nodes },
640 { "Insert at beginning", path_test_insert_beginning },
641 { "Insert at end", path_test_insert_end },
642 { "Insert at middle", path_test_insert_middle },
643 { "Add after insert", path_test_add_line_to },
644 { "Clear then insert", path_test_clear_insert },
645 { "Add string again", path_test_add_string },
646 { "Remove from beginning", path_test_remove_beginning },
647 { "Remove from end", path_test_remove_end },
648 { "Remove from middle", path_test_remove_middle },
649 { "Add after remove", path_test_add_line_to },
650 { "Remove only node", path_test_remove_only },
651 { "Add after remove again", path_test_add_line_to },
652 { "Replace a node", path_test_replace },
653 { "Set description", path_test_set_description },
654 { "Get description", path_test_get_description },
655 { "Convert to cairo path and back", path_test_convert_to_cairo_path },
656 { "Clear", path_test_clear },
657 { "Get position", path_test_get_position },
658 { "Check node boxed type", path_test_boxed_type },
659 { "Get length", path_test_get_length }
663 compare_node (const ClutterPathNode *node, gpointer data_p)
665 CallbackData *data = data_p;
667 if (data->nodes_found >= data->n_nodes)
668 data->nodes_different = TRUE;
671 guint n_points = 0, i;
672 const ClutterPathNode *onode = data->nodes + data->nodes_found;
674 if (node->type != onode->type)
675 data->nodes_different = TRUE;
677 switch (node->type & ~CLUTTER_PATH_RELATIVE)
679 case CLUTTER_PATH_MOVE_TO: n_points = 1; break;
680 case CLUTTER_PATH_LINE_TO: n_points = 1; break;
681 case CLUTTER_PATH_CURVE_TO: n_points = 3; break;
682 case CLUTTER_PATH_CLOSE: n_points = 0; break;
685 data->nodes_different = TRUE;
689 for (i = 0; i < n_points; i++)
690 if (node->points[i].x != onode->points[i].x
691 || node->points[i].y != onode->points[i].y)
693 data->nodes_different = TRUE;
702 compare_nodes (CallbackData *data)
704 data->nodes_different = FALSE;
705 data->nodes_found = 0;
707 clutter_path_foreach (data->path, compare_node, data);
709 return !data->nodes_different && data->nodes_found == data->n_nodes;
713 path_base (TestConformSimpleFixture *fixture,
719 memset (&data, 0, sizeof (data));
721 data.path = clutter_path_new ();
723 for (i = 0; i < G_N_ELEMENTS (path_tests); i++)
727 if (g_test_verbose ())
728 g_print ("%s... ", path_tests[i].desc);
730 succeeded = path_tests[i].func (&data) && compare_nodes (&data);
732 if (g_test_verbose ())
733 g_print ("%s\n", succeeded ? "ok" : "FAIL");
735 g_assert (succeeded);
738 g_object_unref (data.path);