1
/*
2
 * Copyright © 2004 Red Hat, Inc.
3
 * Copyright © 2008 Chris Wilson
4
 *
5
 * Permission to use, copy, modify, distribute, and sell this software
6
 * and its documentation for any purpose is hereby granted without
7
 * fee, provided that the above copyright notice appear in all copies
8
 * and that both that copyright notice and this permission notice
9
 * appear in supporting documentation, and that the name of
10
 * Red Hat, Inc. not be used in advertising or publicity pertaining to
11
 * distribution of the software without specific, written prior
12
 * permission. Red Hat, Inc. makes no representations about the
13
 * suitability of this software for any purpose.  It is provided "as
14
 * is" without express or implied warranty.
15
 *
16
 * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18
 * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
19
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
22
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
 *
24
 * Author: Carl D. Worth <cworth@cworth.org>
25
 *         Chris Wilson <chris@chris-wilson.co.uk>
26
 */
27

            
28
#include "config.h"
29

            
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <stdarg.h>
33
#include <ctype.h>
34
#include <assert.h>
35
#if HAVE_UNISTD_H
36
#include <unistd.h>
37
#endif
38
#include <errno.h>
39
#include <string.h>
40
#if HAVE_FCFINI
41
#include <fontconfig/fontconfig.h>
42
#endif
43
#if CAIRO_HAS_REAL_PTHREAD
44
#include <pthread.h>
45
#endif
46
#if HAVE_SYS_STAT_H
47
#include <sys/stat.h>
48
#endif
49

            
50
#if HAVE_VALGRIND
51
#include <valgrind.h>
52
#else
53
#define RUNNING_ON_VALGRIND 0
54
#endif
55

            
56
#if HAVE_MEMFAULT
57
#include <memfault.h>
58
#define MF(x) x
59
#else
60
#define MF(x)
61
#endif
62

            
63
#include "cairo-test-private.h"
64

            
65
#include "buffer-diff.h"
66

            
67
#ifdef _MSC_VER
68
#include <crtdbg.h>
69
#include <direct.h>
70
#define F_OK 0
71
#define HAVE_MKDIR 1
72
#define mkdir _mkdir
73
#endif
74

            
75
#ifndef FALSE
76
#define FALSE 0
77
#endif
78
#ifndef TRUE
79
#define TRUE !FALSE
80
#endif
81

            
82
#if ! HAVE_ALARM || ! defined(SIGALRM)
83
#define alarm(X);
84
#endif
85

            
86
static const cairo_user_data_key_t _cairo_test_context_key;
87

            
88
static void
89
_xunlink (const cairo_test_context_t *ctx, const char *pathname);
90

            
91
static const char *fail_face = "", *xfail_face="", *normal_face = "";
92
static cairo_bool_t print_fail_on_stdout;
93
static int cairo_test_timeout = 60;
94

            
95
#define NUM_DEVICE_OFFSETS 2
96
#define NUM_DEVICE_SCALE 2
97

            
98
cairo_bool_t
99
2354
cairo_test_mkdir (const char *path)
100
{
101
#if ! HAVE_MKDIR
102
    return FALSE;
103
#elif HAVE_MKDIR == 1
104
    if (mkdir (path) == 0)
105
	return TRUE;
106
#elif HAVE_MKDIR == 2
107
2354
    if (mkdir (path, 0770) == 0)
108
1
	return TRUE;
109
#else
110
#error Bad value for HAVE_MKDIR
111
#endif
112

            
113
2353
    return errno == EEXIST;
114
}
115

            
116
static char *
117
1240
_cairo_test_fixup_name (const char *original)
118
{
119
    char *name, *s;
120

            
121
1240
    s = name = xstrdup (original);
122
3689
    while ((s = strchr (s, '_')) != NULL)
123
2449
	*s++ = '-';
124

            
125
1240
    return name;
126
}
127

            
128
char *
129
632
cairo_test_get_name (const cairo_test_t *test)
130
{
131
632
    return _cairo_test_fixup_name (test->name);
132
}
133

            
134
static void
135
608
_cairo_test_init (cairo_test_context_t *ctx,
136
		  const cairo_test_context_t *parent,
137
		  const cairo_test_t *test,
138
		  const char *test_name,
139
		  const char *output)
140
{
141
    char *log_name;
142

            
143
    MF (MEMFAULT_DISABLE_FAULTS ());
144

            
145
#if HAVE_FEENABLEEXCEPT
146
608
    feenableexcept (FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
147
#endif
148

            
149
608
    ctx->test = test;
150
608
    ctx->test_name = _cairo_test_fixup_name (test_name);
151
608
    ctx->output = output;
152

            
153
608
    cairo_test_mkdir (ctx->output);
154

            
155
608
    ctx->malloc_failure = 0;
156
#if HAVE_MEMFAULT
157
    if (getenv ("CAIRO_TEST_MALLOC_FAILURE"))
158
	ctx->malloc_failure = atoi (getenv ("CAIRO_TEST_MALLOC_FAILURE"));
159
    if (ctx->malloc_failure && ! RUNNING_ON_MEMFAULT ())
160
	ctx->malloc_failure = 0;
161
#endif
162

            
163
608
    ctx->timeout = cairo_test_timeout;
164
608
    if (getenv ("CAIRO_TEST_TIMEOUT"))
165
	ctx->timeout = atoi (getenv ("CAIRO_TEST_TIMEOUT"));
166

            
167
608
    xasprintf (&log_name, "%s/%s%s", ctx->output, ctx->test_name, CAIRO_TEST_LOG_SUFFIX);
168
608
    _xunlink (NULL, log_name);
169

            
170
608
    ctx->log_file = fopen (log_name, "a");
171
608
    if (ctx->log_file == NULL) {
172
	fprintf (stderr, "Error opening log file: %s\n", log_name);
173
	ctx->log_file = stderr;
174
    }
175
608
    free (log_name);
176

            
177
608
    ctx->ref_name = NULL;
178
608
    ctx->ref_image = NULL;
179
608
    ctx->ref_image_flattened = NULL;
180

            
181
608
    if (parent != NULL) {
182
607
	ctx->targets_to_test = parent->targets_to_test;
183
607
	ctx->num_targets = parent->num_targets;
184
607
	ctx->limited_targets = parent->limited_targets;
185
607
	ctx->own_targets = FALSE;
186

            
187
607
	ctx->srcdir = parent->srcdir;
188
607
	ctx->refdir = xstrdup (parent->refdir);
189
    } else {
190
	int tmp_num_targets;
191
	cairo_bool_t tmp_limited_targets;
192

            
193
1
	ctx->targets_to_test = cairo_boilerplate_get_targets (&tmp_num_targets, &tmp_limited_targets);
194
1
	ctx->num_targets = tmp_num_targets;
195
1
	ctx->limited_targets = tmp_limited_targets;
196
1
	ctx->own_targets = TRUE;
197

            
198
1
	ctx->srcdir = getenv ("srcdir");
199
1
	if (ctx->srcdir == NULL) {
200
            ctx->srcdir = ".";
201
#if HAVE_SYS_STAT_H
202
            struct stat st;
203
            if (stat ("srcdir", &st) == 0 && (st.st_mode & S_IFDIR))
204
                ctx->srcdir = "srcdir";
205
#endif
206
        }
207

            
208
1
	ctx->refdir = xstrdup (getenv ("CAIRO_REF_DIR"));
209
1
        if (ctx->refdir == NULL)
210
1
            xasprintf (&ctx->refdir, "%s/reference", ctx->srcdir);
211
    }
212

            
213
#ifdef HAVE_UNISTD_H
214
608
    if (*fail_face == '\0' && isatty (2)) {
215
	fail_face = "\033[41;37;1m";
216
	xfail_face = "\033[43;37;1m";
217
	normal_face = "\033[m";
218
	if (isatty (1))
219
	    print_fail_on_stdout = FALSE;
220
    }
221
#endif
222

            
223
608
    printf ("\nTESTING %s\n", ctx->test_name);
224
608
}
225

            
226
void
227
607
_cairo_test_context_init_for_test (cairo_test_context_t *ctx,
228
				   const cairo_test_context_t *parent,
229
				   const cairo_test_t *test)
230
{
231
607
    _cairo_test_init (ctx, parent, test, test->name, CAIRO_TEST_OUTPUT_DIR);
232
607
}
233

            
234
void
235
1
cairo_test_init (cairo_test_context_t *ctx,
236
		 const char *test_name,
237
		 const char *output)
238
{
239
1
    _cairo_test_init (ctx, NULL, NULL, test_name, output);
240
1
}
241

            
242
void
243
608
cairo_test_fini (cairo_test_context_t *ctx)
244
{
245
608
    if (ctx->log_file == NULL)
246
	return;
247

            
248
608
    if (ctx->log_file != stderr)
249
608
	fclose (ctx->log_file);
250
608
    ctx->log_file = NULL;
251

            
252
608
    free (ctx->refdir);
253
608
    free (ctx->ref_name);
254
608
    cairo_surface_destroy (ctx->ref_image);
255
608
    cairo_surface_destroy (ctx->ref_image_flattened);
256

            
257
608
    if (ctx->test_name != NULL)
258
608
	free ((char *) ctx->test_name);
259

            
260
608
    if (ctx->own_targets)
261
1
	cairo_boilerplate_free_targets (ctx->targets_to_test);
262

            
263
608
    cairo_boilerplate_fini ();
264

            
265
608
    cairo_debug_reset_static_data ();
266
#if HAVE_FCFINI
267
608
    FcFini ();
268
#endif
269
}
270

            
271
void
272
11312
cairo_test_logv (const cairo_test_context_t *ctx,
273
	        const char *fmt, va_list va)
274
{
275
11312
    FILE *file = ctx && ctx->log_file ? ctx->log_file : stderr;
276
11312
    vfprintf (file, fmt, va);
277
11312
}
278

            
279
void
280
10669
cairo_test_log (const cairo_test_context_t *ctx, const char *fmt, ...)
281
{
282
    va_list va;
283

            
284
10669
    va_start (va, fmt);
285
10669
    cairo_test_logv (ctx, fmt, va);
286
10669
    va_end (va);
287
10669
}
288

            
289
static void
290
3651
_xunlink (const cairo_test_context_t *ctx, const char *pathname)
291
{
292
3651
    if (unlink (pathname) < 0 && errno != ENOENT) {
293
	cairo_test_log (ctx, "Error: Cannot remove %s: %s\n",
294
			pathname, strerror (errno));
295
	exit (1);
296
    }
297
3651
}
298

            
299
char *
300
10698
cairo_test_reference_filename (const cairo_test_context_t *ctx,
301
			       const char *base_name,
302
			       const char *test_name,
303
			       const char *target_name,
304
			       const char *base_target_name,
305
			       const char *format,
306
			       const char *suffix,
307
			       const char *extension)
308
{
309
10698
    char *ref_name = NULL;
310

            
311
    /* First look for a previous build for comparison. */
312
10698
    if (ctx->refdir != NULL && strcmp(suffix, CAIRO_TEST_REF_SUFFIX) == 0) {
313
3444
	xasprintf (&ref_name, "%s/%s" CAIRO_TEST_OUT_SUFFIX "%s",
314
3444
		   ctx->refdir,
315
		   base_name,
316
		   extension);
317
3444
	if (access (ref_name, F_OK) != 0)
318
3444
	    free (ref_name);
319
	else
320
	    goto done;
321
    }
322

            
323
10698
    if (target_name != NULL) {
324
	/* Next look for a target/format-specific reference image. */
325
5532
	xasprintf (&ref_name, "%s/reference/%s.%s.%s%s%s",
326
5532
		   ctx->srcdir,
327
		   test_name,
328
		   target_name,
329
		   format,
330
		   suffix,
331
		   extension);
332
5532
	if (access (ref_name, F_OK) != 0)
333
5505
	    free (ref_name);
334
	else
335
27
	    goto done;
336

            
337
	/* Next, look for target-specific reference image. */
338
5505
	xasprintf (&ref_name, "%s/reference/%s.%s%s%s",
339
5505
		   ctx->srcdir,
340
		   test_name,
341
		   target_name,
342
		   suffix,
343
		   extension);
344
5505
	if (access (ref_name, F_OK) != 0)
345
5307
	    free (ref_name);
346
	else
347
198
	    goto done;
348
    }
349

            
350
10473
    if (base_target_name != NULL) {
351
	/* Next look for a base/format-specific reference image. */
352
5307
	xasprintf (&ref_name, "%s/reference/%s.%s.%s%s%s",
353
5307
		   ctx->srcdir,
354
		   test_name,
355
		   base_target_name,
356
		   format,
357
		   suffix,
358
		   extension);
359
5307
	if (access (ref_name, F_OK) != 0)
360
5307
	    free (ref_name);
361
	else
362
	    goto done;
363

            
364
	/* Next, look for base-specific reference image. */
365
5307
	xasprintf (&ref_name, "%s/reference/%s.%s%s%s",
366
5307
		   ctx->srcdir,
367
		   test_name,
368
		   base_target_name,
369
		   suffix,
370
		   extension);
371
5307
	if (access (ref_name, F_OK) != 0)
372
5304
	    free (ref_name);
373
	else
374
3
	    goto done;
375
    }
376

            
377
    /* Next, look for format-specific reference image. */
378
10470
    xasprintf (&ref_name, "%s/reference/%s.%s%s%s",
379
10470
	       ctx->srcdir,
380
	       test_name,
381
	       format,
382
	       suffix,
383
	       extension);
384
10470
    if (access (ref_name, F_OK) != 0)
385
10230
	free (ref_name);
386
    else
387
240
	goto done;
388

            
389
    /* Finally, look for the standard reference image. */
390
10230
    xasprintf (&ref_name, "%s/reference/%s%s%s", ctx->srcdir,
391
	       test_name,
392
	       suffix,
393
	       extension);
394
10230
    if (access (ref_name, F_OK) != 0)
395
7272
	free (ref_name);
396
    else
397
2958
	goto done;
398

            
399
7272
    ref_name = NULL;
400

            
401
10698
done:
402
10698
    return ref_name;
403
}
404

            
405
cairo_test_similar_t
406
cairo_test_target_has_similar (const cairo_test_context_t *ctx,
407
			       const cairo_boilerplate_target_t *target)
408
{
409
    cairo_surface_t *surface;
410
    cairo_test_similar_t has_similar;
411
    cairo_t * cr;
412
    cairo_surface_t *similar;
413
    cairo_status_t status;
414
    void *closure;
415
    char *path;
416

            
417
    /* ignore image intermediate targets */
418
    if (target->expected_type == CAIRO_SURFACE_TYPE_IMAGE)
419
	return DIRECT;
420

            
421
    if (getenv ("CAIRO_TEST_IGNORE_SIMILAR"))
422
	return DIRECT;
423

            
424
    xasprintf (&path, "%s/%s",
425
	       cairo_test_mkdir (ctx->output) ? ctx->output : ".",
426
	       ctx->test_name);
427

            
428
    has_similar = DIRECT;
429
    do {
430
	do {
431
	    surface = (target->create_surface) (path,
432
						target->content,
433
						ctx->test->width,
434
						ctx->test->height,
435
						ctx->test->width* NUM_DEVICE_SCALE + 25 * NUM_DEVICE_OFFSETS,
436
						ctx->test->height* NUM_DEVICE_SCALE + 25 * NUM_DEVICE_OFFSETS,
437
						CAIRO_BOILERPLATE_MODE_TEST,
438
						&closure);
439
	    if (surface == NULL)
440
		goto out;
441
	} while (cairo_test_malloc_failure (ctx, cairo_surface_status (surface)));
442

            
443
	if (cairo_surface_status (surface))
444
	    goto out;
445

            
446
	cr = cairo_create (surface);
447
	cairo_push_group_with_content (cr,
448
				       cairo_boilerplate_content (target->content));
449
	similar = cairo_get_group_target (cr);
450
	status = cairo_surface_status (similar);
451

            
452
	if (cairo_surface_get_type (similar) == cairo_surface_get_type (surface))
453
	    has_similar = SIMILAR;
454
	else
455
	    has_similar = DIRECT;
456

            
457
	cairo_destroy (cr);
458
	cairo_surface_destroy (surface);
459

            
460
	if (target->cleanup)
461
	    target->cleanup (closure);
462
    } while (! has_similar && cairo_test_malloc_failure (ctx, status));
463
out:
464
    free (path);
465

            
466
    return has_similar;
467
}
468

            
469
static cairo_surface_t *
470
1779
_cairo_test_flatten_reference_image (cairo_test_context_t *ctx,
471
				     cairo_bool_t flatten)
472
{
473
    cairo_surface_t *surface;
474
    cairo_t *cr;
475

            
476
1779
    if (! flatten)
477
1779
	return ctx->ref_image;
478

            
479
    if (ctx->ref_image_flattened != NULL)
480
	return ctx->ref_image_flattened;
481

            
482
    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
483
					  cairo_image_surface_get_width (ctx->ref_image),
484
					  cairo_image_surface_get_height (ctx->ref_image));
485
    cr = cairo_create (surface);
486
    cairo_surface_destroy (surface);
487

            
488
    cairo_set_source_rgb (cr, 1, 1, 1);
489
    cairo_paint (cr);
490

            
491
    cairo_set_source_surface (cr, ctx->ref_image, 0, 0);
492
    cairo_paint (cr);
493

            
494
    surface = cairo_surface_reference (cairo_get_target (cr));
495
    cairo_destroy (cr);
496

            
497
    if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
498
	ctx->ref_image_flattened = surface;
499
    return surface;
500
}
501

            
502
cairo_surface_t *
503
1779
cairo_test_get_reference_image (cairo_test_context_t *ctx,
504
				const char *filename,
505
				cairo_bool_t flatten)
506
{
507
    cairo_surface_t *surface;
508

            
509
1779
    if (ctx->ref_name != NULL) {
510
413
	if (strcmp (ctx->ref_name, filename) == 0)
511
216
	    return _cairo_test_flatten_reference_image (ctx, flatten);
512

            
513
197
	cairo_surface_destroy (ctx->ref_image);
514
197
	ctx->ref_image = NULL;
515

            
516
197
	cairo_surface_destroy (ctx->ref_image_flattened);
517
197
	ctx->ref_image_flattened = NULL;
518

            
519
197
	free (ctx->ref_name);
520
197
	ctx->ref_name = NULL;
521
    }
522

            
523
1563
    surface = cairo_image_surface_create_from_png (filename);
524
1563
    if (cairo_surface_status (surface))
525
	return surface;
526

            
527
1563
    ctx->ref_name = xstrdup (filename);
528
1563
    ctx->ref_image = surface;
529
1563
    return _cairo_test_flatten_reference_image (ctx, flatten);
530
}
531

            
532
static cairo_bool_t
533
3300
cairo_test_file_is_older (const char *filename,
534
	                  char **ref_filenames,
535
			  int num_ref_filenames)
536
{
537
#if HAVE_SYS_STAT_H
538
    struct stat st;
539

            
540
3300
    if (stat (filename, &st) < 0)
541
3300
	return FALSE;
542

            
543
    while (num_ref_filenames--) {
544
	struct stat ref;
545
	char *ref_filename = *ref_filenames++;
546

            
547
	if (ref_filename == NULL)
548
	    continue;
549

            
550
	if (stat (ref_filename++, &ref) < 0)
551
	    continue;
552

            
553
	if (st.st_mtime <= ref.st_mtime)
554
	    return TRUE;
555
    }
556
#endif
557

            
558
    return FALSE;
559
}
560

            
561
static cairo_bool_t
562
11504
cairo_test_files_equal (const char *test_filename,
563
			const char *pass_filename)
564
{
565
    FILE *test, *pass;
566
    int t, p;
567

            
568
11504
    if (test_filename == NULL || pass_filename == NULL)
569
5366
	return FALSE;
570

            
571
6138
    test = fopen (test_filename, "rb");
572
6138
    if (test == NULL)
573
	return FALSE;
574

            
575
6138
    pass = fopen (pass_filename, "rb");
576
6138
    if (pass == NULL) {
577
3016
	fclose (test);
578
3016
	return FALSE;
579
    }
580

            
581
    /* as simple as it gets */
582
    do {
583
354391
	t = getc (test);
584
354391
	p = getc (pass);
585
354391
	if (t != p)
586
2838
	    break;
587
351553
    } while (t != EOF && p != EOF);
588

            
589
3122
    fclose (pass);
590
3122
    fclose (test);
591

            
592
3122
    return t == p; /* both EOF */
593
}
594

            
595
static cairo_bool_t
596
1366
cairo_test_copy_file (const char *src_filename,
597
		      const char *dst_filename)
598
{
599
    FILE *src, *dst;
600
    int c;
601

            
602
#if HAVE_LINK
603
1366
    if (link (src_filename, dst_filename) == 0)
604
1366
	return TRUE;
605

            
606
    unlink (dst_filename);
607
#endif
608

            
609
    src = fopen (src_filename, "rb");
610
    if (src == NULL)
611
	return FALSE;
612

            
613
    dst = fopen (dst_filename, "wb");
614
    if (dst == NULL) {
615
	fclose (src);
616
	return FALSE;
617
    }
618

            
619
    /* as simple as it gets */
620
    while ((c = getc (src)) != EOF)
621
	putc (c, dst);
622

            
623
    fclose (src);
624
    fclose (dst);
625

            
626
    return TRUE;
627
}
628

            
629
static cairo_test_status_t
630
1722
cairo_test_for_target (cairo_test_context_t		 *ctx,
631
		       const cairo_boilerplate_target_t	 *target,
632
		       int				  dev_offset,
633
		       int				  dev_scale,
634
		       cairo_bool_t                       similar)
635
{
636
    cairo_status_t finish_status;
637
1722
    cairo_surface_t *surface = NULL;
638
    cairo_t *cr;
639
1722
    const char *empty_str = "";
640
    char *offset_str;
641
    char *scale_str;
642
    char *base_name, *base_path;
643
    char *out_png_path;
644
1722
    char *ref_path = NULL, *ref_png_path, *cmp_png_path = NULL;
645
1722
    char *new_path = NULL, *new_png_path;
646
1722
    char *xfail_path = NULL, *xfail_png_path;
647
    char *base_ref_png_path;
648
    char *base_new_png_path;
649
    char *base_xfail_png_path;
650
    char *diff_png_path;
651
1722
    char *test_filename = NULL, *pass_filename = NULL, *fail_filename = NULL;
652
    cairo_test_status_t ret, test_status;
653
    cairo_content_t expected_content;
654
    cairo_font_options_t *font_options;
655
    const char *format;
656
1722
    cairo_bool_t have_output = FALSE;
657
1722
    cairo_bool_t have_result = FALSE;
658
    void *closure;
659
    double width, height;
660
    cairo_bool_t have_output_dir;
661
#if HAVE_MEMFAULT
662
    int malloc_failure_iterations = ctx->malloc_failure;
663
    int last_fault_count = 0;
664
#endif
665

            
666
    /* Get the strings ready that we'll need. */
667
1722
    format = cairo_boilerplate_content_name (target->content);
668
1722
    if (dev_offset)
669
	xasprintf (&offset_str, ".%d", dev_offset);
670
    else
671
1722
	offset_str = (char *) empty_str;
672

            
673
1722
    if (dev_scale != 1)
674
	xasprintf (&scale_str, ".x%d", dev_scale);
675
    else
676
1722
	scale_str = (char *) empty_str;
677

            
678
1722
    xasprintf (&base_name, "%s.%s.%s%s%s%s",
679
	       ctx->test_name,
680
1722
	       target->name,
681
	       format,
682
	       similar ? ".similar" : "",
683
	       offset_str,
684
               scale_str);
685

            
686
1722
    if (offset_str != empty_str)
687
      free (offset_str);
688
1722
    if (scale_str != empty_str)
689
      free (scale_str);
690

            
691
1722
    ref_png_path = cairo_test_reference_filename (ctx,
692
						  base_name,
693
						  ctx->test_name,
694
1722
						  target->name,
695
1722
						  target->basename,
696
						  format,
697
						  CAIRO_TEST_REF_SUFFIX,
698
						  CAIRO_TEST_PNG_EXTENSION);
699
1722
    new_png_path = cairo_test_reference_filename (ctx,
700
						  base_name,
701
						  ctx->test_name,
702
1722
						  target->name,
703
1722
						  target->basename,
704
						  format,
705
						  CAIRO_TEST_NEW_SUFFIX,
706
						  CAIRO_TEST_PNG_EXTENSION);
707
1722
    xfail_png_path = cairo_test_reference_filename (ctx,
708
						    base_name,
709
						    ctx->test_name,
710
1722
						    target->name,
711
1722
						    target->basename,
712
						    format,
713
						    CAIRO_TEST_XFAIL_SUFFIX,
714
						    CAIRO_TEST_PNG_EXTENSION);
715

            
716
1722
    base_ref_png_path = cairo_test_reference_filename (ctx,
717
						  base_name,
718
						  ctx->test_name,
719
						  NULL, NULL,
720
						  format,
721
						  CAIRO_TEST_REF_SUFFIX,
722
						  CAIRO_TEST_PNG_EXTENSION);
723
1722
    base_new_png_path = cairo_test_reference_filename (ctx,
724
						  base_name,
725
						  ctx->test_name,
726
						  NULL, NULL,
727
						  format,
728
						  CAIRO_TEST_NEW_SUFFIX,
729
						  CAIRO_TEST_PNG_EXTENSION);
730
1722
    base_xfail_png_path = cairo_test_reference_filename (ctx,
731
						    base_name,
732
						    ctx->test_name,
733
						    NULL, NULL,
734
						    format,
735
						    CAIRO_TEST_XFAIL_SUFFIX,
736
						    CAIRO_TEST_PNG_EXTENSION);
737

            
738
1722
    if (target->file_extension != NULL) {
739
	ref_path = cairo_test_reference_filename (ctx,
740
						  base_name,
741
						  ctx->test_name,
742
						  target->name,
743
						  target->basename,
744
						  format,
745
						  CAIRO_TEST_REF_SUFFIX,
746
						  target->file_extension);
747
	new_path = cairo_test_reference_filename (ctx,
748
						  base_name,
749
						  ctx->test_name,
750
						  target->name,
751
						  target->basename,
752
						  format,
753
						  CAIRO_TEST_NEW_SUFFIX,
754
						  target->file_extension);
755
	xfail_path = cairo_test_reference_filename (ctx,
756
						    base_name,
757
						    ctx->test_name,
758
						    target->name,
759
						    target->basename,
760
						    format,
761
						    CAIRO_TEST_XFAIL_SUFFIX,
762
						    target->file_extension);
763
    }
764

            
765
1722
    have_output_dir = cairo_test_mkdir (ctx->output);
766
1722
    xasprintf (&base_path, "%s/%s",
767
	       have_output_dir ? ctx->output : ".",
768
	       base_name);
769
1722
    xasprintf (&out_png_path, "%s" CAIRO_TEST_OUT_PNG, base_path);
770
1722
    xasprintf (&diff_png_path, "%s" CAIRO_TEST_DIFF_PNG, base_path);
771

            
772
1722
    if (ctx->test->requirements != NULL) {
773
	const char *required;
774

            
775
216
	required = target->is_vector ? "target=raster" : "target=vector";
776
216
	if (strstr (ctx->test->requirements, required) != NULL) {
777
	    cairo_test_log (ctx, "Error: Skipping for %s target %s\n",
778
			    target->is_vector ? "vector" : "raster",
779
			    target->name);
780
	    ret = CAIRO_TEST_UNTESTED;
781
	    goto UNWIND_STRINGS;
782
	}
783

            
784
216
	required = target->is_recording ? "target=!recording" : "target=recording";
785
216
	if (strstr (ctx->test->requirements, required) != NULL) {
786
	    cairo_test_log (ctx, "Error: Skipping for %s target %s\n",
787
			    target->is_recording ? "recording" : "non-recording",
788
			    target->name);
789
	    ret = CAIRO_TEST_UNTESTED;
790
	    goto UNWIND_STRINGS;
791
	}
792
    }
793

            
794
1722
    width = ctx->test->width;
795
1722
    height = ctx->test->height;
796
1722
    if (width && height) {
797
1680
	width *= dev_scale;
798
1680
	height *= dev_scale;
799
1680
	width += dev_offset;
800
1680
	height += dev_offset;
801
    }
802

            
803
#if HAVE_MEMFAULT
804
REPEAT:
805
    MEMFAULT_CLEAR_FAULTS ();
806
    MEMFAULT_RESET_LEAKS ();
807
    ctx->last_fault_count = 0;
808
    last_fault_count = MEMFAULT_COUNT_FAULTS ();
809

            
810
    /* Pre-initialise fontconfig so that the configuration is loaded without
811
     * malloc failures (our primary goal is to test cairo fault tolerance).
812
     */
813
#if HAVE_FCINIT
814
    FcInit ();
815
#endif
816

            
817
    MEMFAULT_ENABLE_FAULTS ();
818
#endif
819
1722
    have_output = FALSE;
820
1722
    have_result = FALSE;
821

            
822
    /* Run the actual drawing code. */
823
1722
    ret = CAIRO_TEST_SUCCESS;
824
1722
    surface = (target->create_surface) (base_path,
825
1722
					target->content,
826
					width, height,
827
1722
					ctx->test->width * NUM_DEVICE_SCALE + 25 * NUM_DEVICE_OFFSETS,
828
1722
					ctx->test->height * NUM_DEVICE_SCALE + 25 * NUM_DEVICE_OFFSETS,
829
					CAIRO_BOILERPLATE_MODE_TEST,
830
					&closure);
831
1722
    if (surface == NULL) {
832
	cairo_test_log (ctx, "Error: Failed to set %s target\n", target->name);
833
	ret = CAIRO_TEST_UNTESTED;
834
	goto UNWIND_STRINGS;
835
    }
836

            
837
#if HAVE_MEMFAULT
838
    if (ctx->malloc_failure &&
839
	MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
840
	cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY)
841
    {
842
	goto REPEAT;
843
    }
844
#endif
845

            
846
1722
    if (cairo_surface_status (surface)) {
847
	MF (MEMFAULT_PRINT_FAULTS ());
848
	cairo_test_log (ctx, "Error: Created an error surface: %s\n",
849
			cairo_status_to_string (cairo_surface_status (surface)));
850
	ret = CAIRO_TEST_FAILURE;
851
	goto UNWIND_STRINGS;
852
    }
853

            
854
    /* Check that we created a surface of the expected type. */
855
1722
    if (cairo_surface_get_type (surface) != target->expected_type) {
856
	MF (MEMFAULT_PRINT_FAULTS ());
857
	cairo_test_log (ctx, "Error: Created surface is of type %d (expected %d)\n",
858
			cairo_surface_get_type (surface), target->expected_type);
859
	ret = CAIRO_TEST_UNTESTED;
860
	goto UNWIND_SURFACE;
861
    }
862

            
863
    /* Check that we created a surface of the expected content,
864
     * (ignore the artificial CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED value).
865
     */
866
1722
    expected_content = cairo_boilerplate_content (target->content);
867

            
868
1722
    if (cairo_surface_get_content (surface) != expected_content) {
869
	MF (MEMFAULT_PRINT_FAULTS ());
870
	cairo_test_log (ctx, "Error: Created surface has content %d (expected %d)\n",
871
			cairo_surface_get_content (surface), expected_content);
872
	ret = CAIRO_TEST_FAILURE;
873
	goto UNWIND_SURFACE;
874
    }
875

            
876
1722
    if (cairo_surface_set_user_data (surface,
877
				     &cairo_boilerplate_output_basename_key,
878
				     base_path,
879
				     NULL))
880
    {
881
#if HAVE_MEMFAULT
882
	cairo_surface_destroy (surface);
883

            
884
	if (target->cleanup)
885
	    target->cleanup (closure);
886

            
887
	goto REPEAT;
888
#else
889
	ret = CAIRO_TEST_FAILURE;
890
	goto UNWIND_SURFACE;
891
#endif
892
    }
893

            
894
1722
    cairo_surface_set_device_offset (surface, dev_offset, dev_offset);
895
1722
    cairo_surface_set_device_scale (surface, dev_scale, dev_scale);
896

            
897
1722
    cr = cairo_create (surface);
898
1722
    if (cairo_set_user_data (cr, &_cairo_test_context_key, (void*) ctx, NULL)) {
899
#if HAVE_MEMFAULT
900
	cairo_destroy (cr);
901
	cairo_surface_destroy (surface);
902

            
903
	if (target->cleanup)
904
	    target->cleanup (closure);
905

            
906
	goto REPEAT;
907
#else
908
	ret = CAIRO_TEST_FAILURE;
909
	goto UNWIND_CAIRO;
910
#endif
911
    }
912

            
913
1722
    if (similar)
914
	cairo_push_group_with_content (cr, expected_content);
915

            
916
    /* Clear to transparent (or black) depending on whether the target
917
     * surface supports alpha. */
918
1722
    cairo_save (cr);
919
1722
    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
920
1722
    cairo_paint (cr);
921
1722
    cairo_restore (cr);
922

            
923
1722
    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
924
			    CAIRO_FONT_SLANT_NORMAL,
925
			    CAIRO_FONT_WEIGHT_NORMAL);
926
    
927
    /* Set all components of font_options to avoid backend differences
928
     * and reduce number of needed reference images. */
929
1722
    font_options = cairo_font_options_create ();
930
1722
    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
931
1722
    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_ON);
932
1722
    cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_GRAY);
933
1722
    cairo_set_font_options (cr, font_options);
934
1722
    cairo_font_options_destroy (font_options);
935

            
936
1722
    cairo_save (cr);
937
    alarm (ctx->timeout);
938
1722
    test_status = (ctx->test->draw) (cr, ctx->test->width, ctx->test->height);
939
    alarm (0);
940
1722
    cairo_restore (cr);
941

            
942
1722
    if (similar) {
943
	cairo_pop_group_to_source (cr);
944
	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
945
	cairo_paint (cr);
946
    }
947

            
948
#if HAVE_MEMFAULT
949
    MEMFAULT_DISABLE_FAULTS ();
950

            
951
    /* repeat test after malloc failure injection */
952
    if (ctx->malloc_failure &&
953
	MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
954
	(test_status == CAIRO_TEST_NO_MEMORY ||
955
	 cairo_status (cr) == CAIRO_STATUS_NO_MEMORY ||
956
	 cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY))
957
    {
958
	cairo_destroy (cr);
959
	cairo_surface_destroy (surface);
960
	if (target->cleanup)
961
	    target->cleanup (closure);
962
	cairo_debug_reset_static_data ();
963
#if HAVE_FCFINI
964
	FcFini ();
965
#endif
966
	if (MEMFAULT_COUNT_LEAKS () > 0) {
967
	    MEMFAULT_PRINT_FAULTS ();
968
	    MEMFAULT_PRINT_LEAKS ();
969
	}
970

            
971
	goto REPEAT;
972
    }
973
#endif
974

            
975
    /* Then, check all the different ways it could fail. */
976
1722
    if (test_status) {
977
3
	cairo_test_log (ctx, "Error: Function under test failed\n");
978
3
	ret = test_status;
979
3
	goto UNWIND_CAIRO;
980
    }
981

            
982
#if HAVE_MEMFAULT
983
    if (MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
984
	MEMFAULT_HAS_FAULTS ())
985
    {
986
	VALGRIND_PRINTF ("Unreported memfaults...");
987
	MEMFAULT_PRINT_FAULTS ();
988
    }
989
#endif
990

            
991
1719
    if (target->finish_surface != NULL) {
992
#if HAVE_MEMFAULT
993
	/* We need to re-enable faults as most recording-surface processing
994
	 * is done during cairo_surface_finish().
995
	 */
996
	MEMFAULT_CLEAR_FAULTS ();
997
	last_fault_count = MEMFAULT_COUNT_FAULTS ();
998
	MEMFAULT_ENABLE_FAULTS ();
999
#endif
	/* also check for infinite loops whilst replaying */
	alarm (ctx->timeout);
	finish_status = target->finish_surface (surface);
	alarm (0);
#if HAVE_MEMFAULT
	MEMFAULT_DISABLE_FAULTS ();
	if (ctx->malloc_failure &&
	    MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
	    finish_status == CAIRO_STATUS_NO_MEMORY)
	{
	    cairo_destroy (cr);
	    cairo_surface_destroy (surface);
	    if (target->cleanup)
		target->cleanup (closure);
	    cairo_debug_reset_static_data ();
#if HAVE_FCFINI
	    FcFini ();
#endif
	    if (MEMFAULT_COUNT_LEAKS () > 0) {
		MEMFAULT_PRINT_FAULTS ();
		MEMFAULT_PRINT_LEAKS ();
	    }
	    goto REPEAT;
	}
#endif
	if (finish_status) {
	    cairo_test_log (ctx, "Error: Failed to finish surface: %s\n",
			    cairo_status_to_string (finish_status));
	    ret = CAIRO_TEST_FAILURE;
	    goto UNWIND_CAIRO;
	}
    }
    /* Skip image check for tests with no image (width,height == 0,0) */
1719
    if (ctx->test->width != 0 && ctx->test->height != 0) {
	cairo_surface_t *ref_image;
	cairo_surface_t *test_image;
	cairo_surface_t *diff_image;
	buffer_diff_result_t result;
	cairo_status_t diff_status;
1677
	if (ref_png_path == NULL) {
27
	    cairo_test_log (ctx, "Error: Cannot find reference image for %s\n",
			    base_name);
	    /* we may be running this test to generate reference images */
27
	    _xunlink (ctx, out_png_path);
	    /* be more generous as we may need to use external renderers */
	    alarm (4 * ctx->timeout);
27
	    test_image = target->get_image_surface (surface, 0,
27
		                                    ctx->test->width,
27
						    ctx->test->height);
	    alarm (0);
27
	    diff_status = cairo_surface_write_to_png (test_image, out_png_path);
27
	    cairo_surface_destroy (test_image);
27
	    if (diff_status) {
		if (cairo_surface_status (test_image) == CAIRO_STATUS_INVALID_STATUS)
		    ret = CAIRO_TEST_CRASHED;
		else
		    ret = CAIRO_TEST_FAILURE;
		cairo_test_log (ctx,
			        "Error: Failed to write output image: %s\n",
			        cairo_status_to_string (diff_status));
	    }
27
	    have_output = TRUE;
27
	    ret = CAIRO_TEST_XFAILURE;
311
	    goto UNWIND_CAIRO;
	}
1650
	if (target->file_extension != NULL) { /* compare vector surfaces */
	    char *filenames[] = {
		ref_png_path,
		ref_path,
		new_png_path,
		new_path,
		xfail_png_path,
		xfail_path,
		base_ref_png_path,
		base_new_png_path,
		base_xfail_png_path,
	    };
	    xasprintf (&test_filename, "%s.out%s",
		       base_path, target->file_extension);
	    xasprintf (&pass_filename, "%s.pass%s",
		       base_path, target->file_extension);
	    xasprintf (&fail_filename, "%s.fail%s",
		       base_path, target->file_extension);
	    if (cairo_test_file_is_older (pass_filename,
					  filenames,
					  ARRAY_LENGTH (filenames)))
	    {
		_xunlink (ctx, pass_filename);
	    }
	    if (cairo_test_file_is_older (fail_filename,
					  filenames,
					  ARRAY_LENGTH (filenames)))
	    {
		_xunlink (ctx, fail_filename);
	    }
	    if (cairo_test_files_equal (out_png_path, ref_path)) {
		cairo_test_log (ctx, "Vector surface matches reference.\n");
		have_output = FALSE;
		ret = CAIRO_TEST_SUCCESS;
		goto UNWIND_CAIRO;
	    }
	    if (cairo_test_files_equal (out_png_path, new_path)) {
		cairo_test_log (ctx, "Vector surface matches current failure.\n");
		have_output = FALSE;
		ret = CAIRO_TEST_NEW;
		goto UNWIND_CAIRO;
	    }
	    if (cairo_test_files_equal (out_png_path, xfail_path)) {
		cairo_test_log (ctx, "Vector surface matches known failure.\n");
		have_output = FALSE;
		ret = CAIRO_TEST_XFAILURE;
		goto UNWIND_CAIRO;
	    }
	    if (cairo_test_files_equal (test_filename, pass_filename)) {
		/* identical output as last known PASS */
		cairo_test_log (ctx, "Vector surface matches last pass.\n");
		have_output = TRUE;
		ret = CAIRO_TEST_SUCCESS;
		goto UNWIND_CAIRO;
	    }
	    if (cairo_test_files_equal (test_filename, fail_filename)) {
		/* identical output as last known FAIL, fail */
		cairo_test_log (ctx, "Vector surface matches last fail.\n");
		have_result = TRUE; /* presume these were kept around as well */
		have_output = TRUE;
		ret = CAIRO_TEST_FAILURE;
		goto UNWIND_CAIRO;
	    }
	}
	/* be more generous as we may need to use external renderers */
	alarm (4 * ctx->timeout);
1650
	test_image = target->get_image_surface (surface, 0,
1650
					        ctx->test->width,
1650
						ctx->test->height);
	alarm (0);
1650
	if (cairo_surface_status (test_image)) {
	    cairo_test_log (ctx, "Error: Failed to extract image: %s\n",
			    cairo_status_to_string (cairo_surface_status (test_image)));
	    if (cairo_surface_status (test_image) == CAIRO_STATUS_INVALID_STATUS)
		ret = CAIRO_TEST_CRASHED;
	    else
		ret = CAIRO_TEST_FAILURE;
	    cairo_surface_destroy (test_image);
	    goto UNWIND_CAIRO;
	}
1650
	_xunlink (ctx, out_png_path);
1650
	diff_status = cairo_surface_write_to_png (test_image, out_png_path);
1650
	if (diff_status) {
	    cairo_test_log (ctx, "Error: Failed to write output image: %s\n",
			    cairo_status_to_string (diff_status));
	    cairo_surface_destroy (test_image);
	    ret = CAIRO_TEST_FAILURE;
	    goto UNWIND_CAIRO;
	}
1650
	have_output = TRUE;
	/* binary compare png files (no decompression) */
1650
	if (target->file_extension == NULL) {
1650
	    char *filenames[] = {
		ref_png_path,
		new_png_path,
		xfail_png_path,
		base_ref_png_path,
		base_new_png_path,
		base_xfail_png_path,
	    };
1650
	    xasprintf (&test_filename, "%s", out_png_path);
1650
	    xasprintf (&pass_filename, "%s.pass.png", base_path);
1650
	    xasprintf (&fail_filename, "%s.fail.png", base_path);
1650
	    if (cairo_test_file_is_older (pass_filename,
					  filenames,
					  ARRAY_LENGTH (filenames)))
	    {
		_xunlink (ctx, pass_filename);
	    }
1650
	    if (cairo_test_file_is_older (fail_filename,
					  filenames,
					  ARRAY_LENGTH (filenames)))
	    {
		_xunlink (ctx, fail_filename);
	    }
1650
	    if (cairo_test_files_equal (test_filename, pass_filename)) {
		cairo_test_log (ctx, "PNG file exactly matches last pass.\n");
                have_result = TRUE;
		cairo_surface_destroy (test_image);
		ret = CAIRO_TEST_SUCCESS;
284
		goto UNWIND_CAIRO;
	    }
1650
	    if (cairo_test_files_equal (out_png_path, ref_png_path)) {
280
		cairo_test_log (ctx, "PNG file exactly matches reference image.\n");
280
                have_result = TRUE;
280
		cairo_surface_destroy (test_image);
280
		ret = CAIRO_TEST_SUCCESS;
280
		goto UNWIND_CAIRO;
	    }
1370
	    if (cairo_test_files_equal (out_png_path, new_png_path)) {
		cairo_test_log (ctx, "PNG file exactly matches current failure image.\n");
                have_result = TRUE;
		cairo_surface_destroy (test_image);
		ret = CAIRO_TEST_NEW;
		goto UNWIND_CAIRO;
	    }
1370
	    if (cairo_test_files_equal (out_png_path, xfail_png_path)) {
4
		cairo_test_log (ctx, "PNG file exactly matches known failure image.\n");
4
                have_result = TRUE;
4
		cairo_surface_destroy (test_image);
4
		ret = CAIRO_TEST_XFAILURE;
4
		goto UNWIND_CAIRO;
	    }
1366
	    if (cairo_test_files_equal (test_filename, fail_filename)) {
		cairo_test_log (ctx, "PNG file exactly matches last fail.\n");
		have_result = TRUE; /* presume these were kept around as well */
		cairo_surface_destroy (test_image);
		ret = CAIRO_TEST_FAILURE;
		goto UNWIND_CAIRO;
	    }
	} else {
	    if (cairo_test_files_equal (out_png_path, ref_png_path)) {
		cairo_test_log (ctx, "PNG file exactly matches reference image.\n");
		have_result = TRUE;
		cairo_surface_destroy (test_image);
		ret = CAIRO_TEST_SUCCESS;
		goto UNWIND_CAIRO;
	    }
	    if (cairo_test_files_equal (out_png_path, new_png_path)) {
		cairo_test_log (ctx, "PNG file exactly matches current failure image.\n");
		have_result = TRUE;
		cairo_surface_destroy (test_image);
		ret = CAIRO_TEST_NEW;
		goto UNWIND_CAIRO;
	    }
	    if (cairo_test_files_equal (out_png_path, xfail_png_path)) {
		cairo_test_log (ctx, "PNG file exactly matches known failure image.\n");
		have_result = TRUE;
		cairo_surface_destroy (test_image);
		ret = CAIRO_TEST_XFAILURE;
		goto UNWIND_CAIRO;
	    }
	}
1366
	if (cairo_test_files_equal (out_png_path, base_ref_png_path)) {
	    cairo_test_log (ctx, "PNG file exactly reference image.\n");
	    have_result = TRUE;
	    cairo_surface_destroy (test_image);
	    ret = CAIRO_TEST_SUCCESS;
	    goto UNWIND_CAIRO;
	}
1366
	if (cairo_test_files_equal (out_png_path, base_new_png_path)) {
	    cairo_test_log (ctx, "PNG file exactly current failure image.\n");
	    have_result = TRUE;
	    cairo_surface_destroy (test_image);
	    ret = CAIRO_TEST_NEW;
	    goto UNWIND_CAIRO;
	}
1366
	if (cairo_test_files_equal (out_png_path, base_xfail_png_path)) {
	    cairo_test_log (ctx, "PNG file exactly known failure image.\n");
	    have_result = TRUE;
	    cairo_surface_destroy (test_image);
	    ret = CAIRO_TEST_XFAILURE;
	    goto UNWIND_CAIRO;
	}
	/* first compare against the ideal reference */
1366
	ref_image = cairo_test_get_reference_image (ctx, base_ref_png_path,
1366
						    target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED);
1366
	if (cairo_surface_status (ref_image)) {
	    cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n",
			    base_ref_png_path,
			    cairo_status_to_string (cairo_surface_status (ref_image)));
	    cairo_surface_destroy (test_image);
	    ret = CAIRO_TEST_FAILURE;
	    goto UNWIND_CAIRO;
	}
1366
	diff_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
1366
						 ctx->test->width,
1366
						 ctx->test->height);
1366
	cmp_png_path = base_ref_png_path;
1366
	diff_status = image_diff (ctx,
				  test_image, ref_image, diff_image,
				  &result);
1366
	_xunlink (ctx, diff_png_path);
2732
	if (diff_status ||
1366
            image_diff_is_failure (&result, target->error_tolerance))
	{
	    /* that failed, so check against the specific backend */
413
	    ref_image = cairo_test_get_reference_image (ctx, ref_png_path,
413
							target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED);
413
	    if (cairo_surface_status (ref_image)) {
		cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n",
				ref_png_path,
				cairo_status_to_string (cairo_surface_status (ref_image)));
		cairo_surface_destroy (test_image);
		ret = CAIRO_TEST_FAILURE;
		goto UNWIND_CAIRO;
	    }
413
	    cmp_png_path = ref_png_path;
413
	    diff_status = image_diff (ctx,
				      test_image, ref_image,
				      diff_image,
				      &result);
413
	    if (diff_status)
	    {
		cairo_test_log (ctx, "Error: Failed to compare images: %s\n",
				cairo_status_to_string (diff_status));
		ret = CAIRO_TEST_FAILURE;
	    }
413
	    else if (image_diff_is_failure (&result, target->error_tolerance))
	    {
366
		ret = CAIRO_TEST_FAILURE;
366
		diff_status = cairo_surface_write_to_png (diff_image,
							  diff_png_path);
366
		if (diff_status) {
		    cairo_test_log (ctx, "Error: Failed to write differences image: %s\n",
				    cairo_status_to_string (diff_status));
		} else {
366
		    have_result = TRUE;
		}
366
		cairo_test_copy_file (test_filename, fail_filename);
	    }
	    else
	    { /* success */
47
		cairo_test_copy_file (test_filename, pass_filename);
	    }
	}
	else
	{ /* success */
953
	    cairo_test_copy_file (test_filename, pass_filename);
	}
	/* If failed, compare against the current image output,
	 * and attempt to detect systematic failures.
	 */
1366
	if (ret == CAIRO_TEST_FAILURE) {
	    char *image_out_path;
	    image_out_path =
366
		cairo_test_reference_filename (ctx,
					       base_name,
					       ctx->test_name,
					       "image",
					       "image",
					       format,
					       CAIRO_TEST_OUT_SUFFIX,
					       CAIRO_TEST_PNG_EXTENSION);
366
	    if (image_out_path != NULL) {
		if (cairo_test_files_equal (out_png_path,
					    image_out_path))
		{
		    ret = CAIRO_TEST_XFAILURE;
		}
		else
		{
		    ref_image =
			cairo_image_surface_create_from_png (image_out_path);
		    if (cairo_surface_status (ref_image) == CAIRO_STATUS_SUCCESS)
		    {
			diff_status = image_diff (ctx,
						  test_image, ref_image,
						  diff_image,
						  &result);
			if (diff_status == CAIRO_STATUS_SUCCESS &&
			    !image_diff_is_failure (&result, target->error_tolerance))
			{
			    ret = CAIRO_TEST_XFAILURE;
			}
			cairo_surface_destroy (ref_image);
		    }
		}
		free (image_out_path);
	    }
	}
1366
	cairo_surface_destroy (test_image);
1366
	cairo_surface_destroy (diff_image);
    }
1408
    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
	cairo_test_log (ctx, "Error: Function under test left cairo status in an error state: %s\n",
			cairo_status_to_string (cairo_status (cr)));
	ret = CAIRO_TEST_ERROR;
	goto UNWIND_CAIRO;
    }
1408
UNWIND_CAIRO:
1722
    free (test_filename);
1722
    free (fail_filename);
1722
    free (pass_filename);
1722
    test_filename = fail_filename = pass_filename = NULL;
#if HAVE_MEMFAULT
    if (ret == CAIRO_TEST_FAILURE)
	MEMFAULT_PRINT_FAULTS ();
#endif
1722
    cairo_destroy (cr);
1722
UNWIND_SURFACE:
1722
    cairo_surface_destroy (surface);
1722
    if (target->cleanup)
	target->cleanup (closure);
#if HAVE_MEMFAULT
    cairo_debug_reset_static_data ();
#if HAVE_FCFINI
    FcFini ();
#endif
    if (MEMFAULT_COUNT_LEAKS () > 0) {
	if (ret != CAIRO_TEST_FAILURE)
	    MEMFAULT_PRINT_FAULTS ();
	MEMFAULT_PRINT_LEAKS ();
    }
    if (ret == CAIRO_TEST_SUCCESS && --malloc_failure_iterations > 0)
	goto REPEAT;
#endif
1722
    if (have_output)
1677
	cairo_test_log (ctx, "OUTPUT: %s\n", out_png_path);
1722
    if (have_result) {
650
	if (cmp_png_path == NULL) {
	    /* XXX presume we matched the normal ref last time */
284
	    cmp_png_path = ref_png_path;
	}
650
	cairo_test_log (ctx,
			"REFERENCE: %s\nDIFFERENCE: %s\n",
			cmp_png_path, diff_png_path);
    }
1072
UNWIND_STRINGS:
1722
    free (out_png_path);
1722
    free (ref_png_path);
1722
    free (base_ref_png_path);
1722
    free (ref_path);
1722
    free (new_png_path);
1722
    free (base_new_png_path);
1722
    free (new_path);
1722
    free (xfail_png_path);
1722
    free (base_xfail_png_path);
1722
    free (xfail_path);
1722
    free (diff_png_path);
1722
    free (base_path);
1722
    free (base_name);
1722
    return ret;
}
#if defined(HAVE_SIGNAL_H) && defined(HAVE_SETJMP_H)
#include <signal.h>
#include <setjmp.h>
/* Used to catch crashes in a test, so that we report it as such and
 * continue testing, although one crash may already have corrupted memory in
 * an nonrecoverable fashion. */
static jmp_buf jmpbuf;
static void
segfault_handler (int signal)
{
    longjmp (jmpbuf, signal);
}
#endif
cairo_test_status_t
1722
_cairo_test_context_run_for_target (cairo_test_context_t *ctx,
				    const cairo_boilerplate_target_t *target,
				    cairo_bool_t similar,
				    int dev_offset, int dev_scale)
{
    cairo_test_status_t status;
1722
    if (target->get_image_surface == NULL)
	return CAIRO_TEST_UNTESTED;
1722
    if (similar && ! cairo_test_target_has_similar (ctx, target))
	return CAIRO_TEST_UNTESTED;
1722
    cairo_test_log (ctx,
		    "Testing %s with %s%s target (dev offset %d scale: %d)\n",
		    ctx->test_name,
		    similar ? " (similar) " : "",
1722
		    target->name,
		    dev_offset, dev_scale);
3444
    printf ("%s.%s.%s [%dx%d]%s:\t", ctx->test_name, target->name,
1722
	    cairo_boilerplate_content_name (target->content),
	    dev_offset, dev_scale,
	    similar ? " (similar)": "");
1722
    fflush (stdout);
#if defined(HAVE_SIGNAL_H) && defined(HAVE_SETJMP_H)
    if (! RUNNING_ON_VALGRIND) {
	void (* volatile old_segfault_handler)(int);
	void (* volatile old_segfpe_handler)(int);
#ifdef SIGPIPE
	void (* volatile old_sigpipe_handler)(int);
#endif
	void (* volatile old_sigabrt_handler)(int);
#ifdef SIGALRM
	void (* volatile old_sigalrm_handler)(int);
#endif
	/* Set up a checkpoint to get back to in case of segfaults. */
#ifdef SIGSEGV
1722
	old_segfault_handler = signal (SIGSEGV, segfault_handler);
#endif
#ifdef SIGFPE
1722
	old_segfpe_handler = signal (SIGFPE, segfault_handler);
#endif
#ifdef SIGPIPE
1722
	old_sigpipe_handler = signal (SIGPIPE, segfault_handler);
#endif
#ifdef SIGABRT
1722
	old_sigabrt_handler = signal (SIGABRT, segfault_handler);
#endif
#ifdef SIGALRM
1722
	old_sigalrm_handler = signal (SIGALRM, segfault_handler);
#endif
1722
	if (0 == setjmp (jmpbuf))
1722
	    status = cairo_test_for_target (ctx, target, dev_offset, dev_scale, similar);
	else
	    status = CAIRO_TEST_CRASHED;
#ifdef SIGSEGV
1722
	signal (SIGSEGV, old_segfault_handler);
#endif
#ifdef SIGFPE
1722
	signal (SIGFPE, old_segfpe_handler);
#endif
#ifdef SIGPIPE
1722
	signal (SIGPIPE, old_sigpipe_handler);
#endif
#ifdef SIGABRT
1722
	signal (SIGABRT, old_sigabrt_handler);
#endif
#ifdef SIGALRM
1722
	signal (SIGALRM, old_sigalrm_handler);
#endif
    } else {
	status = cairo_test_for_target (ctx, target, dev_offset, dev_scale, similar);
    }
#else
    status = cairo_test_for_target (ctx, target, dev_offset, dev_scale, similar);
#endif
3444
    cairo_test_log (ctx,
		    "TEST: %s TARGET: %s FORMAT: %s OFFSET: %d SCALE: %d SIMILAR: %d RESULT: ",
1722
		    ctx->test_name, target->name,
1722
		    cairo_boilerplate_content_name (target->content),
		    dev_offset, dev_scale, similar);
1722
    switch (status) {
1322
    case CAIRO_TEST_SUCCESS:
1322
	printf ("PASS\n");
1322
	cairo_test_log (ctx, "PASS\n");
1322
	break;
3
    case CAIRO_TEST_UNTESTED:
3
	printf ("UNTESTED\n");
3
	cairo_test_log (ctx, "UNTESTED\n");
3
	break;
    default:
    case CAIRO_TEST_CRASHED:
	if (print_fail_on_stdout) {
	    printf ("!!!CRASHED!!!\n");
	} else {
	    /* eat the test name */
	    printf ("\r");
	    fflush (stdout);
	}
	cairo_test_log (ctx, "CRASHED\n");
	fprintf (stderr, "%s.%s.%s [%dx%d]%s:\t%s!!!CRASHED!!!%s\n",
		 ctx->test_name, target->name,
		 cairo_boilerplate_content_name (target->content), dev_offset, dev_scale, similar ? " (similar)" : "",
		 fail_face, normal_face);
	break;
    case CAIRO_TEST_ERROR:
	if (print_fail_on_stdout) {
	    printf ("!!!ERROR!!!\n");
	} else {
	    /* eat the test name */
	    printf ("\r");
	    fflush (stdout);
	}
	cairo_test_log (ctx, "ERROR\n");
	fprintf (stderr, "%s.%s.%s [%dx%d]%s:\t%s!!!ERROR!!!%s\n",
		 ctx->test_name, target->name,
		 cairo_boilerplate_content_name (target->content), dev_offset, dev_scale, similar ? " (similar)" : "",
		 fail_face, normal_face);
	break;
31
    case CAIRO_TEST_XFAILURE:
31
	if (print_fail_on_stdout) {
	    printf ("XFAIL\n");
	} else {
	    /* eat the test name */
31
	    printf ("\r");
31
	    fflush (stdout);
	}
31
	fprintf (stderr, "%s.%s.%s [%dx%d]%s:\t%sXFAIL%s\n",
31
		 ctx->test_name, target->name,
31
		 cairo_boilerplate_content_name (target->content), dev_offset, dev_scale, similar ? " (similar)" : "",
		 xfail_face, normal_face);
31
	cairo_test_log (ctx, "XFAIL\n");
31
	break;
    case CAIRO_TEST_NEW:
	if (print_fail_on_stdout) {
	    printf ("NEW\n");
	} else {
	    /* eat the test name */
	    printf ("\r");
	    fflush (stdout);
	}
	fprintf (stderr, "%s.%s.%s [%dx%d]%s:\t%sNEW%s\n",
		 ctx->test_name, target->name,
		 cairo_boilerplate_content_name (target->content), dev_offset, dev_scale, similar ? " (similar)" : "",
		 fail_face, normal_face);
	cairo_test_log (ctx, "NEW\n");
	break;
366
    case CAIRO_TEST_NO_MEMORY:
    case CAIRO_TEST_FAILURE:
366
	if (print_fail_on_stdout) {
	    printf ("FAIL\n");
	} else {
	    /* eat the test name */
366
	    printf ("\r");
366
	    fflush (stdout);
	}
366
	fprintf (stderr, "%s.%s.%s [%dx%d]%s:\t%sFAIL%s\n",
366
		 ctx->test_name, target->name,
366
		 cairo_boilerplate_content_name (target->content), dev_offset, dev_scale, similar ? " (similar)" : "",
		 fail_face, normal_face);
366
	cairo_test_log (ctx, "FAIL\n");
366
	break;
    }
1722
    fflush (stdout);
1722
    return status;
}
const cairo_test_context_t *
780
cairo_test_get_context (cairo_t *cr)
{
780
    return cairo_get_user_data (cr, &_cairo_test_context_key);
}
cairo_t *
163
cairo_test_create (cairo_surface_t *surface,
		   const cairo_test_context_t *ctx)
{
163
    cairo_t *cr = cairo_create (surface);
163
    cairo_set_user_data (cr, &_cairo_test_context_key,
			 (void*) ctx, NULL);
163
    return cr;
}
cairo_surface_t *
120
cairo_test_create_surface_from_png (const cairo_test_context_t *ctx,
	                            const char *filename)
{
    cairo_surface_t *image;
    cairo_status_t status;
    char *unique_id;
120
    image = cairo_image_surface_create_from_png (filename);
120
    status = cairo_surface_status (image);
120
    if (status == CAIRO_STATUS_FILE_NOT_FOUND) {
        /* expect not found when running with srcdir != builddir
         * such as when 'make distcheck' is run
         */
120
	if (ctx->srcdir) {
	    char *srcdir_filename;
120
	    xasprintf (&srcdir_filename, "%s/%s", ctx->srcdir, filename);
120
	    cairo_surface_destroy (image);
120
	    image = cairo_image_surface_create_from_png (srcdir_filename);
120
	    free (srcdir_filename);
	}
    }
120
    unique_id = strdup(filename);
120
    cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_UNIQUE_ID,
				 (unsigned char *)unique_id, strlen(unique_id),
				 free, unique_id);
120
    return image;
}
cairo_pattern_t *
21
cairo_test_create_pattern_from_png (const cairo_test_context_t *ctx,
	                            const char *filename)
{
    cairo_surface_t *image;
    cairo_pattern_t *pattern;
21
    image = cairo_test_create_surface_from_png (ctx, filename);
21
    pattern = cairo_pattern_create_for_surface (image);
21
    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
21
    cairo_surface_destroy (image);
21
    return pattern;
}
static cairo_surface_t *
162
_draw_check (int width, int height)
{
    cairo_surface_t *surface;
    cairo_t *cr;
162
    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 12, 12);
162
    cr = cairo_create (surface);
162
    cairo_surface_destroy (surface);
162
    cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); /* light gray */
162
    cairo_paint (cr);
162
    cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); /* dark gray */
162
    cairo_rectangle (cr, width / 2,  0, width / 2, height / 2);
162
    cairo_rectangle (cr, 0, height / 2, width / 2, height / 2);
162
    cairo_fill (cr);
162
    surface = cairo_surface_reference (cairo_get_target (cr));
162
    cairo_destroy (cr);
162
    return surface;
}
void
162
cairo_test_paint_checkered (cairo_t *cr)
{
    cairo_surface_t *check;
162
    check = _draw_check (12, 12);
162
    cairo_save (cr);
162
    cairo_set_source_surface (cr, check, 0, 0);
162
    cairo_surface_destroy (check);
162
    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
162
    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
162
    cairo_paint (cr);
162
    cairo_restore (cr);
162
}
cairo_bool_t
26
cairo_test_is_target_enabled (const cairo_test_context_t *ctx,
			      const char *target)
{
    size_t i;
97
    for (i = 0; i < ctx->num_targets; i++) {
74
	const cairo_boilerplate_target_t *t = ctx->targets_to_test[i];
74
	if (strcmp (t->name, target) == 0) {
	    /* XXX ask the target whether is it possible to run?
	     * e.g. the xlib backend could check whether it is able to connect
	     * to the Display.
	     */
3
	    return t->get_image_surface != NULL;
	}
    }
23
    return FALSE;
}
cairo_bool_t
cairo_test_malloc_failure (const cairo_test_context_t *ctx,
			   cairo_status_t status)
{
    if (! ctx->malloc_failure)
	return FALSE;
    if (status != CAIRO_STATUS_NO_MEMORY)
	return FALSE;
#if HAVE_MEMFAULT
    {
	int n_faults;
	/* prevent infinite loops... */
	n_faults = MEMFAULT_COUNT_FAULTS ();
	if (n_faults == ctx->last_fault_count)
	    return FALSE;
	((cairo_test_context_t *) ctx)->last_fault_count = n_faults;
    }
#endif
    return TRUE;
}
cairo_test_status_t
28
cairo_test_status_from_status (const cairo_test_context_t *ctx,
			       cairo_status_t status)
{
28
    if (status == CAIRO_STATUS_SUCCESS)
28
	return CAIRO_TEST_SUCCESS;
    if (cairo_test_malloc_failure (ctx, status))
	return CAIRO_TEST_NO_MEMORY;
    return CAIRO_TEST_FAILURE;
}
#if CAIRO_HAS_FT_FONT
#include "cairo-ft.h"
static void
_free_face (void *face)
{
    FT_Done_Face ((FT_Face) face);
}
static FT_Library ft_library = NULL;
#endif
static const cairo_user_data_key_t ft_font_key;
cairo_test_status_t
27
cairo_test_ft_select_font_from_file (cairo_t      *cr,
                                     const char   *filename)
{
27
    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
#if CAIRO_HAS_FT_FONT
    FT_Face face;
    cairo_font_face_t *font_face;
27
    char *srcdir_filename = NULL;
27
    if (access (filename, F_OK) != 0) {
	if (ctx->srcdir) {
	    xasprintf (&srcdir_filename, "%s/%s", ctx->srcdir, filename);
            filename = srcdir_filename;
	}
    }
27
    if (access (filename, F_OK) != 0) {
        cairo_test_log (ctx, "Could not find font file: %s\n", filename);
        return CAIRO_TEST_FAILURE;
    }
27
    if (!ft_library) {
27
        if (FT_Init_FreeType (&ft_library))
            return CAIRO_TEST_FAILURE;
    }
27
    if (FT_New_Face (ft_library, filename, 0, &face)) {
        cairo_test_log (ctx, "FT_New_Face failed loading font file: %s\n", filename);
        return CAIRO_TEST_FAILURE;
    }
27
    free (srcdir_filename);
27
    font_face = cairo_ft_font_face_create_for_ft_face (face, 0);
27
    if (cairo_font_face_status (font_face))
        return CAIRO_TEST_FAILURE;
27
    cairo_font_face_set_user_data (font_face, &ft_font_key, face, _free_face);
27
    cairo_set_font_face (cr, font_face);
27
    if (cairo_status (cr))
        return CAIRO_TEST_FAILURE;
27
    cairo_font_face_destroy (font_face);
27
    return CAIRO_TEST_SUCCESS;
#else
    cairo_test_log (ctx, "cairo_test_ft_select_font_from_file() requires the FreeType backend\n");
    return CAIRO_TEST_FAILURE;
#endif
}