[PATCH xserver 10/10] test: Add randr lease tests

Keith Packard keithp at keithp.com
Fri Dec 22 02:54:42 UTC 2017


Tests for RRCreateLease and RRFreeLease

Signed-off-by: Keith Packard <keithp at keithp.com>
---
 configure.ac             |    8 +
 test/Makefile.am         |    2 +-
 test/randr/Makefile.am   |   14 +
 test/randr/randr_lease.c | 1066 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1089 insertions(+), 1 deletion(-)
 create mode 100644 test/randr/Makefile.am
 create mode 100644 test/randr/randr_lease.c

diff --git a/configure.ac b/configure.ac
index c9139e0bd..663e764fd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1285,6 +1285,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)
@@ -2538,6 +2545,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);
+}
-- 
2.15.1



More information about the xorg-devel mailing list