Tizen 2.0 Release
[framework/graphics/cairo.git] / test / cairo-test-trace.c
1 /*
2  * Copyright © 2009 Chris Wilson
3  *
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without
6  * fee, provided that the above copyright notice appear in all copies
7  * and that both that copyright notice and this permission notice
8  * appear in supporting documentation, and that the name of
9  * the authors not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior
11  * permission. The authors make no representations about the
12  * suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
18  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: Chris Wilson <chris@chris-wilson.co.uk>
24  */
25
26 /*
27  * The basic idea is that we feed the trace to multiple backends in parallel
28  * and compare the output at the end of each context (based on the premise
29  * that contexts demarcate expose events, or their logical equivalents) with
30  * that of the image[1] backend. Each backend is executed in a separate
31  * process, for robustness and to isolate the global cairo state, with the
32  * image data residing in shared memory and synchronising over a socket.
33  *
34  * [1] Should be reference implementation, currently the image backend is
35  *     considered to be the reference for all other backends.
36  */
37
38 /* XXX Can't directly compare fills using spans versus trapezoidation,
39  *     i.e. xlib vs image. Gah, kinda renders this whole scheme moot.
40  *     How about reference platforms?
41  *     E.g. accelerated xlib driver vs Xvfb?
42  *
43  *     boilerplate->create_reference_surface()?
44  *     boilerplate->reference->create_surface()?
45  *     So for each backend spawn two processes, a reference and xlib
46  *     (obviously minimising the number of reference processes when possible)
47  */
48
49 /*
50  * XXX Handle show-page as well as cairo_destroy()? Though arguably that is
51  *     only relevant for paginated backends which is currently outside the
52  *     scope of this test.
53  */
54
55 #define _GNU_SOURCE 1   /* getline() */
56
57 #include "cairo-test.h"
58 #include "buffer-diff.h"
59
60 #include "cairo-boilerplate-getopt.h"
61 #include <cairo-script-interpreter.h>
62 #include "cairo-missing.h"
63
64 #if CAIRO_HAS_SCRIPT_SURFACE
65 #include <cairo-script.h>
66 #endif
67
68 /* For basename */
69 #ifdef HAVE_LIBGEN_H
70 #include <libgen.h>
71 #endif
72 #include <ctype.h> /* isspace() */
73
74 #include <sys/types.h>
75 #include <dirent.h>
76 #include <fcntl.h>
77 #include <signal.h>
78 #include <sys/wait.h>
79 #include <sys/stat.h>
80 #include <sys/socket.h>
81 #include <sys/mman.h>
82 #include <sys/poll.h>
83 #include <sys/un.h>
84 #include <errno.h>
85 #include <assert.h>
86 #if CAIRO_HAS_REAL_PTHREAD
87 #include <pthread.h>
88 #endif
89
90 #if HAVE_FCFINI
91 #include <fontconfig/fontconfig.h>
92 #endif
93
94 #define DEBUG 0
95
96 #define ignore_image_differences 0 /* XXX make me a cmdline option! */
97 #define write_results 1
98 #define write_traces 1
99
100 #define DATA_SIZE (256 << 20)
101 #define SHM_PATH_XXX "/.shmem-cairo-trace"
102
103 typedef struct _test_trace {
104     /* Options from command-line */
105     cairo_bool_t list_only;
106     char **names;
107     unsigned int num_names;
108     char **exclude_names;
109     unsigned int num_exclude_names;
110
111     /* Stuff used internally */
112     const cairo_boilerplate_target_t **targets;
113     int num_targets;
114 } test_trace_t;
115
116 typedef struct _test_runner {
117     const char *name;
118     cairo_surface_t *surface;
119     void *closure;
120     uint8_t *base;
121     const char *trace;
122     pid_t pid;
123     int sk;
124     cairo_bool_t is_recording;
125
126     cairo_script_interpreter_t *csi;
127     struct context_closure {
128         struct context_closure *next;
129         unsigned long id;
130         unsigned long start_line;
131         unsigned long end_line;
132         cairo_t *context;
133         cairo_surface_t *surface;
134     } *contexts;
135
136     unsigned long context_id;
137 } test_runner_t;
138
139 struct slave {
140     pid_t pid;
141     int fd;
142     unsigned long image_serial;
143     unsigned long image_ready;
144     unsigned long start_line;
145     unsigned long end_line;
146     cairo_surface_t *image;
147     long width, height;
148     cairo_surface_t *difference;
149     buffer_diff_result_t result;
150     const cairo_boilerplate_target_t *target;
151     const struct slave *reference;
152     cairo_bool_t is_recording;
153 };
154
155 struct request_image {
156     unsigned long id;
157     unsigned long start_line;
158     unsigned long end_line;
159     cairo_format_t format;
160     long width;
161     long height;
162     long stride;
163 };
164
165 struct surface_tag {
166     long width, height;
167 };
168 static const cairo_user_data_key_t surface_tag;
169
170 #define TARGET_NAME(T)  ((T) ? (T)->name : "recording")
171
172 #if CAIRO_HAS_REAL_PTHREAD
173 #define tr_die(t) t->is_recording ? pthread_exit(NULL) : exit(1)
174 #else
175 #define tr_die(t) exit(1)
176 #endif
177
178 static cairo_bool_t
179 writen (int fd, const void *ptr, int len)
180 {
181 #if 0
182     const uint8_t *data = ptr;
183     while (len) {
184         int ret = write (fd, data, len);
185         if (ret < 0) {
186             switch (errno) {
187             case EAGAIN:
188             case EINTR:
189                 continue;
190             default:
191                 return FALSE;
192             }
193         } else if (ret == 0) {
194             return FALSE;
195         } else {
196             data += ret;
197             len -= ret;
198         }
199     }
200     return TRUE;
201 #else
202     int ret = send (fd, ptr, len, 0);
203     return ret == len;
204 #endif
205 }
206
207 static cairo_bool_t
208 readn (int fd, void *ptr, int len)
209 {
210 #if 0
211     uint8_t *data = ptr;
212     while (len) {
213         int ret = read (fd, data, len);
214         if (ret < 0) {
215             switch (errno) {
216             case EAGAIN:
217             case EINTR:
218                 continue;
219             default:
220                 return FALSE;
221             }
222         } else if (ret == 0) {
223             return FALSE;
224         } else {
225             data += ret;
226             len -= ret;
227         }
228     }
229     return TRUE;
230 #else
231     int ret = recv (fd, ptr, len, MSG_WAITALL);
232     return ret == len;
233 #endif
234 }
235
236 static cairo_format_t
237 format_for_content (cairo_content_t content)
238 {
239     switch (content) {
240     case CAIRO_CONTENT_ALPHA:
241         return CAIRO_FORMAT_A8;
242     case CAIRO_CONTENT_COLOR:
243         return CAIRO_FORMAT_RGB24;
244     default:
245     case CAIRO_CONTENT_COLOR_ALPHA:
246         return CAIRO_FORMAT_ARGB32;
247     }
248 }
249
250 static void
251 send_recording_surface (test_runner_t *tr,
252                         int width, int height,
253                         struct context_closure *closure)
254 {
255 #if CAIRO_HAS_REAL_PTHREAD
256     const struct request_image rq = {
257         closure->id,
258         closure->start_line,
259         closure->end_line,
260         -1,
261         width, height,
262         (long) closure->surface,
263     };
264     unsigned long offset;
265     unsigned long serial;
266
267     if (DEBUG > 1) {
268         printf ("send-recording-surface: %lu [%lu, %lu]\n",
269                 closure->id,
270                 closure->start_line,
271                 closure->end_line);
272     }
273     writen (tr->sk, &rq, sizeof (rq));
274     readn (tr->sk, &offset, sizeof (offset));
275
276     /* signal completion */
277     writen (tr->sk, &closure->id, sizeof (closure->id));
278
279     /* wait for image check */
280     serial = 0;
281     readn (tr->sk, &serial, sizeof (serial));
282     if (DEBUG > 1) {
283         printf ("send-recording-surface: serial: %lu\n", serial);
284     }
285     if (serial != closure->id)
286         pthread_exit (NULL);
287 #else
288     exit (1);
289 #endif
290 }
291
292 static void *
293 request_image (test_runner_t *tr,
294                struct context_closure *closure,
295                cairo_format_t format,
296                int width, int height, int stride)
297 {
298     const struct request_image rq = {
299         closure->id,
300         closure->start_line,
301         closure->end_line,
302         format, width, height, stride
303     };
304     unsigned long offset = -1;
305
306     assert (format != (cairo_format_t) -1);
307
308     writen (tr->sk, &rq, sizeof (rq));
309     readn (tr->sk, &offset, sizeof (offset));
310     if (offset == (unsigned long) -1)
311         return NULL;
312
313     return tr->base + offset;
314 }
315
316 static void
317 send_surface (test_runner_t *tr,
318               struct context_closure *closure)
319 {
320     cairo_surface_t *source = closure->surface;
321     cairo_surface_t *image;
322     cairo_format_t format = (cairo_format_t) -1;
323     cairo_t *cr;
324     int width, height, stride;
325     void *data;
326     unsigned long serial;
327
328     if (DEBUG > 1) {
329         printf ("send-surface: '%s', is-recording? %d\n",
330                 tr->name, tr->is_recording);
331     }
332
333     if (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE) {
334         width = cairo_image_surface_get_width (source);
335         height = cairo_image_surface_get_height (source);
336         format = cairo_image_surface_get_format (source);
337     } else {
338         struct surface_tag *tag;
339
340         tag = cairo_surface_get_user_data (source, &surface_tag);
341         if (tag != NULL) {
342             width = tag->width;
343             height = tag->height;
344         } else {
345             double x0, x1, y0, y1;
346
347             /* presumably created using cairo_surface_create_similar() */
348             cr = cairo_create (source);
349             cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
350             cairo_destroy (cr);
351
352             tag = xmalloc (sizeof (*tag));
353             width = tag->width = x1 - x0;
354             height = tag->height = y1 - y0;
355
356             if (cairo_surface_set_user_data (source, &surface_tag, tag, free))
357                 tr_die (tr);
358         }
359     }
360
361     if (tr->is_recording) {
362         send_recording_surface (tr, width, height, closure);
363         return;
364     }
365
366     if (format == (cairo_format_t) -1)
367         format = format_for_content (cairo_surface_get_content (source));
368
369     stride = cairo_format_stride_for_width (format, width);
370
371     data = request_image (tr, closure, format, width, height, stride);
372     if (data == NULL)
373         tr_die (tr);
374
375     image = cairo_image_surface_create_for_data (data,
376                                                  format,
377                                                  width, height,
378                                                  stride);
379     cr = cairo_create (image);
380     cairo_surface_destroy (image);
381
382     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
383     cairo_set_source_surface (cr, source, 0, 0);
384     cairo_paint (cr);
385     cairo_destroy (cr);
386
387     /* signal completion */
388     writen (tr->sk, &closure->id, sizeof (closure->id));
389
390     /* wait for image check */
391     serial = 0;
392     readn (tr->sk, &serial, sizeof (serial));
393     if (serial != closure->id)
394         tr_die (tr);
395 }
396
397 static cairo_surface_t *
398 _surface_create (void *closure,
399                  cairo_content_t content,
400                  double width, double height,
401                  long uid)
402 {
403     test_runner_t *tr = closure;
404     cairo_surface_t *surface;
405
406     surface = cairo_surface_create_similar (tr->surface,
407                                             content, width, height);
408     if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE) {
409         struct surface_tag *tag;
410
411         tag = xmalloc (sizeof (*tag));
412         tag->width = width;
413         tag->height = height;
414         if (cairo_surface_set_user_data (surface, &surface_tag, tag, free))
415             tr_die (tr);
416     }
417
418     return surface;
419 }
420
421 static cairo_t *
422 _context_create (void *closure, cairo_surface_t *surface)
423 {
424     test_runner_t *tr = closure;
425     struct context_closure *l;
426
427     if (DEBUG) {
428         fprintf (stderr, "%s: starting context %lu on line %d\n",
429                  tr->name ? tr->name : "recording" ,
430                  tr->context_id + 1,
431                  cairo_script_interpreter_get_line_number (tr->csi));
432     }
433
434     l = xmalloc (sizeof (*l));
435     l->next = tr->contexts;
436     l->start_line = cairo_script_interpreter_get_line_number (tr->csi);
437     l->end_line = l->start_line;
438     l->context = cairo_create (surface);
439     l->surface = cairo_surface_reference (surface);
440     l->id = ++tr->context_id;
441     if (l->id == 0)
442         l->id = ++tr->context_id;
443     tr->contexts = l;
444
445     return l->context;
446 }
447
448 static void
449 _context_destroy (void *closure, void *ptr)
450 {
451     test_runner_t *tr = closure;
452     struct context_closure *l, **prev = &tr->contexts;
453
454     while ((l = *prev) != NULL) {
455         if (l->context == ptr) {
456             if (DEBUG) {
457                 fprintf (stderr, "%s: context %lu complete on line %d\n",
458                          tr->name ? tr->name : "recording" ,
459                          tr->context_id,
460                          cairo_script_interpreter_get_line_number (tr->csi));
461             }
462             l->end_line =
463                 cairo_script_interpreter_get_line_number (tr->csi);
464             if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) {
465                 send_surface (tr, l);
466             } else {
467                 fprintf (stderr, "%s: error during replay, line %lu: %s!\n",
468                          tr->name,
469                          l->end_line,
470                          cairo_status_to_string (cairo_surface_status (l->surface)));
471                 tr_die (tr);
472             }
473
474             cairo_surface_destroy (l->surface);
475             *prev = l->next;
476             free (l);
477             return;
478         }
479         prev = &l->next;
480     }
481 }
482
483 static void
484 execute (test_runner_t *tr)
485 {
486     const cairo_script_interpreter_hooks_t hooks = {
487         .closure = tr,
488         .surface_create = _surface_create,
489         .context_create = _context_create,
490         .context_destroy = _context_destroy,
491     };
492     pid_t ack;
493
494     tr->csi = cairo_script_interpreter_create ();
495     cairo_script_interpreter_install_hooks (tr->csi, &hooks);
496
497     ack = -1;
498     readn (tr->sk, &ack, sizeof (ack));
499     if (ack != tr->pid)
500         tr_die (tr);
501
502     cairo_script_interpreter_run (tr->csi, tr->trace);
503
504     cairo_script_interpreter_finish (tr->csi);
505     if (cairo_script_interpreter_destroy (tr->csi))
506         tr_die (tr);
507 }
508
509 static int
510 spawn_socket (const char *socket_path, pid_t pid)
511 {
512     struct sockaddr_un addr;
513     int sk;
514
515     sk = socket (PF_UNIX, SOCK_STREAM, 0);
516     if (sk == -1)
517         return -1;
518
519     memset (&addr, 0, sizeof (addr));
520     addr.sun_family = AF_UNIX;
521     strcpy (addr.sun_path, socket_path);
522
523     if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1)
524         return -1;
525
526     if (! writen (sk, &pid, sizeof (pid)))
527         return -1;
528
529     return sk;
530 }
531
532 static void *
533 spawn_shm (const char *shm_path)
534 {
535     void *base;
536     int fd;
537
538     fd = shm_open (shm_path, O_RDWR, 0);
539     if (fd == -1)
540         return MAP_FAILED;
541
542     base = mmap (NULL, DATA_SIZE,
543                  PROT_READ | PROT_WRITE,
544                  MAP_SHARED | MAP_NORESERVE,
545                  fd, 0);
546     close (fd);
547
548     return base;
549 }
550
551 static int
552 spawn_target (const char *socket_path,
553               const char *shm_path,
554               const cairo_boilerplate_target_t *target,
555               const char *trace)
556 {
557     test_runner_t tr;
558     pid_t pid;
559
560     if (DEBUG)
561         printf ("Spawning slave '%s' for %s\n", target->name, trace);
562
563     pid = fork ();
564     if (pid != 0)
565         return pid;
566
567     tr.is_recording = FALSE;
568     tr.pid = getpid ();
569
570     tr.sk = spawn_socket (socket_path, tr.pid);
571     if (tr.sk == -1) {
572         fprintf (stderr, "%s: Failed to open socket.\n",
573                  target->name);
574         exit (-1);
575     }
576
577     tr.base = spawn_shm (shm_path);
578     if (tr.base == MAP_FAILED) {
579         fprintf (stderr, "%s: Failed to map shared memory segment.\n",
580                  target->name);
581         exit (-1);
582     }
583
584     tr.name = target->name;
585     tr.contexts = NULL;
586     tr.context_id = 0;
587     tr.trace = trace;
588
589     tr.surface = target->create_surface (NULL,
590                                          target->content,
591                                          1, 1,
592                                          1, 1,
593                                          CAIRO_BOILERPLATE_MODE_TEST,
594                                          &tr.closure);
595     if (tr.surface == NULL) {
596         fprintf (stderr,
597                  "%s:  Failed to create target surface.\n",
598                  target->name);
599         exit (-1);
600     }
601
602     execute (&tr);
603
604     cairo_surface_destroy (tr.surface);
605
606     if (target->cleanup)
607         target->cleanup (tr.closure);
608
609     close (tr.sk);
610     munmap (tr.base, DATA_SIZE);
611
612     exit (0);
613 }
614
615 #if CAIRO_HAS_REAL_PTHREAD
616 static void
617 cleanup_recorder (void *arg)
618 {
619     test_runner_t *tr = arg;
620
621     cairo_surface_finish (tr->surface);
622     cairo_surface_destroy (tr->surface);
623
624     close (tr->sk);
625     free (tr);
626 }
627
628 static void *
629 record (void *arg)
630 {
631     test_runner_t *tr = arg;
632
633     pthread_cleanup_push (cleanup_recorder, tr);
634     execute (tr);
635     pthread_cleanup_pop (TRUE);
636
637     return NULL;
638 }
639
640 /* The recorder is special:
641  * 1. It doesn't generate an image, but keeps an in-memory trace to
642  *    reconstruct any surface.
643  * 2. Runs in the same process, but separate thread.
644  */
645 static pid_t
646 spawn_recorder (const char *socket_path, const char *trace, test_runner_t **out)
647 {
648     test_runner_t *tr;
649     pthread_t id;
650     pthread_attr_t attr;
651     pid_t pid = getpid ();
652
653     if (DEBUG)
654         printf ("Spawning recorder for %s\n", trace);
655
656     tr = malloc (sizeof (*tr));
657     if (tr == NULL)
658         return -1;
659
660     tr->is_recording = TRUE;
661     tr->pid = pid;
662     tr->sk = spawn_socket (socket_path, tr->pid);
663     if (tr->sk == -1) {
664         free (tr);
665         return -1;
666     }
667
668     tr->base = NULL;
669     tr->name = NULL;
670     tr->contexts = NULL;
671     tr->context_id = 0;
672     tr->trace = trace;
673
674     tr->surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
675                                                   NULL);
676     if (tr->surface == NULL) {
677         cleanup_recorder (tr);
678         return -1;
679     }
680
681     pthread_attr_init (&attr);
682     pthread_attr_setdetachstate (&attr, TRUE);
683     if (pthread_create (&id, &attr, record, tr) < 0) {
684         pthread_attr_destroy (&attr);
685         cleanup_recorder (tr);
686         return -1;
687     }
688     pthread_attr_destroy (&attr);
689
690
691     *out = tr;
692     return pid;
693 }
694 #endif
695
696 /* XXX imagediff - is the extra expense worth it? */
697 static cairo_bool_t
698 matches_reference (struct slave *slave)
699 {
700     cairo_surface_t *a, *b;
701
702     a = slave->image;
703     b = slave->reference->image;
704
705     if (a == b)
706         return TRUE;
707
708     if (a == NULL || b == NULL)
709         return FALSE;
710
711     if (cairo_surface_status (a) || cairo_surface_status (b))
712         return FALSE;
713
714     if (cairo_surface_get_type (a) != cairo_surface_get_type (b))
715         return FALSE;
716
717     if (cairo_image_surface_get_format (a) != cairo_image_surface_get_format (b))
718         return FALSE;
719
720     if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
721         return FALSE;
722
723     if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
724         return FALSE;
725
726     if (cairo_image_surface_get_stride (a) != cairo_image_surface_get_stride (b))
727         return FALSE;
728
729     if (FALSE && cairo_surface_get_content (a) & CAIRO_CONTENT_COLOR) {
730         cairo_surface_t *diff;
731         int width, height, stride, size;
732         unsigned char *data;
733         cairo_status_t status;
734
735         width = cairo_image_surface_get_width (a);
736         height = cairo_image_surface_get_height (a);
737         stride = cairo_image_surface_get_stride (a);
738         size = height * stride * 4;
739         data = malloc (size);
740         if (data == NULL)
741             return FALSE;
742
743         diff = cairo_image_surface_create_for_data (data,
744                                                     cairo_image_surface_get_format (a),
745                                                     width, height, stride);
746         cairo_surface_set_user_data (diff, (cairo_user_data_key_t *) diff,
747                                      data, free);
748
749         status = image_diff (NULL, a, b, diff, &slave->result);
750         if (status) {
751             cairo_surface_destroy (diff);
752             return FALSE;
753         }
754
755         if (image_diff_is_failure (&slave->result, slave->target->error_tolerance)) {
756             slave->difference = diff;
757             return FALSE;
758         } else {
759             cairo_surface_destroy (diff);
760             return TRUE;
761         }
762     } else {
763         int width, height, stride;
764         const uint8_t *aa, *bb;
765         int x, y;
766
767         width = cairo_image_surface_get_width (a);
768         height = cairo_image_surface_get_height (a);
769         stride = cairo_image_surface_get_stride (a);
770
771         aa = cairo_image_surface_get_data (a);
772         bb = cairo_image_surface_get_data (b);
773         switch (cairo_image_surface_get_format (a)) {
774         case CAIRO_FORMAT_ARGB32:
775             for (y = 0; y < height; y++) {
776                 const uint32_t *ua = (uint32_t *) aa;
777                 const uint32_t *ub = (uint32_t *) bb;
778                 for (x = 0; x < width; x++) {
779                     if (ua[x] != ub[x]) {
780                         int channel;
781
782                         for (channel = 0; channel < 4; channel++) {
783                             unsigned va, vb, diff;
784
785                             va = (ua[x] >> (channel*8)) & 0xff;
786                             vb = (ub[x] >> (channel*8)) & 0xff;
787                             diff = abs (va - vb);
788                             if (diff > slave->target->error_tolerance)
789                                 return FALSE;
790                         }
791                     }
792                 }
793                 aa += stride;
794                 bb += stride;
795             }
796             break;
797
798         case CAIRO_FORMAT_RGB24:
799             for (y = 0; y < height; y++) {
800                 const uint32_t *ua = (uint32_t *) aa;
801                 const uint32_t *ub = (uint32_t *) bb;
802                 for (x = 0; x < width; x++) {
803                     if ((ua[x] & 0x00ffffff) != (ub[x] & 0x00ffffff)) {
804                         int channel;
805
806                         for (channel = 0; channel < 3; channel++) {
807                             unsigned va, vb, diff;
808
809                             va = (ua[x] >> (channel*8)) & 0xff;
810                             vb = (ub[x] >> (channel*8)) & 0xff;
811                             diff = abs (va - vb);
812                             if (diff > slave->target->error_tolerance)
813                                 return FALSE;
814                         }
815                     }
816                 }
817                 aa += stride;
818                 bb += stride;
819             }
820             break;
821
822         case CAIRO_FORMAT_A8:
823             for (y = 0; y < height; y++) {
824                 for (x = 0; x < width; x++) {
825                     if (aa[x] != bb[x]) {
826                         unsigned diff = abs (aa[x] - bb[x]);
827                         if (diff > slave->target->error_tolerance)
828                             return FALSE;
829                     }
830                 }
831                 aa += stride;
832                 bb += stride;
833             }
834             break;
835
836         case CAIRO_FORMAT_A1:
837             width /= 8;
838             for (y = 0; y < height; y++) {
839                 if (memcmp (aa, bb, width))
840                     return FALSE;
841                 aa += stride;
842                 bb += stride;
843             }
844             break;
845
846         case CAIRO_FORMAT_RGB30:
847         case CAIRO_FORMAT_RGB16_565:
848         case CAIRO_FORMAT_INVALID:
849             assert (0);
850         }
851
852         return TRUE;
853     }
854 }
855
856 static cairo_bool_t
857 check_images (struct slave *slaves, int num_slaves)
858 {
859     int n;
860
861     if (ignore_image_differences)
862         return TRUE;
863
864     for (n = 0; n < num_slaves; n++) {
865         if (slaves[n].reference == NULL)
866             continue;
867
868         if (! matches_reference (&slaves[n]))
869             return FALSE;
870     }
871
872     return TRUE;
873 }
874
875 static void
876 write_images (const char *trace, struct slave *slave, int num_slaves)
877 {
878     while (num_slaves--) {
879         if (slave->image != NULL && ! slave->is_recording) {
880             char *filename;
881
882             xasprintf (&filename, "%s-%s-fail.png",
883                        trace, slave->target->name);
884             cairo_surface_write_to_png (slave->image, filename);
885             free (filename);
886
887             if (slave->difference) {
888                 xasprintf (&filename, "%s-%s-diff.png",
889                            trace, slave->target->name);
890                 cairo_surface_write_to_png (slave->difference, filename);
891                 free (filename);
892             }
893         }
894
895         slave++;
896     }
897 }
898
899 static void
900 write_result (const char *trace, struct slave *slave)
901 {
902     static int index;
903     char *filename;
904
905     xasprintf (&filename, "%s-%s-pass-%d-%d-%d.png",
906                trace, slave->target->name, ++index,
907                slave->start_line, slave->end_line);
908     cairo_surface_write_to_png (slave->image, filename);
909     free (filename);
910 }
911
912 static void
913 write_trace (const char *trace, const char *id, struct slave *slave)
914 {
915 #if CAIRO_HAS_SCRIPT_SURFACE
916     cairo_device_t *script;
917     char *filename;
918
919     assert (slave->is_recording);
920
921     xasprintf (&filename, "%s-%s.trace", trace, id);
922
923     script = cairo_script_create (filename);
924     cairo_script_from_recording_surface (script, slave->image);
925     cairo_device_destroy (script);
926
927     free (filename);
928 #endif
929 }
930
931 static void
932 dump_traces (test_runner_t *tr,
933              const char *trace,
934              const char *target,
935              const char *fail)
936 {
937 #if CAIRO_HAS_SCRIPT_SURFACE
938     struct context_closure *c;
939
940     for (c = tr->contexts; c; c = c->next) {
941         cairo_device_t *script;
942         char *filename;
943
944         xasprintf (&filename, "%s-%s-%s.%lu.trace",
945                    trace, target, fail, c->start_line);
946
947         script = cairo_script_create (filename);
948         cairo_script_from_recording_surface (script, c->surface);
949         cairo_device_destroy (script);
950
951         free (filename);
952     }
953 #endif
954 }
955
956 static unsigned long
957 allocate_image_for_slave (uint8_t *base,
958                           unsigned long offset,
959                           struct slave *slave)
960 {
961     struct request_image rq;
962     int size;
963     uint8_t *data;
964
965     assert (slave->image == NULL);
966
967     readn (slave->fd, &rq, sizeof (rq));
968     slave->image_serial = rq.id;
969     slave->start_line = rq.start_line;
970     slave->end_line = rq.end_line;
971
972     slave->width = rq.width;
973     slave->height = rq.height;
974
975     if (DEBUG > 1) {
976         printf ("allocate-image-for-slave: %s %lu [%lu, %lu] %ldx%ld stride=%lu => %lu, is-recording? %d\n",
977                 TARGET_NAME (slave->target),
978                 slave->image_serial,
979                 slave->start_line,
980                 slave->end_line,
981                 slave->width,
982                 slave->height,
983                 rq.stride,
984                 offset,
985                 slave->is_recording);
986     }
987
988     if (slave->is_recording) {
989         /* special communication with recording-surface thread */
990         slave->image = cairo_surface_reference ((cairo_surface_t *) rq.stride);
991     } else {
992         size = rq.height * rq.stride;
993         size = (size + 4095) & -4096;
994         data = base + offset;
995         offset += size;
996         assert (offset <= DATA_SIZE);
997
998         slave->image = cairo_image_surface_create_for_data (data, rq.format,
999                                                             rq.width, rq.height,
1000                                                             rq.stride);
1001     }
1002
1003     return offset;
1004 }
1005
1006 struct error_info {
1007     unsigned long context_id;
1008     unsigned long start_line;
1009     unsigned long end_line;
1010 };
1011
1012 static cairo_bool_t
1013 test_run (void *base,
1014           int sk,
1015           const char *trace,
1016           struct slave *slaves,
1017           int num_slaves,
1018           struct error_info *error)
1019 {
1020     struct pollfd *pfd;
1021     int npfd, cnt, n, i;
1022     int completion, err = 0;
1023     cairo_bool_t ret = FALSE;
1024     unsigned long image;
1025
1026     if (DEBUG) {
1027         printf ("Running trace '%s' over %d slaves\n",
1028                 trace, num_slaves);
1029     }
1030
1031     pfd = xcalloc (num_slaves+1, sizeof (*pfd));
1032
1033     pfd[0].fd = sk;
1034     pfd[0].events = POLLIN;
1035     npfd = 1;
1036
1037     completion = 0;
1038     image = 0;
1039     while ((cnt = poll (pfd, npfd, -1)) > 0) {
1040         if (pfd[0].revents) {
1041             int fd;
1042
1043             while ((fd = accept (sk, NULL, NULL)) != -1) {
1044                 pid_t pid;
1045
1046                 readn (fd, &pid, sizeof (pid));
1047                 for (n = 0; n < num_slaves; n++) {
1048                     if (slaves[n].pid == pid) {
1049                         slaves[n].fd = fd;
1050                         break;
1051                     }
1052                 }
1053                 if (n == num_slaves) {
1054                     if (DEBUG)
1055                         printf ("unknown slave pid\n");
1056                     goto out;
1057                 }
1058
1059                 pfd[npfd].fd = fd;
1060                 pfd[npfd].events = POLLIN;
1061                 npfd++;
1062
1063                 if (! writen (fd, &pid, sizeof (pid)))
1064                     goto out;
1065             }
1066             cnt--;
1067         }
1068
1069         for (n = 1; n < npfd && cnt; n++) {
1070             if (! pfd[n].revents)
1071                 continue;
1072
1073             if (pfd[n].revents & POLLHUP) {
1074                 pfd[n].events = pfd[n].revents = 0;
1075                 completion++;
1076                 continue;
1077             }
1078
1079             for (i = 0; i < num_slaves; i++) {
1080                 if (slaves[i].fd == pfd[n].fd) {
1081                     /* Communication with the slave is done in three phases,
1082                      * and we do each pass synchronously.
1083                      *
1084                      * 1. The slave requests an image buffer, which we
1085                      * allocate and then return to the slave the offset into
1086                      * the shared memory segment.
1087                      *
1088                      * 2. The slave indicates that it has finished writing
1089                      * into the shared image buffer. The slave now waits
1090                      * for the server to collate all the image data - thereby
1091                      * throttling the slaves.
1092                      *
1093                      * 3. After all slaves have finished writing their images,
1094                      * we compare them all against the reference image and,
1095                      * if satisfied, send an acknowledgement to all slaves.
1096                      */
1097                     if (slaves[i].image_serial == 0) {
1098                         unsigned long offset;
1099
1100                         image =
1101                             allocate_image_for_slave (base,
1102                                                       offset = image,
1103                                                       &slaves[i]);
1104                         if (! writen (pfd[n].fd, &offset, sizeof (offset))) {
1105                             pfd[n].events = pfd[n].revents = 0;
1106                             err = 1;
1107                             completion++;
1108                             continue;
1109                         }
1110                     } else {
1111                         readn (pfd[n].fd,
1112                                &slaves[i].image_ready,
1113                                sizeof (slaves[i].image_ready));
1114                         if (DEBUG) {
1115                             printf ("slave '%s' reports completion on %lu (expecting %lu)\n",
1116                                     TARGET_NAME (slaves[i].target),
1117                                     slaves[i].image_ready,
1118                                     slaves[i].image_serial);
1119                         }
1120                         if (slaves[i].image_ready != slaves[i].image_serial) {
1121                             pfd[n].events = pfd[n].revents = 0;
1122                             err = 1;
1123                             completion++;
1124                             continue;
1125                         }
1126
1127                         /* Can anyone spell 'P·E·D·A·N·T'? */
1128                         if (! slaves[i].is_recording)
1129                             cairo_surface_mark_dirty (slaves[i].image);
1130                         completion++;
1131                     }
1132
1133                     break;
1134                 }
1135             }
1136
1137             cnt--;
1138         }
1139
1140         if (completion >= num_slaves) {
1141             if (err) {
1142                 if (DEBUG > 1)
1143                     printf ("error detected\n");
1144                 goto out;
1145             }
1146
1147             if (DEBUG > 1) {
1148                 printf ("all saves report completion\n");
1149             }
1150             if (slaves[0].end_line >= slaves[0].start_line &&
1151                 ! check_images (slaves, num_slaves)) {
1152                 error->context_id = slaves[0].image_serial;
1153                 error->start_line = slaves[0].start_line;
1154                 error->end_line = slaves[0].end_line;
1155
1156                 if (DEBUG) {
1157                     printf ("check_images failed: %lu, [%lu, %lu]\n",
1158                             slaves[0].image_serial,
1159                             slaves[0].start_line,
1160                             slaves[0].end_line);
1161                 }
1162
1163                 write_images (trace, slaves, num_slaves);
1164
1165                 if (slaves[0].is_recording)
1166                     write_trace (trace, "fail", &slaves[0]);
1167
1168                 goto out;
1169             }
1170
1171             if (write_results) write_result (trace, &slaves[1]);
1172             if (write_traces && slaves[0].is_recording) {
1173                 char buf[80];
1174                 snprintf (buf, sizeof (buf), "%d", slaves[0].image_serial);
1175                 write_trace (trace, buf, &slaves[0]);
1176             }
1177
1178             /* ack */
1179             for (i = 0; i < num_slaves; i++) {
1180                 cairo_surface_destroy (slaves[i].image);
1181                 slaves[i].image = NULL;
1182
1183                 if (DEBUG > 1) {
1184                     printf ("sending continuation to '%s'\n",
1185                             TARGET_NAME (slaves[i].target));
1186                 }
1187                 if (! writen (slaves[i].fd,
1188                               &slaves[i].image_serial,
1189                               sizeof (slaves[i].image_serial)))
1190                 {
1191                     goto out;
1192                 }
1193
1194                 slaves[i].image_serial = 0;
1195                 slaves[i].image_ready = 0;
1196             }
1197
1198             completion = 0;
1199             image = 0;
1200         }
1201     }
1202 done:
1203     ret = TRUE;
1204
1205 out:
1206     if (DEBUG) {
1207         printf ("run complete: %d\n", ret);
1208     }
1209
1210     for (n = 0; n < num_slaves; n++) {
1211         if (slaves[n].fd != -1)
1212             close (slaves[n].fd);
1213
1214         if (slaves[n].image == NULL)
1215             continue;
1216
1217         cairo_surface_destroy (slaves[n].image);
1218         slaves[n].image = NULL;
1219
1220         cairo_surface_destroy (slaves[n].difference);
1221         slaves[n].difference = NULL;
1222
1223         slaves[n].image_serial = 0;
1224         slaves[n].image_ready = 0;
1225     }
1226
1227     free (pfd);
1228
1229     return ret;
1230 }
1231
1232 static int
1233 server_socket (const char *socket_path)
1234 {
1235     long flags;
1236     struct sockaddr_un addr;
1237     int sk;
1238
1239     sk = socket (PF_UNIX, SOCK_STREAM, 0);
1240     if (sk == -1)
1241         return -1;
1242
1243     memset (&addr, 0, sizeof (addr));
1244     addr.sun_family = AF_UNIX;
1245     strcpy (addr.sun_path, socket_path);
1246     if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
1247         close (sk);
1248         return -1;
1249     }
1250
1251     flags = fcntl (sk, F_GETFL);
1252     if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
1253         close (sk);
1254         return -1;
1255     }
1256
1257     if (listen (sk, 5) == -1) {
1258         close (sk);
1259         return -1;
1260     }
1261
1262     return sk;
1263 }
1264
1265 static int
1266 server_shm (const char *shm_path)
1267 {
1268     int fd;
1269
1270     fd = shm_open (shm_path, O_RDWR | O_EXCL | O_CREAT, 0777);
1271     if (fd == -1)
1272         return -1;
1273
1274     if (ftruncate (fd, DATA_SIZE) == -1) {
1275         close (fd);
1276         return -1;
1277     }
1278
1279     return fd;
1280 }
1281
1282 static cairo_bool_t
1283 _test_trace (test_trace_t *test,
1284              const char *trace,
1285              const char *name,
1286              struct error_info *error)
1287 {
1288     const char *shm_path = SHM_PATH_XXX;
1289     const cairo_boilerplate_target_t *target, *image;
1290     struct slave *slaves, *s;
1291     test_runner_t *recorder = NULL;
1292     pid_t slave;
1293     char socket_dir[] = "/tmp/cairo-test-trace.XXXXXX";
1294     char *socket_path;
1295     int sk, fd;
1296     int i, num_slaves;
1297     void *base;
1298     cairo_bool_t ret = FALSE;
1299
1300     if (DEBUG)
1301         printf ("setting up trace '%s'\n", trace);
1302
1303     /* create a socket to control the test runners */
1304     if (mkdtemp (socket_dir) == NULL) {
1305         fprintf (stderr, "Unable to create temporary name for socket\n");
1306         return FALSE;
1307     }
1308
1309     xasprintf (&socket_path, "%s/socket", socket_dir);
1310     sk = server_socket (socket_path);
1311     if (sk == -1) {
1312         fprintf (stderr, "Unable to create socket for server\n");
1313         goto cleanup_paths;
1314     }
1315
1316     /* allocate some shared memory */
1317     fd = server_shm (shm_path);
1318     if (fd == -1) {
1319         fprintf (stderr, "Unable to create shared memory '%s': %s\n",
1320                  shm_path, strerror (errno));
1321         goto cleanup_sk;
1322     }
1323
1324     image = cairo_boilerplate_get_image_target (CAIRO_CONTENT_COLOR_ALPHA);
1325     assert (image != NULL);
1326
1327     s = slaves = xcalloc (2*test->num_targets + 1, sizeof (struct slave));
1328
1329 #if CAIRO_HAS_REAL_PTHREAD
1330     /* set-up a recording-surface to reconstruct errors */
1331     slave = spawn_recorder (socket_path, trace, &recorder);
1332     if (slave < 0) {
1333         fprintf (stderr, "Unable to create recording surface\n");
1334         goto cleanup_sk;
1335     }
1336
1337     s->pid = slave;
1338     s->is_recording = TRUE;
1339     s->target = NULL;
1340     s->fd = -1;
1341     s->reference = NULL;
1342     s++;
1343 #endif
1344
1345     /* spawn slave processes to run the trace */
1346     for (i = 0; i < test->num_targets; i++) {
1347         const cairo_boilerplate_target_t *reference;
1348         struct slave *master;
1349
1350         target = test->targets[i];
1351
1352         if (DEBUG)
1353             printf ("setting up target[%d]? '%s' (image? %d, measurable? %d)\n",
1354                     i, target->name, target == image, target->is_measurable);
1355
1356         if (target == image || ! target->is_measurable)
1357             continue;
1358
1359         /* find a matching slave to use as a reference for this target */
1360         if (target->reference_target != NULL) {
1361             reference =
1362                 cairo_boilerplate_get_target_by_name (target->reference_target,
1363                                                       target->content);
1364             assert (reference != NULL);
1365         } else {
1366             reference = image;
1367         }
1368         for (master = slaves; master < s; master++) {
1369             if (master->target == reference)
1370                 break;
1371         }
1372
1373         if (master == s) {
1374             /* no match found, spawn a slave to render the reference image */
1375             slave = spawn_target (socket_path, shm_path, reference, trace);
1376             if (slave < 0)
1377                 continue;
1378
1379             s->pid = slave;
1380             s->target = reference;
1381             s->fd = -1;
1382             s->reference = NULL;
1383             s++;
1384         }
1385
1386         slave = spawn_target (socket_path, shm_path, target, trace);
1387         if (slave < 0)
1388             continue;
1389
1390         s->pid = slave;
1391         s->target = target;
1392         s->fd = -1;
1393         s->reference = master;
1394         s++;
1395     }
1396     num_slaves = s - slaves;
1397     if (num_slaves == 1) {
1398         fprintf (stderr, "No targets to test\n");
1399         goto cleanup;
1400     }
1401
1402     base = mmap (NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1403     if (base == MAP_FAILED) {
1404         fprintf (stderr, "Unable to mmap shared memory\n");
1405         goto cleanup;
1406     }
1407     ret = test_run (base, sk, name, slaves, num_slaves, error);
1408     munmap (base, DATA_SIZE);
1409
1410 cleanup:
1411     close (fd);
1412     while (s-- > slaves) {
1413         int status;
1414
1415         if (s->fd != -1)
1416             close (s->fd);
1417
1418         cairo_surface_destroy (s->image);
1419         cairo_surface_destroy (s->difference);
1420
1421         if (s->is_recording) /* in-process */
1422             continue;
1423
1424         kill (s->pid, SIGKILL);
1425         waitpid (s->pid, &status, 0);
1426         if (WIFSIGNALED (status) && WTERMSIG(status) != SIGKILL) {
1427             fprintf (stderr, "%s crashed\n", s->target->name);
1428             if (recorder)
1429                 dump_traces (recorder, trace, s->target->name, "crash");
1430         }
1431     }
1432     free (slaves);
1433     shm_unlink (shm_path);
1434 cleanup_sk:
1435     close (sk);
1436
1437 cleanup_paths:
1438     remove (socket_path);
1439     remove (socket_dir);
1440
1441     free (socket_path);
1442     return ret;
1443 }
1444
1445 static void
1446 test_trace (test_trace_t *test, const char *trace)
1447 {
1448     char *trace_cpy, *name, *dot;
1449
1450     trace_cpy = xstrdup (trace);
1451     name = basename (trace_cpy);
1452     dot = strchr (name, '.');
1453     if (dot)
1454         *dot = '\0';
1455
1456     if (test->list_only) {
1457         printf ("%s\n", name);
1458     } else {
1459         struct error_info error = {0};
1460         cairo_bool_t ret;
1461
1462         printf ("%s: ", name);
1463         fflush (stdout);
1464
1465         ret = _test_trace (test, trace, name, &error);
1466         if (ret) {
1467             printf ("PASS\n");
1468         } else {
1469             if (error.context_id) {
1470                 printf ("FAIL (context %lu, lines [%lu, %lu])\n",
1471                         error.context_id,
1472                         error.start_line,
1473                         error.end_line);
1474             } else {
1475                 printf ("FAIL\n");
1476             }
1477         }
1478     }
1479
1480     free (trace_cpy);
1481 }
1482
1483 static cairo_bool_t
1484 read_excludes (test_trace_t *test, const char *filename)
1485 {
1486     FILE *file;
1487     char *line = NULL;
1488     size_t line_size = 0;
1489     char *s, *t;
1490
1491     file = fopen (filename, "r");
1492     if (file == NULL)
1493         return FALSE;
1494
1495     while (getline (&line, &line_size, file) != -1) {
1496         /* terminate the line at a comment marker '#' */
1497         s = strchr (line, '#');
1498         if (s)
1499             *s = '\0';
1500
1501         /* whitespace delimits */
1502         s = line;
1503         while (*s != '\0' && isspace (*s))
1504             s++;
1505
1506         t = s;
1507         while (*t != '\0' && ! isspace (*t))
1508             t++;
1509
1510         if (s != t) {
1511             int i = test->num_exclude_names;
1512             test->exclude_names = xrealloc (test->exclude_names,
1513                                             sizeof (char *) * (i+1));
1514             test->exclude_names[i] = strndup (s, t-s);
1515             test->num_exclude_names++;
1516         }
1517     }
1518     free (line);
1519
1520     fclose (file);
1521
1522     return TRUE;
1523 }
1524
1525 static void
1526 usage (const char *argv0)
1527 {
1528     fprintf (stderr,
1529 "Usage: %s [-l] [-x exclude-file] [test-names ... | traces ...]\n"
1530 "\n"
1531 "Run the cairo test suite over the given traces (all by default).\n"
1532 "The command-line arguments are interpreted as follows:\n"
1533 "\n"
1534 "  -l   list only; just list selected test case names without executing\n"
1535 "  -x   exclude; specify a file to read a list of traces to exclude\n"
1536 "\n"
1537 "If test names are given they are used as sub-string matches so a command\n"
1538 "such as \"%s firefox\" can be used to run all firefox traces.\n"
1539 "Alternatively, you can specify a list of filenames to execute.\n",
1540              argv0, argv0);
1541 }
1542
1543 static void
1544 parse_options (test_trace_t *test, int argc, char *argv[])
1545 {
1546     int c;
1547
1548     test->list_only = FALSE;
1549     test->names = NULL;
1550     test->num_names = 0;
1551     test->exclude_names = NULL;
1552     test->num_exclude_names = 0;
1553
1554     while (1) {
1555         c = _cairo_getopt (argc, argv, "lx:");
1556         if (c == -1)
1557             break;
1558
1559         switch (c) {
1560         case 'l':
1561             test->list_only = TRUE;
1562             break;
1563         case 'x':
1564             if (! read_excludes (test, optarg)) {
1565                 fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
1566                          optarg);
1567                 exit (1);
1568             }
1569             break;
1570         default:
1571             fprintf (stderr, "Internal error: unhandled option: %c\n", c);
1572             /* fall-through */
1573         case '?':
1574             usage (argv[0]);
1575             exit (1);
1576         }
1577     }
1578
1579     if (optind < argc) {
1580         test->names = &argv[optind];
1581         test->num_names = argc - optind;
1582     }
1583 }
1584
1585 static void
1586 test_reset (test_trace_t *test)
1587 {
1588     /* XXX leaking fonts again via recording-surface? */
1589 #if 0
1590     cairo_debug_reset_static_data ();
1591 #if HAVE_FCFINI
1592     FcFini ();
1593 #endif
1594 #endif
1595 }
1596
1597 static void
1598 test_fini (test_trace_t *test)
1599 {
1600     test_reset (test);
1601
1602     cairo_boilerplate_free_targets (test->targets);
1603     free (test->exclude_names);
1604 }
1605
1606 static cairo_bool_t
1607 test_has_filenames (test_trace_t *test)
1608 {
1609     unsigned int i;
1610
1611     if (test->num_names == 0)
1612         return FALSE;
1613
1614     for (i = 0; i < test->num_names; i++)
1615         if (access (test->names[i], R_OK) == 0)
1616             return TRUE;
1617
1618     return FALSE;
1619 }
1620
1621 static cairo_bool_t
1622 test_can_run (test_trace_t *test, const char *name)
1623 {
1624     unsigned int i;
1625     char *copy, *dot;
1626     cairo_bool_t ret;
1627
1628     if (test->num_names == 0 && test->num_exclude_names == 0)
1629         return TRUE;
1630
1631     copy = xstrdup (name);
1632     dot = strrchr (copy, '.');
1633     if (dot != NULL)
1634         *dot = '\0';
1635
1636     if (test->num_names) {
1637         ret = TRUE;
1638         for (i = 0; i < test->num_names; i++)
1639             if (strstr (copy, test->names[i]))
1640                 goto check_exclude;
1641
1642         ret = FALSE;
1643         goto done;
1644     }
1645
1646 check_exclude:
1647     if (test->num_exclude_names) {
1648         ret = FALSE;
1649         for (i = 0; i < test->num_exclude_names; i++)
1650             if (strstr (copy, test->exclude_names[i]))
1651                 goto done;
1652
1653         ret = TRUE;
1654         goto done;
1655     }
1656
1657 done:
1658     free (copy);
1659
1660     return ret;
1661 }
1662
1663 static void
1664 warn_no_traces (const char *message, const char *trace_dir)
1665 {
1666     fprintf (stderr,
1667 "Error: %s '%s'.\n"
1668 "Have you cloned the cairo-traces repository and uncompressed the traces?\n"
1669 "  git clone git://anongit.freedesktop.org/cairo-traces\n"
1670 "  cd cairo-traces && make\n"
1671 "Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
1672             message, trace_dir);
1673 }
1674
1675 static void
1676 interrupt (int sig)
1677 {
1678     shm_unlink (SHM_PATH_XXX);
1679
1680     signal (sig, SIG_DFL);
1681     raise (sig);
1682 }
1683
1684 int
1685 main (int argc, char *argv[])
1686 {
1687     test_trace_t test;
1688     const char *trace_dir = "cairo-traces";
1689     unsigned int n;
1690
1691     signal (SIGPIPE, SIG_IGN);
1692     signal (SIGINT, interrupt);
1693
1694     parse_options (&test, argc, argv);
1695
1696     shm_unlink (SHM_PATH_XXX);
1697
1698     if (getenv ("CAIRO_TRACE_DIR") != NULL)
1699         trace_dir = getenv ("CAIRO_TRACE_DIR");
1700
1701     test.targets = cairo_boilerplate_get_targets (&test.num_targets, NULL);
1702
1703     if (test_has_filenames (&test)) {
1704         for (n = 0; n < test.num_names; n++) {
1705             if (access (test.names[n], R_OK) == 0) {
1706                 test_trace (&test, test.names[n]);
1707                 test_reset (&test);
1708             }
1709         }
1710     } else {
1711         DIR *dir;
1712         struct dirent *de;
1713         int num_traces = 0;
1714
1715         dir = opendir (trace_dir);
1716         if (dir == NULL) {
1717             warn_no_traces ("Failed to open directory", trace_dir);
1718             test_fini (&test);
1719             return 1;
1720         }
1721
1722         while ((de = readdir (dir)) != NULL) {
1723             char *trace;
1724             const char *dot;
1725
1726             dot = strrchr (de->d_name, '.');
1727             if (dot == NULL)
1728                 continue;
1729             if (strcmp (dot, ".trace"))
1730                 continue;
1731
1732             num_traces++;
1733             if (! test_can_run (&test, de->d_name))
1734                 continue;
1735
1736             xasprintf (&trace, "%s/%s", trace_dir, de->d_name);
1737             test_trace (&test, trace);
1738             test_reset (&test);
1739
1740             free (trace);
1741
1742         }
1743         closedir (dir);
1744
1745         if (num_traces == 0) {
1746             warn_no_traces ("Found no traces in", trace_dir);
1747             test_fini (&test);
1748             return 1;
1749         }
1750     }
1751
1752     test_fini (&test);
1753
1754     return 0;
1755 }
1756
1757 void
1758 cairo_test_logv (const cairo_test_context_t *ctx,
1759                  const char *fmt, va_list va)
1760 {
1761 #if 0
1762     vfprintf (stderr, fmt, va);
1763 #endif
1764 }
1765
1766 void
1767 cairo_test_log (const cairo_test_context_t *ctx, const char *fmt, ...)
1768 {
1769 #if 0
1770     va_list va;
1771
1772     va_start (va, fmt);
1773     vfprintf (stderr, fmt, va);
1774     va_end (va);
1775 #endif
1776 }