forked from 12to11/12to11
Check in tests
* tests/Imakefile: * tests/README * tests/basic_test_card.png: * tests/blue.png: * tests/simple_test.c: * tests/simple_test.dump: * tests/svnignore.txt: * tests/test_harness.c: * tests/test_harness.h: New files.
This commit is contained in:
parent
2230f5e406
commit
83a2d26df3
9 changed files with 1063 additions and 0 deletions
37
tests/Imakefile
Normal file
37
tests/Imakefile
Normal file
|
@ -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)
|
6
tests/README
Normal file
6
tests/README
Normal file
|
@ -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.
|
BIN
tests/basic_test_card.png
Normal file
BIN
tests/basic_test_card.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
BIN
tests/blue.png
Normal file
BIN
tests/blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
171
tests/simple_test.c
Normal file
171
tests/simple_test.c
Normal file
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#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 ();
|
||||||
|
}
|
BIN
tests/simple_test.dump
Normal file
BIN
tests/simple_test.dump
Normal file
Binary file not shown.
5
tests/svnignore.txt
Normal file
5
tests/svnignore.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
*-*.h
|
||||||
|
*-*.c
|
||||||
|
vgcore*
|
||||||
|
simple_test
|
||||||
|
Makefile
|
753
tests/test_harness.c
Normal file
753
tests/test_harness.c
Normal file
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
91
tests/test_harness.h
Normal file
91
tests/test_harness.h
Normal file
|
@ -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 <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
#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])
|
Loading…
Add table
Reference in a new issue