/* * 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 */ #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 /* for version information */ /* Coregraphics doesn't seem to like being forked and reports: * "The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec()." * so we don't for on OS X */ #define SHOULD_FORK HAVE_FORK && HAVE_WAITPID && !__APPLE__ #if SHOULD_FORK #if HAVE_UNISTD_H #include #endif #if HAVE_SIGNAL_H #include #endif #include #include #endif #if HAVE_LIBGEN_H #include #endif #if HAVE_VALGRIND #include #else #define RUNNING_ON_VALGRIND 0 #endif #ifdef _MSC_VER #include #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; 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_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 void _tests_reverse (void) { cairo_test_t *list, *next; for (list = tests, tests = NULL; list != NULL; list = next) { next = list->next; list->next = tests; tests = list; } } 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) { #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)); default: return _cairo_test_wait (pid); } } #endif return _cairo_test_context_run_for_target (ctx, target, similar, device_offset); } 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; if (str == NULL) return; old_argc = *argc; old_argv = *argv; doit = FALSE; do { if (doit) *argv = xmalloc (sizeof (char *) * (1 + *argc) + olen); olen = sizeof (char *) * (1 + *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'; } 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); } olen += len; i++; } } while (doit++ == FALSE); (*argv)[i] = NULL; *argc += i; } static void usage (const char *argv0) { fprintf (stderr, "Usage: %s [-afkxsl] [test-names|keywords ...]\n" " %s -l\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" " -s include slow, long running tests\n" " -x exit on first failure\n" " -l list only; just list selected test case names without executing\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" "\"cairo-test-suite -k text\" can be used to run all text test cases, and\n" "\"cairo-test-suite text-transform\" to run the individual case.\n", argv0, argv0); } static void _parse_cmdline (cairo_test_runner_t *runner, int *argc, char **argv[]) { int c; while (1) { c = _cairo_getopt (*argc, *argv, ":afkxsl"); if (c == -1) break; switch (c) { case 'a': runner->full_test = TRUE; break; case 's': runner->slow = TRUE; break; case 'l': runner->list_only = TRUE; break; case 'f': runner->foreground = TRUE; break; case 'x': runner->exit_on_failure = TRUE; break; case 'k': runner->keyword_match = 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, µ)) { 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; } int main (int argc, char **argv) { cairo_test_runner_t runner; cairo_test_t *test; cairo_test_status_t *target_status; unsigned int n, m; char targets[4096]; int len; #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_reverse (); memset (&runner, 0, sizeof (runner)); runner.num_device_offsets = 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 = TRUE; } 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); append_argv (&argc, &argv, getenv ("CAIRO_TESTS")); if (runner.full_test) { runner.num_device_offsets = 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 = tests; test != NULL; test = test->next) { 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 ? cairo_test_target_has_similar (&ctx, target) : DIRECT; for (m = 0; m < runner.num_device_offsets; m++) { int dev_offset = m * 25; cairo_test_similar_t similar; for (similar = DIRECT; similar <= has_similar; similar++) { status = _cairo_test_runner_draw (&runner, &ctx, target, similar, dev_offset); 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 (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); free (target_status); return _runner_fini (&runner); } void cairo_test_register (cairo_test_t *test) { test->next = tests; tests = test; }