diff --git a/tests/Imakefile b/tests/Imakefile index 7194905..643bf94 100644 --- a/tests/Imakefile +++ b/tests/Imakefile @@ -20,6 +20,7 @@ name.c: $(12TO11ROOT)/name.xml name.h @@\ HEADER := $(HEADER) name.h @@\ ScannerTarget(12to11-test) +ScannerTarget(viewporter) SRCS1 = $(COMMONSRCS) simple_test.c OBJS1 = $(COMMONOBJS) simple_test.o @@ -27,7 +28,12 @@ ScannerTarget(12to11-test) OBJS2 = $(COMMONOBJS) damage_test.o SRCS3 = $(COMMONSRCS) transform_test.c OBJS3 = $(COMMONSRCS) transform_test.o - PROGRAMS = simple_test damage_test transform_test + SRCS4 = $(COMMONSRCS) viewporter_test.c + OBJS4 = $(COMMONSRCS) viewporter_test.o + /* Not actually a test. */ + SRCS5 = $(COMMONSRCS) imgview.c + OBJS5 = $(COMMONSRCS) imgview.o + PROGRAMS = simple_test damage_test transform_test viewporter_test imgview /* Make all objects depend on HEADER. */ $(OBJS1): $(HEADER) @@ -38,6 +44,9 @@ depend:: $(HEADER) $(SRCS1) NormalProgramTarget(simple_test,$(OBJS1),NullParameter,$(LOCAL_LIBRARIES),NullParameter) NormalProgramTarget(damage_test,$(OBJS2),NullParameter,$(LOCAL_LIBRARIES),NullParameter) NormalProgramTarget(transform_test,$(OBJS3),NullParameter,$(LOCAL_LIBRARIES),NullParameter) +NormalProgramTarget(viewporter_test,$(OBJS4),NullParameter,$(LOCAL_LIBRARIES),NullParameter) +NormalProgramTarget(imgview,$(OBJS5),NullParameter,$(LOCAL_LIBRARIES),NullParameter) DependTarget3($(SRCS1),$(SRCS2),$(SRCS3)) +DependTarget3($(SRCS4),$(SRCS5),NullParameter) all:: $(PROGRAMS) diff --git a/tests/README b/tests/README index 3ba59b7..ade44f3 100644 --- a/tests/README +++ b/tests/README @@ -19,3 +19,6 @@ would be ``integration tests''. Most likely, you do not want to run these tests manually, as the `run_tests.sh' script does all the setup for you. + +Please note that the EGL renderer currently does not pass the +viewporter test, which is expected behavior. diff --git a/tests/imgview.c b/tests/imgview.c new file mode 100644 index 0000000..9d6ef00 --- /dev/null +++ b/tests/imgview.c @@ -0,0 +1,203 @@ +/* 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 "test_harness.h" + +/* imgview -- simple viewer for .dump files. It opens each file as an + argument and displays it in a window. */ + +/* The display imgview is connected to. */ +static Display *display; + +/* The visual being used. */ +static Visual *visual; + +/* Various image parameters. */ +static int width, height, depth; + +static int +get_depth_for_format (struct image_data_header *header) +{ + switch (header->format) + { + case IMAGE_DATA_ARGB8888_LE: + return 32; + + case IMAGE_DATA_XRGB8888_LE: + return 24; + } + + return 0; +} + +static Visual * +get_visual_for_format (struct image_data_header *header) +{ + XVisualInfo template, *visuals; + Visual *visual; + unsigned long mask; + int n_visuals; + + /* This isn't right if the pixel layout differs, but the program + assumes that all 24-depth visuals are XRGB8888 and all 32-bit + ones are ARGB8888. */ + + template.screen = DefaultScreen (display); + template.class = TrueColor; + + switch (header->format) + { + case IMAGE_DATA_ARGB8888_LE: + template.depth = 32; + break; + + case IMAGE_DATA_XRGB8888_LE: + template.depth = 24; + break; + } + + mask = VisualScreenMask | VisualDepthMask | VisualClassMask; + visuals = XGetVisualInfo (display, mask, &template, &n_visuals); + + if (!n_visuals) + { + if (visuals) + XFree (visuals); + + return NULL; + } + + visual = visuals[0].visual; + XFree (visuals); + return visual; +} + +static Pixmap +get_image (const char *filename) +{ + unsigned char *data; + struct image_data_header header; + XImage *image; + Pixmap pixmap; + GC gc; + + data = load_image_data (filename, &header); + + if (!data) + return None; + + visual = get_visual_for_format (&header); + depth = get_depth_for_format (&header); + + if (!depth || !visual) + { + free (data); + return None; + } + + image = XCreateImage (display, visual, depth, ZPixmap, 0, + (char *) data, header.width, + header.height, 8, header.stride); + image->data = (char *) data; + + pixmap = XCreatePixmap (display, DefaultRootWindow (display), + header.width, header.height, depth); + gc = XCreateGC (display, pixmap, 0, NULL); + + /* Upload the image to the pixmap and free the GC. */ + XPutImage (display, pixmap, gc, image, 0, 0, + 0, 0, header.width, header.height); + XFreeGC (display, gc); + + /* Destroy the XImage data. */ + free (image->data); + image->data = NULL; + + /* Destroy the XImage. */ + XDestroyImage (image); + + /* Set image params. */ + width = header.width; + height = header.height; + depth = depth; + + return pixmap; +} + +static void +loop_for_events (void) +{ + XEvent event; + + while (true) + XNextEvent (display, &event); +} + +static void +open_window (Pixmap image) +{ + Window window; + XSetWindowAttributes attrs; + unsigned long flags; + + attrs.colormap = XCreateColormap (display, + DefaultRootWindow (display), + visual, AllocNone); + /* This should be valid on any TrueColor visual. */ + attrs.border_pixel = 0; + attrs.background_pixmap = image; + attrs.cursor = None; + flags = (CWColormap | CWBorderPixel | CWBackPixmap | CWCursor); + + window = XCreateWindow (display, + DefaultRootWindow (display), + 0, 0, width, height, 0, depth, + InputOutput, visual, flags, + &attrs); + XMapRaised (display, window); +} + +int +main (int argc, char **argv) +{ + Pixmap image; + int i; + + if (argc < 2) + return 1; + + display = XOpenDisplay (NULL); + + if (!display) + return 1; + + for (i = 1; i < argc; ++i) + { + image = get_image (argv[i]); + + if (!image) + continue; + + open_window (image); + } + + loop_for_events (); +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index b5c18d1..2dc4173 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -18,7 +18,7 @@ # along with 12to11. If not, see . pushd "$(dirname $0)" -declare -a standard_tests=( simple_test damage_test transform_test ) +declare -a standard_tests=( simple_test damage_test transform_test viewporter_test ) make -C . "${standard_tests[@]}" diff --git a/tests/svnignore.txt b/tests/svnignore.txt index 87e34f0..6ef7a4e 100644 --- a/tests/svnignore.txt +++ b/tests/svnignore.txt @@ -4,5 +4,10 @@ vgcore* simple_test damage_test transform_test +viewporter_test +imgview +reject.dump Makefile Makefile.bak +viewporter.h +viewporter.c diff --git a/tests/test_harness.c b/tests/test_harness.c index 7910bfb..f2c4177 100644 --- a/tests/test_harness.c +++ b/tests/test_harness.c @@ -35,32 +35,6 @@ along with 12to11. If not, see . */ #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; - - /* Padding. */ - unsigned short pad1; - - /* 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 @@ -560,7 +534,7 @@ byte_order_for_format (enum image_data_format format) /* Load image data from a file. */ -static unsigned char * +unsigned char * load_image_data (const char *filename, struct image_data_header *header) { int fd; @@ -614,38 +588,11 @@ load_image_data (const char *filename, struct image_data_header *header) } 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) +write_image_data_1 (XImage *image, 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; @@ -685,6 +632,46 @@ write_image_data (struct test_display *display, Window window, report_test_failure ("failed to write to output file %s", filename); close (fd); +} + +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)) + { + /* Write the reject to a file. */ + test_log ("writing reject to reject.dump"); + write_image_data_1 (image, "reject.dump"); + + report_test_failure ("row %d differs", row_no); + } +} + +static void +write_image_data (struct test_display *display, Window window, + const char *filename) +{ + XImage *image; + 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); + + write_image_data_1 (image, filename); XDestroyImage (image); test_log ("image data written to %s", filename); @@ -715,6 +702,8 @@ verify_image_data (struct test_display *display, Window window, image = XGetImage (display->x_display, window, 0, 0, attrs.width, attrs.height, ~0, ZPixmap); + test_log ("verifying image data from: %s", filename); + if (!image) report_test_failure ("failed to load from drawable 0x%lx", window); diff --git a/tests/test_harness.h b/tests/test_harness.h index 98d201c..90d5ab4 100644 --- a/tests/test_harness.h +++ b/tests/test_harness.h @@ -65,12 +65,38 @@ struct test_interface void *data; /* Pointer to the interface. */ - struct wl_interface *c_interface; + const struct wl_interface *c_interface; /* The wanted version. */ uint32_t version; }; +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; + + /* Padding. */ + unsigned short pad1; + + /* The stride. */ + unsigned int stride; +}; + +enum image_data_format + { + /* Little-endian ARGB8888. */ + IMAGE_DATA_ARGB8888_LE, + /* Little-endian XRGB8888. */ + IMAGE_DATA_XRGB8888_LE, + }; + extern void die (const char *) __attribute__ ((noreturn)); extern struct test_display *open_test_display (struct test_interface *, int); extern int get_shm_file_descriptor (void); @@ -86,6 +112,8 @@ extern void test_log (const char *, ...) 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 unsigned char *load_image_data (const char *, + struct image_data_header *); extern void verify_image_data (struct test_display *, Window, const char *); extern void test_init (void); extern void test_complete (void) __attribute__ ((noreturn)); diff --git a/tests/viewport_dest_200_150_1.dump b/tests/viewport_dest_200_150_1.dump new file mode 100644 index 0000000..4614a50 Binary files /dev/null and b/tests/viewport_dest_200_150_1.dump differ diff --git a/tests/viewport_dest_200_150_2.dump b/tests/viewport_dest_200_150_2.dump new file mode 100644 index 0000000..2cd2a77 Binary files /dev/null and b/tests/viewport_dest_200_150_2.dump differ diff --git a/tests/viewport_src_50_50_200_200_1.dump b/tests/viewport_src_50_50_200_200_1.dump new file mode 100644 index 0000000..843bea1 Binary files /dev/null and b/tests/viewport_src_50_50_200_200_1.dump differ diff --git a/tests/viewport_src_50_50_200_200_2.dump b/tests/viewport_src_50_50_200_200_2.dump new file mode 100644 index 0000000..6451159 Binary files /dev/null and b/tests/viewport_src_50_50_200_200_2.dump differ diff --git a/tests/viewport_src_50_50_200_200_dest_500_500_1.dump b/tests/viewport_src_50_50_200_200_dest_500_500_1.dump new file mode 100644 index 0000000..b08261c Binary files /dev/null and b/tests/viewport_src_50_50_200_200_dest_500_500_1.dump differ diff --git a/tests/viewport_src_50_50_200_200_dest_500_500_2.dump b/tests/viewport_src_50_50_200_200_dest_500_500_2.dump new file mode 100644 index 0000000..5747944 Binary files /dev/null and b/tests/viewport_src_50_50_200_200_dest_500_500_2.dump differ diff --git a/tests/viewporter_test.c b/tests/viewporter_test.c new file mode 100644 index 0000000..ad614f7 --- /dev/null +++ b/tests/viewporter_test.c @@ -0,0 +1,286 @@ +/* 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" +#include "viewporter.h" + +/* Tests for wp_viewporter. The relevant requests tested are: + + wp_viewporter.set_source (-1, -1, -1, -1) + wp_viewporter.set_destination (200, 150) + + wp_viewporter.set_source (50, 50, 200, 200) + wp_viewporter.set_destination (-1, -1) + + wp_viewporter.set_source (50, 50, 200, 200) + wp_viewporter.set_destination (500, 500) + + Each test is run in order, with both damage_buffer and damage being + used to compute buffer damage. */ + +enum test_kind + { + MAP_WINDOW_KIND, + VIEWPORT_DEST_200_150_KIND, + VIEWPORT_SRC_50_50_200_200_KIND, + VIEWPORT_SRC_50_50_200_200_DEST_500_500_KIND, + }; + +static const char *test_names[] = + { + "map_window", + "viewport_dest_250_150", + "viewport_src_50_50_200_200", + "viewport_src_50_50_200_200_dest_500_500", + }; + +#define LAST_TEST VIEWPORT_SRC_50_50_200_200_DEST_500_500_KIND + +/* The display. */ +static struct test_display *display; + +/* The viewporter interface. */ +static struct wp_viewporter *viewporter; + +/* Test interfaces. */ +static struct test_interface test_interfaces[] = + { + { "wp_viewporter", &viewporter, &wp_viewporter_interface, 1, }, + }; + +/* 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; + +/* The surface viewport. */ +static struct wp_viewport *viewport; + + + +/* Forward declarations. */ +static void wait_frame_callback (struct wl_surface *); + + + +static void +do_viewport_damage_test (double src_x, double src_y, double src_width, + double src_height, int dest_width, + int dest_height, const char *dump_1_name, + const char *dump_2_name) +{ + struct wl_buffer *buffer, *damaged_buffer; + + buffer = load_png_image (display, "viewporter_test.png"); + + if (!buffer) + report_test_failure ("failed to load viewporter_test.png"); + + damaged_buffer = load_png_image (display, "viewporter_test_1.png"); + + if (!damaged_buffer) + report_test_failure ("failed to load viewporter_test_1.png"); + + /* Set the viewport. */ + wp_viewport_set_source (viewport, + wl_fixed_from_double (src_x), + wl_fixed_from_double (src_y), + wl_fixed_from_double (src_width), + wl_fixed_from_double (src_height)); + wp_viewport_set_destination (viewport, dest_width, dest_height); + + wl_surface_attach (wayland_surface, buffer, 0, 0); + wl_surface_damage (wayland_surface, 0, 0, INT_MAX, INT_MAX); + wait_frame_callback (wayland_surface); + + /* Verify the image without any damage applied. */ + verify_image_data (display, test_surface_window, dump_1_name); + + /* Now, load the damaged buffer and apply buffer damage. */ + wl_surface_attach (wayland_surface, damaged_buffer, 0, 0); + wl_surface_damage_buffer (wayland_surface, 100, 100, 30, 30); + wait_frame_callback (wayland_surface); + + /* Verify the image with damage applied. */ + verify_image_data (display, test_surface_window, dump_2_name); + + /* Now, load the untransformed buffer and apply surface damage. */ + wl_surface_attach (wayland_surface, buffer, 0, 0); + wl_surface_damage_buffer (wayland_surface, 100, 100, 30, 30); + wait_frame_callback (wayland_surface); + + /* Verify that the surface with the damage reverted is the same + as the initial contents of the surface. */ + verify_image_data (display, test_surface_window, dump_1_name); + + /* Free both buffers. */ + wl_buffer_destroy (buffer); + wl_buffer_destroy (damaged_buffer); +} + +static void +test_single_step (enum test_kind kind) +{ + struct wl_buffer *buffer; + + test_log ("running test step: %s", test_names[kind]); + + 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_damage (wayland_surface, 0, 0, INT_MAX, INT_MAX); + wl_surface_commit (wayland_surface); + wl_buffer_destroy (buffer); + break; + + case VIEWPORT_DEST_200_150_KIND: + do_viewport_damage_test (-1, -1, -1, -1, 200, 150, + "viewport_dest_200_150_1.dump", + "viewport_dest_200_150_2.dump"); + test_single_step (VIEWPORT_SRC_50_50_200_200_KIND); + break; + + case VIEWPORT_SRC_50_50_200_200_KIND: + do_viewport_damage_test (50, 50, 200, 200, -1, -1, + "viewport_src_50_50_200_200_1.dump", + "viewport_src_50_50_200_200_2.dump"); + test_single_step (VIEWPORT_SRC_50_50_200_200_DEST_500_500_KIND); + break; + + case VIEWPORT_SRC_50_50_200_200_DEST_500_500_KIND: + do_viewport_damage_test (50, 50, 200, 200, 500, 500, + "viewport_src_50_50_200_200_dest_500_500_1.dump", + "viewport_src_50_50_200_200_dest_500_500_2.dump"); + break; + } + + if (kind == LAST_TEST) + test_complete (); +} + + + +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 (VIEWPORT_DEST_200_150_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) +{ + bool *flag; + + wl_callback_destroy (callback); + + /* Now tell wait_frame_callback to break out of the loop. */ + flag = data; + *flag = true; +} + +static const struct wl_callback_listener wl_callback_listener = + { + handle_wl_callback_done, + }; + + + +static void +wait_frame_callback (struct wl_surface *surface) +{ + struct wl_callback *callback; + bool flag; + + /* Commit surface and wait for a frame callback. */ + + callback = wl_surface_frame (surface); + flag = false; + + wl_callback_add_listener (callback, &wl_callback_listener, + &flag); + wl_surface_commit (surface); + + while (!flag) + { + if (wl_display_dispatch (display->display) == -1) + die ("wl_display_dispatch"); + } +} + +static void +run_test (void) +{ + if (!make_test_surface (display, &wayland_surface, + &test_surface)) + report_test_failure ("failed to create test surface"); + + viewport = wp_viewporter_get_viewport (viewporter, + wayland_surface); + + if (!viewport) + report_test_failure ("failed to create viewport"); + + 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/viewporter_test.png b/tests/viewporter_test.png new file mode 100644 index 0000000..892f39c Binary files /dev/null and b/tests/viewporter_test.png differ diff --git a/tests/viewporter_test_1.png b/tests/viewporter_test_1.png new file mode 100644 index 0000000..7faabb5 Binary files /dev/null and b/tests/viewporter_test_1.png differ