2009-06-12 22:19:56 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2009 Chris Wilson
|
|
|
|
|
*
|
|
|
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
|
|
|
* and its documentation for any purpose is hereby granted without
|
|
|
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
|
|
|
* and that both that copyright notice and this permission notice
|
|
|
|
|
* appear in supporting documentation, and that the name of
|
|
|
|
|
* the authors not be used in advertising or publicity pertaining to
|
|
|
|
|
* distribution of the software without specific, written prior
|
|
|
|
|
* permission. The authors make no representations about the
|
|
|
|
|
* suitability of this software for any purpose. It is provided "as
|
|
|
|
|
* is" without express or implied warranty.
|
|
|
|
|
*
|
|
|
|
|
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
|
|
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
|
* FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
|
|
|
|
|
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
|
|
|
|
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
|
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
|
|
|
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
* Authors: Chris Wilson <chris@chris-wilson.co.uk>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
2009-06-13 10:13:20 +01:00
|
|
|
* The basic idea is that we feed the trace to multiple backends in parallel
|
|
|
|
|
* and compare the output at the end of each context (based on the premise
|
|
|
|
|
* that contexts demarcate expose events, or their logical equivalents) with
|
|
|
|
|
* that of the image[1] backend. Each backend is executed in a separate
|
|
|
|
|
* process, for robustness and to isolate the global cairo state, with the
|
|
|
|
|
* image data residing in shared memory and synchronising over a socket.
|
2009-06-12 22:19:56 +01:00
|
|
|
*
|
|
|
|
|
* [1] Should be reference implementation, currently the image backend is
|
|
|
|
|
* considered to be the reference for all other backends.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* XXX Can't directly compare fills using spans versus trapezoidation,
|
|
|
|
|
* i.e. xlib vs image. Gah, kinda renders this whole scheme moot.
|
|
|
|
|
* How about reference platforms?
|
|
|
|
|
* E.g. accelerated xlib driver vs Xvfb?
|
|
|
|
|
*
|
|
|
|
|
* boilerplate->create_reference_surface()?
|
|
|
|
|
* boilerplate->reference->create_surface()?
|
|
|
|
|
* So for each backend spawn two processes, a reference and xlib
|
|
|
|
|
* (obviously minimising the number of reference processes when possible)
|
|
|
|
|
*/
|
2009-06-13 10:13:20 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* XXX Handle show-page as well as cairo_destroy()? Though arguably that is
|
|
|
|
|
* only relevant for paginated backends which is currently outside the
|
|
|
|
|
* scope of this test.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
#include "cairo-test.h"
|
2009-06-19 18:40:43 +01:00
|
|
|
#include "buffer-diff.h"
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
#include "cairo-boilerplate-getopt.h"
|
|
|
|
|
#include <cairo-script-interpreter.h>
|
2011-08-30 16:16:04 +02:00
|
|
|
#include "cairo-missing.h"
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
#if CAIRO_HAS_SCRIPT_SURFACE
|
|
|
|
|
#include <cairo-script.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
/* For basename */
|
|
|
|
|
#ifdef HAVE_LIBGEN_H
|
|
|
|
|
#include <libgen.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include <ctype.h> /* isspace() */
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
#include <errno.h>
|
2009-06-13 18:49:16 +01:00
|
|
|
#include <assert.h>
|
2020-08-30 18:29:05 -06:00
|
|
|
#include <unistd.h>
|
2020-08-31 01:36:20 -06:00
|
|
|
|
2010-06-27 03:03:17 +03:00
|
|
|
#if CAIRO_HAS_REAL_PTHREAD
|
2009-07-03 11:24:42 +01:00
|
|
|
#include <pthread.h>
|
|
|
|
|
#endif
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2020-08-31 01:36:20 -06:00
|
|
|
#if defined(HAVE_POLL_H)
|
|
|
|
|
#include <poll.h>
|
|
|
|
|
#elif defined(HAVE_SYS_POLL_H)
|
|
|
|
|
#include <sys/poll.h>
|
|
|
|
|
#else
|
|
|
|
|
#error No poll.h equivalent found
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
#if HAVE_FCFINI
|
|
|
|
|
#include <fontconfig/fontconfig.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-02-26 15:23:01 +01:00
|
|
|
#ifndef MAP_NORESERVE
|
|
|
|
|
#define MAP_NORESERVE 0
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
#define DEBUG 0
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
#define ignore_image_differences 0 /* XXX make me a cmdline option! */
|
2011-09-21 11:18:24 +01:00
|
|
|
#define write_results 1
|
|
|
|
|
#define write_traces 1
|
2011-07-30 17:28:21 +01:00
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
#define DATA_SIZE (256 << 20)
|
2011-07-30 17:28:21 +01:00
|
|
|
#define SHM_PATH_XXX "/.shmem-cairo-trace"
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
typedef struct _test_trace {
|
2009-06-12 22:19:56 +01:00
|
|
|
/* Options from command-line */
|
|
|
|
|
cairo_bool_t list_only;
|
|
|
|
|
char **names;
|
|
|
|
|
unsigned int num_names;
|
|
|
|
|
char **exclude_names;
|
|
|
|
|
unsigned int num_exclude_names;
|
|
|
|
|
|
|
|
|
|
/* Stuff used internally */
|
|
|
|
|
const cairo_boilerplate_target_t **targets;
|
|
|
|
|
int num_targets;
|
2009-07-04 23:44:09 +01:00
|
|
|
} test_trace_t;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
typedef struct _test_runner {
|
|
|
|
|
const char *name;
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
void *closure;
|
|
|
|
|
uint8_t *base;
|
2009-07-03 11:24:42 +01:00
|
|
|
const char *trace;
|
2009-06-12 22:19:56 +01:00
|
|
|
pid_t pid;
|
|
|
|
|
int sk;
|
2009-10-22 02:13:36 +03:00
|
|
|
cairo_bool_t is_recording;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
cairo_script_interpreter_t *csi;
|
|
|
|
|
struct context_closure {
|
|
|
|
|
struct context_closure *next;
|
2009-06-12 22:19:56 +01:00
|
|
|
unsigned long id;
|
2009-06-19 18:40:43 +01:00
|
|
|
unsigned long start_line;
|
|
|
|
|
unsigned long end_line;
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_t *context;
|
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
} *contexts;
|
|
|
|
|
|
|
|
|
|
unsigned long context_id;
|
2009-07-04 23:44:09 +01:00
|
|
|
} test_runner_t;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
struct slave {
|
|
|
|
|
pid_t pid;
|
|
|
|
|
int fd;
|
|
|
|
|
unsigned long image_serial;
|
|
|
|
|
unsigned long image_ready;
|
2009-06-19 18:40:43 +01:00
|
|
|
unsigned long start_line;
|
|
|
|
|
unsigned long end_line;
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_surface_t *image;
|
2009-07-03 11:24:42 +01:00
|
|
|
long width, height;
|
2009-06-19 18:40:43 +01:00
|
|
|
cairo_surface_t *difference;
|
|
|
|
|
buffer_diff_result_t result;
|
2009-06-12 22:19:56 +01:00
|
|
|
const cairo_boilerplate_target_t *target;
|
2009-06-19 18:40:43 +01:00
|
|
|
const struct slave *reference;
|
2009-10-22 02:13:36 +03:00
|
|
|
cairo_bool_t is_recording;
|
2009-06-12 22:19:56 +01:00
|
|
|
};
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
struct request_image {
|
|
|
|
|
unsigned long id;
|
2009-06-19 18:40:43 +01:00
|
|
|
unsigned long start_line;
|
|
|
|
|
unsigned long end_line;
|
2009-06-13 10:13:20 +01:00
|
|
|
cairo_format_t format;
|
2009-07-03 11:24:42 +01:00
|
|
|
long width;
|
|
|
|
|
long height;
|
|
|
|
|
long stride;
|
2009-06-13 10:13:20 +01:00
|
|
|
};
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
struct surface_tag {
|
2009-07-03 11:24:42 +01:00
|
|
|
long width, height;
|
2009-06-12 22:19:56 +01:00
|
|
|
};
|
|
|
|
|
static const cairo_user_data_key_t surface_tag;
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
#define TARGET_NAME(T) ((T) ? (T)->name : "recording")
|
|
|
|
|
|
2010-06-27 03:03:17 +03:00
|
|
|
#if CAIRO_HAS_REAL_PTHREAD
|
2009-10-22 02:13:36 +03:00
|
|
|
#define tr_die(t) t->is_recording ? pthread_exit(NULL) : exit(1)
|
2009-07-03 11:24:42 +01:00
|
|
|
#else
|
2009-07-04 23:44:09 +01:00
|
|
|
#define tr_die(t) exit(1)
|
2009-07-03 11:24:42 +01:00
|
|
|
#endif
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
static cairo_bool_t
|
|
|
|
|
writen (int fd, const void *ptr, int len)
|
|
|
|
|
{
|
|
|
|
|
#if 0
|
|
|
|
|
const uint8_t *data = ptr;
|
|
|
|
|
while (len) {
|
|
|
|
|
int ret = write (fd, data, len);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
switch (errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
case EINTR:
|
|
|
|
|
continue;
|
|
|
|
|
default:
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
} else if (ret == 0) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
} else {
|
|
|
|
|
data += ret;
|
|
|
|
|
len -= ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
#else
|
|
|
|
|
int ret = send (fd, ptr, len, 0);
|
|
|
|
|
return ret == len;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
|
|
|
|
readn (int fd, void *ptr, int len)
|
|
|
|
|
{
|
|
|
|
|
#if 0
|
|
|
|
|
uint8_t *data = ptr;
|
|
|
|
|
while (len) {
|
|
|
|
|
int ret = read (fd, data, len);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
switch (errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
case EINTR:
|
|
|
|
|
continue;
|
|
|
|
|
default:
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
} else if (ret == 0) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
} else {
|
|
|
|
|
data += ret;
|
|
|
|
|
len -= ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
#else
|
|
|
|
|
int ret = recv (fd, ptr, len, MSG_WAITALL);
|
|
|
|
|
return ret == len;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_format_t
|
|
|
|
|
format_for_content (cairo_content_t content)
|
|
|
|
|
{
|
|
|
|
|
switch (content) {
|
|
|
|
|
case CAIRO_CONTENT_ALPHA:
|
|
|
|
|
return CAIRO_FORMAT_A8;
|
|
|
|
|
case CAIRO_CONTENT_COLOR:
|
|
|
|
|
return CAIRO_FORMAT_RGB24;
|
|
|
|
|
default:
|
|
|
|
|
case CAIRO_CONTENT_COLOR_ALPHA:
|
|
|
|
|
return CAIRO_FORMAT_ARGB32;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
static void
|
2009-10-22 02:13:36 +03:00
|
|
|
send_recording_surface (test_runner_t *tr,
|
|
|
|
|
int width, int height,
|
|
|
|
|
struct context_closure *closure)
|
2009-07-03 11:24:42 +01:00
|
|
|
{
|
2010-06-27 03:03:17 +03:00
|
|
|
#if CAIRO_HAS_REAL_PTHREAD
|
2009-07-03 11:24:42 +01:00
|
|
|
const struct request_image rq = {
|
|
|
|
|
closure->id,
|
|
|
|
|
closure->start_line,
|
|
|
|
|
closure->end_line,
|
|
|
|
|
-1,
|
|
|
|
|
width, height,
|
2011-07-30 17:28:21 +01:00
|
|
|
(long) closure->surface,
|
2009-07-03 11:24:42 +01:00
|
|
|
};
|
|
|
|
|
unsigned long offset;
|
|
|
|
|
unsigned long serial;
|
|
|
|
|
|
|
|
|
|
if (DEBUG > 1) {
|
2009-10-22 02:13:36 +03:00
|
|
|
printf ("send-recording-surface: %lu [%lu, %lu]\n",
|
2009-07-03 11:24:42 +01:00
|
|
|
closure->id,
|
|
|
|
|
closure->start_line,
|
|
|
|
|
closure->end_line);
|
|
|
|
|
}
|
2009-07-04 23:44:09 +01:00
|
|
|
writen (tr->sk, &rq, sizeof (rq));
|
|
|
|
|
readn (tr->sk, &offset, sizeof (offset));
|
2009-07-03 11:24:42 +01:00
|
|
|
|
|
|
|
|
/* signal completion */
|
2009-07-04 23:44:09 +01:00
|
|
|
writen (tr->sk, &closure->id, sizeof (closure->id));
|
2009-07-03 11:24:42 +01:00
|
|
|
|
|
|
|
|
/* wait for image check */
|
|
|
|
|
serial = 0;
|
2009-07-04 23:44:09 +01:00
|
|
|
readn (tr->sk, &serial, sizeof (serial));
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG > 1) {
|
2009-10-22 02:13:36 +03:00
|
|
|
printf ("send-recording-surface: serial: %lu\n", serial);
|
2009-07-03 11:24:42 +01:00
|
|
|
}
|
|
|
|
|
if (serial != closure->id)
|
|
|
|
|
pthread_exit (NULL);
|
|
|
|
|
#else
|
|
|
|
|
exit (1);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
static void *
|
2009-07-04 23:44:09 +01:00
|
|
|
request_image (test_runner_t *tr,
|
2009-06-19 18:40:43 +01:00
|
|
|
struct context_closure *closure,
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_format_t format,
|
|
|
|
|
int width, int height, int stride)
|
|
|
|
|
{
|
2009-06-19 18:40:43 +01:00
|
|
|
const struct request_image rq = {
|
|
|
|
|
closure->id,
|
|
|
|
|
closure->start_line,
|
|
|
|
|
closure->end_line,
|
|
|
|
|
format, width, height, stride
|
|
|
|
|
};
|
2009-07-03 11:24:42 +01:00
|
|
|
unsigned long offset = -1;
|
|
|
|
|
|
|
|
|
|
assert (format != (cairo_format_t) -1);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
writen (tr->sk, &rq, sizeof (rq));
|
|
|
|
|
readn (tr->sk, &offset, sizeof (offset));
|
2009-07-03 11:24:42 +01:00
|
|
|
if (offset == (unsigned long) -1)
|
2009-06-13 10:13:20 +01:00
|
|
|
return NULL;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
return tr->base + offset;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2009-07-04 23:44:09 +01:00
|
|
|
send_surface (test_runner_t *tr,
|
2009-06-19 18:40:43 +01:00
|
|
|
struct context_closure *closure)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
2009-06-19 18:40:43 +01:00
|
|
|
cairo_surface_t *source = closure->surface;
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_surface_t *image;
|
2009-06-13 10:13:20 +01:00
|
|
|
cairo_format_t format = (cairo_format_t) -1;
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_t *cr;
|
|
|
|
|
int width, height, stride;
|
|
|
|
|
void *data;
|
|
|
|
|
unsigned long serial;
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG > 1) {
|
2011-07-30 17:28:21 +01:00
|
|
|
printf ("send-surface: '%s', is-recording? %d\n",
|
|
|
|
|
tr->name, tr->is_recording);
|
2009-07-03 11:24:42 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
if (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE) {
|
|
|
|
|
width = cairo_image_surface_get_width (source);
|
|
|
|
|
height = cairo_image_surface_get_height (source);
|
2009-06-13 10:13:20 +01:00
|
|
|
format = cairo_image_surface_get_format (source);
|
2009-06-12 22:19:56 +01:00
|
|
|
} else {
|
|
|
|
|
struct surface_tag *tag;
|
|
|
|
|
|
|
|
|
|
tag = cairo_surface_get_user_data (source, &surface_tag);
|
2009-06-13 10:13:20 +01:00
|
|
|
if (tag != NULL) {
|
|
|
|
|
width = tag->width;
|
|
|
|
|
height = tag->height;
|
|
|
|
|
} else {
|
|
|
|
|
double x0, x1, y0, y1;
|
|
|
|
|
|
|
|
|
|
/* presumably created using cairo_surface_create_similar() */
|
|
|
|
|
cr = cairo_create (source);
|
|
|
|
|
cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
|
|
|
|
|
cairo_destroy (cr);
|
|
|
|
|
|
|
|
|
|
tag = xmalloc (sizeof (*tag));
|
|
|
|
|
width = tag->width = x1 - x0;
|
|
|
|
|
height = tag->height = y1 - y0;
|
|
|
|
|
|
|
|
|
|
if (cairo_surface_set_user_data (source, &surface_tag, tag, free))
|
2009-07-04 23:44:09 +01:00
|
|
|
tr_die (tr);
|
2009-06-13 10:13:20 +01:00
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-10-22 02:13:36 +03:00
|
|
|
if (tr->is_recording) {
|
|
|
|
|
send_recording_surface (tr, width, height, closure);
|
2009-07-03 11:24:42 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
if (format == (cairo_format_t) -1)
|
|
|
|
|
format = format_for_content (cairo_surface_get_content (source));
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
stride = cairo_format_stride_for_width (format, width);
|
|
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
data = request_image (tr, closure, format, width, height, stride);
|
2009-06-13 10:13:20 +01:00
|
|
|
if (data == NULL)
|
2009-07-04 23:44:09 +01:00
|
|
|
tr_die (tr);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
image = cairo_image_surface_create_for_data (data,
|
|
|
|
|
format,
|
|
|
|
|
width, height,
|
|
|
|
|
stride);
|
|
|
|
|
cr = cairo_create (image);
|
|
|
|
|
cairo_surface_destroy (image);
|
|
|
|
|
|
2009-06-13 21:33:09 +01:00
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_set_source_surface (cr, source, 0, 0);
|
|
|
|
|
cairo_paint (cr);
|
|
|
|
|
cairo_destroy (cr);
|
|
|
|
|
|
|
|
|
|
/* signal completion */
|
2009-07-04 23:44:09 +01:00
|
|
|
writen (tr->sk, &closure->id, sizeof (closure->id));
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
/* wait for image check */
|
|
|
|
|
serial = 0;
|
2009-07-04 23:44:09 +01:00
|
|
|
readn (tr->sk, &serial, sizeof (serial));
|
2009-06-19 18:40:43 +01:00
|
|
|
if (serial != closure->id)
|
2009-07-04 23:44:09 +01:00
|
|
|
tr_die (tr);
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_surface_t *
|
|
|
|
|
_surface_create (void *closure,
|
|
|
|
|
cairo_content_t content,
|
2011-07-30 17:28:21 +01:00
|
|
|
double width, double height,
|
|
|
|
|
long uid)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_runner_t *tr = closure;
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
surface = cairo_surface_create_similar (tr->surface,
|
2009-06-12 22:19:56 +01:00
|
|
|
content, width, height);
|
|
|
|
|
if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE) {
|
|
|
|
|
struct surface_tag *tag;
|
|
|
|
|
|
|
|
|
|
tag = xmalloc (sizeof (*tag));
|
|
|
|
|
tag->width = width;
|
|
|
|
|
tag->height = height;
|
2009-06-13 10:13:20 +01:00
|
|
|
if (cairo_surface_set_user_data (surface, &surface_tag, tag, free))
|
2009-07-04 23:44:09 +01:00
|
|
|
tr_die (tr);
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return surface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_t *
|
|
|
|
|
_context_create (void *closure, cairo_surface_t *surface)
|
|
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_runner_t *tr = closure;
|
2009-06-19 18:40:43 +01:00
|
|
|
struct context_closure *l;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
if (DEBUG) {
|
|
|
|
|
fprintf (stderr, "%s: starting context %lu on line %d\n",
|
|
|
|
|
tr->name ? tr->name : "recording" ,
|
|
|
|
|
tr->context_id + 1,
|
|
|
|
|
cairo_script_interpreter_get_line_number (tr->csi));
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
l = xmalloc (sizeof (*l));
|
2009-07-04 23:44:09 +01:00
|
|
|
l->next = tr->contexts;
|
|
|
|
|
l->start_line = cairo_script_interpreter_get_line_number (tr->csi);
|
2009-06-19 18:40:43 +01:00
|
|
|
l->end_line = l->start_line;
|
2009-06-12 22:19:56 +01:00
|
|
|
l->context = cairo_create (surface);
|
|
|
|
|
l->surface = cairo_surface_reference (surface);
|
2009-07-04 23:44:09 +01:00
|
|
|
l->id = ++tr->context_id;
|
2009-06-12 22:19:56 +01:00
|
|
|
if (l->id == 0)
|
2009-07-04 23:44:09 +01:00
|
|
|
l->id = ++tr->context_id;
|
|
|
|
|
tr->contexts = l;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
return l->context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_context_destroy (void *closure, void *ptr)
|
|
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_runner_t *tr = closure;
|
|
|
|
|
struct context_closure *l, **prev = &tr->contexts;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
while ((l = *prev) != NULL) {
|
|
|
|
|
if (l->context == ptr) {
|
2011-07-30 17:28:21 +01:00
|
|
|
if (DEBUG) {
|
|
|
|
|
fprintf (stderr, "%s: context %lu complete on line %d\n",
|
|
|
|
|
tr->name ? tr->name : "recording" ,
|
|
|
|
|
tr->context_id,
|
|
|
|
|
cairo_script_interpreter_get_line_number (tr->csi));
|
|
|
|
|
}
|
2009-06-19 18:40:43 +01:00
|
|
|
l->end_line =
|
2009-07-04 23:44:09 +01:00
|
|
|
cairo_script_interpreter_get_line_number (tr->csi);
|
2009-06-12 22:19:56 +01:00
|
|
|
if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) {
|
2009-07-04 23:44:09 +01:00
|
|
|
send_surface (tr, l);
|
2009-06-12 22:19:56 +01:00
|
|
|
} else {
|
2009-06-19 18:40:43 +01:00
|
|
|
fprintf (stderr, "%s: error during replay, line %lu: %s!\n",
|
2009-07-04 23:44:09 +01:00
|
|
|
tr->name,
|
2009-06-19 18:40:43 +01:00
|
|
|
l->end_line,
|
|
|
|
|
cairo_status_to_string (cairo_surface_status (l->surface)));
|
2009-07-04 23:44:09 +01:00
|
|
|
tr_die (tr);
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_surface_destroy (l->surface);
|
|
|
|
|
*prev = l->next;
|
|
|
|
|
free (l);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
prev = &l->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2009-07-04 23:44:09 +01:00
|
|
|
execute (test_runner_t *tr)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
const cairo_script_interpreter_hooks_t hooks = {
|
2009-07-04 23:44:09 +01:00
|
|
|
.closure = tr,
|
2009-06-12 22:19:56 +01:00
|
|
|
.surface_create = _surface_create,
|
|
|
|
|
.context_create = _context_create,
|
|
|
|
|
.context_destroy = _context_destroy,
|
|
|
|
|
};
|
2009-07-03 11:24:42 +01:00
|
|
|
pid_t ack;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
tr->csi = cairo_script_interpreter_create ();
|
|
|
|
|
cairo_script_interpreter_install_hooks (tr->csi, &hooks);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
ack = -1;
|
2009-07-04 23:44:09 +01:00
|
|
|
readn (tr->sk, &ack, sizeof (ack));
|
|
|
|
|
if (ack != tr->pid)
|
|
|
|
|
tr_die (tr);
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
cairo_script_interpreter_run (tr->csi, tr->trace);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
cairo_script_interpreter_finish (tr->csi);
|
|
|
|
|
if (cairo_script_interpreter_destroy (tr->csi))
|
|
|
|
|
tr_die (tr);
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spawn_socket (const char *socket_path, pid_t pid)
|
|
|
|
|
{
|
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
|
int sk;
|
|
|
|
|
|
|
|
|
|
sk = socket (PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
|
if (sk == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
memset (&addr, 0, sizeof (addr));
|
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
|
strcpy (addr.sun_path, socket_path);
|
|
|
|
|
|
|
|
|
|
if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (! writen (sk, &pid, sizeof (pid)))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
spawn_shm (const char *shm_path)
|
|
|
|
|
{
|
2009-06-13 10:13:20 +01:00
|
|
|
void *base;
|
2009-06-12 22:19:56 +01:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
fd = shm_open (shm_path, O_RDWR, 0);
|
|
|
|
|
if (fd == -1)
|
2009-06-13 10:13:20 +01:00
|
|
|
return MAP_FAILED;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
base = mmap (NULL, DATA_SIZE,
|
|
|
|
|
PROT_READ | PROT_WRITE,
|
2012-03-30 09:49:05 +02:00
|
|
|
#ifdef MAP_NORESERVE
|
2009-06-12 22:19:56 +01:00
|
|
|
MAP_SHARED | MAP_NORESERVE,
|
2012-03-30 09:49:05 +02:00
|
|
|
#else
|
|
|
|
|
MAP_SHARED,
|
|
|
|
|
#endif
|
2009-06-12 22:19:56 +01:00
|
|
|
fd, 0);
|
|
|
|
|
close (fd);
|
|
|
|
|
|
|
|
|
|
return base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spawn_target (const char *socket_path,
|
|
|
|
|
const char *shm_path,
|
|
|
|
|
const cairo_boilerplate_target_t *target,
|
|
|
|
|
const char *trace)
|
|
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_runner_t tr;
|
2009-06-12 22:19:56 +01:00
|
|
|
pid_t pid;
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG)
|
|
|
|
|
printf ("Spawning slave '%s' for %s\n", target->name, trace);
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
pid = fork ();
|
|
|
|
|
if (pid != 0)
|
|
|
|
|
return pid;
|
|
|
|
|
|
2009-10-22 02:13:36 +03:00
|
|
|
tr.is_recording = FALSE;
|
2009-07-04 23:44:09 +01:00
|
|
|
tr.pid = getpid ();
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
tr.sk = spawn_socket (socket_path, tr.pid);
|
|
|
|
|
if (tr.sk == -1) {
|
2009-06-12 22:19:56 +01:00
|
|
|
fprintf (stderr, "%s: Failed to open socket.\n",
|
|
|
|
|
target->name);
|
|
|
|
|
exit (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
tr.base = spawn_shm (shm_path);
|
|
|
|
|
if (tr.base == MAP_FAILED) {
|
2009-06-12 22:19:56 +01:00
|
|
|
fprintf (stderr, "%s: Failed to map shared memory segment.\n",
|
|
|
|
|
target->name);
|
|
|
|
|
exit (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
tr.name = target->name;
|
|
|
|
|
tr.contexts = NULL;
|
|
|
|
|
tr.context_id = 0;
|
|
|
|
|
tr.trace = trace;
|
|
|
|
|
|
|
|
|
|
tr.surface = target->create_surface (NULL,
|
|
|
|
|
target->content,
|
|
|
|
|
1, 1,
|
|
|
|
|
1, 1,
|
|
|
|
|
CAIRO_BOILERPLATE_MODE_TEST,
|
|
|
|
|
&tr.closure);
|
|
|
|
|
if (tr.surface == NULL) {
|
2009-06-12 22:19:56 +01:00
|
|
|
fprintf (stderr,
|
|
|
|
|
"%s: Failed to create target surface.\n",
|
|
|
|
|
target->name);
|
|
|
|
|
exit (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
execute (&tr);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
cairo_surface_destroy (tr.surface);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
if (target->cleanup)
|
2009-07-04 23:44:09 +01:00
|
|
|
target->cleanup (tr.closure);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
close (tr.sk);
|
|
|
|
|
munmap (tr.base, DATA_SIZE);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
exit (0);
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-27 03:03:17 +03:00
|
|
|
#if CAIRO_HAS_REAL_PTHREAD
|
2009-07-03 11:24:42 +01:00
|
|
|
static void
|
|
|
|
|
cleanup_recorder (void *arg)
|
|
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_runner_t *tr = arg;
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
cairo_surface_finish (tr->surface);
|
|
|
|
|
cairo_surface_destroy (tr->surface);
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
close (tr->sk);
|
|
|
|
|
free (tr);
|
2009-07-03 11:24:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
record (void *arg)
|
|
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_runner_t *tr = arg;
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
pthread_cleanup_push (cleanup_recorder, tr);
|
|
|
|
|
execute (tr);
|
2009-07-03 11:24:42 +01:00
|
|
|
pthread_cleanup_pop (TRUE);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The recorder is special:
|
|
|
|
|
* 1. It doesn't generate an image, but keeps an in-memory trace to
|
|
|
|
|
* reconstruct any surface.
|
|
|
|
|
* 2. Runs in the same process, but separate thread.
|
|
|
|
|
*/
|
|
|
|
|
static pid_t
|
2011-07-30 17:28:21 +01:00
|
|
|
spawn_recorder (const char *socket_path, const char *trace, test_runner_t **out)
|
2009-07-03 11:24:42 +01:00
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_runner_t *tr;
|
2009-07-03 11:24:42 +01:00
|
|
|
pthread_t id;
|
|
|
|
|
pthread_attr_t attr;
|
|
|
|
|
pid_t pid = getpid ();
|
|
|
|
|
|
|
|
|
|
if (DEBUG)
|
|
|
|
|
printf ("Spawning recorder for %s\n", trace);
|
|
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
tr = malloc (sizeof (*tr));
|
|
|
|
|
if (tr == NULL)
|
2009-07-03 11:24:42 +01:00
|
|
|
return -1;
|
|
|
|
|
|
2009-10-22 02:13:36 +03:00
|
|
|
tr->is_recording = TRUE;
|
2009-07-04 23:44:09 +01:00
|
|
|
tr->pid = pid;
|
|
|
|
|
tr->sk = spawn_socket (socket_path, tr->pid);
|
|
|
|
|
if (tr->sk == -1) {
|
|
|
|
|
free (tr);
|
2009-07-03 11:24:42 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-04 23:44:09 +01:00
|
|
|
tr->base = NULL;
|
|
|
|
|
tr->name = NULL;
|
|
|
|
|
tr->contexts = NULL;
|
|
|
|
|
tr->context_id = 0;
|
|
|
|
|
tr->trace = trace;
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-10-22 02:13:36 +03:00
|
|
|
tr->surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
|
2011-07-30 17:28:21 +01:00
|
|
|
NULL);
|
2009-07-04 23:44:09 +01:00
|
|
|
if (tr->surface == NULL) {
|
|
|
|
|
cleanup_recorder (tr);
|
2009-07-03 11:24:42 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_attr_init (&attr);
|
|
|
|
|
pthread_attr_setdetachstate (&attr, TRUE);
|
2009-07-04 23:44:09 +01:00
|
|
|
if (pthread_create (&id, &attr, record, tr) < 0) {
|
2009-07-03 11:24:42 +01:00
|
|
|
pthread_attr_destroy (&attr);
|
2009-07-04 23:44:09 +01:00
|
|
|
cleanup_recorder (tr);
|
2009-07-03 11:24:42 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
pthread_attr_destroy (&attr);
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
|
|
|
*out = tr;
|
2009-07-03 11:24:42 +01:00
|
|
|
return pid;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
/* XXX imagediff - is the extra expense worth it? */
|
2009-06-12 22:19:56 +01:00
|
|
|
static cairo_bool_t
|
2009-06-19 18:40:43 +01:00
|
|
|
matches_reference (struct slave *slave)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
2009-06-19 18:40:43 +01:00
|
|
|
cairo_surface_t *a, *b;
|
|
|
|
|
|
|
|
|
|
a = slave->image;
|
|
|
|
|
b = slave->reference->image;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2011-09-21 11:18:24 +01:00
|
|
|
if (a == b)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (a == NULL || b == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
if (cairo_surface_status (a) || cairo_surface_status (b))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (cairo_surface_get_type (a) != cairo_surface_get_type (b))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (cairo_image_surface_get_format (a) != cairo_image_surface_get_format (b))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (cairo_image_surface_get_stride (a) != cairo_image_surface_get_stride (b))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
if (FALSE && cairo_surface_get_content (a) & CAIRO_CONTENT_COLOR) {
|
|
|
|
|
cairo_surface_t *diff;
|
|
|
|
|
int width, height, stride, size;
|
|
|
|
|
unsigned char *data;
|
|
|
|
|
cairo_status_t status;
|
|
|
|
|
|
|
|
|
|
width = cairo_image_surface_get_width (a);
|
|
|
|
|
height = cairo_image_surface_get_height (a);
|
|
|
|
|
stride = cairo_image_surface_get_stride (a);
|
|
|
|
|
size = height * stride * 4;
|
|
|
|
|
data = malloc (size);
|
|
|
|
|
if (data == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
diff = cairo_image_surface_create_for_data (data,
|
|
|
|
|
cairo_image_surface_get_format (a),
|
|
|
|
|
width, height, stride);
|
|
|
|
|
cairo_surface_set_user_data (diff, (cairo_user_data_key_t *) diff,
|
|
|
|
|
data, free);
|
|
|
|
|
|
|
|
|
|
status = image_diff (NULL, a, b, diff, &slave->result);
|
|
|
|
|
if (status) {
|
|
|
|
|
cairo_surface_destroy (diff);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-28 12:54:54 +02:00
|
|
|
if (image_diff_is_failure (&slave->result, slave->target->error_tolerance)) {
|
2009-06-19 18:40:43 +01:00
|
|
|
slave->difference = diff;
|
|
|
|
|
return FALSE;
|
|
|
|
|
} else {
|
|
|
|
|
cairo_surface_destroy (diff);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2009-06-19 21:21:02 +01:00
|
|
|
int width, height, stride;
|
|
|
|
|
const uint8_t *aa, *bb;
|
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
|
|
width = cairo_image_surface_get_width (a);
|
|
|
|
|
height = cairo_image_surface_get_height (a);
|
|
|
|
|
stride = cairo_image_surface_get_stride (a);
|
|
|
|
|
|
|
|
|
|
aa = cairo_image_surface_get_data (a);
|
|
|
|
|
bb = cairo_image_surface_get_data (b);
|
|
|
|
|
switch (cairo_image_surface_get_format (a)) {
|
|
|
|
|
case CAIRO_FORMAT_ARGB32:
|
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
|
const uint32_t *ua = (uint32_t *) aa;
|
|
|
|
|
const uint32_t *ub = (uint32_t *) bb;
|
|
|
|
|
for (x = 0; x < width; x++) {
|
|
|
|
|
if (ua[x] != ub[x]) {
|
|
|
|
|
int channel;
|
|
|
|
|
|
|
|
|
|
for (channel = 0; channel < 4; channel++) {
|
2021-08-17 06:25:29 +09:30
|
|
|
int va, vb;
|
|
|
|
|
unsigned diff;
|
2009-06-19 21:21:02 +01:00
|
|
|
|
|
|
|
|
va = (ua[x] >> (channel*8)) & 0xff;
|
|
|
|
|
vb = (ub[x] >> (channel*8)) & 0xff;
|
|
|
|
|
diff = abs (va - vb);
|
|
|
|
|
if (diff > slave->target->error_tolerance)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
aa += stride;
|
|
|
|
|
bb += stride;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CAIRO_FORMAT_RGB24:
|
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
|
const uint32_t *ua = (uint32_t *) aa;
|
|
|
|
|
const uint32_t *ub = (uint32_t *) bb;
|
|
|
|
|
for (x = 0; x < width; x++) {
|
|
|
|
|
if ((ua[x] & 0x00ffffff) != (ub[x] & 0x00ffffff)) {
|
|
|
|
|
int channel;
|
|
|
|
|
|
|
|
|
|
for (channel = 0; channel < 3; channel++) {
|
2021-08-17 06:25:29 +09:30
|
|
|
int va, vb;
|
|
|
|
|
unsigned diff;
|
2009-06-19 21:21:02 +01:00
|
|
|
|
|
|
|
|
va = (ua[x] >> (channel*8)) & 0xff;
|
|
|
|
|
vb = (ub[x] >> (channel*8)) & 0xff;
|
|
|
|
|
diff = abs (va - vb);
|
|
|
|
|
if (diff > slave->target->error_tolerance)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
aa += stride;
|
|
|
|
|
bb += stride;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CAIRO_FORMAT_A8:
|
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
|
for (x = 0; x < width; x++) {
|
|
|
|
|
if (aa[x] != bb[x]) {
|
|
|
|
|
unsigned diff = abs (aa[x] - bb[x]);
|
|
|
|
|
if (diff > slave->target->error_tolerance)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
aa += stride;
|
|
|
|
|
bb += stride;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CAIRO_FORMAT_A1:
|
|
|
|
|
width /= 8;
|
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
|
if (memcmp (aa, bb, width))
|
|
|
|
|
return FALSE;
|
|
|
|
|
aa += stride;
|
|
|
|
|
bb += stride;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
|
|
|
case CAIRO_FORMAT_RGB30:
|
|
|
|
|
case CAIRO_FORMAT_RGB16_565:
|
2021-04-18 11:17:38 +01:00
|
|
|
case CAIRO_FORMAT_RGB96F:
|
|
|
|
|
case CAIRO_FORMAT_RGBA128F:
|
2011-07-30 17:28:21 +01:00
|
|
|
case CAIRO_FORMAT_INVALID:
|
|
|
|
|
assert (0);
|
2009-06-19 21:21:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
2009-06-19 18:40:43 +01:00
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
2009-06-13 10:13:20 +01:00
|
|
|
check_images (struct slave *slaves, int num_slaves)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
if (ignore_image_differences)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
2009-06-19 21:21:02 +01:00
|
|
|
for (n = 0; n < num_slaves; n++) {
|
2009-06-19 18:40:43 +01:00
|
|
|
if (slaves[n].reference == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (! matches_reference (&slaves[n]))
|
2009-06-12 22:19:56 +01:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
write_images (const char *trace, struct slave *slave, int num_slaves)
|
|
|
|
|
{
|
|
|
|
|
while (num_slaves--) {
|
2009-10-22 02:13:36 +03:00
|
|
|
if (slave->image != NULL && ! slave->is_recording) {
|
2009-06-12 22:19:56 +01:00
|
|
|
char *filename;
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
xasprintf (&filename, "%s-%s-fail.png",
|
2009-06-12 22:19:56 +01:00
|
|
|
trace, slave->target->name);
|
|
|
|
|
cairo_surface_write_to_png (slave->image, filename);
|
|
|
|
|
free (filename);
|
2009-06-19 18:40:43 +01:00
|
|
|
|
|
|
|
|
if (slave->difference) {
|
|
|
|
|
xasprintf (&filename, "%s-%s-diff.png",
|
|
|
|
|
trace, slave->target->name);
|
|
|
|
|
cairo_surface_write_to_png (slave->difference, filename);
|
|
|
|
|
free (filename);
|
|
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slave++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
static void
|
|
|
|
|
write_result (const char *trace, struct slave *slave)
|
|
|
|
|
{
|
|
|
|
|
static int index;
|
|
|
|
|
char *filename;
|
|
|
|
|
|
2020-08-30 18:29:05 -06:00
|
|
|
xasprintf (&filename, "%s-%s-pass-%d-%ld-%ld.png",
|
2011-07-30 17:28:21 +01:00
|
|
|
trace, slave->target->name, ++index,
|
|
|
|
|
slave->start_line, slave->end_line);
|
|
|
|
|
cairo_surface_write_to_png (slave->image, filename);
|
|
|
|
|
free (filename);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
static void
|
2011-09-16 13:45:11 +01:00
|
|
|
write_trace (const char *trace, const char *id, struct slave *slave)
|
2009-07-03 11:24:42 +01:00
|
|
|
{
|
|
|
|
|
#if CAIRO_HAS_SCRIPT_SURFACE
|
2011-07-30 17:28:21 +01:00
|
|
|
cairo_device_t *script;
|
2009-07-03 11:24:42 +01:00
|
|
|
char *filename;
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
|
|
|
assert (slave->is_recording);
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2011-09-16 13:45:11 +01:00
|
|
|
xasprintf (&filename, "%s-%s.trace", trace, id);
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
|
|
|
script = cairo_script_create (filename);
|
|
|
|
|
cairo_script_from_recording_surface (script, slave->image);
|
|
|
|
|
cairo_device_destroy (script);
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
free (filename);
|
2011-07-30 17:28:21 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
static void
|
|
|
|
|
dump_traces (test_runner_t *tr,
|
|
|
|
|
const char *trace,
|
|
|
|
|
const char *target,
|
|
|
|
|
const char *fail)
|
|
|
|
|
{
|
|
|
|
|
#if CAIRO_HAS_SCRIPT_SURFACE
|
|
|
|
|
struct context_closure *c;
|
|
|
|
|
|
|
|
|
|
for (c = tr->contexts; c; c = c->next) {
|
|
|
|
|
cairo_device_t *script;
|
|
|
|
|
char *filename;
|
|
|
|
|
|
|
|
|
|
xasprintf (&filename, "%s-%s-%s.%lu.trace",
|
|
|
|
|
trace, target, fail, c->start_line);
|
|
|
|
|
|
|
|
|
|
script = cairo_script_create (filename);
|
|
|
|
|
cairo_script_from_recording_surface (script, c->surface);
|
|
|
|
|
cairo_device_destroy (script);
|
2009-09-30 08:54:19 +02:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
free (filename);
|
|
|
|
|
}
|
2009-07-03 11:24:42 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned long
|
|
|
|
|
allocate_image_for_slave (uint8_t *base,
|
|
|
|
|
unsigned long offset,
|
|
|
|
|
struct slave *slave)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
struct request_image rq;
|
|
|
|
|
int size;
|
|
|
|
|
uint8_t *data;
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
assert (slave->image == NULL);
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
readn (slave->fd, &rq, sizeof (rq));
|
|
|
|
|
slave->image_serial = rq.id;
|
2009-06-19 18:40:43 +01:00
|
|
|
slave->start_line = rq.start_line;
|
|
|
|
|
slave->end_line = rq.end_line;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
slave->width = rq.width;
|
|
|
|
|
slave->height = rq.height;
|
|
|
|
|
|
|
|
|
|
if (DEBUG > 1) {
|
2011-07-30 17:28:21 +01:00
|
|
|
printf ("allocate-image-for-slave: %s %lu [%lu, %lu] %ldx%ld stride=%lu => %lu, is-recording? %d\n",
|
|
|
|
|
TARGET_NAME (slave->target),
|
2009-07-03 11:24:42 +01:00
|
|
|
slave->image_serial,
|
|
|
|
|
slave->start_line,
|
|
|
|
|
slave->end_line,
|
|
|
|
|
slave->width,
|
|
|
|
|
slave->height,
|
2011-07-30 17:28:21 +01:00
|
|
|
rq.stride,
|
|
|
|
|
offset,
|
|
|
|
|
slave->is_recording);
|
2009-07-03 11:24:42 +01:00
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-10-22 02:13:36 +03:00
|
|
|
if (slave->is_recording) {
|
|
|
|
|
/* special communication with recording-surface thread */
|
2009-07-03 11:24:42 +01:00
|
|
|
slave->image = cairo_surface_reference ((cairo_surface_t *) rq.stride);
|
|
|
|
|
} else {
|
|
|
|
|
size = rq.height * rq.stride;
|
|
|
|
|
size = (size + 4095) & -4096;
|
|
|
|
|
data = base + offset;
|
|
|
|
|
offset += size;
|
|
|
|
|
assert (offset <= DATA_SIZE);
|
|
|
|
|
|
|
|
|
|
slave->image = cairo_image_surface_create_for_data (data, rq.format,
|
|
|
|
|
rq.width, rq.height,
|
|
|
|
|
rq.stride);
|
|
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-06-13 21:40:05 +01:00
|
|
|
return offset;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
struct error_info {
|
|
|
|
|
unsigned long context_id;
|
|
|
|
|
unsigned long start_line;
|
|
|
|
|
unsigned long end_line;
|
|
|
|
|
};
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
static cairo_bool_t
|
2009-06-12 22:19:56 +01:00
|
|
|
test_run (void *base,
|
|
|
|
|
int sk,
|
|
|
|
|
const char *trace,
|
|
|
|
|
struct slave *slaves,
|
2009-06-19 18:40:43 +01:00
|
|
|
int num_slaves,
|
|
|
|
|
struct error_info *error)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
struct pollfd *pfd;
|
|
|
|
|
int npfd, cnt, n, i;
|
2011-09-16 13:45:11 +01:00
|
|
|
int completion, err = 0;
|
2009-06-13 10:13:20 +01:00
|
|
|
cairo_bool_t ret = FALSE;
|
2009-07-03 11:24:42 +01:00
|
|
|
unsigned long image;
|
|
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
printf ("Running trace '%s' over %d slaves\n",
|
|
|
|
|
trace, num_slaves);
|
|
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
pfd = xcalloc (num_slaves+1, sizeof (*pfd));
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
pfd[0].fd = sk;
|
|
|
|
|
pfd[0].events = POLLIN;
|
|
|
|
|
npfd = 1;
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
completion = 0;
|
2009-06-13 18:49:16 +01:00
|
|
|
image = 0;
|
2009-06-12 22:19:56 +01:00
|
|
|
while ((cnt = poll (pfd, npfd, -1)) > 0) {
|
|
|
|
|
if (pfd[0].revents) {
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
while ((fd = accept (sk, NULL, NULL)) != -1) {
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
|
|
readn (fd, &pid, sizeof (pid));
|
|
|
|
|
for (n = 0; n < num_slaves; n++) {
|
2009-07-03 11:24:42 +01:00
|
|
|
if (slaves[n].pid == pid) {
|
2009-06-12 22:19:56 +01:00
|
|
|
slaves[n].fd = fd;
|
2009-07-03 11:24:42 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (n == num_slaves) {
|
|
|
|
|
if (DEBUG)
|
|
|
|
|
printf ("unknown slave pid\n");
|
|
|
|
|
goto out;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pfd[npfd].fd = fd;
|
|
|
|
|
pfd[npfd].events = POLLIN;
|
|
|
|
|
npfd++;
|
2009-07-03 11:24:42 +01:00
|
|
|
|
|
|
|
|
if (! writen (fd, &pid, sizeof (pid)))
|
|
|
|
|
goto out;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
cnt--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (n = 1; n < npfd && cnt; n++) {
|
|
|
|
|
if (! pfd[n].revents)
|
|
|
|
|
continue;
|
|
|
|
|
|
2011-09-16 13:45:11 +01:00
|
|
|
if (pfd[n].revents & POLLHUP) {
|
2011-09-21 11:18:24 +01:00
|
|
|
pfd[n].events = pfd[n].revents = 0;
|
2011-09-16 13:45:11 +01:00
|
|
|
completion++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-06-13 10:13:20 +01:00
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
for (i = 0; i < num_slaves; i++) {
|
|
|
|
|
if (slaves[i].fd == pfd[n].fd) {
|
|
|
|
|
/* Communication with the slave is done in three phases,
|
|
|
|
|
* and we do each pass synchronously.
|
|
|
|
|
*
|
|
|
|
|
* 1. The slave requests an image buffer, which we
|
2009-06-13 10:13:20 +01:00
|
|
|
* allocate and then return to the slave the offset into
|
|
|
|
|
* the shared memory segment.
|
2009-06-12 22:19:56 +01:00
|
|
|
*
|
|
|
|
|
* 2. The slave indicates that it has finished writing
|
2009-06-13 10:13:20 +01:00
|
|
|
* into the shared image buffer. The slave now waits
|
|
|
|
|
* for the server to collate all the image data - thereby
|
|
|
|
|
* throttling the slaves.
|
2009-06-12 22:19:56 +01:00
|
|
|
*
|
|
|
|
|
* 3. After all slaves have finished writing their images,
|
|
|
|
|
* we compare them all against the reference image and,
|
|
|
|
|
* if satisfied, send an acknowledgement to all slaves.
|
|
|
|
|
*/
|
|
|
|
|
if (slaves[i].image_serial == 0) {
|
2009-07-03 11:24:42 +01:00
|
|
|
unsigned long offset;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-06-13 21:40:05 +01:00
|
|
|
image =
|
|
|
|
|
allocate_image_for_slave (base,
|
|
|
|
|
offset = image,
|
|
|
|
|
&slaves[i]);
|
2011-09-16 13:45:11 +01:00
|
|
|
if (! writen (pfd[n].fd, &offset, sizeof (offset))) {
|
2011-09-21 11:18:24 +01:00
|
|
|
pfd[n].events = pfd[n].revents = 0;
|
2011-09-16 13:45:11 +01:00
|
|
|
err = 1;
|
|
|
|
|
completion++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
} else {
|
|
|
|
|
readn (pfd[n].fd,
|
|
|
|
|
&slaves[i].image_ready,
|
|
|
|
|
sizeof (slaves[i].image_ready));
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG) {
|
|
|
|
|
printf ("slave '%s' reports completion on %lu (expecting %lu)\n",
|
2011-07-30 17:28:21 +01:00
|
|
|
TARGET_NAME (slaves[i].target),
|
2009-07-03 11:24:42 +01:00
|
|
|
slaves[i].image_ready,
|
|
|
|
|
slaves[i].image_serial);
|
|
|
|
|
}
|
2011-09-16 13:45:11 +01:00
|
|
|
if (slaves[i].image_ready != slaves[i].image_serial) {
|
2011-09-21 11:18:24 +01:00
|
|
|
pfd[n].events = pfd[n].revents = 0;
|
2011-09-16 13:45:11 +01:00
|
|
|
err = 1;
|
|
|
|
|
completion++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
/* Can anyone spell 'P·E·D·A·N·T'? */
|
2009-10-22 02:13:36 +03:00
|
|
|
if (! slaves[i].is_recording)
|
2009-07-03 11:24:42 +01:00
|
|
|
cairo_surface_mark_dirty (slaves[i].image);
|
2009-06-13 10:13:20 +01:00
|
|
|
completion++;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cnt--;
|
|
|
|
|
}
|
2009-06-13 10:13:20 +01:00
|
|
|
|
2011-09-21 11:18:24 +01:00
|
|
|
if (completion >= num_slaves) {
|
2011-09-16 13:45:11 +01:00
|
|
|
if (err) {
|
|
|
|
|
if (DEBUG > 1)
|
|
|
|
|
printf ("error detected\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG > 1) {
|
|
|
|
|
printf ("all saves report completion\n");
|
|
|
|
|
}
|
2011-07-30 17:28:21 +01:00
|
|
|
if (slaves[0].end_line >= slaves[0].start_line &&
|
|
|
|
|
! check_images (slaves, num_slaves)) {
|
2009-06-19 18:40:43 +01:00
|
|
|
error->context_id = slaves[0].image_serial;
|
|
|
|
|
error->start_line = slaves[0].start_line;
|
|
|
|
|
error->end_line = slaves[0].end_line;
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG) {
|
|
|
|
|
printf ("check_images failed: %lu, [%lu, %lu]\n",
|
|
|
|
|
slaves[0].image_serial,
|
|
|
|
|
slaves[0].start_line,
|
|
|
|
|
slaves[0].end_line);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
write_images (trace, slaves, num_slaves);
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-10-22 02:13:36 +03:00
|
|
|
if (slaves[0].is_recording)
|
2011-09-16 13:45:11 +01:00
|
|
|
write_trace (trace, "fail", &slaves[0]);
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-21 11:18:24 +01:00
|
|
|
if (write_results) write_result (trace, &slaves[1]);
|
|
|
|
|
if (write_traces && slaves[0].is_recording) {
|
2011-09-16 13:45:11 +01:00
|
|
|
char buf[80];
|
2020-08-30 18:29:05 -06:00
|
|
|
snprintf (buf, sizeof (buf), "%ld", slaves[0].image_serial);
|
2011-09-16 13:45:11 +01:00
|
|
|
write_trace (trace, buf, &slaves[0]);
|
|
|
|
|
}
|
2011-07-30 17:28:21 +01:00
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
/* ack */
|
|
|
|
|
for (i = 0; i < num_slaves; i++) {
|
|
|
|
|
cairo_surface_destroy (slaves[i].image);
|
|
|
|
|
slaves[i].image = NULL;
|
|
|
|
|
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG > 1) {
|
|
|
|
|
printf ("sending continuation to '%s'\n",
|
2011-07-30 17:28:21 +01:00
|
|
|
TARGET_NAME (slaves[i].target));
|
2009-07-03 11:24:42 +01:00
|
|
|
}
|
2009-06-13 10:13:20 +01:00
|
|
|
if (! writen (slaves[i].fd,
|
|
|
|
|
&slaves[i].image_serial,
|
|
|
|
|
sizeof (slaves[i].image_serial)))
|
|
|
|
|
{
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slaves[i].image_serial = 0;
|
|
|
|
|
slaves[i].image_ready = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
completion = 0;
|
2009-06-13 18:49:16 +01:00
|
|
|
image = 0;
|
2009-06-13 10:13:20 +01:00
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
2020-08-30 18:29:05 -06:00
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
ret = TRUE;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
out:
|
2009-07-03 11:24:42 +01:00
|
|
|
if (DEBUG) {
|
|
|
|
|
printf ("run complete: %d\n", ret);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
for (n = 0; n < num_slaves; n++) {
|
|
|
|
|
if (slaves[n].fd != -1)
|
|
|
|
|
close (slaves[n].fd);
|
|
|
|
|
|
|
|
|
|
if (slaves[n].image == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
cairo_surface_destroy (slaves[n].image);
|
|
|
|
|
slaves[n].image = NULL;
|
|
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
cairo_surface_destroy (slaves[n].difference);
|
|
|
|
|
slaves[n].difference = NULL;
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
slaves[n].image_serial = 0;
|
|
|
|
|
slaves[n].image_ready = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (pfd);
|
2009-06-13 10:13:20 +01:00
|
|
|
|
|
|
|
|
return ret;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
static int
|
|
|
|
|
server_socket (const char *socket_path)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
long flags;
|
|
|
|
|
struct sockaddr_un addr;
|
2009-06-13 10:13:20 +01:00
|
|
|
int sk;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
sk = socket (PF_UNIX, SOCK_STREAM, 0);
|
2009-06-13 10:13:20 +01:00
|
|
|
if (sk == -1)
|
|
|
|
|
return -1;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
memset (&addr, 0, sizeof (addr));
|
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
|
strcpy (addr.sun_path, socket_path);
|
|
|
|
|
if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
|
|
|
|
|
close (sk);
|
2009-06-13 10:13:20 +01:00
|
|
|
return -1;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flags = fcntl (sk, F_GETFL);
|
|
|
|
|
if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
|
|
|
|
|
close (sk);
|
2009-06-13 10:13:20 +01:00
|
|
|
return -1;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (listen (sk, 5) == -1) {
|
2009-06-13 10:13:20 +01:00
|
|
|
close (sk);
|
|
|
|
|
return -1;
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
return sk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
server_shm (const char *shm_path)
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
fd = shm_open (shm_path, O_RDWR | O_EXCL | O_CREAT, 0777);
|
2009-06-13 10:13:20 +01:00
|
|
|
if (fd == -1)
|
|
|
|
|
return -1;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
if (ftruncate (fd, DATA_SIZE) == -1) {
|
|
|
|
|
close (fd);
|
2009-06-13 10:13:20 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
2009-07-04 23:44:09 +01:00
|
|
|
_test_trace (test_trace_t *test,
|
2009-06-19 18:40:43 +01:00
|
|
|
const char *trace,
|
|
|
|
|
const char *name,
|
|
|
|
|
struct error_info *error)
|
2009-06-13 10:13:20 +01:00
|
|
|
{
|
|
|
|
|
const char *shm_path = SHM_PATH_XXX;
|
2009-07-03 18:26:50 +01:00
|
|
|
const cairo_boilerplate_target_t *target, *image;
|
2009-06-13 10:13:20 +01:00
|
|
|
struct slave *slaves, *s;
|
2011-07-30 17:28:21 +01:00
|
|
|
test_runner_t *recorder = NULL;
|
2009-07-03 18:26:50 +01:00
|
|
|
pid_t slave;
|
2009-06-13 10:13:20 +01:00
|
|
|
char socket_dir[] = "/tmp/cairo-test-trace.XXXXXX";
|
|
|
|
|
char *socket_path;
|
|
|
|
|
int sk, fd;
|
|
|
|
|
int i, num_slaves;
|
|
|
|
|
void *base;
|
|
|
|
|
cairo_bool_t ret = FALSE;
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
if (DEBUG)
|
|
|
|
|
printf ("setting up trace '%s'\n", trace);
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
/* create a socket to control the test runners */
|
|
|
|
|
if (mkdtemp (socket_dir) == NULL) {
|
|
|
|
|
fprintf (stderr, "Unable to create temporary name for socket\n");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xasprintf (&socket_path, "%s/socket", socket_dir);
|
|
|
|
|
sk = server_socket (socket_path);
|
|
|
|
|
if (sk == -1) {
|
|
|
|
|
fprintf (stderr, "Unable to create socket for server\n");
|
|
|
|
|
goto cleanup_paths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* allocate some shared memory */
|
|
|
|
|
fd = server_shm (shm_path);
|
|
|
|
|
if (fd == -1) {
|
2011-07-30 17:28:21 +01:00
|
|
|
fprintf (stderr, "Unable to create shared memory '%s': %s\n",
|
|
|
|
|
shm_path, strerror (errno));
|
2009-06-12 22:19:56 +01:00
|
|
|
goto cleanup_sk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image = cairo_boilerplate_get_image_target (CAIRO_CONTENT_COLOR_ALPHA);
|
|
|
|
|
assert (image != NULL);
|
|
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
s = slaves = xcalloc (2*test->num_targets + 1, sizeof (struct slave));
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2010-06-27 03:03:17 +03:00
|
|
|
#if CAIRO_HAS_REAL_PTHREAD
|
2009-10-22 02:13:36 +03:00
|
|
|
/* set-up a recording-surface to reconstruct errors */
|
2011-07-30 17:28:21 +01:00
|
|
|
slave = spawn_recorder (socket_path, trace, &recorder);
|
2009-07-03 18:26:50 +01:00
|
|
|
if (slave < 0) {
|
2009-10-22 02:13:36 +03:00
|
|
|
fprintf (stderr, "Unable to create recording surface\n");
|
2009-07-03 18:26:50 +01:00
|
|
|
goto cleanup_sk;
|
2009-07-03 11:24:42 +01:00
|
|
|
}
|
2009-07-03 18:26:50 +01:00
|
|
|
|
|
|
|
|
s->pid = slave;
|
2009-10-22 02:13:36 +03:00
|
|
|
s->is_recording = TRUE;
|
2009-07-03 18:26:50 +01:00
|
|
|
s->target = NULL;
|
|
|
|
|
s->fd = -1;
|
|
|
|
|
s->reference = NULL;
|
|
|
|
|
s++;
|
2009-07-03 11:24:42 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* spawn slave processes to run the trace */
|
2009-06-12 22:19:56 +01:00
|
|
|
for (i = 0; i < test->num_targets; i++) {
|
2009-06-19 18:40:43 +01:00
|
|
|
const cairo_boilerplate_target_t *reference;
|
|
|
|
|
struct slave *master;
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
target = test->targets[i];
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
|
|
|
if (DEBUG)
|
|
|
|
|
printf ("setting up target[%d]? '%s' (image? %d, measurable? %d)\n",
|
|
|
|
|
i, target->name, target == image, target->is_measurable);
|
|
|
|
|
|
|
|
|
|
if (target == image || ! target->is_measurable)
|
2009-06-12 22:19:56 +01:00
|
|
|
continue;
|
|
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
/* find a matching slave to use as a reference for this target */
|
|
|
|
|
if (target->reference_target != NULL) {
|
|
|
|
|
reference =
|
|
|
|
|
cairo_boilerplate_get_target_by_name (target->reference_target,
|
|
|
|
|
target->content);
|
|
|
|
|
assert (reference != NULL);
|
|
|
|
|
} else {
|
|
|
|
|
reference = image;
|
|
|
|
|
}
|
|
|
|
|
for (master = slaves; master < s; master++) {
|
|
|
|
|
if (master->target == reference)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (master == s) {
|
|
|
|
|
/* no match found, spawn a slave to render the reference image */
|
|
|
|
|
slave = spawn_target (socket_path, shm_path, reference, trace);
|
|
|
|
|
if (slave < 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
s->pid = slave;
|
|
|
|
|
s->target = reference;
|
|
|
|
|
s->fd = -1;
|
|
|
|
|
s->reference = NULL;
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
slave = spawn_target (socket_path, shm_path, target, trace);
|
|
|
|
|
if (slave < 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
s->pid = slave;
|
|
|
|
|
s->target = target;
|
|
|
|
|
s->fd = -1;
|
2009-06-19 18:40:43 +01:00
|
|
|
s->reference = master;
|
2009-06-12 22:19:56 +01:00
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
num_slaves = s - slaves;
|
|
|
|
|
if (num_slaves == 1) {
|
|
|
|
|
fprintf (stderr, "No targets to test\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base = mmap (NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
|
|
|
if (base == MAP_FAILED) {
|
2009-06-13 10:13:20 +01:00
|
|
|
fprintf (stderr, "Unable to mmap shared memory\n");
|
2009-06-12 22:19:56 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2009-06-19 18:40:43 +01:00
|
|
|
ret = test_run (base, sk, name, slaves, num_slaves, error);
|
2009-06-12 22:19:56 +01:00
|
|
|
munmap (base, DATA_SIZE);
|
|
|
|
|
|
|
|
|
|
cleanup:
|
2009-06-13 10:13:20 +01:00
|
|
|
close (fd);
|
2009-06-12 22:19:56 +01:00
|
|
|
while (s-- > slaves) {
|
|
|
|
|
int status;
|
2009-07-03 11:24:42 +01:00
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
if (s->fd != -1)
|
|
|
|
|
close (s->fd);
|
|
|
|
|
|
|
|
|
|
cairo_surface_destroy (s->image);
|
|
|
|
|
cairo_surface_destroy (s->difference);
|
|
|
|
|
|
2009-10-22 02:13:36 +03:00
|
|
|
if (s->is_recording) /* in-process */
|
2009-07-03 11:24:42 +01:00
|
|
|
continue;
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
kill (s->pid, SIGKILL);
|
|
|
|
|
waitpid (s->pid, &status, 0);
|
2011-07-30 17:28:21 +01:00
|
|
|
if (WIFSIGNALED (status) && WTERMSIG(status) != SIGKILL) {
|
2009-06-13 10:13:20 +01:00
|
|
|
fprintf (stderr, "%s crashed\n", s->target->name);
|
2011-07-30 17:28:21 +01:00
|
|
|
if (recorder)
|
|
|
|
|
dump_traces (recorder, trace, s->target->name, "crash");
|
|
|
|
|
}
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
free (slaves);
|
|
|
|
|
shm_unlink (shm_path);
|
|
|
|
|
cleanup_sk:
|
|
|
|
|
close (sk);
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
cleanup_paths:
|
2009-06-12 22:19:56 +01:00
|
|
|
remove (socket_path);
|
|
|
|
|
remove (socket_dir);
|
|
|
|
|
|
|
|
|
|
free (socket_path);
|
2009-06-13 10:13:20 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2009-07-04 23:44:09 +01:00
|
|
|
test_trace (test_trace_t *test, const char *trace)
|
2009-06-13 10:13:20 +01:00
|
|
|
{
|
|
|
|
|
char *trace_cpy, *name, *dot;
|
|
|
|
|
|
|
|
|
|
trace_cpy = xstrdup (trace);
|
|
|
|
|
name = basename (trace_cpy);
|
|
|
|
|
dot = strchr (name, '.');
|
|
|
|
|
if (dot)
|
|
|
|
|
*dot = '\0';
|
|
|
|
|
|
|
|
|
|
if (test->list_only) {
|
|
|
|
|
printf ("%s\n", name);
|
|
|
|
|
} else {
|
2009-06-19 18:40:43 +01:00
|
|
|
struct error_info error = {0};
|
2009-06-13 10:13:20 +01:00
|
|
|
cairo_bool_t ret;
|
|
|
|
|
|
|
|
|
|
printf ("%s: ", name);
|
|
|
|
|
fflush (stdout);
|
|
|
|
|
|
2009-06-19 18:40:43 +01:00
|
|
|
ret = _test_trace (test, trace, name, &error);
|
|
|
|
|
if (ret) {
|
|
|
|
|
printf ("PASS\n");
|
|
|
|
|
} else {
|
|
|
|
|
if (error.context_id) {
|
|
|
|
|
printf ("FAIL (context %lu, lines [%lu, %lu])\n",
|
|
|
|
|
error.context_id,
|
|
|
|
|
error.start_line,
|
|
|
|
|
error.end_line);
|
|
|
|
|
} else {
|
|
|
|
|
printf ("FAIL\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-13 10:13:20 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
free (trace_cpy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
2009-07-04 23:44:09 +01:00
|
|
|
read_excludes (test_trace_t *test, const char *filename)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
FILE *file;
|
|
|
|
|
char *line = NULL;
|
|
|
|
|
size_t line_size = 0;
|
|
|
|
|
char *s, *t;
|
|
|
|
|
|
|
|
|
|
file = fopen (filename, "r");
|
|
|
|
|
if (file == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
while (getline (&line, &line_size, file) != -1) {
|
|
|
|
|
/* terminate the line at a comment marker '#' */
|
|
|
|
|
s = strchr (line, '#');
|
|
|
|
|
if (s)
|
|
|
|
|
*s = '\0';
|
|
|
|
|
|
|
|
|
|
/* whitespace delimits */
|
|
|
|
|
s = line;
|
Avoid misuse of ctype(3) functions
The ctype(3) character classification and mapping functions have a
peculiarly limited definition (C11, Sec. 7.4 `Character handling
<ctype.h>', p. 200):
`The header <ctype.h> declares several functions useful for
classifying and mapping characters. In all cases the
argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value
of the macro EOF. If the argument has any other value, the
behavior is undefined.'
In other words, in the most common case of 8-bit char and EOF = -1,
the domain of the 257 allowed arguments is:
-1, 0, 1, 2, ..., 254, 255
The ctype(3) functions are designed for use with stdio functions like
getchar and fgetc which return int values in the same domain.
In an ABI where char is signed (e.g., x86 SysV ABI used by most
Unixish operating systems), passing an argument of type char as is
can go wrong in two ways:
1. The value of a non-EOF input octet interpreted as `char' may
coincide, as an integer, with the value of EOF, leading to wrong
answers for some non-EOF inputs.
E.g., if EOF = 1, and an input octet has all bits set, i.e., 255
as an unsigned char, then as a char the value is -1, which will be
confused with EOF. In the ISO-8859-1 locale, the code point 255
is (in Unicode terminology) LATIN SMALL LETTER Y WITH DIAERESIS,
for which isprint, isalpha, &c., are true. But isprint, isalpha,
&c., are false for EOF. So if char *s points to a string with
that character, isprint(*s) will return false when it should
return true.
2. Passing a negative char whose value does not coincide with EOF is
undefined behaviour.
This isn't purely theoretical: often the functions are implemented
by an array lookup, #define isprint(c) (ctypetab[c] & ISPRINT).
If c is out of range (e.g., 192, ISO-8859-1 for LATIN CAPITAL
LETTER A WITH GRAVE, which convers to (signed) char as -64), then
you can get garbage answers by reading uninitialized memory or
application crashes with SIGSEGV if the page preceding the table
is unmapped.
If what you have is an arbitrary char (e.g., from a char * string
pointing at user input), then the only correct way to use the
ctype(3) functions is by converting to unsigned char first -- e.g.,
isprint((unsigned char)*s). (If the functions were defined as macros
that convert to unsigned char first, they would then spuriously
interpret EOF as a non-EOF, so they can't do that themselves.)
It is possible, in some cases, to prove that the input always
actually lies in {0, 1, 2, ..., 127}, so the conversion to unsigned
char is not necessary. I didn't check whether this was the case --
it's safer to just adopt the habit of always casting char to unsigned
char first before using the ctype(3) macros, which satisfies a
compiler warning on some systems designed to detect this class of
application errors at compile-time.
2022-04-05 12:51:33 +00:00
|
|
|
while (*s != '\0' && isspace ((unsigned char)*s))
|
2009-06-12 22:19:56 +01:00
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
t = s;
|
Avoid misuse of ctype(3) functions
The ctype(3) character classification and mapping functions have a
peculiarly limited definition (C11, Sec. 7.4 `Character handling
<ctype.h>', p. 200):
`The header <ctype.h> declares several functions useful for
classifying and mapping characters. In all cases the
argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value
of the macro EOF. If the argument has any other value, the
behavior is undefined.'
In other words, in the most common case of 8-bit char and EOF = -1,
the domain of the 257 allowed arguments is:
-1, 0, 1, 2, ..., 254, 255
The ctype(3) functions are designed for use with stdio functions like
getchar and fgetc which return int values in the same domain.
In an ABI where char is signed (e.g., x86 SysV ABI used by most
Unixish operating systems), passing an argument of type char as is
can go wrong in two ways:
1. The value of a non-EOF input octet interpreted as `char' may
coincide, as an integer, with the value of EOF, leading to wrong
answers for some non-EOF inputs.
E.g., if EOF = 1, and an input octet has all bits set, i.e., 255
as an unsigned char, then as a char the value is -1, which will be
confused with EOF. In the ISO-8859-1 locale, the code point 255
is (in Unicode terminology) LATIN SMALL LETTER Y WITH DIAERESIS,
for which isprint, isalpha, &c., are true. But isprint, isalpha,
&c., are false for EOF. So if char *s points to a string with
that character, isprint(*s) will return false when it should
return true.
2. Passing a negative char whose value does not coincide with EOF is
undefined behaviour.
This isn't purely theoretical: often the functions are implemented
by an array lookup, #define isprint(c) (ctypetab[c] & ISPRINT).
If c is out of range (e.g., 192, ISO-8859-1 for LATIN CAPITAL
LETTER A WITH GRAVE, which convers to (signed) char as -64), then
you can get garbage answers by reading uninitialized memory or
application crashes with SIGSEGV if the page preceding the table
is unmapped.
If what you have is an arbitrary char (e.g., from a char * string
pointing at user input), then the only correct way to use the
ctype(3) functions is by converting to unsigned char first -- e.g.,
isprint((unsigned char)*s). (If the functions were defined as macros
that convert to unsigned char first, they would then spuriously
interpret EOF as a non-EOF, so they can't do that themselves.)
It is possible, in some cases, to prove that the input always
actually lies in {0, 1, 2, ..., 127}, so the conversion to unsigned
char is not necessary. I didn't check whether this was the case --
it's safer to just adopt the habit of always casting char to unsigned
char first before using the ctype(3) macros, which satisfies a
compiler warning on some systems designed to detect this class of
application errors at compile-time.
2022-04-05 12:51:33 +00:00
|
|
|
while (*t != '\0' && ! isspace ((unsigned char)*t))
|
2009-06-12 22:19:56 +01:00
|
|
|
t++;
|
|
|
|
|
|
|
|
|
|
if (s != t) {
|
|
|
|
|
int i = test->num_exclude_names;
|
|
|
|
|
test->exclude_names = xrealloc (test->exclude_names,
|
|
|
|
|
sizeof (char *) * (i+1));
|
|
|
|
|
test->exclude_names[i] = strndup (s, t-s);
|
|
|
|
|
test->num_exclude_names++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-30 11:04:16 +02:00
|
|
|
free (line);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
fclose (file);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
usage (const char *argv0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
2011-10-02 16:50:37 -07:00
|
|
|
"Usage: %s [-l] [-x exclude-file] [test-names ... | traces ...]\n"
|
2009-06-12 22:19:56 +01:00
|
|
|
"\n"
|
2009-06-13 10:13:20 +01:00
|
|
|
"Run the cairo test suite over the given traces (all by default).\n"
|
2009-06-12 22:19:56 +01:00
|
|
|
"The command-line arguments are interpreted as follows:\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" -l list only; just list selected test case names without executing\n"
|
2011-10-02 16:50:37 -07:00
|
|
|
" -x exclude; specify a file to read a list of traces to exclude\n"
|
2009-06-12 22:19:56 +01:00
|
|
|
"\n"
|
|
|
|
|
"If test names are given they are used as sub-string matches so a command\n"
|
2011-10-02 16:50:37 -07:00
|
|
|
"such as \"%s firefox\" can be used to run all firefox traces.\n"
|
2009-06-12 22:19:56 +01:00
|
|
|
"Alternatively, you can specify a list of filenames to execute.\n",
|
|
|
|
|
argv0, argv0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2009-07-04 23:44:09 +01:00
|
|
|
parse_options (test_trace_t *test, int argc, char *argv[])
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
test->list_only = FALSE;
|
|
|
|
|
test->names = NULL;
|
|
|
|
|
test->num_names = 0;
|
|
|
|
|
test->exclude_names = NULL;
|
|
|
|
|
test->num_exclude_names = 0;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
2011-10-02 16:15:59 -07:00
|
|
|
c = _cairo_getopt (argc, argv, "lx:");
|
2009-06-12 22:19:56 +01:00
|
|
|
if (c == -1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
case 'l':
|
|
|
|
|
test->list_only = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case 'x':
|
|
|
|
|
if (! read_excludes (test, optarg)) {
|
|
|
|
|
fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
|
|
|
|
|
optarg);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf (stderr, "Internal error: unhandled option: %c\n", c);
|
|
|
|
|
/* fall-through */
|
|
|
|
|
case '?':
|
|
|
|
|
usage (argv[0]);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (optind < argc) {
|
|
|
|
|
test->names = &argv[optind];
|
|
|
|
|
test->num_names = argc - optind;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2009-07-04 23:44:09 +01:00
|
|
|
test_reset (test_trace_t *test)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
2009-10-22 02:13:36 +03:00
|
|
|
/* XXX leaking fonts again via recording-surface? */
|
2009-07-03 11:24:42 +01:00
|
|
|
#if 0
|
2009-06-12 22:19:56 +01:00
|
|
|
cairo_debug_reset_static_data ();
|
|
|
|
|
#if HAVE_FCFINI
|
|
|
|
|
FcFini ();
|
|
|
|
|
#endif
|
2009-07-03 11:24:42 +01:00
|
|
|
#endif
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2009-07-04 23:44:09 +01:00
|
|
|
test_fini (test_trace_t *test)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
2009-06-13 10:13:20 +01:00
|
|
|
test_reset (test);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
cairo_boilerplate_free_targets (test->targets);
|
2011-07-30 11:04:16 +02:00
|
|
|
free (test->exclude_names);
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
2009-07-04 23:44:09 +01:00
|
|
|
test_has_filenames (test_trace_t *test)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
if (test->num_names == 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < test->num_names; i++)
|
|
|
|
|
if (access (test->names[i], R_OK) == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static cairo_bool_t
|
2009-07-04 23:44:09 +01:00
|
|
|
test_can_run (test_trace_t *test, const char *name)
|
2009-06-12 22:19:56 +01:00
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
char *copy, *dot;
|
|
|
|
|
cairo_bool_t ret;
|
|
|
|
|
|
|
|
|
|
if (test->num_names == 0 && test->num_exclude_names == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
copy = xstrdup (name);
|
|
|
|
|
dot = strrchr (copy, '.');
|
|
|
|
|
if (dot != NULL)
|
|
|
|
|
*dot = '\0';
|
|
|
|
|
|
|
|
|
|
if (test->num_names) {
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
for (i = 0; i < test->num_names; i++)
|
|
|
|
|
if (strstr (copy, test->names[i]))
|
|
|
|
|
goto check_exclude;
|
|
|
|
|
|
|
|
|
|
ret = FALSE;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check_exclude:
|
|
|
|
|
if (test->num_exclude_names) {
|
|
|
|
|
ret = FALSE;
|
|
|
|
|
for (i = 0; i < test->num_exclude_names; i++)
|
|
|
|
|
if (strstr (copy, test->exclude_names[i]))
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
free (copy);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
warn_no_traces (const char *message, const char *trace_dir)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr,
|
|
|
|
|
"Error: %s '%s'.\n"
|
|
|
|
|
"Have you cloned the cairo-traces repository and uncompressed the traces?\n"
|
|
|
|
|
" git clone git://anongit.freedesktop.org/cairo-traces\n"
|
|
|
|
|
" cd cairo-traces && make\n"
|
|
|
|
|
"Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
|
|
|
|
|
message, trace_dir);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
static void
|
|
|
|
|
interrupt (int sig)
|
|
|
|
|
{
|
|
|
|
|
shm_unlink (SHM_PATH_XXX);
|
|
|
|
|
|
|
|
|
|
signal (sig, SIG_DFL);
|
|
|
|
|
raise (sig);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
int
|
|
|
|
|
main (int argc, char *argv[])
|
|
|
|
|
{
|
2009-07-04 23:44:09 +01:00
|
|
|
test_trace_t test;
|
2009-06-12 22:19:56 +01:00
|
|
|
const char *trace_dir = "cairo-traces";
|
|
|
|
|
unsigned int n;
|
|
|
|
|
|
|
|
|
|
signal (SIGPIPE, SIG_IGN);
|
2009-06-13 10:13:20 +01:00
|
|
|
signal (SIGINT, interrupt);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
parse_options (&test, argc, argv);
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
shm_unlink (SHM_PATH_XXX);
|
|
|
|
|
|
2009-06-12 22:19:56 +01:00
|
|
|
if (getenv ("CAIRO_TRACE_DIR") != NULL)
|
|
|
|
|
trace_dir = getenv ("CAIRO_TRACE_DIR");
|
|
|
|
|
|
|
|
|
|
test.targets = cairo_boilerplate_get_targets (&test.num_targets, NULL);
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
if (test_has_filenames (&test)) {
|
2009-06-12 22:19:56 +01:00
|
|
|
for (n = 0; n < test.num_names; n++) {
|
2009-07-03 11:24:42 +01:00
|
|
|
if (access (test.names[n], R_OK) == 0) {
|
2009-06-12 22:19:56 +01:00
|
|
|
test_trace (&test, test.names[n]);
|
2009-06-13 10:13:20 +01:00
|
|
|
test_reset (&test);
|
2009-06-12 22:19:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
DIR *dir;
|
|
|
|
|
struct dirent *de;
|
|
|
|
|
int num_traces = 0;
|
|
|
|
|
|
|
|
|
|
dir = opendir (trace_dir);
|
|
|
|
|
if (dir == NULL) {
|
|
|
|
|
warn_no_traces ("Failed to open directory", trace_dir);
|
2009-06-13 10:13:20 +01:00
|
|
|
test_fini (&test);
|
2009-06-12 22:19:56 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((de = readdir (dir)) != NULL) {
|
|
|
|
|
char *trace;
|
|
|
|
|
const char *dot;
|
|
|
|
|
|
|
|
|
|
dot = strrchr (de->d_name, '.');
|
|
|
|
|
if (dot == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
if (strcmp (dot, ".trace"))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
num_traces++;
|
2009-06-13 10:13:20 +01:00
|
|
|
if (! test_can_run (&test, de->d_name))
|
2009-06-12 22:19:56 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
xasprintf (&trace, "%s/%s", trace_dir, de->d_name);
|
|
|
|
|
test_trace (&test, trace);
|
2009-06-13 10:13:20 +01:00
|
|
|
test_reset (&test);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
free (trace);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
closedir (dir);
|
|
|
|
|
|
|
|
|
|
if (num_traces == 0) {
|
|
|
|
|
warn_no_traces ("Found no traces in", trace_dir);
|
2009-06-13 10:13:20 +01:00
|
|
|
test_fini (&test);
|
2009-06-12 22:19:56 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-13 10:13:20 +01:00
|
|
|
test_fini (&test);
|
2009-06-12 22:19:56 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-06-19 18:40:43 +01:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cairo_test_logv (const cairo_test_context_t *ctx,
|
|
|
|
|
const char *fmt, va_list va)
|
|
|
|
|
{
|
|
|
|
|
#if 0
|
|
|
|
|
vfprintf (stderr, fmt, va);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cairo_test_log (const cairo_test_context_t *ctx, const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
#if 0
|
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
|
|
va_start (va, fmt);
|
|
|
|
|
vfprintf (stderr, fmt, va);
|
|
|
|
|
va_end (va);
|
|
|
|
|
#endif
|
|
|
|
|
}
|