summaryrefslogtreecommitdiff
path: root/test/cairo-test-runner.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/cairo-test-runner.c')
-rw-r--r--test/cairo-test-runner.c1108
1 files changed, 1108 insertions, 0 deletions
diff --git a/test/cairo-test-runner.c b/test/cairo-test-runner.c
new file mode 100644
index 000000000..f9e7d4712
--- /dev/null
+++ b/test/cairo-test-runner.c
@@ -0,0 +1,1108 @@
+/*
+ * Copyright © 2008 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
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON 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.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test-private.h"
+#include "cairo-boilerplate-getopt.h"
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#undef CAIRO_VERSION_MAJOR
+#undef CAIRO_VERSION_MINOR
+#undef CAIRO_VERSION_MICRO
+#include "../cairo-version.h"
+
+#include <pixman.h> /* for version information */
+
+#define SHOULD_FORK HAVE_FORK && HAVE_WAITPID
+#if SHOULD_FORK
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+#if HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#if HAVE_VALGRIND
+#include <valgrind.h>
+#else
+#define RUNNING_ON_VALGRIND 0
+#endif
+
+#ifdef _MSC_VER
+#include <crtdbg.h>
+#endif
+
+typedef struct _cairo_test_list {
+ const cairo_test_t *test;
+ struct _cairo_test_list *next;
+} cairo_test_list_t;
+
+typedef struct _cairo_test_runner {
+ cairo_test_context_t base;
+
+ unsigned int num_device_offsets;
+ unsigned int num_device_scales;
+
+ cairo_bool_t passed;
+ int num_passed;
+ int num_skipped;
+ int num_failed;
+ int num_xfailed;
+ int num_error;
+ int num_crashed;
+
+ cairo_test_list_t *crashes_preamble;
+ cairo_test_list_t *errors_preamble;
+ cairo_test_list_t *fails_preamble;
+
+ cairo_test_list_t **crashes_per_target;
+ cairo_test_list_t **errors_per_target;
+ cairo_test_list_t **fails_per_target;
+
+ int *num_failed_per_target;
+ int *num_error_per_target;
+ int *num_crashed_per_target;
+
+ cairo_bool_t foreground;
+ cairo_bool_t exit_on_failure;
+ cairo_bool_t list_only;
+ cairo_bool_t full_test;
+ cairo_bool_t keyword_match;
+ cairo_bool_t slow;
+ cairo_bool_t force_pass;
+} cairo_test_runner_t;
+
+typedef enum {
+ GE,
+ GT
+} cairo_test_compare_op_t;
+
+static cairo_test_list_t *tests;
+
+static void CAIRO_BOILERPLATE_PRINTF_FORMAT(2,3)
+_log (cairo_test_context_t *ctx,
+ const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vprintf (fmt, ap);
+ va_end (ap);
+
+ va_start (ap, fmt);
+ cairo_test_logv (ctx, fmt, ap);
+ va_end (ap);
+}
+
+static cairo_test_list_t *
+_list_prepend (cairo_test_list_t *head, const cairo_test_t *test)
+{
+ cairo_test_list_t *list;
+
+ list = xmalloc (sizeof (cairo_test_list_t));
+ list->test = test;
+ list->next = head;
+ head = list;
+
+ return head;
+}
+
+static cairo_test_list_t *
+_list_reverse (cairo_test_list_t *head)
+{
+ cairo_test_list_t *list, *next;
+
+ for (list = head, head = NULL; list != NULL; list = next) {
+ next = list->next;
+ list->next = head;
+ head = list;
+ }
+
+ return head;
+}
+
+static void
+_list_free (cairo_test_list_t *list)
+{
+ while (list != NULL) {
+ cairo_test_list_t *next = list->next;
+ free (list);
+ list = next;
+ }
+}
+
+static cairo_bool_t
+is_running_under_debugger (void)
+{
+#if HAVE_UNISTD_H && HAVE_LIBGEN_H && __linux__
+ char buf[1024];
+
+ sprintf (buf, "/proc/%d/exe", getppid ());
+ if (readlink (buf, buf, sizeof (buf)) != -1 &&
+ strncmp (basename (buf), "gdb", 3) == 0)
+ {
+ return TRUE;
+ }
+#endif
+
+ if (RUNNING_ON_VALGRIND)
+ return TRUE;
+
+ return FALSE;
+}
+
+#if SHOULD_FORK
+static cairo_test_status_t
+_cairo_test_wait (pid_t pid)
+{
+ int exitcode;
+
+ if (waitpid (pid, &exitcode, 0) != pid)
+ return CAIRO_TEST_CRASHED;
+
+ if (WIFSIGNALED (exitcode)) {
+ switch (WTERMSIG (exitcode)) {
+ case SIGINT:
+#if HAVE_RAISE
+ raise (SIGINT);
+#endif
+ return CAIRO_TEST_UNTESTED;
+ default:
+ return CAIRO_TEST_CRASHED;
+ }
+ }
+
+ return WEXITSTATUS (exitcode);
+}
+#endif
+
+static cairo_test_status_t
+_cairo_test_runner_preamble (cairo_test_runner_t *runner,
+ cairo_test_context_t *ctx)
+{
+#if SHOULD_FORK
+ if (! runner->foreground) {
+ pid_t pid;
+
+ switch ((pid = fork ())) {
+ case -1: /* error */
+ return CAIRO_TEST_UNTESTED;
+
+ case 0: /* child */
+ exit (ctx->test->preamble (ctx));
+
+ default:
+ return _cairo_test_wait (pid);
+ }
+ }
+#endif
+ return ctx->test->preamble (ctx);
+}
+
+static cairo_test_status_t
+_cairo_test_runner_draw (cairo_test_runner_t *runner,
+ cairo_test_context_t *ctx,
+ const cairo_boilerplate_target_t *target,
+ cairo_bool_t similar,
+ int device_offset, int device_scale)
+{
+#if SHOULD_FORK
+ if (! runner->foreground) {
+ pid_t pid;
+
+ switch ((pid = fork ())) {
+ case -1: /* error */
+ return CAIRO_TEST_UNTESTED;
+
+ case 0: /* child */
+ exit (_cairo_test_context_run_for_target (ctx, target,
+ similar, device_offset, device_scale));
+
+ default:
+ return _cairo_test_wait (pid);
+ }
+ }
+#endif
+ return _cairo_test_context_run_for_target (ctx, target,
+ similar, device_offset, device_scale);
+}
+
+static void
+append_argv (int *argc, char ***argv, const char *str)
+{
+ int old_argc;
+ char **old_argv;
+ cairo_bool_t doit;
+ const char *s, *t;
+ int olen;
+ int len;
+ int i;
+ int args_to_add = 0;
+
+ if (str == NULL)
+ return;
+
+ old_argc = *argc;
+ old_argv = *argv;
+
+ doit = FALSE;
+ do {
+ if (doit)
+ *argv = xmalloc (olen);
+
+ olen = sizeof (char *) * (args_to_add + *argc);
+ for (i = 0; i < old_argc; i++) {
+ len = strlen (old_argv[i]) + 1;
+ if (doit) {
+ (*argv)[i] = (char *) *argv + olen;
+ memcpy ((*argv)[i], old_argv[i], len);
+ }
+ olen += len;
+ }
+
+ s = str;
+ while ((t = strpbrk (s, " \t,:;")) != NULL) {
+ if (t - s) {
+ len = t - s;
+ if (doit) {
+ (*argv)[i] = (char *) *argv + olen;
+ memcpy ((*argv)[i], s, len);
+ (*argv)[i][len] = '\0';
+ } else {
+ olen += sizeof (char *);
+ }
+ args_to_add++;
+ olen += len + 1;
+ i++;
+ }
+ s = t + 1;
+ }
+ if (*s != '\0') {
+ len = strlen (s) + 1;
+ if (doit) {
+ (*argv)[i] = (char *) *argv + olen;
+ memcpy ((*argv)[i], s, len);
+ } else {
+ olen += sizeof (char *);
+ }
+ args_to_add++;
+ olen += len;
+ i++;
+ }
+ } while (doit++ == FALSE);
+ *argc = i;
+}
+
+static void
+usage (const char *argv0)
+{
+ fprintf (stderr,
+ "Usage: %s [-afkxsl] [test-names|keywords ...]\n"
+ "\n"
+ "Run the cairo conformance test suite over the given tests (all by default)\n"
+ "The command-line arguments are interpreted as follows:\n"
+ "\n"
+ " -a all; run the full set of tests. By default the test suite\n"
+ " skips similar surface and device offset testing.\n"
+ " -f foreground; do not fork\n"
+ " -k match tests by keyword\n"
+ " -l list only; just list selected test case names without executing\n"
+ " -s include slow, long running tests\n"
+ " -x exit on first failure\n"
+ "\n"
+ "If test names are given they are used as matches either to a specific\n"
+ "test case or to a keyword, so a command such as\n"
+ "\"%s -k text\" can be used to run all text test cases, and\n"
+ "\"%s text-transform\" to run the individual case.\n",
+ argv0, argv0, argv0);
+}
+
+static void
+_parse_cmdline (cairo_test_runner_t *runner, int *argc, char **argv[])
+{
+ int c;
+
+ while (1) {
+ c = _cairo_getopt (*argc, *argv, ":afklsx");
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ runner->full_test = ~0;
+ break;
+ case 'f':
+ runner->foreground = TRUE;
+ break;
+ case 'k':
+ runner->keyword_match = TRUE;
+ break;
+ case 'l':
+ runner->list_only = TRUE;
+ break;
+ case 's':
+ runner->slow = TRUE;
+ break;
+ case 'x':
+ runner->exit_on_failure = TRUE;
+ break;
+ default:
+ fprintf (stderr, "Internal error: unhandled option: %c\n", c);
+ /* fall-through */
+ case '?':
+ usage ((*argv)[0]);
+ exit (1);
+ }
+ }
+
+ *argc -= optind;
+ *argv += optind;
+}
+
+static void
+_runner_init (cairo_test_runner_t *runner)
+{
+ cairo_test_init (&runner->base, "cairo-test-suite", ".");
+
+ runner->passed = TRUE;
+
+ runner->fails_preamble = NULL;
+ runner->crashes_preamble = NULL;
+ runner->errors_preamble = NULL;
+
+ runner->fails_per_target = xcalloc (sizeof (cairo_test_list_t *),
+ runner->base.num_targets);
+ runner->crashes_per_target = xcalloc (sizeof (cairo_test_list_t *),
+ runner->base.num_targets);
+ runner->errors_per_target = xcalloc (sizeof (cairo_test_list_t *),
+ runner->base.num_targets);
+ runner->num_failed_per_target = xcalloc (sizeof (int),
+ runner->base.num_targets);
+ runner->num_error_per_target = xcalloc (sizeof (int),
+ runner->base.num_targets);
+ runner->num_crashed_per_target = xcalloc (sizeof (int),
+ runner->base.num_targets);
+}
+
+static void
+_runner_print_versions (cairo_test_runner_t *runner)
+{
+ _log (&runner->base,
+ "Compiled against cairo %s, running on %s.\n",
+ CAIRO_VERSION_STRING, cairo_version_string ());
+ _log (&runner->base,
+ "Compiled against pixman %s, running on %s.\n",
+ PIXMAN_VERSION_STRING, pixman_version_string ());
+
+ fflush (runner->base.log_file);
+}
+
+static void
+_runner_print_summary (cairo_test_runner_t *runner)
+{
+ _log (&runner->base,
+ "%d Passed, %d Failed [%d crashed, %d expected], %d Skipped\n",
+ runner->num_passed,
+
+ runner->num_failed + runner->num_crashed + runner->num_xfailed,
+ runner->num_crashed,
+ runner->num_xfailed,
+
+ runner->num_skipped);
+}
+
+static void
+_runner_print_details (cairo_test_runner_t *runner)
+{
+ cairo_test_list_t *list;
+ unsigned int n;
+
+ if (runner->crashes_preamble) {
+ int count = 0;
+
+ for (list = runner->crashes_preamble; list != NULL; list = list->next)
+ count++;
+
+ _log (&runner->base, "Preamble: %d crashed! -", count);
+
+ for (list = runner->crashes_preamble; list != NULL; list = list->next) {
+ char *name = cairo_test_get_name (list->test);
+ _log (&runner->base, " %s", name);
+ free (name);
+ }
+ _log (&runner->base, "\n");
+ }
+ if (runner->errors_preamble) {
+ int count = 0;
+
+ for (list = runner->errors_preamble; list != NULL; list = list->next)
+ count++;
+
+ _log (&runner->base, "Preamble: %d error -", count);
+
+ for (list = runner->errors_preamble; list != NULL; list = list->next) {
+ char *name = cairo_test_get_name (list->test);
+ _log (&runner->base, " %s", name);
+ free (name);
+ }
+ _log (&runner->base, "\n");
+ }
+ if (runner->fails_preamble) {
+ int count = 0;
+
+ for (list = runner->fails_preamble; list != NULL; list = list->next)
+ count++;
+
+ _log (&runner->base, "Preamble: %d failed -", count);
+
+ for (list = runner->fails_preamble; list != NULL; list = list->next) {
+ char *name = cairo_test_get_name (list->test);
+ _log (&runner->base, " %s", name);
+ free (name);
+ }
+ _log (&runner->base, "\n");
+ }
+
+ for (n = 0; n < runner->base.num_targets; n++) {
+ const cairo_boilerplate_target_t *target;
+
+ target = runner->base.targets_to_test[n];
+ if (runner->num_crashed_per_target[n]) {
+ _log (&runner->base, "%s (%s): %d crashed! -",
+ target->name,
+ cairo_boilerplate_content_name (target->content),
+ runner->num_crashed_per_target[n]);
+
+ for (list = runner->crashes_per_target[n];
+ list != NULL;
+ list = list->next)
+ {
+ char *name = cairo_test_get_name (list->test);
+ _log (&runner->base, " %s", name);
+ free (name);
+ }
+ _log (&runner->base, "\n");
+ }
+ if (runner->num_error_per_target[n]) {
+ _log (&runner->base, "%s (%s): %d error -",
+ target->name,
+ cairo_boilerplate_content_name (target->content),
+ runner->num_error_per_target[n]);
+
+ for (list = runner->errors_per_target[n];
+ list != NULL;
+ list = list->next)
+ {
+ char *name = cairo_test_get_name (list->test);
+ _log (&runner->base, " %s", name);
+ free (name);
+ }
+ _log (&runner->base, "\n");
+ }
+
+ if (runner->num_failed_per_target[n]) {
+ _log (&runner->base, "%s (%s): %d failed -",
+ target->name,
+ cairo_boilerplate_content_name (target->content),
+ runner->num_failed_per_target[n]);
+
+ for (list = runner->fails_per_target[n];
+ list != NULL;
+ list = list->next)
+ {
+ char *name = cairo_test_get_name (list->test);
+ _log (&runner->base, " %s", name);
+ free (name);
+ }
+ _log (&runner->base, "\n");
+ }
+ }
+}
+
+static void
+_runner_print_results (cairo_test_runner_t *runner)
+{
+ _runner_print_summary (runner);
+ _runner_print_details (runner);
+
+ if (! runner->passed && ! runner->num_crashed) {
+ _log (&runner->base,
+"\n"
+"Note: These failures may be due to external factors.\n"
+"Please read test/README -- \"Getting the elusive zero failures\".\n");
+ }
+}
+
+static cairo_test_status_t
+_runner_fini (cairo_test_runner_t *runner)
+{
+ unsigned int n;
+
+ _list_free (runner->crashes_preamble);
+ _list_free (runner->errors_preamble);
+ _list_free (runner->fails_preamble);
+
+ for (n = 0; n < runner->base.num_targets; n++) {
+ _list_free (runner->crashes_per_target[n]);
+ _list_free (runner->errors_per_target[n]);
+ _list_free (runner->fails_per_target[n]);
+ }
+ free (runner->crashes_per_target);
+ free (runner->errors_per_target);
+ free (runner->fails_per_target);
+
+ free (runner->num_crashed_per_target);
+ free (runner->num_error_per_target);
+ free (runner->num_failed_per_target);
+
+ cairo_test_fini (&runner->base);
+
+ if (runner->force_pass)
+ return CAIRO_TEST_SUCCESS;
+
+ return runner->num_failed + runner->num_crashed ?
+ CAIRO_TEST_FAILURE :
+ runner->num_passed + runner->num_xfailed ?
+ CAIRO_TEST_SUCCESS : CAIRO_TEST_UNTESTED;
+}
+
+static cairo_bool_t
+_version_compare (int a, cairo_test_compare_op_t op, int b)
+{
+ switch (op) {
+ case GT: return a > b;
+ case GE: return a >= b;
+ default: return FALSE;
+ }
+}
+
+
+static cairo_bool_t
+_get_required_version (const char *str,
+ cairo_test_compare_op_t *op,
+ int *major,
+ int *minor,
+ int *micro)
+{
+ while (*str == ' ')
+ str++;
+
+ if (strncmp (str, ">=", 2) == 0) {
+ *op = GE;
+ str += 2;
+ } else if (strncmp (str, ">", 1) == 0) {
+ *op = GT;
+ str += 1;
+ } else
+ return FALSE;
+
+ while (*str == ' ')
+ str++;
+
+ if (sscanf (str, "%d.%d.%d", major, minor, micro) != 3) {
+ *micro = 0;
+ if (sscanf (str, "%d.%d", major, minor) != 2)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_has_required_cairo_version (const char *str)
+{
+ cairo_test_compare_op_t op;
+ int major, minor, micro;
+
+ if (! _get_required_version (str + 5 /* advance over "cairo" */,
+ &op, &major, &minor, &micro))
+ {
+ fprintf (stderr, "unrecognised cairo version requirement '%s'\n", str);
+ return FALSE;
+ }
+
+ return _version_compare (cairo_version (),
+ op,
+ CAIRO_VERSION_ENCODE (major, minor, micro));
+}
+
+static cairo_bool_t
+_has_required_ghostscript_version (const char *str)
+{
+#if ! CAIRO_CAN_TEST_PS_SURFACE
+ return TRUE;
+#endif
+
+ str += 2; /* advance over "gs" */
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_has_required_poppler_version (const char *str)
+{
+#if ! CAIRO_CAN_TEST_PDF_SURFACE
+ return TRUE;
+#endif
+
+ str += 7; /* advance over "poppler" */
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_has_required_rsvg_version (const char *str)
+{
+#if ! CAIRO_CAN_TEST_SVG_SURFACE
+ return TRUE;
+#endif
+
+ str += 4; /* advance over "rsvg" */
+
+ return TRUE;
+}
+
+#define TEST_SIMILAR 0x1
+#define TEST_OFFSET 0x2
+#define TEST_SCALE 0x4
+int
+main (int argc, char **argv)
+{
+ cairo_test_runner_t runner;
+ cairo_test_list_t *test_list;
+ cairo_test_status_t *target_status;
+ unsigned int n, m, k;
+ char targets[4096];
+ int len;
+ char *cairo_tests_env;
+
+#ifdef _MSC_VER
+ /* We don't want an assert dialog, we want stderr */
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+#endif
+
+ _cairo_test_runner_register_tests ();
+ tests = _list_reverse (tests);
+
+ memset (&runner, 0, sizeof (runner));
+ runner.num_device_offsets = 1;
+ runner.num_device_scales = 1;
+
+ if (is_running_under_debugger ())
+ runner.foreground = TRUE;
+
+ if (getenv ("CAIRO_TEST_MODE")) {
+ const char *env = getenv ("CAIRO_TEST_MODE");
+
+ if (strstr (env, "full")) {
+ runner.full_test = ~0;
+ }
+ if (strstr (env, "similar")) {
+ runner.full_test |= TEST_SIMILAR;
+ }
+ if (strstr (env, "offset")) {
+ runner.full_test |= TEST_OFFSET;
+ }
+ if (strstr (env, "scale")) {
+ runner.full_test |= TEST_SCALE;
+ }
+ if (strstr (env, "foreground")) {
+ runner.foreground = TRUE;
+ }
+ if (strstr (env, "exit-on-failure")) {
+ runner.exit_on_failure = TRUE;
+ }
+ }
+
+ if (getenv ("CAIRO_TEST_FORCE_PASS")) {
+ const char *env = getenv ("CAIRO_TEST_FORCE_PASS");
+
+ runner.force_pass = atoi (env);
+ }
+
+ _parse_cmdline (&runner, &argc, &argv);
+
+ cairo_tests_env = getenv("CAIRO_TESTS");
+ append_argv (&argc, &argv, cairo_tests_env);
+
+ if (runner.full_test & TEST_OFFSET) {
+ runner.num_device_offsets = 2;
+ }
+ if (runner.full_test & TEST_SCALE) {
+ runner.num_device_scales = 2;
+ }
+
+ target_status = NULL; /* silence the compiler */
+ if (! runner.list_only) {
+ _runner_init (&runner);
+ _runner_print_versions (&runner);
+ target_status = xmalloc (sizeof (cairo_test_status_t) *
+ runner.base.num_targets);
+ }
+
+ for (test_list = tests; test_list != NULL; test_list = test_list->next) {
+ const cairo_test_t *test = test_list->test;
+ cairo_test_context_t ctx;
+ cairo_test_status_t status;
+ cairo_bool_t failed = FALSE, xfailed = FALSE, error = FALSE, crashed = FALSE, skipped = TRUE;
+ cairo_bool_t in_preamble = FALSE;
+ char *name = cairo_test_get_name (test);
+ int i;
+
+ /* check for restricted runs */
+ if (argc) {
+ cairo_bool_t found = FALSE;
+ const char *keywords = test->keywords;
+
+ for (i = 0; i < argc; i++) {
+ const char *match = argv[i];
+ cairo_bool_t invert = match[0] == '!';
+ if (invert)
+ match++;
+
+ if (runner.keyword_match) {
+ if (keywords != NULL && strstr (keywords, match) != NULL) {
+ found = ! invert;
+ break;
+ } else if (invert) {
+ found = TRUE;
+ }
+ } else {
+ /* exact match on test name */
+ if (strcmp (name, match) == 0) {
+ found = ! invert;
+ break;
+ } else if (invert) {
+ found = TRUE;
+ }
+ }
+ }
+
+ if (! found) {
+ free (name);
+ continue;
+ }
+ }
+
+ /* check to see if external requirements match */
+ if (test->requirements != NULL) {
+ const char *requirements = test->requirements;
+ const char *str;
+
+ str = strstr (requirements, "slow");
+ if (str != NULL && ! runner.slow) {
+ if (runner.list_only)
+ goto TEST_NEXT;
+ else
+ goto TEST_SKIPPED;
+ }
+
+ str = strstr (requirements, "cairo");
+ if (str != NULL && ! _has_required_cairo_version (str)) {
+ if (runner.list_only)
+ goto TEST_NEXT;
+ else
+ goto TEST_SKIPPED;
+ }
+
+ str = strstr (requirements, "gs");
+ if (str != NULL && ! _has_required_ghostscript_version (str)) {
+ if (runner.list_only)
+ goto TEST_NEXT;
+ else
+ goto TEST_SKIPPED;
+ }
+
+ str = strstr (requirements, "poppler");
+ if (str != NULL && ! _has_required_poppler_version (str)) {
+ if (runner.list_only)
+ goto TEST_NEXT;
+ else
+ goto TEST_SKIPPED;
+ }
+
+ str = strstr (requirements, "rsvg");
+ if (str != NULL && ! _has_required_rsvg_version (str)) {
+ if (runner.list_only)
+ goto TEST_NEXT;
+ else
+ goto TEST_SKIPPED;
+ }
+ }
+
+ if (runner.list_only) {
+ printf ("%s ", name);
+ goto TEST_NEXT;
+ }
+
+ _cairo_test_context_init_for_test (&ctx, &runner.base, test);
+ memset (target_status, 0,
+ sizeof (cairo_test_status_t) * ctx.num_targets);
+
+ if (ctx.test->preamble != NULL) {
+ status = _cairo_test_runner_preamble (&runner, &ctx);
+ switch (status) {
+ case CAIRO_TEST_SUCCESS:
+ in_preamble = TRUE;
+ skipped = FALSE;
+ break;
+
+ case CAIRO_TEST_XFAILURE:
+ in_preamble = TRUE;
+ xfailed = TRUE;
+ goto TEST_DONE;
+
+ case CAIRO_TEST_NEW:
+ case CAIRO_TEST_FAILURE:
+ runner.fails_preamble = _list_prepend (runner.fails_preamble,
+ test);
+ in_preamble = TRUE;
+ failed = TRUE;
+ goto TEST_DONE;
+
+ case CAIRO_TEST_ERROR:
+ runner.errors_preamble = _list_prepend (runner.errors_preamble,
+ test);
+ in_preamble = TRUE;
+ failed = TRUE;
+ goto TEST_DONE;
+
+ case CAIRO_TEST_NO_MEMORY:
+ case CAIRO_TEST_CRASHED:
+ runner.crashes_preamble = _list_prepend (runner.crashes_preamble,
+ test);
+ in_preamble = TRUE;
+ failed = TRUE;
+ goto TEST_DONE;
+
+ case CAIRO_TEST_UNTESTED:
+ goto TEST_DONE;
+ }
+ }
+
+ if (ctx.test->draw == NULL)
+ goto TEST_DONE;
+
+ for (n = 0; n < ctx.num_targets; n++) {
+ const cairo_boilerplate_target_t *target;
+ cairo_bool_t target_failed = FALSE,
+ target_xfailed = FALSE,
+ target_error = FALSE,
+ target_crashed = FALSE,
+ target_skipped = TRUE;
+ cairo_test_similar_t has_similar;
+
+ target = ctx.targets_to_test[n];
+
+ has_similar = runner.full_test & TEST_SIMILAR ?
+ cairo_test_target_has_similar (&ctx, target) :
+ DIRECT;
+ for (m = 0; m < runner.num_device_offsets; m++) {
+ for (k = 0; k < runner.num_device_scales; k++) {
+ int dev_offset = m * 25;
+ int dev_scale = k + 1;
+ cairo_test_similar_t similar;
+
+ for (similar = DIRECT; similar <= has_similar; similar++) {
+ status = _cairo_test_runner_draw (&runner, &ctx, target,
+ similar, dev_offset, dev_scale);
+ switch (status) {
+ case CAIRO_TEST_SUCCESS:
+ target_skipped = FALSE;
+ break;
+ case CAIRO_TEST_XFAILURE:
+ target_xfailed = TRUE;
+ break;
+ case CAIRO_TEST_NEW:
+ case CAIRO_TEST_FAILURE:
+ target_failed = TRUE;
+ break;
+ case CAIRO_TEST_ERROR:
+ target_error = TRUE;
+ break;
+ case CAIRO_TEST_NO_MEMORY:
+ case CAIRO_TEST_CRASHED:
+ target_crashed = TRUE;
+ break;
+ case CAIRO_TEST_UNTESTED:
+ break;
+ }
+ }
+ }
+ }
+
+ if (target_crashed) {
+ target_status[n] = CAIRO_TEST_CRASHED;
+ runner.num_crashed_per_target[n]++;
+ runner.crashes_per_target[n] = _list_prepend (runner.crashes_per_target[n],
+ test);
+ crashed = TRUE;
+ } else if (target_error) {
+ target_status[n] = CAIRO_TEST_ERROR;
+ runner.num_error_per_target[n]++;
+ runner.errors_per_target[n] = _list_prepend (runner.errors_per_target[n],
+ test);
+
+ error = TRUE;
+ } else if (target_failed) {
+ target_status[n] = CAIRO_TEST_FAILURE;
+ runner.num_failed_per_target[n]++;
+ runner.fails_per_target[n] = _list_prepend (runner.fails_per_target[n],
+ test);
+
+ failed = TRUE;
+ } else if (target_xfailed) {
+ target_status[n] = CAIRO_TEST_XFAILURE;
+ xfailed = TRUE;
+ } else if (target_skipped) {
+ target_status[n] = CAIRO_TEST_UNTESTED;
+ } else {
+ target_status[n] = CAIRO_TEST_SUCCESS;
+ skipped = FALSE;
+ }
+ }
+
+ TEST_DONE:
+ cairo_test_fini (&ctx);
+ TEST_SKIPPED:
+ targets[0] = '\0';
+ if (crashed) {
+ if (! in_preamble) {
+ len = 0;
+ for (n = 0 ; n < runner.base.num_targets; n++) {
+ if (target_status[n] == CAIRO_TEST_CRASHED) {
+ if (strstr (targets,
+ runner.base.targets_to_test[n]->name) == NULL)
+ {
+ len += snprintf (targets + len, sizeof (targets) - len,
+ "%s, ",
+ runner.base.targets_to_test[n]->name);
+ }
+ }
+ }
+ targets[len-2] = '\0';
+ _log (&runner.base, "\n%s: CRASH! (%s)\n", name, targets);
+ } else {
+ _log (&runner.base, "\n%s: CRASH!\n", name);
+ }
+ runner.num_crashed++;
+ runner.passed = FALSE;
+ } else if (error) {
+ if (! in_preamble) {
+ len = 0;
+ for (n = 0 ; n < runner.base.num_targets; n++) {
+ if (target_status[n] == CAIRO_TEST_ERROR) {
+ if (strstr (targets,
+ runner.base.targets_to_test[n]->name) == NULL)
+ {
+ len += snprintf (targets + len,
+ sizeof (targets) - len,
+ "%s, ",
+ runner.base.targets_to_test[n]->name);
+ }
+ }
+ }
+ targets[len-2] = '\0';
+ _log (&runner.base, "%s: ERROR (%s)\n", name, targets);
+ } else {
+ _log (&runner.base, "%s: ERROR\n", name);
+ }
+ runner.num_error++;
+ runner.passed = FALSE;
+ } else if (failed) {
+ if (! in_preamble) {
+ len = 0;
+ for (n = 0 ; n < runner.base.num_targets; n++) {
+ if (target_status[n] == CAIRO_TEST_FAILURE) {
+ if (strstr (targets,
+ runner.base.targets_to_test[n]->name) == NULL)
+ {
+ len += snprintf (targets + len,
+ sizeof (targets) - len,
+ "%s, ",
+ runner.base.targets_to_test[n]->name);
+ }
+ }
+ }
+ targets[len-2] = '\0';
+ _log (&runner.base, "%s: FAIL (%s)\n", name, targets);
+ } else {
+ _log (&runner.base, "%s: FAIL\n", name);
+ }
+ runner.num_failed++;
+ runner.passed = FALSE;
+ } else if (xfailed) {
+ _log (&runner.base, "%s: XFAIL\n", name);
+ runner.num_xfailed++;
+ } else if (skipped) {
+ _log (&runner.base, "%s: UNTESTED\n", name);
+ runner.num_skipped++;
+ } else {
+ _log (&runner.base, "%s: PASS\n", name);
+ runner.num_passed++;
+ }
+ fflush (runner.base.log_file);
+
+ TEST_NEXT:
+ free (name);
+ if (runner.exit_on_failure && ! runner.passed)
+ break;
+
+ }
+
+ if (cairo_tests_env)
+ free(argv);
+
+ if (runner.list_only) {
+ printf ("\n");
+ return CAIRO_TEST_SUCCESS;
+ }
+
+ for (n = 0 ; n < runner.base.num_targets; n++) {
+ runner.crashes_per_target[n] = _list_reverse (runner.crashes_per_target[n]);
+ runner.errors_per_target[n] = _list_reverse (runner.errors_per_target[n]);
+ runner.fails_per_target[n] = _list_reverse (runner.fails_per_target[n]);
+ }
+
+ _runner_print_results (&runner);
+
+ _list_free (tests);
+ free (target_status);
+ return _runner_fini (&runner);
+}
+
+void
+cairo_test_register (cairo_test_t *test)
+{
+ tests = _list_prepend (tests, test);
+}