1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 #include "adg-internal.h"
36 /* Using adg_nop() would require to pull in the whole libadg stack:
37 * better to replicate that trivial function instead.
45 adg_test_init(int *p_argc
, char **p_argv
[])
47 #if GLIB_CHECK_VERSION(2, 34, 0)
49 /* On GLib older than 2.34.0 g_type_init() *must* be called */
52 g_test_init(p_argc
, p_argv
, NULL
);
54 g_log_set_always_fatal(0);
56 /* When building in silent mode (default), the ADG_QUIET
57 * environment variable is set to 1 by the Makefile and the
58 * warnings are discarded to reduce visual cluttering.
60 if (g_getenv("ADG_QUIET") != NULL
)
61 g_log_set_default_handler((GLogFunc
) _adg_nop
, NULL
);
63 g_test_bug_base("http://dev.entidi.com/p/adg/issues/%s/");
67 adg_test_invalid_pointer(void)
69 static int junk
[10] = { 0 };
74 adg_test_cairo_context(void)
76 cairo_surface_t
*surface
;
79 surface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, 800, 600);
80 cr
= cairo_create(surface
);
81 cairo_surface_destroy(surface
);
87 adg_test_cairo_num_data(cairo_t
*cr
)
89 cairo_path_t
*path
= cairo_copy_path(cr
);
90 int length
= path
->num_data
;
91 cairo_path_destroy(path
);
99 static cairo_path_data_t data
[] = {
101 /* First segment: a valid segment with all primitive types */
102 { .header
= { CPML_MOVE
, 2 }},
103 { .point
= { 0, 1 }},
104 { .header
= { CPML_LINE
, 2 }},
105 { .point
= { 3, 1 }},
106 { .header
= { CPML_ARC
, 3 }},
107 { .point
= { 4, 5 }},
108 { .point
= { 6, 7 }},
109 { .header
= { CPML_CURVE
, 4 }},
110 { .point
= { 8, 9 }},
111 { .point
= { 10, 11 }},
112 { .point
= { -2, 2 }},
113 { .header
= { CPML_CLOSE
, 1 }},
115 /* Useless CPML_MOVE */
116 { .header
= { CPML_MOVE
, 2 }},
117 { .point
= { 0, 0 }},
119 /* Second segment: a couple of lines of length 1 and 2;
120 * line 2 intersects line 1 of the first segment in (1, 1) */
121 { .header
= { CPML_MOVE
, 2 }},
122 { .point
= { 0, 0 }},
123 { .header
= { CPML_LINE
, 2 }},
124 { .point
= { 1, 0 }},
125 { .header
= { CPML_LINE
, 2 }},
126 { .point
= { 1, 2 }},
128 /* Another useless CPML_MOVE with useless embedded data */
129 { .header
= { CPML_MOVE
, 4 }},
130 { .point
= { 1, 2 }},
131 { .point
= { 3, 4 }},
132 { .point
= { 5, 6 }},
134 /* Third segment: a Bézier curve with a trailing CPML_CLOSE */
135 { .header
= { CPML_MOVE
, 2 }},
136 { .point
= { 10, 13 }},
137 { .header
= { CPML_CURVE
, 4 }},
138 { .point
= { 8, 9 }},
139 { .point
= { 10, 11 }},
140 { .point
= { 12, 13 }},
141 { .header
= { CPML_CLOSE
, 1 }},
143 /* A valid cairo segment considered invalid by CPML
144 * because does not have a leading CPML_MOVE */
145 { .header
= { CPML_LINE
, 2 }},
146 { .point
= { 10, 0 }},
147 { .header
= { CPML_CLOSE
, 1 }},
149 /* Another valid cairo segment invalid in CPML */
150 { .header
= { CPML_CLOSE
, 1 }},
152 /* Forth segment: a couple of arcs */
153 { .header
= { CPML_MOVE
, 2 }},
154 { .point
= { 14, 15 }},
155 { .header
= { CPML_ARC
, 3 }},
156 { .point
= { 16, 17 }},
157 { .point
= { 18, 19 }},
158 { .header
= { CPML_ARC
, 3 }},
159 { .point
= { 20, 21 }},
160 { .point
= { 22, 23 }},
162 /* Fifth segment: a floating CPML_CLOSE */
163 { .header
= { CPML_MOVE
, 2 }},
164 { .point
= { 24, 25 }},
165 { .header
= { CPML_CLOSE
, 1 }}
167 static cairo_path_t path
= {
168 CAIRO_STATUS_SUCCESS
,
176 _adg_enum_checks(gconstpointer user_data
)
181 type
= GPOINTER_TO_INT(user_data
);
182 g_assert_true(G_TYPE_IS_ENUM(type
));
184 class = g_type_class_ref(type
);
185 g_assert_nonnull(class);
187 g_assert_null(g_enum_get_value(class, -1));
188 g_assert_nonnull(g_enum_get_value(class, 0));
189 g_assert_null(g_enum_get_value_by_name(class, "unexistent value"));
193 adg_test_add_enum_checks(const gchar
*testpath
, GType type
)
195 g_test_add_data_func(testpath
, GINT_TO_POINTER(type
),
196 (gpointer
) _adg_enum_checks
);
200 _adg_boxed_checks(_BoxedData
*boxed_data
)
204 g_assert_true(G_TYPE_IS_BOXED(boxed_data
->type
));
206 g_assert_null(g_boxed_copy(boxed_data
->type
, NULL
));
208 replica
= g_boxed_copy(boxed_data
->type
, boxed_data
->instance
);
209 g_assert_nonnull(replica
);
211 g_boxed_free(boxed_data
->type
, replica
);
212 g_boxed_free(boxed_data
->type
, boxed_data
->instance
);
218 adg_test_add_boxed_checks(const gchar
*testpath
, GType type
, gpointer instance
)
220 _BoxedData
*boxed_data
= g_new(_BoxedData
, 1);
221 boxed_data
->type
= type
;
222 boxed_data
->instance
= instance
;
224 g_test_add_data_func(testpath
, boxed_data
,
225 (gpointer
) _adg_boxed_checks
);
229 _adg_object_checks(gconstpointer user_data
)
231 GType type
= GPOINTER_TO_INT(user_data
);
232 g_assert_true(G_TYPE_IS_OBJECT(type
));
234 if (! G_TYPE_IS_ABSTRACT(type
)) {
235 gpointer result
= GINT_TO_POINTER(0xdead);
236 GObject
*object
= g_object_new(type
, NULL
);
238 g_assert_nonnull(object
);
239 g_assert_true(G_IS_OBJECT(object
));
241 /* Check that unknown or unexistent properties does
242 * not return values (result should pass unmodified)
244 g_assert_cmpint(GPOINTER_TO_INT(result
), ==, 0xdead);
245 g_object_set(object
, "unknown", NULL
, NULL
);
246 g_object_get(object
, "unknown", &result
, NULL
);
247 g_assert_cmpint(GPOINTER_TO_INT(result
), ==, 0xdead);
248 g_object_get(object
, "unexistent", &result
, NULL
);
249 g_assert_cmpint(GPOINTER_TO_INT(result
), ==, 0xdead);
251 /* Check object lifetime: this can be done with a weak pointer
252 * that will "nullifies" result after object is destructed
254 g_object_add_weak_pointer(object
, &result
);
255 g_object_ref(object
);
256 g_object_unref(object
);
257 g_assert_nonnull(result
);
258 g_object_unref(object
);
259 g_assert_null(result
);
261 result
= GINT_TO_POINTER(0xdead);
262 object
= g_object_new(type
, NULL
);
264 g_object_add_weak_pointer(object
, &result
);
265 g_assert_nonnull(result
);
266 g_object_ref(object
);
267 g_object_ref(object
);
268 g_object_run_dispose(object
);
269 g_assert_null(result
);
274 adg_test_add_object_checks(const gchar
*testpath
, GType type
)
276 g_test_add_data_func(testpath
, GINT_TO_POINTER(type
),
277 (gpointer
) _adg_object_checks
);
281 _adg_entity_checks(gconstpointer user_data
)
283 GType type
= GPOINTER_TO_INT(user_data
);
284 g_assert_true(g_type_is_a(type
, ADG_TYPE_ENTITY
));
286 if (! G_TYPE_IS_ABSTRACT(type
)) {
287 AdgEntity
*entity
= g_object_new(type
, NULL
);
288 const CpmlExtents
*extents
;
290 g_assert_nonnull(entity
);
291 g_assert_true(ADG_IS_ENTITY(entity
));
293 /* Ensure that the entity extents are not initially defined */
294 extents
= adg_entity_get_extents(entity
);
295 g_assert_false(extents
->is_defined
);
297 adg_entity_arrange(entity
);
298 extents
= adg_entity_get_extents(entity
);
299 if (extents
->is_defined
) {
300 /* RENDERABLE ENTITY */
301 /* Check that invalidate() clears the cached extents */
302 adg_entity_invalidate(entity
);
303 extents
= adg_entity_get_extents(entity
);
304 g_assert_false(extents
->is_defined
);
306 /* NON-RENDERABLE ENTITY (e.g., empty container) */
309 g_object_unref(entity
);
314 adg_test_add_entity_checks(const gchar
*testpath
, GType type
)
316 g_test_add_data_func(testpath
, GINT_TO_POINTER(type
),
317 (gpointer
) _adg_entity_checks
);
321 _adg_global_space_checks(AdgEntity
*entity
)
324 const CpmlExtents
*extents
;
325 cairo_matrix_t scale2X
;
326 gdouble width
, height
;
328 cr
= adg_test_cairo_context();
329 cairo_matrix_init_scale(&scale2X
, 2, 2);
331 /* Store the original extents size in width/height */
332 adg_entity_render(entity
, cr
);
333 extents
= adg_entity_get_extents(entity
);
334 g_assert_true(extents
->is_defined
);
335 width
= extents
->size
.x
;
336 height
= extents
->size
.y
;
338 /* Check that a zoom in global space scales is roughly equivalent to
339 * the same zoom on extents too (not excactly because of fonts) */
340 adg_entity_transform_global_map(entity
, &scale2X
, ADG_TRANSFORM_BEFORE
);
341 adg_entity_invalidate(entity
);
342 g_assert_false(extents
->is_defined
);
343 adg_entity_render(entity
, cr
);
344 extents
= adg_entity_get_extents(entity
);
345 g_assert_cmpfloat(extents
->size
.x
, >, width
* 1.7);
346 g_assert_cmpfloat(extents
->size
.x
, <, width
* 2.3);
347 g_assert_cmpfloat(extents
->size
.y
, >, height
* 1.7);
348 g_assert_cmpfloat(extents
->size
.y
, <, height
* 2.3);
350 /* Restore the original global scale */
351 cairo_matrix_invert(&scale2X
);
352 adg_entity_transform_global_map(entity
, &scale2X
, ADG_TRANSFORM_BEFORE
);
353 adg_entity_invalidate(entity
);
354 g_assert_false(extents
->is_defined
);
355 adg_entity_render(entity
, cr
);
356 extents
= adg_entity_get_extents(entity
);
357 g_assert_cmpfloat(extents
->size
.x
, ==, width
);
358 g_assert_cmpfloat(extents
->size
.y
, ==, height
);
361 g_object_unref(entity
);
365 adg_test_add_global_space_checks(const gchar
*testpath
, gpointer entity
)
367 g_test_add_data_func(testpath
, entity
,
368 (gpointer
) _adg_global_space_checks
);
372 _adg_local_space_checks(AdgEntity
*entity
)
375 const CpmlExtents
*extents
;
376 cairo_matrix_t scale2X
;
377 gdouble width
, height
;
379 cr
= adg_test_cairo_context();
380 cairo_matrix_init_scale(&scale2X
, 2, 2);
382 /* Store the original extents size in width/height */
383 adg_entity_render(entity
, cr
);
384 extents
= adg_entity_get_extents(entity
);
385 g_assert_true(extents
->is_defined
);
386 width
= extents
->size
.x
;
387 height
= extents
->size
.y
;
389 /* Check that a scale in local space somewhat scales the extents too */
390 adg_entity_transform_local_map(entity
, &scale2X
, ADG_TRANSFORM_BEFORE
);
391 adg_entity_invalidate(entity
);
392 g_assert_false(extents
->is_defined
);
393 adg_entity_render(entity
, cr
);
394 extents
= adg_entity_get_extents(entity
);
395 g_assert_cmpfloat(extents
->size
.x
, >, width
);
396 g_assert_cmpfloat(extents
->size
.y
, >, height
);
398 /* Restore the original local scale */
399 cairo_matrix_invert(&scale2X
);
400 adg_entity_transform_local_map(entity
, &scale2X
, ADG_TRANSFORM_BEFORE
);
401 adg_entity_invalidate(entity
);
402 g_assert_false(extents
->is_defined
);
403 adg_entity_render(entity
, cr
);
404 extents
= adg_entity_get_extents(entity
);
405 g_assert_cmpfloat(extents
->size
.x
, ==, width
);
406 g_assert_cmpfloat(extents
->size
.y
, ==, height
);
409 g_object_unref(entity
);
413 adg_test_add_local_space_checks(const gchar
*testpath
, gpointer entity
)
415 g_test_add_data_func(testpath
, entity
,
416 (gpointer
) _adg_local_space_checks
);
419 #if GLIB_CHECK_VERSION(2, 38, 0)
422 _adg_trap(AdgTrapsFunc func
, gint i
)
424 if (g_test_subprocess()) {
428 g_test_trap_subprocess(NULL
, 0, 0);
429 g_print("\b\b\b%2d ", i
);
437 #define ADG_TEST_TRAP_FLAGS (G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)
440 _adg_trap(AdgTrapsFunc func
, gint i
)
442 if (g_test_trap_fork(0, ADG_TEST_TRAP_FLAGS
)) {
446 g_print("\b\b\b%2d ", i
);
453 _adg_traps(_TrapsData
*traps_data
)
455 AdgTrapsFunc func
= traps_data
->func
;
456 gint n_fragments
= traps_data
->n_fragments
;
461 for (i
= 1; i
<= n_fragments
; ++i
) {
467 adg_test_add_traps(const gchar
*testpath
, AdgTrapsFunc func
, gint n_fragments
)
469 _TrapsData
*traps_data
;
471 g_return_if_fail(n_fragments
> 0);
473 traps_data
= g_new(_TrapsData
, 1);
474 traps_data
->func
= func
;
475 traps_data
->n_fragments
= n_fragments
;
476 g_test_add_data_func(testpath
, traps_data
, (gpointer
) _adg_traps
);