diff --git a/tests/Imakefile b/tests/Imakefile new file mode 100644 index 0000000..2fb7655 --- /dev/null +++ b/tests/Imakefile @@ -0,0 +1,37 @@ +#include "../12to11.conf" + + 12TO11ROOT = .. + DEPLIBS = $(DEPXLIB) +LOCAL_LIBRARIES = $(WAYLAND_CLIENT) $(XLIB) $(PNG) + COMMONOBJS = test_harness.o + COMMONSRCS = test_harness.c + HEADER = test_harness.h + EXTRA_DEFINES := -D_GNU_SOURCE -U_BSD_SOURCE -U_SVID_SOURCE + +#define ScannerTarget(name) @@\ +name.h: $(12TO11ROOT)/name.xml @@\ + $(WAYLAND_SCANNER) client-header $< $@ @@\ + @@\ +name.c: $(12TO11ROOT)/name.xml name.h @@\ + $(WAYLAND_SCANNER) private-code $< $@ @@\ + @@\ + COMMONOBJS := $(COMMONOBJS) name.o @@\ + COMMONSRCS := $(COMMONSRCS) name.c @@\ + HEADER := $(HEADER) name.h @@\ + +ScannerTarget(12to11-test) + + SRCS1 = $(COMMONSRCS) simple_test.c + OBJS1 = $(COMMONOBJS) simple_test.o + PROGRAMS = simple_test + +/* Make all objects depend on HEADER. */ +$(OBJS1): $(HEADER) + +/* And depend on all sources and headers. */ +depend:: $(HEADER) $(SRCS1) + +NormalProgramTarget(simple_test,$(OBJS1),NullParameter,$(LOCAL_LIBRARIES),NullParameter) +DependTarget3($(SRCS1),NullParameter,NullParameter) + +all:: $(PROGRAMS) diff --git a/tests/README b/tests/README new file mode 100644 index 0000000..b236882 --- /dev/null +++ b/tests/README @@ -0,0 +1,6 @@ +This directory holds some work-in-progress code for testing the +protocol translator. At present, there is only a simple smoke test of +limited utility. + +Each test must be individually run on a system with an a8r8g8b8 +visual, GLOBAL_SCALE and OUTPUT_SCALE set to 1. diff --git a/tests/basic_test_card.png b/tests/basic_test_card.png new file mode 100644 index 0000000..027ca85 Binary files /dev/null and b/tests/basic_test_card.png differ diff --git a/tests/blue.png b/tests/blue.png new file mode 100644 index 0000000..c650f7e Binary files /dev/null and b/tests/blue.png differ diff --git a/tests/simple_test.c b/tests/simple_test.c new file mode 100644 index 0000000..b3a732f --- /dev/null +++ b/tests/simple_test.c @@ -0,0 +1,171 @@ +/* Tests for the Wayland compositor running on the X server. + +Copyright (C) 2022 to various contributors. + +This file is part of 12to11. + +12to11 is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +12to11 is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with 12to11. If not, see . */ + +#include "test_harness.h" + +enum test_kind + { + MAP_WINDOW_KIND, + BASIC_TEST_CARD_IMAGE_KIND, + }; + +/* The display. */ +static struct test_display *display; + +/* Test interfaces. */ +static struct test_interface test_interfaces[] = + { + /* No interfaces yet. */ + }; + +/* The test surface window. */ +static Window test_surface_window; + +/* The test surface and Wayland surface. */ +static struct test_surface *test_surface; +static struct wl_surface *wayland_surface; + + + +/* Forward declarations. */ +static void submit_frame_callback (struct wl_surface *, enum test_kind); + + + +static void +verify_single_step (enum test_kind kind) +{ + verify_image_data (display, test_surface_window, "simple_test.dump"); +} + +static void +test_single_step (enum test_kind kind) +{ + struct wl_buffer *buffer; + + switch (kind) + { + case MAP_WINDOW_KIND: + buffer = load_png_image (display, "blue.png"); + + if (!buffer) + report_test_failure ("failed to load blue.png"); + + wl_surface_attach (wayland_surface, buffer, 0, 0); + wl_surface_commit (wayland_surface); + wl_buffer_destroy (buffer); + break; + + case BASIC_TEST_CARD_IMAGE_KIND: + buffer = load_png_image (display, "basic_test_card.png"); + + if (!buffer) + report_test_failure ("failed to load basic_test_card.png"); + + wl_surface_attach (wayland_surface, buffer, 0, 0); + submit_frame_callback (wayland_surface, kind); + wl_surface_commit (wayland_surface); + wl_buffer_destroy (buffer); + break; + } +} + + + +static void +handle_test_surface_mapped (void *data, struct test_surface *surface, + uint32_t xid, const char *display_string) +{ + /* Sleep for 1 second to ensure that the window is exposed and + redirected. */ + sleep (1); + + /* Start the test. */ + test_surface_window = xid; + + /* Run the test again. */ + test_single_step (BASIC_TEST_CARD_IMAGE_KIND); +} + +static const struct test_surface_listener test_surface_listener = + { + handle_test_surface_mapped, + }; + + + +static void +handle_wl_callback_done (void *data, struct wl_callback *callback, + uint32_t callback_data) +{ + enum test_kind kind; + + kind = (intptr_t) data; + wl_callback_destroy (callback); + verify_single_step (kind); +} + +static const struct wl_callback_listener wl_callback_listener = + { + handle_wl_callback_done, + }; + + + +static void +submit_frame_callback (struct wl_surface *surface, enum test_kind kind) +{ + struct wl_callback *callback; + + callback = wl_surface_frame (surface); + wl_callback_set_user_data (callback, (void *) (intptr_t) kind); + wl_callback_add_listener (callback, &wl_callback_listener, + NULL); +} + +static void +run_test (void) +{ + if (!make_test_surface (display, &wayland_surface, + &test_surface)) + report_test_failure ("failed to create test surface"); + + test_surface_add_listener (test_surface, &test_surface_listener, + NULL); + test_single_step (MAP_WINDOW_KIND); + + while (true) + { + if (wl_display_dispatch (display->display) == -1) + die ("wl_display_dispatch"); + } +} + +int +main (void) +{ + test_init (); + display = open_test_display (test_interfaces, + ARRAYELTS (test_interfaces)); + + if (!display) + report_test_failure ("failed to open display"); + + run_test (); +} diff --git a/tests/simple_test.dump b/tests/simple_test.dump new file mode 100644 index 0000000..17aa3d1 Binary files /dev/null and b/tests/simple_test.dump differ diff --git a/tests/svnignore.txt b/tests/svnignore.txt new file mode 100644 index 0000000..ccbcac7 --- /dev/null +++ b/tests/svnignore.txt @@ -0,0 +1,5 @@ +*-*.h +*-*.c +vgcore* +simple_test +Makefile diff --git a/tests/test_harness.c b/tests/test_harness.c new file mode 100644 index 0000000..826e62c --- /dev/null +++ b/tests/test_harness.c @@ -0,0 +1,753 @@ +/* Tests for the Wayland compositor running on the X server. + +Copyright (C) 2022 to various contributors. + +This file is part of 12to11. + +12to11 is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +12to11 is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with 12to11. If not, see . */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "test_harness.h" + +#define BIG_ENDIAN_BYTE_ORDER (1 << 8) + +struct image_data_header +{ + /* Currently 1. High bit is byte order. */ + unsigned char version; + + /* The data format. Currently always 0. */ + unsigned char format; + + /* The width and height. */ + unsigned short width, height; + + /* The stride. */ + unsigned int stride; +}; + +enum image_data_format + { + /* Little-endian ARGB8888. */ + IMAGE_DATA_ARGB8888_LE, + /* Little-endian XRGB8888. */ + IMAGE_DATA_XRGB8888_LE, + }; + +#if PNG_LIBPNG_VER < 10500 +#define PNG_JMPBUF(ptr) ((ptr)->jmpbuf) +# else +#define PNG_JMPBUF(ptr) \ + (*png_set_longjmp_fn (ptr, longjmp, sizeof (jmp_buf))) +#endif + +/* Whether or not to write image data instead of verifying it. */ +static bool write_image_data_instead; + +static void +handle_test_manager_display_string (void *data, struct test_manager *manager, + const char *display_string) +{ + struct test_display *display; + + display = data; + display->x_display = XOpenDisplay (display_string); + display->pixmap_formats + = XListPixmapFormats (display->x_display, + &display->num_pixmap_formats); +} + +static const struct test_manager_listener test_manager_listener = + { + handle_test_manager_display_string, + }; + +static bool +test_manager_check (struct test_display *display) +{ + return (display->x_display != NULL + && display->num_pixmap_formats + && display->pixmap_formats); +} + + + +static void +handle_registry_global (void *data, struct wl_registry *registry, + uint32_t id, const char *interface, + uint32_t version) +{ + struct test_display *display; + int i; + + display = data; + + if (!strcmp (interface, "wl_compositor") && version >= 5) + display->compositor + = wl_registry_bind (registry, id, &wl_compositor_interface, + 5); + if (!strcmp (interface, "wl_shm") && version >= 1) + display->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1); + else if (!strcmp (interface, "test_manager")) + display->test_manager + = wl_registry_bind (registry, id, &test_manager_interface, 1); + else + { + /* Look through the user specified list of interfaces. */ + for (i = 0; i < display->num_test_interfaces; ++i) + { + if (!strcmp (interface, display->interfaces[i].interface) + && display->interfaces[i].version >= version) + /* Bind to it. */ + *((void **) display->interfaces[i].data) + = wl_registry_bind (registry, id, + display->interfaces[i].c_interface, + display->interfaces[i].version); + } + } +} + +static void +handle_registry_global_remove (void *data, struct wl_registry *registry, + uint32_t name) +{ + return; +} + +static const struct wl_registry_listener registry_listener = + { + handle_registry_global, + handle_registry_global_remove, + }; + +/* Check whether or not all required globals were found and bound + to. */ + +static bool +registry_listener_check (struct test_display *display) +{ + int i; + + if (!display->compositor) + return false; + + if (!display->shm) + return false; + + if (!display->test_manager) + return false; + + for (i = 0; i < display->num_test_interfaces; ++i) + { + if (!*((void **) display->interfaces[i].interface)) + return false; + } + + return true; +} + +void __attribute__ ((noreturn)) +die (const char *reason) +{ + perror (reason); + exit (1); +} + +struct test_display * +open_test_display (struct test_interface *interfaces, int num_interfaces) +{ + struct test_display *display; + + display = malloc (sizeof *display); + + if (!display) + return NULL; + + display->display = wl_display_connect (NULL); + + if (!display->display) + goto error_1; + + display->registry = wl_display_get_registry (display->display); + + if (!display->registry) + goto error_2; + + display->interfaces = interfaces; + display->num_test_interfaces = num_interfaces; + wl_registry_add_listener (display->registry, ®istry_listener, + display); + wl_display_roundtrip (display->display); + + if (!registry_listener_check (display)) + goto error_2; + + /* Now establish the connection to the X display. */ + test_manager_add_listener (display->test_manager, + &test_manager_listener, display); + wl_display_roundtrip (display->display); + + if (!test_manager_check (display)) + goto error_3; + + return display; + + error_3: + if (display->x_display) + XCloseDisplay (display->x_display); + error_2: + wl_display_disconnect (display->display); + error_1: + free (display); + return NULL; +} + +int +get_shm_file_descriptor (void) +{ + char name[sizeof "test_driver_buffer_XXXXXXXX"]; + int fd; + uint32_t i; + + i = 0; + + while (i <= 0xffffffff) + { + sprintf (name, "test_driver_buffer_%"PRIu32, i); + fd = shm_open (name, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (fd >= 0) + { + shm_unlink (name); + return fd; + } + + if (errno == EEXIST) + ++i; + else + return -1; + } + + return -1; +} + +#define IMAGE_PAD(nbytes, pad) ((((nbytes) + ((pad) - 1)) / (pad)) * ((pad) >> 3)) + +size_t +get_image_stride (struct test_display *display, int depth, int width) +{ + int bpp, scanline_pad, i; + size_t stride; + + for (i = 0; i < display->num_pixmap_formats; ++i) + { + if (display->pixmap_formats[i].depth == depth) + { + bpp = display->pixmap_formats[i].bits_per_pixel; + scanline_pad = display->pixmap_formats[i].bits_per_pixel; + stride = IMAGE_PAD (width * bpp, scanline_pad); + + return stride; + } + } + + return 0; +} + +struct wl_buffer * +upload_image_data (struct test_display *display, const char *data, + int width, int height, int depth) +{ + size_t stride; + int fd; + void *mapping; + struct wl_shm_pool *pool; + struct wl_buffer *buffer; + + if (depth != 32 && depth != 24) + return NULL; + + stride = get_image_stride (display, depth, width); + + if (!stride) + return NULL; + + fd = get_shm_file_descriptor (); + + if (fd < 0) + return NULL; + + if (ftruncate (fd, stride * height) < 0) + return NULL; + + mapping = mmap (NULL, stride * height, PROT_WRITE, MAP_SHARED, + fd, 0); + + if (mapping == (void *) -1) + { + perror ("mmap"); + close (fd); + return NULL; + } + + memcpy (mapping, data, stride * height); + + if (munmap (mapping, stride * height) < 0) + die ("munmap"); + + pool = wl_shm_create_pool (display->shm, fd, stride * height); + + if (!pool) + { + close (fd); + return NULL; + } + + close (fd); + buffer = wl_shm_pool_create_buffer (pool, 0, width, height, stride, + (depth == 32 + ? WL_SHM_FORMAT_ARGB8888 + : WL_SHM_FORMAT_XRGB8888)); + wl_shm_pool_destroy (pool); + + return buffer; +} + +void __attribute__ ((noreturn, format (gnu_printf, 1, 2))) +report_test_failure (const char *format, ...) +{ + va_list ap; + + va_start (ap, format); + fputs ("failure: ", stderr); + vfprintf (stderr, format, ap); + fputs ("\n", stderr); + va_end (ap); + + exit (1); +} + +void __attribute__ ((format (gnu_printf, 1, 2))) +test_log (const char *format, ...) +{ + va_list ap; + + va_start (ap, format); + fputs ("note: ", stderr); + vfprintf (stderr, format, ap); + fputs ("\n", stderr); + va_end (ap); +} + +bool +make_test_surface (struct test_display *display, + struct wl_surface **surface_return, + struct test_surface **test_surface_return) +{ + struct wl_surface *surface; + struct test_surface *test_surface; + + surface = wl_compositor_create_surface (display->compositor); + + if (!surface) + return false; + + test_surface + = test_manager_get_test_surface (display->test_manager, + surface); + + if (!test_surface) + { + wl_surface_destroy (surface); + return false; + } + + *surface_return = surface; + *test_surface_return = test_surface; + return true; +} + +/* Swizzle the big-endian data in ROW_DATA to the little-endian format + Wayland mandates. */ + +static void +swizzle_png_row (unsigned char *row_data, int width) +{ + int i; + unsigned char byte_1, byte_2, byte_3, byte_4; + + for (i = 0; i < width; ++i) + { + byte_1 = row_data[i * 4 + 0]; + byte_2 = row_data[i * 4 + 1]; + byte_3 = row_data[i * 4 + 2]; + byte_4 = row_data[i * 4 + 3]; + + row_data[i * 4 + 0] = byte_3; + row_data[i * 4 + 1] = byte_2; + row_data[i * 4 + 2] = byte_1; + row_data[i * 4 + 3] = byte_4; + } +} + +/* Load a PNG image into a wl_buffer. The image must be either + PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_RGB_ALPHA. The image + background is ignored. */ + +struct wl_buffer * +load_png_image (struct test_display *display, const char *filename) +{ + FILE *file; + unsigned char signature[8]; + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height; + int bit_depth, color_type, depth; + png_uint_32 i, rowbytes; + png_bytep *row_pointers; + unsigned char *image_data; + struct wl_buffer *buffer; + + image_data = NULL; + file = fopen (filename, "r"); + + if (!file) + return NULL; + + if (fread (signature, 1, 8, file) != 8) + goto error_1; + + if (!png_check_sig (signature, 8)) + goto error_1; + + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, + NULL); + + if (!png_ptr) + goto error_1; + + info_ptr = png_create_info_struct (png_ptr); + + if (!info_ptr) + goto error_2; + + if (setjmp (PNG_JMPBUF (png_ptr))) + goto error_3; + + png_init_io (png_ptr, file); + png_set_sig_bytes (png_ptr, 8); + png_read_info (png_ptr, info_ptr); + png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, NULL, NULL, NULL); + + if (color_type != PNG_COLOR_TYPE_RGB + && color_type != PNG_COLOR_TYPE_RGB_ALPHA) + goto error_3; + + /* Get data as ARGB. */ + if (color_type == PNG_COLOR_TYPE_RGB) + png_set_filler (png_ptr, 0, PNG_FILLER_AFTER); + + /* Start reading the PNG data. Get the stride and depth. */ + depth = (color_type == PNG_COLOR_TYPE_RGB_ALPHA + ? 32 : 24); + + png_read_update_info (png_ptr, info_ptr); + rowbytes = get_image_stride (display, depth, width); + + /* Allocate a buffer for the image data. */ + image_data = malloc (rowbytes * height); + + if (!image_data) + goto error_3; + + /* Allocate the array of row pointers. */ + row_pointers = alloca (sizeof *row_pointers * height); + + for (i = 0; i < height; ++i) + row_pointers[i] = image_data + i * rowbytes; + + /* Read the data. */ + png_read_image (png_ptr, row_pointers); + png_read_end (png_ptr, NULL); + + for (i = 0; i < height; ++i) + /* Swizzle the big-endian data. */ + swizzle_png_row (row_pointers[i], width); + + /* Now, destroy the read struct and close the file. */ + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + fclose (file); + + /* Upload the image data. */ + buffer = upload_image_data (display, (const char *) image_data, + width, height, depth); + + /* Free the image data. */ + free (image_data); + + return buffer; + + error_3: + if (image_data) + free (image_data); + + /* This looks silly... */ + if (true) + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + else + error_2: + png_destroy_read_struct (&png_ptr, NULL, NULL); + error_1: + fclose (file); + return NULL; +} + +static unsigned short +bytes_per_pixel_for_format (enum image_data_format format) +{ + switch (format) + { + case IMAGE_DATA_ARGB8888_LE: + case IMAGE_DATA_XRGB8888_LE: + return 4; + + default: + return 0; + } +} + +static int +byte_order_for_format (enum image_data_format format) +{ + switch (format) + { + case IMAGE_DATA_ARGB8888_LE: + case IMAGE_DATA_XRGB8888_LE: + return LSBFirst; + + default: + return 0; + } +} + +/* Load image data from a file. */ + +static unsigned char * +load_image_data (const char *filename, struct image_data_header *header) +{ + int fd; + struct iovec iov; + unsigned char *buffer; + unsigned short bpp; + + fd = open (filename, O_RDONLY); + + if (fd < 0) + return NULL; + + iov.iov_base = header; + iov.iov_len = sizeof *header; + + if (readv (fd, &iov, 1) != iov.iov_len) + goto error_1; + +#ifdef __BIG_ENDIAN__ + if (!(header->version & BIG_ENDIAN_BYTE_ORDER)) + goto error_1; +#endif + + if ((header->version & ~BIG_ENDIAN_BYTE_ORDER) != 1) + goto error_1; + + bpp = bytes_per_pixel_for_format (header->format); + + if (!bpp || header->stride < header->height * bpp) + goto error_1; + + buffer = malloc (header->stride * header->height); + + if (!buffer) + goto error_1; + + iov.iov_base = buffer; + iov.iov_len = header->stride * header->height; + + if (readv (fd, &iov, 1) != iov.iov_len) + goto error_2; + + close (fd); + return buffer; + + error_2: + free (buffer); + error_1: + close (fd); + return NULL; +} + +static void +compare_single_row (unsigned char *data, int row_no, + struct image_data_header *header, XImage *image) +{ + char *xdata; + unsigned short bytes_per_pixel; + + bytes_per_pixel = bytes_per_pixel_for_format (header->format); + data = data + header->stride * row_no; + xdata = image->data + image->bytes_per_line * row_no; + + if (memcmp (data, xdata, bytes_per_pixel * header->width)) + report_test_failure ("row %d differs", row_no); +} + +static void +write_image_data (struct test_display *display, Window window, + const char *filename) +{ + struct image_data_header header; + XImage *image; + struct iovec iovecs[2]; + int fd; + XWindowAttributes attrs; + + test_log ("writing contents of drawable to reference %s", filename); + + XGetWindowAttributes (display->x_display, window, &attrs); + image = XGetImage (display->x_display, window, 0, 0, attrs.width, + attrs.height, ~0, ZPixmap); + + if (!image) + report_test_failure ("failed to load from drawable 0x%lx", window); + + memset (&header, 0, sizeof header); + header.version = 1; +#ifdef __BIG_ENDIAN__ + header.version |= BIG_ENDIAN_BYTE_ORDER; +#endif + + if ((image->depth != 24 && image->depth != 32) + || image->bits_per_pixel != 32) + report_test_failure ("don't know how to save image of depth %d (bpp %d)", + image->depth, image->bits_per_pixel); + + if (image->byte_order != LSBFirst) + report_test_failure ("don't know how to save big-endian image"); + + /* TODO: determine the image format based on the visual of the + window. */ + header.format = (image->depth == 24 + ? IMAGE_DATA_XRGB8888_LE + : IMAGE_DATA_ARGB8888_LE); + header.width = image->width; + header.height = image->height; + header.stride = image->bytes_per_line; + + /* Open the output file and write the data. */ + fd = open (filename, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + + if (fd < 0) + report_test_failure ("failed to open output file %s", filename); + + iovecs[0].iov_base = &header; + iovecs[0].iov_len = sizeof header; + iovecs[1].iov_base = image->data; + iovecs[1].iov_len = image->bytes_per_line * image->height; + + if (writev (fd, iovecs, 2) != iovecs[0].iov_len + iovecs[1].iov_len) + report_test_failure ("failed to write to output file %s", filename); + + close (fd); + XDestroyImage (image); + + test_log ("image data written to %s", filename); +} + +/* Verify the image data against a file. */ + +void +verify_image_data (struct test_display *display, Window window, + const char *filename) +{ + XImage *image; + XWindowAttributes attrs; + unsigned char *data; + struct image_data_header header; + unsigned short data_bpp, i; + int byte_order; + + if (write_image_data_instead) + write_image_data (display, window, filename); + + data = load_image_data (filename, &header); + + if (!data) + report_test_failure ("failed to load input file: %s", filename); + + XGetWindowAttributes (display->x_display, window, &attrs); + image = XGetImage (display->x_display, window, 0, 0, attrs.width, + attrs.height, ~0, ZPixmap); + + if (!image) + report_test_failure ("failed to load from drawable 0x%lx", window); + + /* Check if the image data is compatible. */ + data_bpp = bytes_per_pixel_for_format (header.format); + byte_order = byte_order_for_format (header.format); + + if (byte_order != image->byte_order) + report_test_failure ("image data has wrong byte order"); + + if (data_bpp * 8 != image->bits_per_pixel) + report_test_failure ("image data has %d bits per pixel, but reference" + " data has %hd * 8", image->bits_per_pixel, data_bpp); + + if (image->width != header.width + || image->height != header.height) + report_test_failure ("image data is %d by %d, but reference data is" + " %hd by %hd", image->width, image->height, + header.width, header.height); + + /* Now compare the actual image data. Make sure this is done with + the same visual as the reference data was saved in! */ + + for (i = 0; i < header.height; ++i) + compare_single_row (data, i, &header, image); + + /* Destroy the images. */ + free (data); + XDestroyImage (image); + + test_log ("verified image data"); +} + +void +test_init (void) +{ + write_image_data_instead + = getenv ("TEST_WRITE_REFERENCE") != NULL; +} diff --git a/tests/test_harness.h b/tests/test_harness.h new file mode 100644 index 0000000..0e97f40 --- /dev/null +++ b/tests/test_harness.h @@ -0,0 +1,91 @@ +/* Tests for the Wayland compositor running on the X server. + +Copyright (C) 2022 to various contributors. + +This file is part of 12to11. + +12to11 is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +12to11 is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with 12to11. If not, see . */ + +#include +#include +#include + +#include + +#include +#include + +#include "12to11-test.h" + +struct test_display +{ + /* The wayland display. */ + struct wl_display *display; + + /* The X display. */ + Display *x_display; + + /* List of pixmap formats. */ + XPixmapFormatValues *pixmap_formats; + + /* Number of pixmap formats. */ + int num_pixmap_formats; + + /* The registry and various Wayland interfaces. */ + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shm *shm; + struct test_manager *test_manager; + + /* Test interfaces. */ + struct test_interface *interfaces; + + /* The number of such interfaces. */ + int num_test_interfaces; +}; + +struct test_interface +{ + /* The name of the interface. */ + const char *interface; + + /* Pointer to the interface data. */ + void *data; + + /* Pointer to the interface. */ + struct wl_interface *c_interface; + + /* The wanted version. */ + uint32_t version; +}; + +extern void die (const char *); +extern struct test_display *open_test_display (struct test_interface *, int); +extern int get_shm_file_descriptor (void); +extern size_t get_image_stride (struct test_display *, int, int); +extern struct wl_buffer *upload_image_data (struct test_display *, + const char *, int, int, + int); +extern void report_test_failure (const char *, ...) + __attribute__ ((noreturn, format (gnu_printf, 1, 2))); +extern void test_log (const char *, ...) + __attribute__ ((format (gnu_printf, 1, 2))); + +extern bool make_test_surface (struct test_display *, struct wl_surface **, + struct test_surface **); +extern struct wl_buffer *load_png_image (struct test_display *, const char *); +extern void verify_image_data (struct test_display *, Window, const char *); +extern void test_init (void); + +#define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0])