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