/* * Copyright © 2004 Red Hat, Inc. * * 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 * Red Hat, Inc. not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Red Hat, Inc. makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL RED HAT, INC. 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: Carl D. Worth */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #if HAVE_FCFINI #include #endif #include "cairo-test.h" #include "buffer-diff.h" #include "read-png.h" #include "write-png.h" #include "xmalloc.h" /* This is copied from cairoint.h. That makes it painful to keep in * sync, but the slim stuff makes cairoint.h "hard" to include when * not actually building the cairo library itself. Fortunately, since * we're checking all these values, we do have a safeguard for keeping * them in sync. */ typedef enum cairo_internal_surface_type { CAIRO_INTERNAL_SURFACE_TYPE_META = 0x1000, CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED } cairo_internal_surface_type_t; #ifdef _MSC_VER #define vsnprintf _vsnprintf #define access _access #define F_OK 0 #endif static void xunlink (const char *pathname); #define CAIRO_TEST_LOG_SUFFIX ".log" #define CAIRO_TEST_PNG_SUFFIX "-out.png" #define CAIRO_TEST_REF_SUFFIX "-ref.png" #define CAIRO_TEST_DIFF_SUFFIX "-diff.png" /* A fake format we use for the flattened ARGB output of the PS and * PDF surfaces. */ #define CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED -1 /* Static data is messy, but we're coding for tests here, not a * general-purpose library, and it keeps the tests cleaner to avoid a * context object there, (though not a whole lot). */ FILE *cairo_test_log_file; void cairo_test_init (const char *test_name) { char *log_name; xasprintf (&log_name, "%s%s", test_name, CAIRO_TEST_LOG_SUFFIX); xunlink (log_name); cairo_test_log_file = fopen (log_name, "a"); if (cairo_test_log_file == NULL) { fprintf (stderr, "Error opening log file: %s\n", log_name); cairo_test_log_file = stderr; } free (log_name); } void cairo_test_log (const char *fmt, ...) { va_list va; va_start (va, fmt); vfprintf (cairo_test_log_file, fmt, va); va_end (va); } void xasprintf (char **strp, const char *fmt, ...) { #ifdef HAVE_VASPRINTF va_list va; int ret; va_start (va, fmt); ret = vasprintf (strp, fmt, va); va_end (va); if (ret < 0) { cairo_test_log ("Out of memory\n"); exit (1); } #else /* !HAVE_VASNPRINTF */ #define BUF_SIZE 1024 va_list va; char buffer[BUF_SIZE]; int ret; va_start (va, fmt); ret = vsnprintf (buffer, sizeof(buffer), fmt, va); va_end (va); if (ret < 0) { cairo_test_log ("Failure in vsnprintf\n"); exit (1); } if (strlen (buffer) == sizeof(buffer) - 1) { cairo_test_log ("Overflowed fixed buffer\n"); exit (1); } *strp = strdup (buffer); if (!*strp) { cairo_test_log ("Out of memory\n"); exit (1); } #endif /* !HAVE_VASNPRINTF */ } static void xunlink (const char *pathname) { if (unlink (pathname) < 0 && errno != ENOENT) { cairo_test_log (" Error: Cannot remove %s: %s\n", pathname, strerror (errno)); exit (1); } } typedef cairo_surface_t * (*cairo_test_create_target_surface_t) (cairo_test_t *test, cairo_content_t content, void **closure); typedef cairo_status_t (*cairo_test_write_to_png_t) (cairo_surface_t *surface, const char *filename); typedef void (*cairo_test_cleanup_target_t) (void *closure); typedef struct _cairo_test_target { const char *name; cairo_surface_type_t expected_type; cairo_content_t content; cairo_test_create_target_surface_t create_target_surface; cairo_test_write_to_png_t write_to_png; cairo_test_cleanup_target_t cleanup_target; void *closure; } cairo_test_target_t; static char * _cairo_test_content_name (cairo_content_t content) { switch (content) { case CAIRO_CONTENT_COLOR: return "rgb24"; case CAIRO_CONTENT_COLOR_ALPHA: case CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED: return "argb32"; default: assert (0); /* not reached */ return "---"; } } static cairo_surface_t * create_image_surface (cairo_test_t *test, cairo_content_t content, void **closure) { cairo_format_t format; *closure = NULL; if (content == CAIRO_CONTENT_COLOR_ALPHA) { format = CAIRO_FORMAT_ARGB32; } else if (content == CAIRO_CONTENT_COLOR) { format = CAIRO_FORMAT_RGB24; } else { assert (0); /* not reached */ return NULL; } return cairo_image_surface_create (format, test->width, test->height); } #ifdef CAIRO_HAS_TEST_SURFACES #include "test-fallback-surface.h" #include "test-meta-surface.h" #include "test-paginated-surface.h" static cairo_surface_t * create_test_fallback_surface (cairo_test_t *test, cairo_content_t content, void **closure) { *closure = NULL; return _test_fallback_surface_create (content, test->width, test->height); } static cairo_surface_t * create_test_meta_surface (cairo_test_t *test, cairo_content_t content, void **closure) { *closure = NULL; return _test_meta_surface_create (content, test->width, test->height); } static const cairo_user_data_key_t test_paginated_closure_key; typedef struct { unsigned char *data; cairo_content_t content; int width; int height; int stride; } test_paginated_closure_t; static cairo_surface_t * create_test_paginated_surface (cairo_test_t *test, cairo_content_t content, void **closure) { test_paginated_closure_t *tpc; cairo_surface_t *surface; *closure = tpc = xmalloc (sizeof (test_paginated_closure_t)); tpc->content = content; tpc->width = test->width; tpc->height = test->height; tpc->stride = test->width * 4; tpc->data = xcalloc (tpc->stride * test->height, 1); surface = _test_paginated_surface_create_for_data (tpc->data, tpc->content, tpc->width, tpc->height, tpc->stride); cairo_surface_set_user_data (surface, &test_paginated_closure_key, tpc, NULL); return surface; } /* The only reason we go through all these machinations to write a PNG * image is to _really ensure_ that the data actually landed in our * buffer through the paginated surface to the test_paginated_surface. * * If we didn't implement this function then the default * cairo_surface_write_to_png would result in the paginated_surface's * acquire_source_image function replaying the meta-surface to an * intermediate image surface. And in that case the * test_paginated_surface would not be involved and wouldn't be * tested. */ static cairo_status_t test_paginated_write_to_png (cairo_surface_t *surface, const char *filename) { cairo_surface_t *image; cairo_format_t format; test_paginated_closure_t *tpc; tpc = cairo_surface_get_user_data (surface, &test_paginated_closure_key); switch (tpc->content) { case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break; case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break; default: assert (0); /* not reached */ return CAIRO_STATUS_NO_MEMORY; } image = cairo_image_surface_create_for_data (tpc->data, format, tpc->width, tpc->height, tpc->stride); cairo_surface_write_to_png (image, filename); cairo_surface_destroy (image); return CAIRO_STATUS_SUCCESS; } static void cleanup_test_paginated (void *closure) { test_paginated_closure_t *tpc = closure; free (tpc->data); free (tpc); } #endif #ifdef CAIRO_HAS_GLITZ_SURFACE #include #include static const cairo_user_data_key_t glitz_closure_key; typedef struct _glitz_target_closure_base { int width; int height; cairo_content_t content; } glitz_target_closure_base_t; #if CAIRO_CAN_TEST_GLITZ_GLX_SURFACE #include typedef struct _glitz_glx_target_closure { glitz_target_closure_base_t base; Display *dpy; int scr; Window win; } glitz_glx_target_closure_t; static glitz_surface_t * create_glitz_glx_surface (glitz_format_name_t formatname, int width, int height, glitz_glx_target_closure_t *closure) { Display * dpy = closure->dpy; int scr = closure->scr; glitz_drawable_format_t templ; glitz_drawable_format_t * dformat = NULL; unsigned long mask; glitz_drawable_t * drawable = NULL; glitz_format_t * format; glitz_surface_t * sr; XSizeHints xsh; XSetWindowAttributes xswa; XVisualInfo * vinfo; memset(&templ, 0, sizeof(templ)); templ.color.red_size = 8; templ.color.green_size = 8; templ.color.blue_size = 8; templ.color.alpha_size = 8; templ.color.fourcc = GLITZ_FOURCC_RGB; templ.samples = 1; glitz_glx_init (NULL); mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_FOURCC_MASK | GLITZ_FORMAT_RED_SIZE_MASK | GLITZ_FORMAT_GREEN_SIZE_MASK | GLITZ_FORMAT_BLUE_SIZE_MASK; if (formatname == GLITZ_STANDARD_ARGB32) mask |= GLITZ_FORMAT_ALPHA_SIZE_MASK; /* Try for a pbuffer first */ if (!getenv("CAIRO_TEST_FORCE_GLITZ_WINDOW")) dformat = glitz_glx_find_pbuffer_format (dpy, scr, mask, &templ, 0); if (dformat) { closure->win = None; drawable = glitz_glx_create_pbuffer_drawable (dpy, scr, dformat, width, height); if (!drawable) goto FAIL; } else { /* No pbuffer, try window */ dformat = glitz_glx_find_window_format (dpy, scr, mask, &templ, 0); if (!dformat) goto FAIL; vinfo = glitz_glx_get_visual_info_from_format(dpy, DefaultScreen(dpy), dformat); if (!vinfo) goto FAIL; xsh.flags = PSize; xsh.x = 0; xsh.y = 0; xsh.width = width; xsh.height = height; xswa.colormap = XCreateColormap (dpy, RootWindow(dpy, scr), vinfo->visual, AllocNone); closure->win = XCreateWindow (dpy, RootWindow(dpy, scr), xsh.x, xsh.y, xsh.width, xsh.height, 0, vinfo->depth, CopyFromParent, vinfo->visual, CWColormap, &xswa); XFree (vinfo); drawable = glitz_glx_create_drawable_for_window (dpy, scr, dformat, closure->win, width, height); if (!drawable) goto DESTROY_WINDOW; } format = glitz_find_standard_format (drawable, formatname); if (!format) goto DESTROY_DRAWABLE; sr = glitz_surface_create (drawable, format, width, height, 0, NULL); if (!sr) goto DESTROY_DRAWABLE; if (closure->win == None || dformat->doublebuffer) { glitz_surface_attach (sr, drawable, GLITZ_DRAWABLE_BUFFER_BACK_COLOR); } else { XMapWindow (closure->dpy, closure->win); glitz_surface_attach (sr, drawable, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); } glitz_drawable_destroy (drawable); return sr; DESTROY_DRAWABLE: glitz_drawable_destroy (drawable); DESTROY_WINDOW: if (closure->win) XDestroyWindow (dpy, closure->win); FAIL: return NULL; } static cairo_surface_t * create_cairo_glitz_glx_surface (cairo_test_t *test, cairo_content_t content, void **closure) { int width = test->width; int height = test->height; glitz_glx_target_closure_t *gxtc; glitz_surface_t * glitz_surface; cairo_surface_t * surface; *closure = gxtc = xmalloc (sizeof (glitz_glx_target_closure_t)); if (width == 0) width = 1; if (height == 0) height = 1; gxtc->dpy = XOpenDisplay (getenv("CAIRO_TEST_GLITZ_DISPLAY")); if (!gxtc->dpy) { cairo_test_log ("Failed to open display: %s\n", XDisplayName(0)); goto FAIL; } XSynchronize (gxtc->dpy, 1); gxtc->scr = DefaultScreen(gxtc->dpy); switch (content) { case CAIRO_CONTENT_COLOR: glitz_surface = create_glitz_glx_surface (GLITZ_STANDARD_RGB24, width, height, gxtc); break; case CAIRO_CONTENT_COLOR_ALPHA: glitz_surface = create_glitz_glx_surface (GLITZ_STANDARD_ARGB32, width, height, gxtc); break; default: cairo_test_log ("Invalid content for glitz-glx test: %d\n", content); goto FAIL_CLOSE_DISPLAY; } if (!glitz_surface) { cairo_test_log ("Failed to create glitz-glx surface\n"); goto FAIL_CLOSE_DISPLAY; } surface = cairo_glitz_surface_create (glitz_surface); gxtc->base.width = test->width; gxtc->base.height = test->height; gxtc->base.content = content; cairo_surface_set_user_data (surface, &glitz_closure_key, gxtc, NULL); return surface; FAIL_CLOSE_DISPLAY: XCloseDisplay (gxtc->dpy); FAIL: return NULL; } static void cleanup_cairo_glitz_glx (void *closure) { glitz_glx_target_closure_t *gxtc = closure; glitz_glx_fini (); if (gxtc->win) XDestroyWindow (gxtc->dpy, gxtc->win); XCloseDisplay (gxtc->dpy); free (gxtc); } #endif /* CAIRO_CAN_TEST_GLITZ_GLX_SURFACE */ #if CAIRO_CAN_TEST_GLITZ_AGL_SURFACE #include typedef struct _glitz_agl_target_closure { glitz_target_closure_base_t base; } glitz_agl_target_closure_t; static glitz_surface_t * create_glitz_agl_surface (glitz_format_name_t formatname, int width, int height, glitz_agl_target_closure_t *closure) { glitz_drawable_format_t *dformat; glitz_drawable_format_t templ; glitz_drawable_t *gdraw; glitz_format_t *format; glitz_surface_t *sr = NULL; unsigned long mask; memset(&templ, 0, sizeof(templ)); templ.color.red_size = 8; templ.color.green_size = 8; templ.color.blue_size = 8; templ.color.alpha_size = 8; templ.color.fourcc = GLITZ_FOURCC_RGB; templ.samples = 1; mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_FOURCC_MASK | GLITZ_FORMAT_RED_SIZE_MASK | GLITZ_FORMAT_GREEN_SIZE_MASK | GLITZ_FORMAT_BLUE_SIZE_MASK; if (formatname == GLITZ_STANDARD_ARGB32) mask |= GLITZ_FORMAT_ALPHA_SIZE_MASK; dformat = glitz_agl_find_pbuffer_format (mask, &templ, 0); if (!dformat) { cairo_test_log ("Glitz failed to find pbuffer format for template."); goto FAIL; } gdraw = glitz_agl_create_pbuffer_drawable (dformat, width, height); if (!gdraw) { cairo_test_log ("Glitz failed to create pbuffer drawable."); goto FAIL; } format = glitz_find_standard_format (gdraw, formatname); if (!format) { cairo_test_log ("Glitz failed to find standard format for drawable."); goto DESTROY_DRAWABLE; } sr = glitz_surface_create (gdraw, format, width, height, 0, NULL); if (!sr) { cairo_test_log ("Glitz failed to create a surface."); goto DESTROY_DRAWABLE; } glitz_surface_attach (sr, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); DESTROY_DRAWABLE: glitz_drawable_destroy (gdraw); FAIL: return sr; /* will be NULL unless we create it and attach */ } static cairo_surface_t * create_cairo_glitz_agl_surface (cairo_test_t *test, cairo_content_t content, void **closure) { glitz_surface_t *glitz_surface; cairo_surface_t *surface; glitz_agl_target_closure_t *aglc; glitz_agl_init (); *closure = aglc = xmalloc (sizeof (glitz_agl_target_closure_t)); switch (content) { case CAIRO_CONTENT_COLOR: glitz_surface = create_glitz_agl_surface (GLITZ_STANDARD_RGB24, test->width, test->height, NULL); break; case CAIRO_CONTENT_COLOR_ALPHA: glitz_surface = create_glitz_agl_surface (GLITZ_STANDARD_ARGB32, test->width, test->height, NULL); break; default: cairo_test_log ("Invalid content for glitz-agl test: %d\n", content); goto FAIL; } if (!glitz_surface) goto FAIL; surface = cairo_glitz_surface_create (glitz_surface); aglc->base.width = test->width; aglc->base.height = test->height; aglc->base.content = content; cairo_surface_set_user_data (surface, &glitz_closure_key, aglc, NULL); return surface; FAIL: return NULL; } static void cleanup_cairo_glitz_agl (void *closure) { free (closure); glitz_agl_fini (); } #endif /* CAIRO_CAN_TEST_GLITZ_AGL_SURFACE */ #if CAIRO_CAN_TEST_GLITZ_WGL_SURFACE #include typedef struct _glitz_wgl_target_closure { glitz_target_closure_base_t base; } glitz_wgl_target_closure_t; static glitz_surface_t * create_glitz_wgl_surface (glitz_format_name_t formatname, int width, int height, glitz_wgl_target_closure_t *closure) { glitz_drawable_format_t *dformat; glitz_drawable_format_t templ; glitz_drawable_t *gdraw; glitz_format_t *format; glitz_surface_t *sr = NULL; unsigned long mask; memset(&templ, 0, sizeof(templ)); templ.color.red_size = 8; templ.color.green_size = 8; templ.color.blue_size = 8; templ.color.alpha_size = 8; templ.color.fourcc = GLITZ_FOURCC_RGB; templ.samples = 1; mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_FOURCC_MASK | GLITZ_FORMAT_RED_SIZE_MASK | GLITZ_FORMAT_GREEN_SIZE_MASK | GLITZ_FORMAT_BLUE_SIZE_MASK; if (formatname == GLITZ_STANDARD_ARGB32) mask |= GLITZ_FORMAT_ALPHA_SIZE_MASK; dformat = glitz_wgl_find_pbuffer_format (mask, &templ, 0); if (!dformat) { cairo_test_log ("Glitz failed to find pbuffer format for template."); goto FAIL; } gdraw = glitz_wgl_create_pbuffer_drawable (dformat, width, height); if (!gdraw) { cairo_test_log ("Glitz failed to create pbuffer drawable."); goto FAIL; } format = glitz_find_standard_format (gdraw, formatname); if (!format) { cairo_test_log ("Glitz failed to find standard format for drawable."); goto DESTROY_DRAWABLE; } sr = glitz_surface_create (gdraw, format, width, height, 0, NULL); if (!sr) { cairo_test_log ("Glitz failed to create a surface."); goto DESTROY_DRAWABLE; } glitz_surface_attach (sr, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); DESTROY_DRAWABLE: glitz_drawable_destroy (gdraw); FAIL: return sr; /* will be NULL unless we create it and attach */ } static cairo_surface_t * create_cairo_glitz_wgl_surface (cairo_test_t *test, cairo_content_t content, void **closure) { glitz_surface_t *glitz_surface; cairo_surface_t *surface; glitz_wgl_target_closure_t *wglc; glitz_wgl_init (NULL); *closure = wglc = xmalloc (sizeof (glitz_wgl_target_closure_t)); switch (content) { case CAIRO_CONTENT_COLOR: glitz_surface = create_glitz_wgl_surface (GLITZ_STANDARD_RGB24, test->width, test->height, NULL); break; case CAIRO_CONTENT_COLOR_ALPHA: glitz_surface = create_glitz_wgl_surface (GLITZ_STANDARD_ARGB32, test->width, test->height, NULL); break; default: cairo_test_log ("Invalid content for glitz-wgl test: %d\n", content); goto FAIL; } if (!glitz_surface) goto FAIL; surface = cairo_glitz_surface_create (glitz_surface); wglc->base.width = test->width; wglc->base.height = test->height; wglc->base.content = content; cairo_surface_set_user_data (surface, &glitz_closure_key, wglc, NULL); return surface; FAIL: return NULL; } static void cleanup_cairo_glitz_wgl (void *closure) { free (closure); glitz_wgl_fini (); } #endif /* CAIRO_CAN_TEST_GLITZ_WGL_SURFACE */ #endif /* CAIRO_HAS_GLITZ_SURFACE */ #if 0 && CAIRO_HAS_QUARTZ_SURFACE static cairo_surface_t * create_quartz_surface (int width, int height, void **closure) { #error Not yet implemented } static void cleanup_quartz (void *closure) { #error Not yet implemented } #endif /* Testing the win32 surface isn't interesting, since for * ARGB images it just chains to the image backend */ #if CAIRO_HAS_WIN32_SURFACE #include "cairo-win32.h" typedef struct _win32_target_closure { HDC dc; HBITMAP bmp; } win32_target_closure_t; static cairo_surface_t * create_win32_surface (cairo_test_t *test, cairo_content_t content, void **closure) { int width = test->width; int height = test->height; BITMAPINFO bmpInfo; unsigned char *bits = NULL; win32_target_closure_t *data = malloc(sizeof(win32_target_closure_t)); *closure = data; data->dc = CreateCompatibleDC(NULL); /* initialize the bitmapinfoheader */ memset(&bmpInfo.bmiHeader, 0, sizeof(BITMAPINFOHEADER)); bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = width; bmpInfo.bmiHeader.biHeight = -height; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 24; bmpInfo.bmiHeader.biCompression = BI_RGB; /* create a DIBSection */ data->bmp = CreateDIBSection(data->dc, &bmpInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0); /* Flush GDI to make sure the DIBSection is actually created */ GdiFlush(); /* Select the bitmap in to the DC */ SelectObject(data->dc, data->bmp); return cairo_win32_surface_create(data->dc); } static void cleanup_win32 (void *closure) { win32_target_closure_t *data = (win32_target_closure_t*)closure; DeleteObject(data->bmp); DeleteDC(data->dc); free(closure); } #endif #if CAIRO_HAS_XCB_SURFACE #include "cairo-xcb-xrender.h" typedef struct _xcb_target_closure { XCBConnection *c; XCBDRAWABLE drawable; } xcb_target_closure_t; /* XXX: This is a nasty hack. Something like this should be in XCB's * bindings for Render, not here in this test. */ static XCBRenderPICTFORMINFO _format_from_cairo(XCBConnection *c, cairo_format_t fmt) { XCBRenderPICTFORMINFO ret = {{ 0 }}; struct tmpl_t { XCBRenderDIRECTFORMAT direct; CARD8 depth; }; static const struct tmpl_t templates[] = { /* CAIRO_FORMAT_ARGB32 */ { { 16, 0xff, 8, 0xff, 0, 0xff, 24, 0xff }, 32 }, /* CAIRO_FORMAT_RGB24 */ { { 16, 0xff, 8, 0xff, 0, 0xff, 0, 0x00 }, 24 }, /* CAIRO_FORMAT_A8 */ { { 0, 0x00, 0, 0x00, 0, 0x00, 0, 0xff }, 8 }, /* CAIRO_FORMAT_A1 */ { { 0, 0x00, 0, 0x00, 0, 0x00, 0, 0x01 }, 1 }, }; const struct tmpl_t *tmpl; XCBRenderQueryPictFormatsRep *r; XCBRenderPICTFORMINFOIter fi; if(fmt < 0 || fmt >= (sizeof(templates) / sizeof(*templates))) return ret; tmpl = templates + fmt; r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); if(!r) return ret; for(fi = XCBRenderQueryPictFormatsFormatsIter(r); fi.rem; XCBRenderPICTFORMINFONext(&fi)) { const XCBRenderDIRECTFORMAT *t, *f; if(fi.data->type != XCBRenderPictTypeDirect) continue; if(fi.data->depth != tmpl->depth) continue; t = &tmpl->direct; f = &fi.data->direct; if(t->red_mask && (t->red_mask != f->red_mask || t->red_shift != f->red_shift)) continue; if(t->green_mask && (t->green_mask != f->green_mask || t->green_shift != f->green_shift)) continue; if(t->blue_mask && (t->blue_mask != f->blue_mask || t->blue_shift != f->blue_shift)) continue; if(t->alpha_mask && (t->alpha_mask != f->alpha_mask || t->alpha_shift != f->alpha_shift)) continue; ret = *fi.data; } free(r); return ret; } static cairo_surface_t * create_xcb_surface (int width, int height, void **closure) { XCBSCREEN *root; xcb_target_closure_t *xtc; cairo_surface_t *surface; XCBConnection *c; XCBRenderPICTFORMINFO render_format; cairo_format_t format; *closure = xtc = xmalloc (sizeof (xcb_target_closure_t)); if (width == 0) width = 1; if (height == 0) height = 1; xtc->c = c = XCBConnectBasic(); if (c == NULL) { cairo_test_log ("Failed to connect to X server through XCB\n"); return NULL; } root = XCBConnSetupSuccessRepRootsIter(XCBGetSetup(c)).data; xtc->drawable.pixmap = XCBPIXMAPNew (c); { XCBDRAWABLE root_drawable; root_drawable.window = root->root; XCBCreatePixmap (c, 32, xtc->drawable.pixmap, root_drawable, width, height); } switch (content) { case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break; case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break; default: cairo_test_log ("Invalid content for XCB test: %d\n", content); return NULL; } render_format = _format_from_cairo (c, format); if (render_format.id.xid == 0) return NULL; surface = cairo_xcb_surface_create_with_xrender_format (c, xtc->drawable, &render_format, width, height); return surface; } static void cleanup_xcb (void *closure) { xcb_target_closure_t *xtc = closure; XCBFreePixmap (xtc->c, xtc->drawable.pixmap); XCBDisconnect (xtc->c); free (xtc); } #endif #if CAIRO_HAS_XLIB_SURFACE #include "cairo-xlib-xrender.h" typedef struct _xlib_target_closure { Display *dpy; Pixmap pixmap; } xlib_target_closure_t; static cairo_surface_t * create_xlib_surface (cairo_test_t *test, cairo_content_t content, void **closure) { int width = test->width; int height = test->height; xlib_target_closure_t *xtc; cairo_surface_t *surface; Display *dpy; XRenderPictFormat *xrender_format; *closure = xtc = xmalloc (sizeof (xlib_target_closure_t)); if (width == 0) width = 1; if (height == 0) height = 1; xtc->dpy = dpy = XOpenDisplay (NULL); if (xtc->dpy == NULL) { cairo_test_log ("Failed to open display: %s\n", XDisplayName(0)); return NULL; } XSynchronize (xtc->dpy, 1); /* XXX: Currently we don't do any xlib testing when the X server * doesn't have the Render extension. We could do better here, * (perhaps by converting the tests from ARGB32 to RGB24). One * step better would be to always test the non-Render fallbacks * for each test even if the server does have the Render * extension. That would probably be through another * cairo_test_target which would use an extended version of * cairo_test_xlib_disable_render. */ switch (content) { case CAIRO_CONTENT_COLOR_ALPHA: xrender_format = XRenderFindStandardFormat (dpy, PictStandardARGB32); break; case CAIRO_CONTENT_COLOR: xrender_format = XRenderFindStandardFormat (dpy, PictStandardRGB24); break; default: cairo_test_log ("Invalid content for xlib test: %d\n", content); return NULL; } if (xrender_format == NULL) { cairo_test_log ("X server does not have the Render extension.\n"); return NULL; } xtc->pixmap = XCreatePixmap (dpy, DefaultRootWindow (dpy), width, height, xrender_format->depth); surface = cairo_xlib_surface_create_with_xrender_format (dpy, xtc->pixmap, DefaultScreenOfDisplay (dpy), xrender_format, width, height); return surface; } static void cleanup_xlib (void *closure) { xlib_target_closure_t *xtc = closure; XFreePixmap (xtc->dpy, xtc->pixmap); XCloseDisplay (xtc->dpy); free (xtc); } #endif #if CAIRO_HAS_BEOS_SURFACE /* BeOS test functions are external as they need to be C++ */ #include "cairo-test-beos.h" #endif #if CAIRO_HAS_DIRECTFB_SURFACE #include "cairo-test-directfb.h" #endif #if CAIRO_HAS_PS_SURFACE #include "cairo-ps.h" cairo_user_data_key_t ps_closure_key; typedef struct _ps_target_closure { char *filename; int width; int height; cairo_surface_t *target; } ps_target_closure_t; static cairo_surface_t * create_ps_surface (cairo_test_t *test, cairo_content_t content, void **closure) { int width = test->width; int height = test->height; ps_target_closure_t *ptc; cairo_surface_t *surface; /* Sanitize back to a real cairo_content_t value. */ if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED) content = CAIRO_CONTENT_COLOR_ALPHA; *closure = ptc = xmalloc (sizeof (ps_target_closure_t)); xasprintf (&ptc->filename, "%s-ps-%s-out.ps", test->name, _cairo_test_content_name (content)); ptc->width = width; ptc->height = height; surface = cairo_ps_surface_create (ptc->filename, width, height); if (cairo_surface_status (surface)) { free (ptc->filename); free (ptc); return NULL; } cairo_ps_surface_set_dpi (surface, 72., 72.); if (content == CAIRO_CONTENT_COLOR) { ptc->target = surface; surface = cairo_surface_create_similar (ptc->target, CAIRO_CONTENT_COLOR, width, height); } else { ptc->target = NULL; } cairo_surface_set_user_data (surface, &ps_closure_key, ptc, NULL); return surface; } static cairo_status_t ps_surface_write_to_png (cairo_surface_t *surface, const char *filename) { ps_target_closure_t *ptc = cairo_surface_get_user_data (surface, &ps_closure_key); char command[4096]; if (ptc->target) { cairo_t *cr; cr = cairo_create (ptc->target); cairo_set_source_surface (cr, surface, 0, 0); cairo_paint (cr); cairo_show_page (cr); cairo_destroy (cr); cairo_surface_finish (surface); surface = ptc->target; } cairo_surface_finish (surface); sprintf (command, "gs -q -r72 -g%dx%d -dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -sOutputFile=%s %s", ptc->width, ptc->height, filename, ptc->filename); if (system (command) == 0) return CAIRO_STATUS_SUCCESS; return CAIRO_STATUS_WRITE_ERROR; } static void cleanup_ps (void *closure) { ps_target_closure_t *ptc = closure; free (ptc->filename); free (ptc); } #endif /* CAIRO_HAS_PS_SURFACE */ #if CAIRO_HAS_PDF_SURFACE && CAIRO_CAN_TEST_PDF_SURFACE #include "cairo-pdf.h" cairo_user_data_key_t pdf_closure_key; typedef struct _pdf_target_closure { char *filename; int width; int height; cairo_surface_t *target; } pdf_target_closure_t; static cairo_surface_t * create_pdf_surface (cairo_test_t *test, cairo_content_t content, void **closure) { int width = test->width; int height = test->height; pdf_target_closure_t *ptc; cairo_surface_t *surface; /* Sanitizie back to a real cairo_content_t value. */ if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED) content = CAIRO_CONTENT_COLOR_ALPHA; *closure = ptc = xmalloc (sizeof (pdf_target_closure_t)); ptc->width = width; ptc->height = height; xasprintf (&ptc->filename, "%s-pdf-%s-out.pdf", test->name, _cairo_test_content_name (content)); surface = cairo_pdf_surface_create (ptc->filename, width, height); if (cairo_surface_status (surface)) { free (ptc->filename); free (ptc); return NULL; } cairo_pdf_surface_set_dpi (surface, 72., 72.); if (content == CAIRO_CONTENT_COLOR) { ptc->target = surface; surface = cairo_surface_create_similar (ptc->target, CAIRO_CONTENT_COLOR, width, height); } else { ptc->target = NULL; } cairo_surface_set_user_data (surface, &pdf_closure_key, ptc, NULL); return surface; } static cairo_status_t pdf_surface_write_to_png (cairo_surface_t *surface, const char *filename) { pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface, &pdf_closure_key); char command[4096]; if (ptc->target) { cairo_t *cr; cr = cairo_create (ptc->target); cairo_set_source_surface (cr, surface, 0, 0); cairo_paint (cr); cairo_show_page (cr); cairo_destroy (cr); cairo_surface_finish (surface); surface = ptc->target; } cairo_surface_finish (surface); sprintf (command, "./pdf2png %s %s 1", ptc->filename, filename); if (system (command) != 0) return CAIRO_STATUS_WRITE_ERROR; return CAIRO_STATUS_SUCCESS; } static void cleanup_pdf (void *closure) { pdf_target_closure_t *ptc = closure; free (ptc->filename); free (ptc); } #endif /* CAIRO_HAS_PDF_SURFACE && CAIRO_CAN_TEST_PDF_SURFACE */ #if CAIRO_HAS_SVG_SURFACE && CAIRO_CAN_TEST_SVG_SURFACE #include "cairo-svg.h" cairo_user_data_key_t svg_closure_key; typedef struct _svg_target_closure { char *filename; int width, height; } svg_target_closure_t; static cairo_surface_t * create_svg_surface (cairo_test_t *test, cairo_content_t content, void **closure) { int width = test->width; int height = test->height; svg_target_closure_t *ptc; cairo_surface_t *surface; *closure = ptc = xmalloc (sizeof (svg_target_closure_t)); ptc->width = width; ptc->height = height; xasprintf (&ptc->filename, "%s-svg-%s-out.svg", test->name, _cairo_test_content_name (content)); surface = cairo_svg_surface_create (ptc->filename, width, height); if (cairo_surface_status (surface)) { free (ptc->filename); free (ptc); return NULL; } cairo_surface_set_user_data (surface, &svg_closure_key, ptc, NULL); return surface; } static cairo_status_t svg_surface_write_to_png (cairo_surface_t *surface, const char *filename) { svg_target_closure_t *ptc = cairo_surface_get_user_data (surface, &svg_closure_key); char command[4096]; cairo_surface_finish (surface); sprintf (command, "./svg2png %s %s", ptc->filename, filename); if (system (command) != 0) return CAIRO_STATUS_WRITE_ERROR; return CAIRO_STATUS_WRITE_ERROR; } static void cleanup_svg (void *closure) { svg_target_closure_t *ptc = closure; free (ptc->filename); free (ptc); } #endif /* CAIRO_HAS_SVG_SURFACE && CAIRO_CAN_TEST_SVG_SURFACE */ static cairo_test_status_t cairo_test_for_target (cairo_test_t *test, cairo_test_draw_function_t draw, cairo_test_target_t *target) { cairo_test_status_t status; cairo_surface_t *surface; cairo_t *cr; char *png_name, *ref_name, *diff_name; char *srcdir; char *format; cairo_test_status_t ret; /* Get the strings ready that we'll need. */ srcdir = getenv ("srcdir"); if (!srcdir) srcdir = "."; format = _cairo_test_content_name (target->content); xasprintf (&png_name, "%s-%s-%s%s", test->name, target->name, format, CAIRO_TEST_PNG_SUFFIX); xasprintf (&ref_name, "%s/%s-%s-%s%s", srcdir, test->name, target->name, format, CAIRO_TEST_REF_SUFFIX); if (access (ref_name, F_OK) != 0) { free (ref_name); if (target->content == CAIRO_CONTENT_COLOR_ALPHA || target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED) xasprintf (&ref_name, "%s/%s%s", srcdir, test->name, CAIRO_TEST_REF_SUFFIX); else xasprintf (&ref_name, "%s/%s-%s%s", srcdir, test->name, format,CAIRO_TEST_REF_SUFFIX); } xasprintf (&diff_name, "%s-%s-%s%s", test->name, target->name, format, CAIRO_TEST_DIFF_SUFFIX); /* Run the actual drawing code. */ surface = (target->create_target_surface) (test, target->content, &target->closure); if (surface == NULL) { cairo_test_log ("Error: Failed to set %s target\n", target->name); ret = CAIRO_TEST_UNTESTED; goto UNWIND_STRINGS; } if (cairo_surface_get_type (surface) != target->expected_type) { cairo_test_log ("Error: Created surface is of type %d (expected %d)\n", cairo_surface_get_type (surface), target->expected_type); ret = CAIRO_TEST_FAILURE; goto UNWIND_SURFACE; } cr = cairo_create (surface); /* Clear to transparent (or black) depending on whether the target * surface supports alpha. */ cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); status = (draw) (cr, test->width, test->height); /* Then, check all the different ways it could fail. */ if (status) { cairo_test_log ("Error: Function under test failed\n"); ret = status; goto UNWIND_CAIRO; } cairo_show_page (cr); if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) { cairo_test_log ("Error: Function under test left cairo status in an error state: %s\n", cairo_status_to_string (cairo_status (cr))); ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } /* Skip image check for tests with no image (width,height == 0,0) */ if (test->width != 0 && test->height != 0) { int pixels_changed; xunlink (png_name); (target->write_to_png) (surface, png_name); if (target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED) pixels_changed = image_diff_flattened (png_name, ref_name, diff_name); else pixels_changed = image_diff (png_name, ref_name, diff_name); if (pixels_changed) { if (pixels_changed > 0) cairo_test_log ("Error: %d pixels differ from reference image %s\n", pixels_changed, ref_name); ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } } ret = CAIRO_TEST_SUCCESS; UNWIND_CAIRO: cairo_destroy (cr); UNWIND_SURFACE: cairo_surface_destroy (surface); cairo_debug_reset_static_data (); if (target->cleanup_target) target->cleanup_target (target->closure); UNWIND_STRINGS: free (png_name); free (ref_name); free (diff_name); return ret; } static cairo_test_status_t cairo_test_expecting (cairo_test_t *test, cairo_test_draw_function_t draw, cairo_test_status_t expectation) { int i, num_targets; const char *tname; cairo_test_status_t status, ret; cairo_test_target_t **targets_to_test; cairo_test_target_t targets[] = { { "image", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, create_image_surface, cairo_surface_write_to_png, NULL}, { "image", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, create_image_surface, cairo_surface_write_to_png, NULL}, #ifdef CAIRO_HAS_TEST_SURFACES { "test-fallback", CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, CAIRO_CONTENT_COLOR_ALPHA, create_test_fallback_surface, cairo_surface_write_to_png, NULL }, { "test-fallback", CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, CAIRO_CONTENT_COLOR, create_test_fallback_surface, cairo_surface_write_to_png, NULL }, { "test-meta", CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, CAIRO_CONTENT_COLOR_ALPHA, create_test_meta_surface, cairo_surface_write_to_png, NULL }, { "test-meta", CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, CAIRO_CONTENT_COLOR, create_test_meta_surface, cairo_surface_write_to_png, NULL }, { "test-paginated", CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, CAIRO_CONTENT_COLOR_ALPHA, create_test_paginated_surface, test_paginated_write_to_png, cleanup_test_paginated }, { "test-paginated", CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, CAIRO_CONTENT_COLOR, create_test_paginated_surface, test_paginated_write_to_png, cleanup_test_paginated }, #endif #ifdef CAIRO_HAS_GLITZ_SURFACE #if CAIRO_CAN_TEST_GLITZ_GLX_SURFACE { "glitz-glx", CAIRO_SURFACE_TYPE_GLITZ,CAIRO_CONTENT_COLOR_ALPHA, create_cairo_glitz_glx_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_glx }, { "glitz-glx", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, create_cairo_glitz_glx_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_glx }, #endif #if CAIRO_CAN_TEST_GLITZ_AGL_SURFACE { "glitz-agl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR_ALPHA, create_cairo_glitz_agl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_agl }, { "glitz-agl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, create_cairo_glitz_agl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_agl }, #endif #if CAIRO_CAN_TEST_GLITZ_WGL_SURFACE { "glitz-wgl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR_ALPHA, create_cairo_glitz_wgl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_wgl }, { "glitz-wgl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, create_cairo_glitz_wgl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_wgl }, #endif #endif /* CAIRO_HAS_GLITZ_SURFACE */ #if 0 && CAIRO_HAS_QUARTZ_SURFACE { "quartz", CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_CONTENT_COLOR, create_quartz_surface, cairo_surface_write_to_png, cleanup_quartz }, #endif #if CAIRO_HAS_WIN32_SURFACE { "win32", CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR, create_win32_surface, cairo_surface_write_to_png, cleanup_win32 }, #endif #if CAIRO_HAS_XCB_SURFACE { "xcb", CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR_ALPHA, create_xcb_surface, cairo_surface_write_to_png, cleanup_xcb}, #endif #if CAIRO_HAS_XLIB_SURFACE { "xlib", CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR_ALPHA, create_xlib_surface, cairo_surface_write_to_png, cleanup_xlib}, { "xlib", CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, create_xlib_surface, cairo_surface_write_to_png, cleanup_xlib}, #endif #if CAIRO_HAS_PS_SURFACE { "ps", CAIRO_SURFACE_TYPE_PS, CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, create_ps_surface, ps_surface_write_to_png, cleanup_ps }, /* XXX: We expect type image here only due to a limitation in * the current PS/meta-surface code. A PS surface is * "naturally" COLOR_ALPHA, so the COLOR-only variant goes * through create_similar in create_ps_surface which results * in the similar surface being used as a source. We do not yet * have source support for PS/meta-surfaces, so the * create_similar path for all paginated surfaces currently * returns an image surface.*/ { "ps", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, create_ps_surface, ps_surface_write_to_png, cleanup_ps }, #endif #if CAIRO_HAS_PDF_SURFACE && CAIRO_CAN_TEST_PDF_SURFACE { "pdf", CAIRO_SURFACE_TYPE_PDF, CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, create_pdf_surface, pdf_surface_write_to_png, cleanup_pdf }, /* XXX: We expect type image here only due to a limitation in * the current PDF/meta-surface code. A PDF surface is * "naturally" COLOR_ALPHA, so the COLOR-only variant goes * through create_similar in create_pdf_surface which results * in the similar surface being used as a source. We do not yet * have source support for PDF/meta-surfaces, so the * create_similar path for all paginated surfaces currently * returns an image surface.*/ { "pdf", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, create_pdf_surface, pdf_surface_write_to_png, cleanup_pdf }, #endif #if CAIRO_HAS_SVG_SURFACE && CAIRO_CAN_TEST_SVG_SURFACE { "svg", CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR, create_svg_surface, svg_surface_write_to_png, cleanup_svg }, { "svg", CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, create_svg_surface, svg_surface_write_to_png, cleanup_svg }, #endif #if CAIRO_HAS_BEOS_SURFACE { "beos", CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR, create_beos_surface, cairo_surface_write_to_png, cleanup_beos}, { "beos_bitmap", CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR, create_beos_bitmap_surface, cairo_surface_write_to_png, cleanup_beos_bitmap}, { "beos_bitmap", CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR_ALPHA, create_beos_bitmap_surface, cairo_surface_write_to_png, cleanup_beos_bitmap}, #endif #if CAIRO_HAS_DIRECTFB_SURFACE { "directfb", CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_CONTENT_COLOR, create_directfb_surface, cairo_surface_write_to_png, cleanup_directfb}, { "directfb_bitmap", CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_CONTENT_COLOR_ALPHA, create_directfb_bitmap_surface, cairo_surface_write_to_png,cleanup_directfb}, #endif }; if ((tname = getenv ("CAIRO_TEST_TARGET")) != NULL) { const char *tname = getenv ("CAIRO_TEST_TARGET"); num_targets = 0; targets_to_test = NULL; /* realloc isn't exactly the best thing here, but meh. */ for (i = 0; i < sizeof(targets)/sizeof(targets[0]); i++) { if (strcmp (targets[i].name, tname) == 0) { targets_to_test = realloc (targets_to_test, sizeof(cairo_test_target_t *) * (num_targets+1)); targets_to_test[num_targets++] = &targets[i]; } } if (num_targets == 0) { fprintf (stderr, "CAIRO_TEST_TARGET '%s' not found in targets list!\n", tname); exit(-1); } } else { num_targets = sizeof(targets)/sizeof(targets[0]); targets_to_test = malloc (sizeof(cairo_test_target_t*) * num_targets); for (i = 0; i < num_targets; i++) targets_to_test[i] = &targets[i]; } cairo_test_init (test->name); /* The intended logic here is that we return overall SUCCESS * iff. there is at least one tested backend and that all tested * backends return SUCCESS. In other words: * * if any backend FAILURE * -> FAILURE * else if all backends UNTESTED * -> FAILURE * else (== some backend SUCCESS) * -> SUCCESS */ ret = CAIRO_TEST_UNTESTED; for (i = 0; i < num_targets; i++) { cairo_test_target_t *target = targets_to_test[i]; cairo_test_log ("Testing %s with %s target\n", test->name, target->name); printf ("%s-%s-%s:\t", test->name, target->name, _cairo_test_content_name (target->content)); status = cairo_test_for_target (test, draw, target); cairo_test_log ("TEST: %s TARGET: %s FORMAT: %s RESULT: ", test->name, target->name, _cairo_test_content_name (target->content)); switch (status) { case CAIRO_TEST_SUCCESS: printf ("PASS\n"); cairo_test_log ("PASS\n"); if (ret == CAIRO_TEST_UNTESTED) ret = CAIRO_TEST_SUCCESS; break; case CAIRO_TEST_UNTESTED: printf ("UNTESTED\n"); cairo_test_log ("UNTESTED\n"); break; default: case CAIRO_TEST_FAILURE: if (expectation == CAIRO_TEST_FAILURE) { printf ("XFAIL\n"); cairo_test_log ("XFAIL\n"); } else { printf ("FAIL\n"); cairo_test_log ("FAIL\n"); } ret = status; break; } } if (ret == CAIRO_TEST_UNTESTED) ret = CAIRO_TEST_FAILURE; fclose (cairo_test_log_file); free (targets_to_test); #if HAVE_FCFINI FcFini (); #endif return ret; } cairo_test_status_t cairo_test_expect_failure (cairo_test_t *test, cairo_test_draw_function_t draw, const char *because) { printf ("\n%s is expected to fail:\n\t%s\n", test->name, because); return cairo_test_expecting (test, draw, CAIRO_TEST_FAILURE); } cairo_test_status_t cairo_test (cairo_test_t *test, cairo_test_draw_function_t draw) { printf ("\n"); return cairo_test_expecting (test, draw, CAIRO_TEST_SUCCESS); } cairo_surface_t * cairo_test_create_surface_from_png (const char *filename) { cairo_surface_t *image; char *srcdir = getenv ("srcdir"); image = cairo_image_surface_create_from_png (filename); if (cairo_surface_status(image)) { /* expect not found when running with srcdir != builddir * such as when 'make distcheck' is run */ if (srcdir) { char *srcdir_filename; xasprintf (&srcdir_filename, "%s/%s", srcdir, filename); image = cairo_image_surface_create_from_png (srcdir_filename); free (srcdir_filename); } if (cairo_surface_status(image)) return NULL; } return image; } cairo_pattern_t * cairo_test_create_pattern_from_png (const char *filename) { cairo_surface_t *image; cairo_pattern_t *pattern; image = cairo_test_create_surface_from_png (filename); pattern = cairo_pattern_create_for_surface (image); cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); cairo_surface_destroy (image); return pattern; }