doc: update copyright line for 2021
[adg.git] / src / tests / adg-test.c
blob87d5bc180f2ca594de693cbad3f29f7bbbdd6f3f
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2021 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/adg-internal.h>
22 #include <adg/adg-model.h>
23 #include <adg/adg-trail.h>
24 #include <adg/adg-path.h>
25 #include <adg/adg-container.h>
26 #include <adg/adg-stroke.h>
27 #include <adg/adg-table.h>
28 #include <adg/adg-title-block.h>
29 #include <adg/adg-canvas.h>
30 #include "adg-test.h"
33 typedef struct {
34 GType type;
35 gpointer instance;
36 } _BoxedData;
38 typedef struct {
39 AdgTrapsFunc func;
40 gint n_fragments;
41 } _TrapsData;
43 typedef struct {
44 gpointer instance;
45 gulong handler;
46 gboolean flag;
47 } _SignalData;
50 /* Using adg_nop() would require to pull in the whole libadg stack:
51 * better to replicate that trivial function instead.
53 static void
54 _adg_nop(void)
58 void
59 adg_test_init(int *p_argc, char **p_argv[])
61 #if GLIB_CHECK_VERSION(2, 34, 0)
62 #else
63 /* On GLib older than 2.34.0 g_type_init() *must* be called */
64 g_type_init();
65 #endif
66 g_test_init(p_argc, p_argv, NULL);
68 g_log_set_always_fatal(0);
70 /* When building in silent mode (default), the ADG_QUIET
71 * environment variable is set to 1 by the Makefile and the
72 * warnings are discarded to reduce visual cluttering.
74 if (g_getenv("ADG_QUIET") != NULL)
75 g_log_set_default_handler((GLogFunc) _adg_nop, NULL);
77 g_test_bug_base("https://track.entidi.com/%s");
80 const gpointer
81 adg_test_invalid_pointer(void)
83 static int junk[10] = { 0 };
84 return junk;
87 cairo_t *
88 adg_test_cairo_context(void)
90 cairo_surface_t *surface;
91 cairo_t *cr;
93 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 800, 600);
94 cr = cairo_create(surface);
95 cairo_surface_destroy(surface);
97 return cr;
101 adg_test_cairo_num_data(cairo_t *cr)
103 cairo_path_t *path = cairo_copy_path(cr);
104 int length = path->num_data;
105 cairo_path_destroy(path);
107 return length;
110 const cairo_path_t *
111 adg_test_path(void)
113 static cairo_path_data_t data[] = {
115 /* First segment: a valid segment with all primitive types */
116 { .header = { CPML_MOVE, 2 }},
117 { .point = { 0, 1 }},
118 { .header = { CPML_LINE, 2 }},
119 { .point = { 3, 1 }},
120 { .header = { CPML_ARC, 3 }},
121 { .point = { 4, 5 }},
122 { .point = { 6, 7 }},
123 { .header = { CPML_CURVE, 4 }},
124 { .point = { 8, 9 }},
125 { .point = { 10, 11 }},
126 { .point = { -2, 2 }},
127 { .header = { CPML_CLOSE, 1 }},
129 /* Useless CPML_MOVE */
130 { .header = { CPML_MOVE, 2 }},
131 { .point = { 0, 0 }},
133 /* Second segment: a couple of lines of length 1 and 2;
134 * line 2 intersects line 1 of the first segment in (1, 1) */
135 { .header = { CPML_MOVE, 2 }},
136 { .point = { 0, 0 }},
137 { .header = { CPML_LINE, 2 }},
138 { .point = { 1, 0 }},
139 { .header = { CPML_LINE, 2 }},
140 { .point = { 1, 2 }},
142 /* Another useless CPML_MOVE with useless embedded data */
143 { .header = { CPML_MOVE, 4 }},
144 { .point = { 1, 2 }},
145 { .point = { 3, 4 }},
146 { .point = { 5, 6 }},
148 /* Third segment: a Bézier curve with a trailing CPML_CLOSE */
149 { .header = { CPML_MOVE, 2 }},
150 { .point = { 10, 13 }},
151 { .header = { CPML_CURVE, 4 }},
152 { .point = { 8, 9 }},
153 { .point = { 10, 11 }},
154 { .point = { 12, 13 }},
155 { .header = { CPML_CLOSE, 1 }},
157 /* A valid cairo segment considered invalid by CPML
158 * because does not have a leading CPML_MOVE */
159 { .header = { CPML_LINE, 2 }},
160 { .point = { 10, 0 }},
161 { .header = { CPML_CLOSE, 1 }},
163 /* Another valid cairo segment invalid in CPML */
164 { .header = { CPML_CLOSE, 1 }},
166 /* Forth segment: a couple of arcs */
167 { .header = { CPML_MOVE, 2 }},
168 { .point = { 14, 15 }},
169 { .header = { CPML_ARC, 3 }},
170 { .point = { 17, 16 }},
171 { .point = { 18, 19 }},
172 { .header = { CPML_ARC, 3 }},
173 { .point = { 21, 20 }},
174 { .point = { 22, 23 }},
176 /* Fifth segment: a floating CPML_CLOSE */
177 { .header = { CPML_MOVE, 2 }},
178 { .point = { 24, 25 }},
179 { .header = { CPML_CLOSE, 1 }}
181 static cairo_path_t path = {
182 CAIRO_STATUS_SUCCESS,
183 data,
184 G_N_ELEMENTS(data)
186 return &path;
189 gpointer
190 adg_test_canvas(void)
192 AdgPath *path;
193 AdgStroke *stroke;
194 AdgCanvas *canvas;
196 path = adg_path_new();
197 adg_path_move_to_explicit(path, 0, 0);
198 adg_path_line_to_explicit(path, 1, 1);
199 stroke = adg_stroke_new(ADG_TRAIL(path));
200 g_object_unref(path);
202 canvas = adg_canvas_new();
203 adg_canvas_set_margins(canvas, 0, 0, 0, 0);
204 adg_canvas_set_paddings(canvas, 0, 0, 0, 0);
206 adg_container_add(ADG_CONTAINER(canvas), ADG_ENTITY(stroke));
208 return canvas;
211 static void
212 _adg_enum_checks(GType *p_type)
214 GType type = *p_type;
215 gpointer class;
217 g_free(p_type);
219 g_assert_true(G_TYPE_IS_ENUM(type));
221 class = g_type_class_ref(type);
222 g_assert_nonnull(class);
224 g_assert_null(g_enum_get_value(class, -1));
225 g_assert_nonnull(g_enum_get_value(class, 0));
226 g_assert_null(g_enum_get_value_by_name(class, "unexistent value"));
229 void
230 adg_test_add_enum_checks(const gchar *testpath, GType type)
232 GType *p_type = g_new(GType, 1);
233 *p_type = type;
234 g_test_add_data_func(testpath, p_type, (gpointer) _adg_enum_checks);
237 static void
238 _adg_boxed_checks(_BoxedData *boxed_data)
240 gpointer replica;
242 g_assert_true(G_TYPE_IS_BOXED(boxed_data->type));
244 g_assert_null(g_boxed_copy(boxed_data->type, NULL));
246 replica = g_boxed_copy(boxed_data->type, boxed_data->instance);
247 g_assert_nonnull(replica);
249 g_boxed_free(boxed_data->type, replica);
250 g_boxed_free(boxed_data->type, boxed_data->instance);
252 g_free(boxed_data);
255 void
256 adg_test_add_boxed_checks(const gchar *testpath, GType type, gpointer instance)
258 _BoxedData *boxed_data = g_new(_BoxedData, 1);
259 boxed_data->type = type;
260 boxed_data->instance = instance;
262 g_test_add_data_func(testpath, boxed_data,
263 (gpointer) _adg_boxed_checks);
266 static void
267 _adg_object_checks(GType *p_type)
269 GType type = *p_type;
271 g_free(p_type);
273 g_assert_true(G_TYPE_IS_OBJECT(type));
275 if (! G_TYPE_IS_ABSTRACT(type)) {
276 gpointer result = GINT_TO_POINTER(0xdead);
277 GObject *object = g_object_new(type, NULL);
279 g_assert_nonnull(object);
280 g_assert_true(G_IS_OBJECT(object));
282 /* Convert the floating reference, if present, to a hard reference so
283 * we can handle GTK+ widgets (that usually have a floating reference)
284 * and other object (that do not usually have floating references) in
285 * the same way.
287 if (g_object_is_floating(object)) {
288 g_object_ref_sink(object);
291 /* Check that unknown or unexistent properties does
292 * not return values (result should pass unmodified)
294 g_assert_cmpint(GPOINTER_TO_INT(result), ==, 0xdead);
295 g_object_set(object, "unknown", NULL, NULL);
296 g_object_get(object, "unknown", &result, NULL);
297 g_assert_cmpint(GPOINTER_TO_INT(result), ==, 0xdead);
298 g_object_get(object, "unexistent", &result, NULL);
299 g_assert_cmpint(GPOINTER_TO_INT(result), ==, 0xdead);
301 /* Check object lifetime: this can be done with a weak pointer
302 * that will "nullifies" result after object is destructed
304 g_object_add_weak_pointer(object, &result);
305 g_object_ref(object);
306 g_object_unref(object);
307 g_assert_nonnull(result);
308 g_object_unref(object);
309 g_assert_null(result);
311 result = GINT_TO_POINTER(0xdead);
312 object = g_object_new(type, NULL);
314 g_object_add_weak_pointer(object, &result);
315 g_assert_nonnull(result);
316 g_object_ref(object);
317 g_object_ref(object);
318 g_object_run_dispose(object);
319 g_assert_null(result);
323 void
324 adg_test_add_object_checks(const gchar *testpath, GType type)
326 GType *p_type = g_new(GType, 1);
327 *p_type = type;
328 g_test_add_data_func(testpath, p_type, (gpointer) _adg_object_checks);
331 static void
332 _adg_test_set(gboolean *p_flag)
334 *p_flag = TRUE;
337 static _SignalData signal_data;
339 void
340 adg_test_signal(gpointer instance, const gchar *detailed_signal)
342 signal_data.instance = instance;
344 /* The swapped variant *must* be used, otherwise the position of flag
345 * would depend on the number of parameters of the signal. This also
346 * implies I cannot use _adg_increment() here. */
347 signal_data.handler = g_signal_connect_swapped(instance,
348 detailed_signal,
349 G_CALLBACK(_adg_test_set),
350 &signal_data.flag);
351 signal_data.flag = FALSE;
354 gboolean
355 adg_test_signal_check(gboolean disconnect)
357 gboolean last_flag;
359 if (disconnect) {
360 /* A handler cannot be disconnected twice */
361 g_assert(signal_data.instance != NULL);
362 g_signal_handler_disconnect(signal_data.instance, signal_data.handler);
363 signal_data.instance = NULL;
366 last_flag = signal_data.flag;
367 signal_data.flag = FALSE;
369 return last_flag;
372 static void
373 _adg_model_checks(GType *p_type)
375 GType type = *p_type;
377 g_free(p_type);
379 g_assert_true(g_type_is_a(type, ADG_TYPE_MODEL));
381 if (! G_TYPE_IS_ABSTRACT(type)) {
382 AdgModel *model = g_object_new(type, NULL);
384 g_assert_nonnull(model);
385 g_assert_true(ADG_IS_MODEL(model));
387 /* Check the clear signal */
388 adg_test_signal(model, "clear");
389 adg_model_clear(model);
390 g_assert_true(adg_test_signal_check(TRUE));
392 /* Check the reset signal */
393 adg_test_signal(model, "reset");
394 adg_model_reset(model);
395 g_assert_true(adg_test_signal_check(TRUE));
397 /* Check the reset signal triggers the clear signal */
398 adg_test_signal(model, "clear");
399 adg_model_reset(model);
400 g_assert_true(adg_test_signal_check(TRUE));
402 /* Check the changed signal */
403 adg_test_signal(model, "changed");
404 adg_model_changed(model);
405 g_assert_true(adg_test_signal_check(TRUE));
407 g_object_unref(model);
411 void
412 adg_test_add_model_checks(const gchar *testpath, GType type)
414 GType *p_type = g_new(GType, 1);
415 *p_type = type;
416 g_test_add_data_func(testpath, p_type, (gpointer) _adg_model_checks);
419 static void
420 _adg_entity_checks(GType *p_type)
422 GType type = *p_type;
424 g_free(p_type);
426 g_assert_true(g_type_is_a(type, ADG_TYPE_ENTITY));
428 if (! G_TYPE_IS_ABSTRACT(type)) {
429 AdgEntity *entity = g_object_new(type, NULL);
430 const CpmlExtents *extents;
432 g_assert_nonnull(entity);
433 g_assert_true(ADG_IS_ENTITY(entity));
435 /* Ensure that the entity extents are not initially defined */
436 extents = adg_entity_get_extents(entity);
437 g_assert_false(extents->is_defined);
439 adg_test_signal(entity, "arrange");
440 adg_entity_arrange(entity);
441 g_assert_true(adg_test_signal_check(TRUE));
443 extents = adg_entity_get_extents(entity);
444 if (extents->is_defined) {
445 /* RENDERABLE ENTITY */
446 /* Check that invalidate() clears the cached extents */
447 adg_test_signal(entity, "invalidate");
448 adg_entity_invalidate(entity);
449 g_assert_true(adg_test_signal_check(TRUE));
451 extents = adg_entity_get_extents(entity);
452 g_assert_false(extents->is_defined);
453 } else {
454 /* NON-RENDERABLE ENTITY (e.g., empty container) */
457 g_object_unref(entity);
461 void
462 adg_test_add_entity_checks(const gchar *testpath, GType type)
464 GType *p_type = g_new(GType, 1);
465 *p_type = type;
466 g_test_add_data_func(testpath, p_type, (gpointer) _adg_entity_checks);
469 static void
470 _adg_increment(GObject *object, gint *counter)
472 ++(*counter);
475 static void
476 _adg_container_checks(GType *p_type)
478 GType type = *p_type;
480 g_free(p_type);
482 g_assert_true(g_type_is_a(type, ADG_TYPE_CONTAINER));
484 if (! G_TYPE_IS_ABSTRACT(type)) {
485 AdgContainer *container;
486 AdgPath *path;
487 AdgStroke *stroke;
488 const CpmlExtents *extents;
489 gint counter;
491 container = g_object_new(type, NULL);
493 g_assert_nonnull(container);
494 g_assert_true(ADG_IS_CONTAINER(container));
496 counter = 0;
497 adg_container_foreach(container, G_CALLBACK(_adg_increment), &counter);
498 g_assert_cmpint(counter, ==, 0);
500 path = adg_path_new();
501 adg_path_move_to_explicit(path, -123456, -789012);
502 adg_path_line_to_explicit(path, 654321, 210987);
503 stroke = adg_stroke_new(ADG_TRAIL(path));
504 g_object_unref(path);
506 /* stroke has a floating reference that will be owned by container */
507 g_assert_null(adg_entity_get_parent(ADG_ENTITY(stroke)));
508 adg_test_signal(container, "add");
509 adg_container_add(container, ADG_ENTITY(stroke));
510 g_assert_true(adg_test_signal_check(TRUE));
511 g_assert_true(adg_entity_get_parent(ADG_ENTITY(stroke)) == ADG_ENTITY(container));
513 /* Ensure container-add add a reference to stroke */
514 g_assert_true(ADG_IS_STROKE(stroke));
516 /* The following command should basically be a no-op */
517 adg_container_add(container, ADG_ENTITY(stroke));
519 counter = 0;
520 adg_container_foreach(container, G_CALLBACK(_adg_increment), &counter);
521 g_assert_cmpint(counter, ==, 1);
523 /* Check the extents are *at least* as big as stroke. We cannot use
524 * equality because some container can have margins (e.g. AdgCanvas) */
525 adg_entity_arrange(ADG_ENTITY(container));
526 extents = adg_entity_get_extents(ADG_ENTITY(container));
527 g_assert_true(extents->is_defined);
528 g_assert_cmpfloat(extents->org.x, <=, -123456);
529 g_assert_cmpfloat(extents->org.y, <=, -789012);
530 g_assert_cmpfloat(extents->size.x, >=, 123456 + 654321);
531 g_assert_cmpfloat(extents->size.y, >=, 789012 + 210987);
533 /* Keep stroke around */
534 g_object_ref(stroke);
536 adg_test_signal(container, "remove");
537 adg_container_remove(container, ADG_ENTITY(stroke));
538 g_assert_true(adg_test_signal_check(TRUE));
539 g_assert_null(adg_entity_get_parent(ADG_ENTITY(stroke)));
541 counter = 0;
542 adg_container_foreach(container, G_CALLBACK(_adg_increment), &counter);
543 g_assert_cmpint(counter, ==, 0);
545 adg_entity_arrange(ADG_ENTITY(container));
546 g_assert_cmpfloat(extents->org.x, >, -123456);
547 g_assert_cmpfloat(extents->org.y, >, -789012);
548 g_assert_cmpfloat(extents->size.x, <, 123456 + 654321);
549 g_assert_cmpfloat(extents->size.y, <, 789012 + 210987);
551 /* Check destroying a child remove it from container */
552 adg_container_add(container, ADG_ENTITY(stroke));
553 g_object_unref(stroke);
555 counter = 0;
556 adg_container_foreach(container, G_CALLBACK(_adg_increment), &counter);
557 g_assert_cmpint(counter, ==, 1);
559 g_object_run_dispose(G_OBJECT(stroke));
561 counter = 0;
562 adg_container_foreach(container, G_CALLBACK(_adg_increment), &counter);
563 g_assert_cmpint(counter, ==, 0);
565 g_object_unref(container);
569 void
570 adg_test_add_container_checks(const gchar *testpath, GType type)
572 GType *p_type = g_new(GType, 1);
573 *p_type = type;
574 g_test_add_data_func(testpath, p_type, (gpointer) _adg_container_checks);
577 static void
578 _adg_global_space_checks(AdgEntity *entity)
580 cairo_t *cr;
581 const CpmlExtents *extents;
582 cairo_matrix_t scale2X;
583 gdouble width, height;
585 cr = adg_test_cairo_context();
586 cairo_matrix_init_scale(&scale2X, 2, 2);
588 adg_switch_extents(g_test_rand_bit());
590 /* Store the original extents size in width/height */
591 adg_entity_render(entity, cr);
592 extents = adg_entity_get_extents(entity);
593 g_assert_true(extents->is_defined);
594 width = extents->size.x;
595 height = extents->size.y;
597 /* Check explicit global-changed emission */
598 adg_test_signal(entity, "global-changed");
599 adg_entity_global_changed(entity);
600 g_assert_true(adg_test_signal_check(FALSE));
602 /* Check that a zoom in global space scales is roughly equivalent to
603 * the same zoom on extents too (not excactly because of fonts) */
604 adg_entity_transform_global_map(entity, &scale2X, ADG_TRANSFORM_BEFORE);
605 /* The "global-changed" signal handler has not been called here because
606 * its emission is lazy, that is it will be emitted only when needed */
607 g_assert_false(adg_test_signal_check(FALSE));
609 adg_entity_invalidate(entity);
610 g_assert_false(extents->is_defined);
611 /* Still no global-changed signal emitted */
612 g_assert_false(adg_test_signal_check(FALSE));
614 adg_entity_arrange(entity);
615 /* The "global-changed" signal has been emitted in the arrange phase */
616 g_assert_true(adg_test_signal_check(TRUE));
618 adg_test_signal(entity, "render");
619 adg_entity_render(entity, cr);
620 g_assert_true(adg_test_signal_check(TRUE));
622 /* Let's call render twice to check caching does not break anything */
623 adg_test_signal(entity, "render");
624 adg_entity_render(entity, cr);
625 g_assert_true(adg_test_signal_check(TRUE));
627 extents = adg_entity_get_extents(entity);
628 g_assert_cmpfloat(extents->size.x, >, width * 1.7);
629 g_assert_cmpfloat(extents->size.x, <, width * 2.3);
630 g_assert_cmpfloat(extents->size.y, >, height * 1.7);
631 g_assert_cmpfloat(extents->size.y, <, height * 2.3);
633 /* Restore the original global scale */
634 cairo_matrix_invert(&scale2X);
635 adg_entity_transform_global_map(entity, &scale2X, ADG_TRANSFORM_BEFORE);
636 adg_entity_invalidate(entity);
637 g_assert_false(extents->is_defined);
638 adg_entity_render(entity, cr);
639 extents = adg_entity_get_extents(entity);
640 g_assert_cmpfloat(extents->size.x, ==, width);
641 g_assert_cmpfloat(extents->size.y, ==, height);
643 cairo_destroy(cr);
644 g_object_unref(entity);
647 void
648 adg_test_add_global_space_checks(const gchar *testpath, gpointer entity)
650 g_test_add_data_func(testpath, entity,
651 (gpointer) _adg_global_space_checks);
654 static void
655 _adg_local_space_checks(AdgEntity *entity)
657 cairo_t *cr;
658 const CpmlExtents *extents;
659 cairo_matrix_t scale2X;
660 gdouble width, height;
662 cr = adg_test_cairo_context();
663 cairo_matrix_init_scale(&scale2X, 2, 2);
665 /* Store the original extents size in width/height */
666 adg_test_signal(entity, "render");
667 adg_entity_render(entity, cr);
668 g_assert_true(adg_test_signal_check(TRUE));
670 extents = adg_entity_get_extents(entity);
671 g_assert_true(extents->is_defined);
672 width = extents->size.x;
673 height = extents->size.y;
675 /* Check that a scale in local space somewhat scales the extents too */
676 adg_test_signal(entity, "local-changed");
677 adg_entity_transform_local_map(entity, &scale2X, ADG_TRANSFORM_BEFORE);
678 g_assert_false(adg_test_signal_check(FALSE));
680 adg_entity_invalidate(entity);
681 g_assert_false(extents->is_defined);
682 adg_entity_render(entity, cr);
683 /* The "local-changed" signal has been emitted here because "render" must
684 * also call "arrange" that, in turn, should trigger "local-changed" */
685 g_assert_true(adg_test_signal_check(TRUE));
687 extents = adg_entity_get_extents(entity);
688 g_assert_cmpfloat(extents->size.x, >, width);
689 g_assert_cmpfloat(extents->size.y, >, height);
691 /* Restore the original local scale */
692 cairo_matrix_invert(&scale2X);
693 adg_entity_transform_local_map(entity, &scale2X, ADG_TRANSFORM_BEFORE);
694 adg_entity_invalidate(entity);
695 g_assert_false(extents->is_defined);
696 adg_entity_render(entity, cr);
697 extents = adg_entity_get_extents(entity);
698 g_assert_cmpfloat(extents->size.x, ==, width);
699 g_assert_cmpfloat(extents->size.y, ==, height);
701 cairo_destroy(cr);
702 g_object_unref(entity);
705 void
706 adg_test_add_local_space_checks(const gchar *testpath, gpointer entity)
708 g_test_add_data_func(testpath, entity,
709 (gpointer) _adg_local_space_checks);
712 #if GLIB_CHECK_VERSION(2, 38, 0)
714 static void
715 _adg_trap(AdgTrapsFunc func, gint i)
717 if (g_test_subprocess()) {
718 func(i);
719 return;
721 g_test_trap_subprocess(NULL, 0, 0);
722 g_print("\b\b\b%2d ", i);
723 func(0);
726 #else
728 #include <stdlib.h>
730 #define ADG_TEST_TRAP_FLAGS (G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)
732 static void
733 _adg_trap(AdgTrapsFunc func, gint i)
735 if (g_test_trap_fork(0, ADG_TEST_TRAP_FLAGS)) {
736 func(i);
737 exit(0);
739 g_print("\b\b\b%2d ", i);
740 func(0);
743 #endif
745 static void
746 _adg_traps(_TrapsData *traps_data)
748 AdgTrapsFunc func = traps_data->func;
749 gint n_fragments = traps_data->n_fragments;
750 gint i;
752 g_free(traps_data);
754 for (i = 1; i <= n_fragments; ++i) {
755 _adg_trap(func, i);
759 void
760 adg_test_add_traps(const gchar *testpath, AdgTrapsFunc func, gint n_fragments)
762 _TrapsData *traps_data;
764 g_return_if_fail(n_fragments > 0);
766 traps_data = g_new(_TrapsData, 1);
767 traps_data->func = func;
768 traps_data->n_fragments = n_fragments;
769 g_test_add_data_func(testpath, traps_data, (gpointer) _adg_traps);