[test] Code review after sleep

Review cairo-test-trace.c and rewrite parts to ease understanding and fix
various bugs - such as failure to notice the slaves crashing and not
releasing our shared memory after an interrupt.
This commit is contained in:
Chris Wilson 2009-06-13 10:13:20 +01:00
parent 1f542965f0
commit 3dde883b77

View file

@ -24,12 +24,12 @@
*/ */
/* /*
* The basic premise is that we feed the trace to multiple backends in * The basic idea is that we feed the trace to multiple backends in parallel
* parallel and compare the output at the end of each context (based on the * and compare the output at the end of each context (based on the premise
* premise that contexts demarcate expose events, or their logical * that contexts demarcate expose events, or their logical equivalents) with
* equivalents) with that of the image[1] backend. Each backend is executed in * that of the image[1] backend. Each backend is executed in a separate
* a separate process, for robustness, with the image data residing in shared * process, for robustness and to isolate the global cairo state, with the
* memory and synchronising over a socket. * image data residing in shared memory and synchronising over a socket.
* *
* [1] Should be reference implementation, currently the image backend is * [1] Should be reference implementation, currently the image backend is
* considered to be the reference for all other backends. * considered to be the reference for all other backends.
@ -45,7 +45,14 @@
* So for each backend spawn two processes, a reference and xlib * So for each backend spawn two processes, a reference and xlib
* (obviously minimising the number of reference processes when possible) * (obviously minimising the number of reference processes when possible)
*/ */
#define _GNU_SOURCE 1 /* for sched_getaffinity() and getline() */
/*
* 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.
*/
#define _GNU_SOURCE 1 /* getline() */
#include "cairo-test.h" #include "cairo-test.h"
@ -77,32 +84,38 @@
/* manage the shared memory using Doug Lea's malloc */ /* manage the shared memory using Doug Lea's malloc */
#define USE_LOCKS 0 #define USE_LOCKS 0
#define USE_DL_PREFIX 1 #define USE_DL_PREFIX 1
#if HAVE_FFS
#define USE_BUILTIN_FFS 1 #define USE_BUILTIN_FFS 1
#endif
/* We only use the segment created my create_mspace_with_base. */ /* We only use the segment created my create_mspace_with_base. */
#define MSPACES 1 #define MSPACES 1
#define ONLY_MSPACES 1 #define ONLY_MSPACES 1
/* Disable manipulation of the memory segment */
#define HAVE_MORECORE 0 #define HAVE_MORECORE 0
#define HAVE_MMAP 1 #define HAVE_MMAP 1
#define HAVE_MREMAP 0 #define HAVE_MREMAP 0
#define mmap(a, b, c, d, e, f) MFAIL
#define munmap(a, b) (-1)
#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
/* We have no use for this, so save some code and data. */ /* We have no use for this, so save some code and data. */
#define NO_MALLINFO 1 #define NO_MALLINFO 1
#define ABORT_ON_ASSERT_FAILURE 0
/* We need all allocations to be in regular segments. */
#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
/* Don't allocate more than a page unless needed. */ /* Don't allocate more than a page unless needed. */
#define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize) #define DEFAULT_GRANULARITY ((size_t)malloc_getpagesize)
#define mmap(a, b, c, d, e, f) MFAIL
#define munmap(a, b) (-1)
#include "dlmalloc.c" #include "dlmalloc.c"
#undef mmap #undef mmap
#undef munmap #undef munmap
#undef assert
#include <assert.h>
#define DATA_SIZE (64 << 20) #define DATA_SIZE (64 << 20)
#define SHM_PATH_XXX "/shmem-cairo-trace"
typedef struct _test_runner { typedef struct _test_runner {
/* Options from command-line */ /* Options from command-line */
@ -144,12 +157,19 @@ struct slave {
const cairo_boilerplate_target_t *target; const cairo_boilerplate_target_t *target;
}; };
struct request_image {
unsigned long id;
cairo_format_t format;
int width;
int height;
int stride;
};
struct surface_tag { struct surface_tag {
int width, height; int width, height;
}; };
static const cairo_user_data_key_t surface_tag; static const cairo_user_data_key_t surface_tag;
static cairo_bool_t static cairo_bool_t
writen (int fd, const void *ptr, int len) writen (int fd, const void *ptr, int len)
{ {
@ -222,14 +242,6 @@ format_for_content (cairo_content_t content)
} }
} }
struct request_image {
unsigned long id;
cairo_format_t format;
int width;
int height;
int stride;
};
static void * static void *
request_image (test_runner_thread_t *thread, request_image (test_runner_thread_t *thread,
unsigned long id, unsigned long id,
@ -241,8 +253,8 @@ request_image (test_runner_thread_t *thread,
writen (thread->sk, &rq, sizeof (rq)); writen (thread->sk, &rq, sizeof (rq));
readn (thread->sk, &offset, sizeof (offset)); readn (thread->sk, &offset, sizeof (offset));
if (offset == (size_t) -1)
assert (offset != (size_t) -1); return NULL;
return thread->base + offset; return thread->base + offset;
} }
@ -253,29 +265,47 @@ push_surface (test_runner_thread_t *thread,
unsigned long id) unsigned long id)
{ {
cairo_surface_t *image; cairo_surface_t *image;
cairo_format_t format; cairo_format_t format = (cairo_format_t) -1;
cairo_t *cr; cairo_t *cr;
int width, height, stride; int width, height, stride;
void *data; void *data;
unsigned long serial; unsigned long serial;
format = format_for_content (cairo_surface_get_content (source));
if (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE) { if (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE) {
width = cairo_image_surface_get_width (source); width = cairo_image_surface_get_width (source);
height = cairo_image_surface_get_height (source); height = cairo_image_surface_get_height (source);
format = cairo_image_surface_get_format (source);
} else { } else {
struct surface_tag *tag; struct surface_tag *tag;
tag = cairo_surface_get_user_data (source, &surface_tag); tag = cairo_surface_get_user_data (source, &surface_tag);
assert (tag != NULL); if (tag != NULL) {
width = tag->width; width = tag->width;
height = tag->height; 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))
exit (-1);
}
} }
if (format == (cairo_format_t) -1)
format = format_for_content (cairo_surface_get_content (source));
stride = cairo_format_stride_for_width (format, width); stride = cairo_format_stride_for_width (format, width);
data = request_image (thread, id, format, width, height, stride); data = request_image (thread, id, format, width, height, stride);
assert (data != NULL); if (data == NULL)
exit (-1);
image = cairo_image_surface_create_for_data (data, image = cairo_image_surface_create_for_data (data,
format, format,
@ -314,7 +344,8 @@ _surface_create (void *closure,
tag = xmalloc (sizeof (*tag)); tag = xmalloc (sizeof (*tag));
tag->width = width; tag->width = width;
tag->height = height; tag->height = height;
cairo_surface_set_user_data (surface, &surface_tag, tag, free); if (cairo_surface_set_user_data (surface, &surface_tag, tag, free))
exit (-1);
} }
return surface; return surface;
@ -349,7 +380,8 @@ _context_destroy (void *closure, void *ptr)
if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) { if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) {
push_surface (thread, l->surface, l->id); push_surface (thread, l->surface, l->id);
} else { } else {
fprintf (stderr, "error during replay: %s!\n", fprintf (stderr, "%s: error during replay: %s!\n",
thread->target->name,
cairo_status_to_string (cairo_surface_status cairo_status_to_string (cairo_surface_status
(l->surface))); (l->surface)));
exit (1); exit (1);
@ -382,7 +414,8 @@ execute (test_runner_thread_t *thread,
cairo_script_interpreter_run (csi, trace); cairo_script_interpreter_run (csi, trace);
cairo_script_interpreter_finish (csi); cairo_script_interpreter_finish (csi);
cairo_script_interpreter_destroy (csi); if (cairo_script_interpreter_destroy (csi))
exit (1);
} }
static int static int
@ -408,24 +441,21 @@ spawn_socket (const char *socket_path, pid_t pid)
return sk; return sk;
} }
static void * static void *
spawn_shm (const char *shm_path) spawn_shm (const char *shm_path)
{ {
void *base = MAP_FAILED; void *base;
int fd; int fd;
fd = shm_open (shm_path, O_RDWR, 0); fd = shm_open (shm_path, O_RDWR, 0);
if (fd == -1) if (fd == -1)
return base; return MAP_FAILED;
base = mmap (NULL, DATA_SIZE, base = mmap (NULL, DATA_SIZE,
PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_NORESERVE, MAP_SHARED | MAP_NORESERVE,
fd, 0); fd, 0);
close (fd); close (fd);
if (base == MAP_FAILED)
return base;
return base; return base;
} }
@ -490,6 +520,7 @@ spawn_target (const char *socket_path,
exit (0); exit (0);
} }
/* XXX imagediff - is the extra expense worth it? */
static cairo_bool_t static cairo_bool_t
matching_images (cairo_surface_t *a, cairo_surface_t *b) matching_images (cairo_surface_t *a, cairo_surface_t *b)
{ {
@ -521,39 +552,17 @@ matching_images (cairo_surface_t *a, cairo_surface_t *b)
} }
static cairo_bool_t static cairo_bool_t
check_images_and_ack (mspace msp, struct slave *slaves, int num_slaves) check_images (struct slave *slaves, int num_slaves)
{ {
int n; int n;
/* wait for completion */
if (slaves[0].image_ready == 0)
return TRUE;
for (n = 1; n < num_slaves; n++) { for (n = 1; n < num_slaves; n++) {
if (slaves[n].image_ready != slaves[0].image_ready) assert (slaves[n].image_ready == slaves[0].image_ready);
return TRUE;
}
/* compare */
for (n = 1; n < num_slaves; n++) {
if (! matching_images (slaves[n].image, slaves[0].image)) if (! matching_images (slaves[n].image, slaves[0].image))
return FALSE; return FALSE;
} }
/* ack */
for (n = 0; n < num_slaves; n++) {
cairo_surface_finish (slaves[n].image);
mspace_free (msp, cairo_image_surface_get_data (slaves[n].image));
cairo_surface_destroy (slaves[n].image);
slaves[n].image = NULL;
writen (slaves[n].fd,
&slaves[n].image_serial,
sizeof (slaves[n].image_serial));
slaves[n].image_serial = 0;
slaves[n].image_ready = 0;
}
return TRUE; return TRUE;
} }
@ -596,7 +605,7 @@ allocate_image_for_slave (uint8_t *base, mspace *msp, struct slave *slave)
return data - base; return data - base;
} }
static void static cairo_bool_t
test_run (void *base, test_run (void *base,
mspace msp, mspace msp,
int sk, int sk,
@ -606,14 +615,16 @@ test_run (void *base,
{ {
struct pollfd *pfd; struct pollfd *pfd;
int npfd, cnt, n, i; int npfd, cnt, n, i;
int completion;
cairo_bool_t ret = FALSE;
pfd = xcalloc (num_slaves+2, sizeof (*pfd)); pfd = xcalloc (num_slaves+2, sizeof (*pfd));
pfd[0].fd = sk; pfd[0].fd = sk;
pfd[0].events = POLLIN; pfd[0].events = POLLIN;
pfd[0].revents = 0; /* valgrind */
npfd = 1; npfd = 1;
completion = 0;
while ((cnt = poll (pfd, npfd, -1)) > 0) { while ((cnt = poll (pfd, npfd, -1)) > 0) {
if (pfd[0].revents) { if (pfd[0].revents) {
int fd; int fd;
@ -638,21 +649,22 @@ test_run (void *base,
if (! pfd[n].revents) if (! pfd[n].revents)
continue; continue;
if (pfd[n].revents & POLLHUP)
goto done;
for (i = 0; i < num_slaves; i++) { for (i = 0; i < num_slaves; i++) {
if (slaves[i].fd == pfd[n].fd) { if (slaves[i].fd == pfd[n].fd) {
if (pfd[n].revents & POLLHUP)
/* XXX check exitcode? */
goto done;
/* Communication with the slave is done in three phases, /* Communication with the slave is done in three phases,
* and we do each pass synchronously. * and we do each pass synchronously.
* *
* 1. The slave requests an image buffer, which we * 1. The slave requests an image buffer, which we
* allocate and then return the offset into the shared * allocate and then return to the slave the offset into
* memory segment. * the shared memory segment.
* *
* 2. The slave indicates that it has finished writing * 2. The slave indicates that it has finished writing
* into the shared image buffer. * into the shared image buffer. The slave now waits
* for the server to collate all the image data - thereby
* throttling the slaves.
* *
* 3. After all slaves have finished writing their images, * 3. After all slaves have finished writing their images,
* we compare them all against the reference image and, * we compare them all against the reference image and,
@ -664,30 +676,18 @@ test_run (void *base,
offset = offset =
allocate_image_for_slave (base, msp, allocate_image_for_slave (base, msp,
&slaves[i]); &slaves[i]);
if (! writen (pfd[n].fd, &offset, sizeof (offset))) { if (! writen (pfd[n].fd, &offset, sizeof (offset)))
fprintf (stderr,
"communication error with slave\n");
goto out; goto out;
}
} else { } else {
readn (pfd[n].fd, readn (pfd[n].fd,
&slaves[i].image_ready, &slaves[i].image_ready,
sizeof (slaves[i].image_ready)); sizeof (slaves[i].image_ready));
if (slaves[i].image_ready != slaves[i].image_serial) { if (slaves[i].image_ready != slaves[i].image_serial)
fprintf (stderr,
"communication error with slave: "
"expected serial %lu, got %lu\n",
slaves[i].image_serial,
slaves[i].image_ready);
goto out; goto out;
}
if (! check_images_and_ack (msp, slaves, num_slaves)) { /* Can anyone spell 'P·E·D·A·N·T'? */
printf ("FAIL: backends differ after context %lu\n", cairo_surface_mark_dirty (slaves[i].image);
slaves[0].image_serial); completion++;
write_images (trace, slaves, num_slaves);
goto out;
}
} }
break; break;
@ -696,9 +696,37 @@ test_run (void *base,
cnt--; cnt--;
} }
if (completion == num_slaves) {
if (! check_images (slaves, num_slaves)) {
write_images (trace, slaves, num_slaves);
goto out;
}
/* ack */
for (i = 0; i < num_slaves; i++) {
cairo_surface_finish (slaves[i].image);
mspace_free (msp,
cairo_image_surface_get_data (slaves[i].image));
cairo_surface_destroy (slaves[i].image);
slaves[i].image = NULL;
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;
}
} }
done: done:
printf ("PASS\n"); ret = TRUE;
out: out:
for (n = 0; n < num_slaves; n++) { for (n = 0; n < num_slaves; n++) {
@ -715,9 +743,12 @@ out:
slaves[n].image_serial = 0; slaves[n].image_serial = 0;
slaves[n].image_ready = 0; slaves[n].image_ready = 0;
ret = FALSE;
} }
free (pfd); free (pfd);
return ret;
} }
/* Paginated surfaces require finalization and external converters and so /* Paginated surfaces require finalization and external converters and so
@ -763,93 +794,96 @@ target_is_measurable (const cairo_boilerplate_target_t *target)
return TRUE; return TRUE;
} }
/* XXX cleanup on SIGINT. */ static int
static void server_socket (const char *socket_path)
test_trace (test_runner_t *test, const char *trace)
{ {
const char *shm_path = "/shmem";
const cairo_boilerplate_target_t *target, *image;
char *trace_cpy, *name, *dot;
struct slave *slaves, *s;
char socket_dir[] = "/tmp/cairo-test-trace.XXXXXX";
char *socket_path;
int sk, fd;
long flags; long flags;
struct sockaddr_un addr; struct sockaddr_un addr;
int i, num_slaves; int sk;
void *base;
mspace msp;
trace_cpy = xstrdup (trace);
name = basename (trace_cpy);
dot = strchr (name, '.');
if (dot)
*dot = '\0';
if (test->list_only) {
printf ("%s\n", name);
free (trace_cpy);
return;
}
printf ("%s: ", name);
fflush (stdout);
/* create a socket to control the test runners */
if (mkdtemp (socket_dir) == NULL) {
fprintf (stderr, "unable to create temporary name for socket\n");
return;
}
xasprintf (&socket_path, "%s/socket", socket_dir);
sk = socket (PF_UNIX, SOCK_STREAM, 0); sk = socket (PF_UNIX, SOCK_STREAM, 0);
if (sk == -1) { if (sk == -1)
fprintf (stderr, "unable to create socket\n"); return -1;
return;
}
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strcpy (addr.sun_path, socket_path); strcpy (addr.sun_path, socket_path);
if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) { if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
close (sk); close (sk);
fprintf (stderr, "unable to bind socket\n"); return -1;
return;
} }
flags = fcntl (sk, F_GETFL); flags = fcntl (sk, F_GETFL);
if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) { if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
close (sk); close (sk);
fprintf (stderr, "unable to set socket to non-blocking\n"); return -1;
return;
} }
if (listen (sk, 5) == -1) { if (listen (sk, 5) == -1) {
fprintf (stderr, "unable to listen on socket\n"); close (sk);
goto cleanup_sk; return -1;
} }
shm_path="/shmem"; return sk;
}
static int
server_shm (const char *shm_path)
{
int fd;
fd = shm_open (shm_path, O_RDWR | O_EXCL | O_CREAT, 0777); fd = shm_open (shm_path, O_RDWR | O_EXCL | O_CREAT, 0777);
if (fd == -1) { if (fd == -1)
fprintf (stderr, return -1;
"unable to create shared memory '%s': %s\n",
shm_path, strerror (errno));
goto cleanup_sk;
}
if (ftruncate (fd, DATA_SIZE) == -1) { if (ftruncate (fd, DATA_SIZE) == -1) {
fprintf (stderr, "unable to resize shared memory\n");
close (fd); close (fd);
return -1;
}
return fd;
}
static cairo_bool_t
_test_trace (test_runner_t *test, const char *trace, const char *name)
{
const char *shm_path = SHM_PATH_XXX;
const cairo_boilerplate_target_t *target, *image;
struct slave *slaves, *s;
char socket_dir[] = "/tmp/cairo-test-trace.XXXXXX";
char *socket_path;
int sk, fd;
int i, num_slaves;
void *base;
mspace msp;
cairo_bool_t ret = FALSE;
/* 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) {
fprintf (stderr, "Unable to create shared memory '%s'\n",
shm_path);
goto cleanup_sk; goto cleanup_sk;
} }
image = cairo_boilerplate_get_image_target (CAIRO_CONTENT_COLOR_ALPHA); image = cairo_boilerplate_get_image_target (CAIRO_CONTENT_COLOR_ALPHA);
assert (image != NULL); assert (image != NULL);
/* spawn slave processes to run the trace */
s = slaves = xcalloc (test->num_targets + 1, sizeof (struct slave)); s = slaves = xcalloc (test->num_targets + 1, sizeof (struct slave));
s->pid = spawn_target (socket_path, shm_path, image, trace); s->pid = spawn_target (socket_path, shm_path, image, trace);
if (s->pid < 0) if (s->pid < 0)
goto cleanup; goto cleanup;
@ -879,36 +913,66 @@ test_trace (test_runner_t *test, const char *trace)
goto cleanup; goto cleanup;
} }
/* map our shared memory and manage using a dlmalloc pool */
base = mmap (NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); base = mmap (NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
if (base == MAP_FAILED) { if (base == MAP_FAILED) {
fprintf (stderr, "unable to mmap shared memory\n"); fprintf (stderr, "Unable to mmap shared memory\n");
goto cleanup; goto cleanup;
} }
msp = create_mspace_with_base (base, DATA_SIZE, 0); msp = create_mspace_with_base (base, DATA_SIZE, 0);
test_run (base, msp, sk, name, slaves, num_slaves); ret = test_run (base, msp, sk, name, slaves, num_slaves);
destroy_mspace (msp); destroy_mspace (msp);
munmap (base, DATA_SIZE); munmap (base, DATA_SIZE);
cleanup: cleanup:
close (fd);
while (s-- > slaves) { while (s-- > slaves) {
int status; int status;
kill (s->pid, SIGKILL); kill (s->pid, SIGKILL);
waitpid (s->pid, &status, 0); waitpid (s->pid, &status, 0);
ret &= WIFEXITED (status) && WEXITSTATUS (status) == 0;
if (WIFSIGNALED (status) && WTERMSIG(status) != SIGKILL) {
fprintf (stderr, "%s crashed\n", s->target->name);
}
} }
free (slaves); free (slaves);
shm_unlink (shm_path); shm_unlink (shm_path);
cleanup_sk: cleanup_sk:
close (sk); close (sk);
cleanup_paths:
remove (socket_path); remove (socket_path);
remove (socket_dir); remove (socket_dir);
free (socket_path); free (socket_path);
return ret;
}
static void
test_trace (test_runner_t *test, const char *trace)
{
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 {
cairo_bool_t ret;
printf ("%s: ", name);
fflush (stdout);
ret = _test_trace (test, trace, name);
printf ("%s\n", ret ? "PASS" : "FAIL");
}
free (trace_cpy); free (trace_cpy);
} }
@ -1008,7 +1072,7 @@ usage (const char *argv0)
"Usage: %s [-x exclude-file] [test-names ... | traces ...]\n" "Usage: %s [-x exclude-file] [test-names ... | traces ...]\n"
" %s -l\n" " %s -l\n"
"\n" "\n"
"Run the cairo performance test suite over the given tests (all by default)\n" "Run the cairo test suite over the given traces (all by default).\n"
"The command-line arguments are interpreted as follows:\n" "The command-line arguments are interpreted as follows:\n"
"\n" "\n"
" -x exclude; specify a file to read a list of traces to exclude\n" " -x exclude; specify a file to read a list of traces to exclude\n"
@ -1063,7 +1127,7 @@ parse_options (test_runner_t *test, int argc, char *argv[])
} }
static void static void
cairo_test_reset (test_runner_t *test) test_reset (test_runner_t *test)
{ {
cairo_debug_reset_static_data (); cairo_debug_reset_static_data ();
#if HAVE_FCFINI #if HAVE_FCFINI
@ -1072,9 +1136,9 @@ cairo_test_reset (test_runner_t *test)
} }
static void static void
cairo_test_fini (test_runner_t *test) test_fini (test_runner_t *test)
{ {
cairo_test_reset (test); test_reset (test);
cairo_boilerplate_free_targets (test->targets); cairo_boilerplate_free_targets (test->targets);
if (test->exclude_names) if (test->exclude_names)
@ -1082,7 +1146,7 @@ cairo_test_fini (test_runner_t *test)
} }
static cairo_bool_t static cairo_bool_t
have_trace_filenames (test_runner_t *test) test_has_filenames (test_runner_t *test)
{ {
unsigned int i; unsigned int i;
@ -1097,8 +1161,7 @@ have_trace_filenames (test_runner_t *test)
} }
static cairo_bool_t static cairo_bool_t
cairo_test_can_run (test_runner_t *test, test_can_run (test_runner_t *test, const char *name)
const char *name)
{ {
unsigned int i; unsigned int i;
char *copy, *dot; char *copy, *dot;
@ -1151,6 +1214,15 @@ warn_no_traces (const char *message, const char *trace_dir)
message, trace_dir); message, trace_dir);
} }
static void
interrupt (int sig)
{
shm_unlink (SHM_PATH_XXX);
signal (sig, SIG_DFL);
raise (sig);
}
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
@ -1159,6 +1231,7 @@ main (int argc, char *argv[])
unsigned int n; unsigned int n;
signal (SIGPIPE, SIG_IGN); signal (SIGPIPE, SIG_IGN);
signal (SIGINT, interrupt);
parse_options (&test, argc, argv); parse_options (&test, argc, argv);
@ -1167,14 +1240,13 @@ main (int argc, char *argv[])
test.targets = cairo_boilerplate_get_targets (&test.num_targets, NULL); test.targets = cairo_boilerplate_get_targets (&test.num_targets, NULL);
/* do we have a list of filenames? */ if (test_has_filenames (&test)) {
if (have_trace_filenames (&test)) {
for (n = 0; n < test.num_names; n++) { for (n = 0; n < test.num_names; n++) {
if (cairo_test_can_run (&test, test.names[n]) && if (test_can_run (&test, test.names[n]) &&
access (test.names[n], R_OK) == 0) access (test.names[n], R_OK) == 0)
{ {
test_trace (&test, test.names[n]); test_trace (&test, test.names[n]);
cairo_test_reset (&test); test_reset (&test);
} }
} }
} else { } else {
@ -1185,7 +1257,7 @@ main (int argc, char *argv[])
dir = opendir (trace_dir); dir = opendir (trace_dir);
if (dir == NULL) { if (dir == NULL) {
warn_no_traces ("Failed to open directory", trace_dir); warn_no_traces ("Failed to open directory", trace_dir);
cairo_test_fini (&test); test_fini (&test);
return 1; return 1;
} }
@ -1200,12 +1272,12 @@ main (int argc, char *argv[])
continue; continue;
num_traces++; num_traces++;
if (! cairo_test_can_run (&test, de->d_name)) if (! test_can_run (&test, de->d_name))
continue; continue;
xasprintf (&trace, "%s/%s", trace_dir, de->d_name); xasprintf (&trace, "%s/%s", trace_dir, de->d_name);
test_trace (&test, trace); test_trace (&test, trace);
cairo_test_reset (&test); test_reset (&test);
free (trace); free (trace);
@ -1214,12 +1286,12 @@ main (int argc, char *argv[])
if (num_traces == 0) { if (num_traces == 0) {
warn_no_traces ("Found no traces in", trace_dir); warn_no_traces ("Found no traces in", trace_dir);
cairo_test_fini (&test); test_fini (&test);
return 1; return 1;
} }
} }
cairo_test_fini (&test); test_fini (&test);
return 0; return 0;
} }