summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2017-12-20 19:09:49 -0800
committerKeith Packard <keithp@keithp.com>2018-01-24 10:51:08 +1100
commit9e8d2a9450c69f86024d62d3ed0525c744c0717e (patch)
treec113f231fd7c9930cd3a0c8a7eafe5baca112e93
parent91fd74e7cfdd6e9700cbfaef9bb98dc0291a3f7e (diff)
test: Add randr lease tests
Tests for RRCreateLease and RRFreeLease Signed-off-by: Keith Packard <keithp@keithp.com>
-rw-r--r--configure.ac8
-rw-r--r--test/Makefile.am2
-rw-r--r--test/randr/Makefile.am14
-rw-r--r--test/randr/randr_lease.c1066
4 files changed, 1089 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 62d0892e1..bd5815cd0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1290,6 +1290,13 @@ if test "x$DRI" = xyes || test "x$DRI2" = xyes || test "x$DRI3" = xyes || test "
fi
fi
+AM_CONDITIONAL(DRM, test "x$DRM" = xyes)
+
+TEST_RANDR_REQUIRED_LIBS="xcb xcb-randr"
+PKG_CHECK_MODULES([TEST_RANDR], $TEST_RANDR_REQUIRED_LIBS)
+AC_SUBST(TEST_RANDR_CFLAGS)
+AC_SUBST(TEST_RANDR_LIBS)
+
if test "x$GLX" = xyes; then
PKG_CHECK_MODULES([XLIB], [x11])
PKG_CHECK_MODULES([GL], $GLPROTO $LIBGL)
@@ -2543,6 +2550,7 @@ hw/kdrive/ephyr/man/Makefile
hw/kdrive/src/Makefile
hw/xwayland/Makefile
test/Makefile
+test/randr/Makefile
xserver.ent
xorg-server.pc
])
diff --git a/test/Makefile.am b/test/Makefile.am
index 2dbb2df03..f42b50f5e 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,5 +1,5 @@
if ENABLE_UNIT_TESTS
-SUBDIRS= .
+SUBDIRS= . randr
AM_CFLAGS = $(DIX_CFLAGS) @XORG_CFLAGS@
AM_CPPFLAGS = $(XORG_INCS)
diff --git a/test/randr/Makefile.am b/test/randr/Makefile.am
new file mode 100644
index 000000000..abaf8e6ac
--- /dev/null
+++ b/test/randr/Makefile.am
@@ -0,0 +1,14 @@
+randr_lease_SOURCES = \
+ randr_lease.c
+
+if DRM
+RANDR_LEASE=randr_lease
+endif
+
+noinst_PROGRAMS = $(RANDR_LEASE)
+
+TESTS = $(RANDR_LEASE)
+
+AM_CFLAGS=$(XORG_CFLAGS) $(TEST_RANDR_CFLAGS) $(LIBDRM_CFLAGS)
+
+randr_lease_LDADD = $(TEST_RANDR_LIBS) $(LIBDRM_LIBS)
diff --git a/test/randr/randr_lease.c b/test/randr/randr_lease.c
new file mode 100644
index 000000000..53dc0cb01
--- /dev/null
+++ b/test/randr/randr_lease.c
@@ -0,0 +1,1066 @@
+/*
+ * Copyright © 2017 Keith Packard
+ *
+ * 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 the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS 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.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <xcb/randr.h>
+#include <drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+/* Test X Leases */
+
+/* Information about one lease */
+typedef struct {
+ int fd;
+
+ /* X resources */
+ xcb_randr_output_t randr_output;
+ xcb_randr_crtc_t randr_crtc;
+ xcb_randr_lease_t randr_lease;
+ xcb_randr_mode_info_t randr_mode;
+
+ /* DRM resources */
+ uint32_t crtc_id;
+ uint32_t connector_id;
+ drmModeModeInfo drm_mode;
+ uint32_t bo_handle;
+ uint32_t fb_handle;
+
+ /* Frame buffer */
+ uint32_t *fb;
+ uint32_t size;
+ uint32_t width, height, stride;
+} lease_t;
+
+/* Global test information */
+typedef struct {
+ xcb_connection_t *conn;
+ xcb_window_t root;
+ xcb_timestamp_t config_timestamp;
+ uint8_t event_base;
+ uint8_t error_base;
+ xcb_randr_get_screen_resources_reply_t *gsr_r;
+} test_t;
+
+/* standard VGA mode, in case the target output has no modes */
+xcb_randr_mode_info_t vga_mode = {
+ .dot_clock = 23750 * 1000,
+
+ .width = 640,
+ .hsync_start = 664,
+ .hsync_end = 720,
+ .htotal = 800,
+ .hskew = 0,
+
+ .height = 480,
+ .vsync_start = 483,
+ .vsync_end = 487,
+ .vtotal = 500,
+
+ .mode_flags = XCB_RANDR_MODE_FLAG_HSYNC_NEGATIVE | XCB_RANDR_MODE_FLAG_VSYNC_POSITIVE,
+};
+
+/* Convert an X mode into a DRM mode */
+drmModeModeInfo
+randr_mode_to_drm_mode(xcb_randr_mode_info_t *randr_mode)
+{
+ drmModeModeInfo drm_mode = {
+ .clock = randr_mode->dot_clock / 1000,
+
+ .hdisplay = randr_mode->width,
+ .hsync_start = randr_mode->hsync_start,
+ .hsync_end = randr_mode->hsync_end,
+ .htotal = randr_mode->htotal,
+ .hskew = 0,
+
+ .vdisplay = randr_mode->height,
+ .vsync_start = randr_mode->vsync_start,
+ .vsync_end = randr_mode->vsync_end,
+ .vtotal = randr_mode->vtotal,
+ .vscan = 0,
+
+ .vrefresh = 0,
+ .flags = randr_mode->mode_flags,
+ .type = DRM_MODE_TYPE_USERDEF,
+
+ };
+
+ sprintf(drm_mode.name, "%dx%d", randr_mode->width, randr_mode->height);
+
+ return drm_mode;
+}
+
+/* Handle EINTR from ioctl by looping */
+int
+test_ioctl(int fd, unsigned long request, void *arg)
+{
+ int ret;
+
+ do {
+ ret = ioctl(fd, request, arg);
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+/* Draw a rectangle in 32-bpp format */
+void
+rect(lease_t *lease, int x, int y, int width, int height, uint32_t color)
+{
+ uint32_t *scanline = lease->fb + (lease->stride * 8 / 32) * y + x;
+
+ while (height--) {
+ int w = width;
+ uint32_t *pixel = scanline;
+
+ scanline += lease->stride * 8 / 32;
+
+ while (w--)
+ *pixel++ = color;
+ }
+}
+
+/* Create a buffer object, map it, and create a frame buffer */
+int
+create_fb(test_t *test, lease_t *lease)
+{
+ struct drm_mode_create_dumb create;
+
+ memset(&create, 0, sizeof(create));
+ create.width = lease->drm_mode.hdisplay;
+ create.height = lease->drm_mode.vdisplay;
+ create.bpp = 32;
+
+ create.handle = 0;
+ test_ioctl(lease->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
+ if (!create.handle) {
+ printf("\t\tfailed to create scanout bo\n");
+ return 0;
+ }
+ if (create.size < lease->drm_mode.hdisplay * lease->drm_mode.vdisplay * 32 / 8) {
+ printf("\t\tscanout buffer too small\n");
+ return 0;
+ }
+
+ lease->bo_handle = create.handle;
+
+ lease->width = lease->drm_mode.hdisplay;
+ lease->height = lease->drm_mode.vdisplay;
+ lease->stride = create.pitch;
+
+ struct drm_mode_map_dumb map;
+
+ memset(&map, 0, sizeof(map));
+
+ map.handle = lease->bo_handle;
+
+ if (test_ioctl(lease->fd, DRM_IOCTL_MODE_MAP_DUMB, &map) < 0) {
+ printf("\t\tmap scanout failed\n");
+ return 0;
+ }
+
+ lease->size = create.size;
+ lease->fb = mmap(NULL, lease->size, PROT_READ|PROT_WRITE, MAP_SHARED, lease->fd, map.offset);
+
+ if (lease->fb == MAP_FAILED) {
+ printf("\t\tmmap scanout failed\n");
+ return 0;
+ }
+
+ /* grey */
+ rect(lease, 0, 0, lease->width, lease->height, 0x00808080);
+
+ /* with an orange center */
+ rect(lease, lease->width / 4, lease->height / 4, lease->width / 2, lease->height / 2, 0x00ffa500);
+
+ if (drmModeAddFB(lease->fd, lease->width, lease->height, 24, 32, lease->stride, lease->bo_handle,
+ &lease->fb_handle)) {
+ printf("\t\tadd fb failed\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+destroy_fb(test_t *test, lease_t *lease)
+{
+ struct drm_mode_destroy_dumb destroy;
+
+ if (lease->fb) {
+ munmap(lease->fb, lease->size);
+ drmModeRmFB(lease->fd, lease->fb_handle);
+ lease->fb = 0;
+
+ memset(&destroy, 0, sizeof(destroy));
+ destroy.handle = lease->bo_handle;
+ test_ioctl(lease->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
+ }
+}
+
+/*
+ * Create a lease for the desired output, select a crtc if none was
+ * provided.
+ */
+static int
+make_randr_lease(test_t *test, lease_t *lease, xcb_randr_output_t output, xcb_randr_crtc_t crtc, bool whinge, xcb_generic_error_t **error)
+{
+ xcb_randr_mode_info_t mode_info;
+
+ mode_info.id = XCB_NONE;
+
+ /* Pick a suitable mode */
+ xcb_randr_get_output_info_cookie_t goi_c = xcb_randr_get_output_info(test->conn, output, test->gsr_r->config_timestamp);
+ xcb_randr_get_output_info_reply_t *goi_r = xcb_randr_get_output_info_reply(test->conn, goi_c, NULL);
+
+ if (goi_r->num_modes > 0) {
+ xcb_randr_mode_t mode = xcb_randr_get_output_info_modes(goi_r)[0];
+
+ for (xcb_randr_mode_info_iterator_t iter = xcb_randr_get_screen_resources_modes_iterator(test->gsr_r);
+ iter.rem;
+ xcb_randr_mode_info_next(&iter))
+ {
+ if (iter.data->id == mode) {
+ mode_info = *iter.data;
+ break;
+ }
+ }
+ }
+
+ if (mode_info.id == 0)
+ mode_info = vga_mode;
+
+ /* Pick a crtc if none was provided */
+ if (crtc == XCB_NONE) {
+ xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(test->gsr_r);
+
+ xcb_randr_crtc_t idle_crtc = XCB_NONE;
+ xcb_randr_crtc_t active_crtc = XCB_NONE;
+
+ /* Find either a crtc already connected to the desired output or idle */
+ for (int c = 0; active_crtc == XCB_NONE && c < test->gsr_r->num_crtcs; c++) {
+ xcb_randr_get_crtc_info_cookie_t gci_c = xcb_randr_get_crtc_info(test->conn, rc[c], test->gsr_r->config_timestamp);
+
+ xcb_randr_get_crtc_info_reply_t *gci_r = xcb_randr_get_crtc_info_reply(test->conn, gci_c, NULL);
+
+ if (gci_r->mode) {
+ int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r);
+ xcb_randr_output_t *outputs = xcb_randr_get_crtc_info_outputs(gci_r);
+ for (int o = 0; o < num_outputs; o++)
+ if (outputs[o] == output && num_outputs == 1) {
+ active_crtc = rc[c];
+ break;
+ }
+
+ } else if (idle_crtc == 0) {
+ int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r);
+ xcb_randr_output_t *possible = xcb_randr_get_crtc_info_possible(gci_r);
+ for (int p = 0; p < num_possible; p++)
+ if (possible[p] == output) {
+ idle_crtc = rc[c];
+ break;
+ }
+ }
+
+ free(gci_r);
+ }
+ if (active_crtc)
+ crtc = active_crtc;
+ else
+ crtc = idle_crtc;
+
+ }
+
+ if (crtc == XCB_NONE) {
+ if (whinge)
+ printf("\t\tcannot find usable CRTC\n");
+ return 0;
+ }
+
+ /*
+ * Create a RandR lease
+ */
+
+ xcb_randr_lease_t randr_lease = xcb_generate_id(test->conn);
+
+ xcb_randr_create_lease_cookie_t cl_c = xcb_randr_create_lease(test->conn,
+ test->root,
+ randr_lease,
+ 1,
+ 1,
+ &crtc,
+ &output);
+ xcb_randr_create_lease_reply_t *cl_r = xcb_randr_create_lease_reply(test->conn, cl_c, error);
+ if (!cl_r) {
+ if (whinge)
+ printf ("create lease failed\n");
+ return 0;
+ }
+
+ int fd = -1;
+ if (cl_r->nfd > 0) {
+ int *rcl_f = xcb_randr_create_lease_reply_fds(test->conn, cl_r);
+
+ fd = rcl_f[0];
+ }
+ free (cl_r);
+ if (fd < 0) {
+ if (whinge)
+ printf("\t\tLease returned invalid fd\n");
+ return 0;
+ }
+
+ lease->fd = fd;
+ lease->randr_output = output;
+ lease->randr_crtc = crtc;
+ lease->randr_mode = mode_info;
+ lease->randr_lease = randr_lease;
+
+ return 1;
+}
+
+static int
+get_drm_resources(test_t *test, lease_t *lease)
+{
+ /*
+ * Find the kernel resource IDs for the lease. This is pretty easy
+ * as there should be only one of each
+ */
+
+ drmModeResPtr mode_res = drmModeGetResources(lease->fd);
+
+ if (!mode_res) {
+ printf("\t\tget mode resources failed\n");
+ return 0;
+ }
+
+ if (mode_res->count_connectors != 1) {
+ printf("\t\tunexpected DRM connector count %d\n", mode_res->count_connectors);
+ goto fail_res;
+ }
+
+ if (mode_res->count_crtcs != 1) {
+ printf("\t\tunexpected DRM crtc count %d\n", mode_res->count_crtcs);
+ goto fail_res;
+ }
+
+ lease->crtc_id = mode_res->crtcs[0];
+ lease->connector_id = mode_res->connectors[0];
+ lease->drm_mode = randr_mode_to_drm_mode(&lease->randr_mode);
+ lease->bo_handle = 0;
+ lease->fb_handle = 0;
+
+ lease->fb = NULL;
+ lease->size = 0;
+ lease->width = 0;
+ lease->height = 0;
+ lease->stride = 0;
+
+ return 1;
+
+fail_res:
+ drmModeFreeResources(mode_res);
+
+ return 0;
+}
+
+static int
+init_lease(lease_t *lease)
+{
+ memset(lease, 0, sizeof (*lease));
+ lease->fd = -1;
+}
+
+static int
+make_lease(test_t *test, lease_t *lease, xcb_randr_output_t output, xcb_randr_crtc_t crtc)
+{
+ init_lease(lease);
+
+ if (!make_randr_lease(test, lease, output, crtc, true, NULL))
+ return 0;
+
+ if (!get_drm_resources(test, lease)) {
+ close(lease->fd);
+ lease->fd = -1;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+free_randr_lease(test_t *test, lease_t *lease)
+{
+ if (lease->randr_lease) {
+ xcb_randr_free_lease(test->conn, lease->randr_lease, 0);
+ free(xcb_get_input_focus_reply(test->conn, xcb_get_input_focus(test->conn), NULL));
+ lease->randr_lease = 0;
+ }
+}
+
+static void
+close_kernel_lease(test_t *test, lease_t *lease)
+{
+ if (lease->fd >= 0) {
+ close(lease->fd);
+ lease->fd = -1;
+ }
+}
+
+static void
+close_lease(test_t *test, lease_t *lease)
+{
+ destroy_fb(test, lease);
+ close_kernel_lease(test, lease);
+ free_randr_lease(test, lease);
+}
+
+static int
+modeset_lease(test_t *test, lease_t *lease)
+{
+ int ret;
+
+ ret = drmModeSetCrtc(lease->fd, lease->crtc_id, lease->fb_handle,
+ 0, 0, &lease->connector_id, 1, &lease->drm_mode);
+ if (ret) {
+ printf("\t\tmodeset failed %d %s\n", errno, strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+describe_error(test_t *test, xcb_generic_error_t *error)
+{
+ if (error->error_code <= XCB_IMPLEMENTATION) {
+ printf("\t\tcore error %d\n", error->error_code);
+ } else
+ switch (error->error_code - test->error_base) {
+ case XCB_RANDR_BAD_OUTPUT:
+ printf("\t\tbad output\n"); break;
+ case XCB_RANDR_BAD_CRTC:
+ printf("\t\tbad crtc\n"); break;
+ case XCB_RANDR_BAD_MODE:
+ printf("\t\tbad mode\n"); break;
+ case XCB_RANDR_BAD_PROVIDER:
+ printf("\t\tbad provider\n"); break;
+ default:
+ printf("\t\terror code %d\n", error->error_code);
+ }
+}
+
+static int
+test_init(test_t *test)
+{
+ memset(test, 0, sizeof(*test));
+
+ int screen;
+
+ test->conn = xcb_connect(NULL, &screen);
+
+ if (!test->conn) {
+ printf("\t\tCannot connect to X server\n");
+ return 0;
+ }
+
+ const xcb_setup_t *setup = xcb_get_setup(test->conn);
+
+ /*
+ * Find our root window
+ */
+ xcb_screen_iterator_t iter;
+ for (iter = xcb_setup_roots_iterator(setup); iter.rem; xcb_screen_next(&iter)) {
+ if (screen == 0) {
+ test->root = iter.data->root;
+ break;
+ }
+ --screen;
+ }
+
+ const xcb_query_extension_reply_t *qer = xcb_get_extension_data(test->conn, &xcb_randr_id);
+
+ if (!qer) {
+ printf("\t\tCannot get randr extension data\n");
+ xcb_disconnect(test->conn);
+ return 0;
+ }
+
+ test->error_base = qer->first_error;
+ test->event_base = qer->first_event;
+
+ return 1;
+}
+
+static int
+test_get_randr_resources(test_t *test)
+{
+ if (test->gsr_r) {
+ free(test->gsr_r);
+ test->gsr_r = NULL;
+ }
+
+ xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(test->conn, test->root);
+
+ xcb_generic_error_t *error;
+
+ test->gsr_r = xcb_randr_get_screen_resources_reply(test->conn, gsr_c, &error);
+
+ if (!test->gsr_r) {
+ printf("\t\tget_screen_resources failed\n");
+ if (error)
+ describe_error(test, error);
+ return 0;
+ }
+
+ test->config_timestamp = test->gsr_r->config_timestamp;
+
+ return 1;
+}
+
+static void
+test_fini(test_t *test)
+{
+ free(test->gsr_r);
+ test->gsr_r = NULL;
+
+ xcb_disconnect(test->conn);
+ test->conn = NULL;
+}
+
+/* Create a lease and display an image */
+static int lease_simple(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ if (!make_lease(test, &lease, output, XCB_NONE))
+ return 0;
+ if (!create_fb(test, &lease)) {
+ close_lease(test, &lease);
+ return 0;
+ }
+ if (!modeset_lease(test, &lease)) {
+ close_lease(test, &lease);
+ return 0;
+ }
+ sleep(5); /* slightly annoying, but monitors are slow */
+ close_lease(test, &lease);
+
+ return 1;
+}
+
+static int lease_close_x_first(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ if (!make_lease(test, &lease, output, XCB_NONE))
+ return 0;
+
+ free_randr_lease(test, &lease);
+ close_kernel_lease(test, &lease);
+
+ return 1;
+}
+
+static int lease_close_kernel_first(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ if (!make_lease(test, &lease, output, XCB_NONE))
+ return 0;
+
+ close_kernel_lease(test, &lease);
+ free_randr_lease(test, &lease);
+
+ return 1;
+}
+
+static int
+lease_not_use_x(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ if (!make_lease(test, &lease, output, XCB_NONE))
+ return 0;
+
+ /* Attempt to clear and then set a mode */
+ xcb_randr_set_crtc_config_cookie_t sccc = xcb_randr_set_crtc_config(test->conn,
+ lease.randr_crtc,
+ XCB_CURRENT_TIME,
+ test->config_timestamp,
+ 0, 0,
+ XCB_NONE,
+ XCB_RANDR_ROTATION_ROTATE_0,
+ 0, NULL);
+
+ xcb_generic_error_t *error;
+
+ xcb_randr_set_crtc_config_reply_t *sccr = xcb_randr_set_crtc_config_reply(test->conn,
+ sccc,
+ &error);
+
+ if (sccr) {
+ printf("\t\tunexpected successful set config\n");
+ goto fail;
+ }
+
+ if (error) {
+ if (error->error_code != XCB_ACCESS) {
+ printf ("Unexpected error code in set config\n");
+ describe_error(test, error);
+ goto fail;
+ }
+ }
+
+ sccc = xcb_randr_set_crtc_config(test->conn,
+ lease.randr_crtc,
+ XCB_CURRENT_TIME,
+ test->config_timestamp,
+ 0, 0,
+ lease.randr_mode.id,
+ XCB_RANDR_ROTATION_ROTATE_0,
+ 1, &lease.randr_output);
+
+ sccr = xcb_randr_set_crtc_config_reply(test->conn,
+ sccc,
+ &error);
+
+ if (sccr) {
+ printf("\t\tunexpected successful set config\n");
+ goto fail;
+ }
+
+ if (error) {
+ if (error->error_code != XCB_ACCESS) {
+ printf ("Unexpected error code in set config\n");
+ describe_error(test, error);
+ goto fail;
+ }
+ }
+
+ close_lease(test, &lease);
+
+ return 1;
+
+fail:
+ close_lease(test, &lease);
+ return 0;
+}
+
+static int
+lease_crtc_no_possible_outputs(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ if (!make_lease(test, &lease, output, XCB_NONE))
+ return 0;
+
+ xcb_randr_get_crtc_info_cookie_t gcic = xcb_randr_get_crtc_info(test->conn, lease.randr_crtc, test->config_timestamp);
+
+ xcb_generic_error_t *error;
+
+ xcb_randr_get_crtc_info_reply_t *gcir = xcb_randr_get_crtc_info_reply(test->conn, gcic, &error);
+
+ if (!gcir) {
+ printf("\t\tunexpected error\n");
+ describe_error(test, error);
+ close_lease(test, &lease);
+ return 0;
+ }
+
+ if (gcir->num_possible_outputs != 0) {
+ printf("\t\tnum_possible_outputs %d, not zero\n", gcir->num_outputs);
+ close_lease(test, &lease);
+ return 0;
+ }
+
+ close_lease(test, &lease);
+ return 1;
+}
+
+static int
+lease_check_crtc_disabled(test_t *test, lease_t *lease)
+{
+ xcb_randr_get_crtc_info_cookie_t gcic = xcb_randr_get_crtc_info(test->conn, lease->randr_crtc, test->config_timestamp);
+
+ xcb_generic_error_t *error;
+
+ xcb_randr_get_crtc_info_reply_t *gcir = xcb_randr_get_crtc_info_reply(test->conn, gcic, &error);
+
+ if (!gcir) {
+ printf("\t\tunexpected error\n");
+ describe_error(test, error);
+ goto fail;
+ }
+
+ if (gcir->x != 0) {
+ printf("\t\tcrtc x %d, not zero\n", gcir->x);
+ goto fail;
+ }
+
+ if (gcir->y != 0) {
+ printf("\t\tcrtc y %d, not zero\n", gcir->y);
+ goto fail;
+ }
+
+ if (gcir->width != 0) {
+ printf("\t\tcrtc width %d, not zero\n", gcir->width);
+ goto fail;
+ }
+
+ if (gcir->height != 0) {
+ printf("\t\tcrtc height %d, not zero\n", gcir->height);
+ goto fail;
+ }
+
+ if (gcir->mode != XCB_NONE) {
+ printf("\t\tcrtc mode %d, not None\n", gcir->mode);
+ goto fail;
+ }
+
+ if (gcir->rotation != XCB_RANDR_ROTATION_ROTATE_0) {
+ printf("\t\tcrtc rotation %d, not ROTATE_0\n", gcir->rotation);
+ goto fail;
+ }
+
+ if (gcir->num_outputs != 0) {
+ printf("\t\tcrtc num_outputs %d, not zero\n", gcir->num_outputs);
+ goto fail;
+ }
+
+ return 1;
+fail:
+ return 0;
+}
+
+static int
+lease_crtc_disabled(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ if (!make_lease(test, &lease, output, XCB_NONE))
+ return 0;
+
+ /* Make sure the crtc appears to be 'disabled' */
+ if (!lease_check_crtc_disabled(test, &lease))
+ goto fail;
+
+ close_lease(test, &lease);
+ return 1;
+fail:
+ close_lease(test, &lease);
+ return 0;
+}
+
+static int
+lease_check_output_disabled(test_t *test, lease_t *lease)
+{
+ xcb_randr_get_output_info_cookie_t goi_c = xcb_randr_get_output_info(test->conn, lease->randr_output, test->config_timestamp);
+
+ xcb_generic_error_t *error;
+
+ xcb_randr_get_output_info_reply_t *goi_r = xcb_randr_get_output_info_reply(test->conn, goi_c, &error);
+
+ if (!goi_r) {
+ printf("\t\tunexpected error\n");
+ describe_error(test, error);
+ goto fail;
+ }
+
+ if (goi_r->num_crtcs != 0) {
+ printf("\t\toutput num_crtcs %d not zero\n", goi_r->num_crtcs);
+ goto fail;
+ }
+
+ if (goi_r->num_clones != 0) {
+ printf("\t\toutput num_clones %d not zero\n", goi_r->num_clones);
+ goto fail;
+ }
+
+ if (goi_r->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
+ printf("\t\toutput conection %d not Disconnected\n", goi_r->connection);
+ goto fail;
+ }
+
+ return 1;
+
+fail:
+ return 0;
+}
+
+static int
+lease_outputs_no_crtcs_clones_connection(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ if (!make_lease(test, &lease, output, XCB_NONE))
+ return 0;
+
+ if (!lease_check_output_disabled(test, &lease))
+ goto fail;
+
+ close_lease(test, &lease);
+ return 1;
+
+fail:
+ close_lease(test, &lease);
+ return 0;
+}
+
+/* Lease remains active (in that the crtc and output appear disabled)
+ * after the X connection has been terminated
+ */
+static int
+lease_active_after_x(test_t *test, xcb_randr_output_t output)
+{
+ lease_t lease;
+
+ xcb_connection_t *save_conn = test->conn;
+
+ int screen;
+
+ /* Create another X connection */
+ xcb_connection_t *second_conn = xcb_connect(NULL, &screen);
+
+ if (!second_conn) {
+ printf("\t\tcan't reconnect to X server\n");
+ goto fail;
+ }
+
+ test->conn = second_conn;
+
+ if (!make_lease(test, &lease, output, XCB_NONE)) {
+ goto fail_restore;
+ }
+
+ /* Close the first connection */
+ xcb_disconnect(second_conn);
+
+ /* Our lease ID is no longer valid */
+ lease.randr_lease = XCB_NONE;
+
+ /* Restore our original connection */
+ test->conn = save_conn;
+
+ /* See if the lease is still active */
+
+ if (!lease_check_crtc_disabled(test, &lease))
+ goto fail;
+
+ if (!lease_check_output_disabled(test, &lease))
+ goto fail;
+
+ if (!create_fb(test, &lease)) {
+ close_lease(test, &lease);
+ return 0;
+ }
+
+ if (!modeset_lease(test, &lease)) {
+ close_lease(test, &lease);
+ return 0;
+ }
+ sleep(5); /* slightly annoying, but monitors are slow */
+
+ close_lease(test, &lease);
+
+ return 1;
+
+fail:
+
+ close_lease(test, &lease);
+
+fail_restore:
+
+ /* Restore our original connection */
+ test->conn = save_conn;
+
+ return 0;
+}
+
+static int
+lease_access_twice(test_t *test_1, xcb_randr_output_t output)
+{
+ lease_t lease_1, lease_2;
+ test_t test_2;
+
+ if (!test_init(&test_2))
+ return 0;
+
+ if (!test_get_randr_resources(&test_2))
+ return 0;
+
+ init_lease(&lease_1);
+ init_lease(&lease_2);
+
+ if (!make_lease(test_1, &lease_1, output, XCB_NONE))
+ goto fail_test;
+
+ xcb_generic_error_t *error = NULL;
+
+ /* Attempt to create the lease again on a new connection using the
+ * same resources
+ */
+
+ if (make_randr_lease(&test_2, &lease_2, output, lease_1.randr_crtc, false, &error)) {
+ printf("\t\tcreate lease twice succeeded\n");
+ goto fail_lease_1;
+ }
+
+ if (!error) {
+ printf("\t\tno error reported from lease failure\n");
+ goto fail_lease_1;
+ }
+
+ if (error->error_code != XCB_ACCESS) {
+ printf("\t\terror code %d, not Access\n", error->error_code);
+ goto fail_lease_1;
+ }
+
+ close_lease(&test_2, &lease_2);
+
+ close_lease(test_1, &lease_1);
+
+ test_fini(&test_2);
+
+ return 1;
+
+fail_lease_2:
+ close_lease(&test_2, &lease_2);
+fail_lease_1:
+ close_lease(test_1, &lease_1);
+
+fail_test:
+ test_fini(&test_2);
+
+ return 0;
+}
+
+/*
+ * First, just try to light up the monitors using the leased DRM fd.
+ *
+ * Then, test the following assertions:
+ *
+ * RRFreeLease
+ *
+ * 1. The X lease can be freed before the kernel fd
+ *
+ * 2. The kernel fd can be freed before the X lease
+ *
+ * RRCreateLease
+ *
+ * 1. Leased resources are not usable through X
+ *
+ * 2. Leased crtcs are reported as having no outputs
+ *
+ * 3. Leased crtcs will show as Disabled
+ *
+ * 4. Leased outputs will have no crtcs, no clones and show disconnected
+ *
+ * 5. Lease remains in effect until the FD is closed, even if the
+ * client disconnects from the X server.
+ *
+ * 6. Returns an Access error if any resources are already leased to
+ * another client
+ */
+
+static struct {
+ const char *name;
+ int (*func)(test_t *test, xcb_randr_output_t output);
+} tests[] = {
+
+ /* Touch test including DRM */
+ { "lease_simple", lease_simple },
+
+ /* RRFreeLease tests */
+ { "lease_close_x_first", lease_close_x_first },
+ { "lease_close_kernel_first", lease_close_kernel_first },
+
+ /* RRCreateLease tests */
+ { "lease_not_use_x", lease_not_use_x },
+ { "lease_crtc_no_possible_outputs", lease_crtc_no_possible_outputs },
+ { "lease_crtc_disabled", lease_crtc_disabled },
+ { "lease_output_no_crtcs_clones_connection", lease_outputs_no_crtcs_clones_connection },
+ { "lease_active_after_x", lease_active_after_x },
+ { "lease_access_twice", lease_access_twice },
+
+ {}
+};
+
+int main (int argc, char **argv)
+{
+ int failed = 0;
+
+ for (int t = 0; tests[t].name; t++) {
+ int screen;
+ test_t test;
+
+ printf("Starting test %s\n", tests[t].name);
+
+ if (!test_init(&test))
+ exit(1);
+
+ for (int o = 0; ; o++) {
+
+ if (!test_get_randr_resources(&test))
+ break;
+
+ if (o >= test.gsr_r->num_outputs)
+ break;
+
+ xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(test.gsr_r);
+ xcb_randr_output_t output = ro[o];
+ xcb_randr_get_output_info_cookie_t goi_c = xcb_randr_get_output_info(test.conn, output, test.config_timestamp);
+
+ xcb_randr_get_output_info_reply_t *goi_r = xcb_randr_get_output_info_reply(test.conn, goi_c, NULL);
+
+ uint8_t *output_name = xcb_randr_get_output_info_name(goi_r);
+ int output_name_length = xcb_randr_get_output_info_name_length(goi_r);
+
+ if (goi_r->connection != XCB_RANDR_CONNECTION_CONNECTED && goi_r->num_modes == 0) {
+ printf("\tTest %s %*.*s skipped\n", tests[t].name,
+ output_name_length, output_name_length, output_name);
+ } else {
+ if ((tests[t].func)(&test, output)) {
+ printf ("\tTest %s %*.*s passed\n", tests[t].name,
+ output_name_length, output_name_length, output_name);
+ } else {
+ printf ("\tTest %s %*.*s failed\n", tests[t].name,
+ output_name_length, output_name_length, output_name);
+ ++failed;
+ }
+ }
+ free(goi_r);
+ }
+
+ test_fini(&test);
+ }
+
+ exit(failed);
+}