[PATCH v2 2/3] Add XInput 2.x integration test framework

Peter Hutterer peter.hutterer at who-t.net
Tue May 15 16:26:18 PDT 2012


On Tue, May 15, 2012 at 11:05:26AM -0700, Chase Douglas wrote:
> Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
> ---
> Changes since v1:
> * Split commit into XInput 2.x integration test framework and XIQueryPointer
>   test
> 
>  configure.ac                 |   14 +++
>  test/integration/.gitignore  |    1 +
>  test/integration/Makefile.am |   24 ++++++
>  test/integration/xi2.cpp     |  194 ++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 233 insertions(+)
>  create mode 100644 test/integration/.gitignore
>  create mode 100644 test/integration/xi2.cpp
> 
> diff --git a/configure.ac b/configure.ac
> index fe350c9..bc9f46f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -2141,6 +2141,20 @@ if [test "x$XORG" = xyes && test "x$enable_integration_tests" != xno]; then
>      fi
>  fi
>  
> +PKG_CHECK_MODULES(TEST_X11, x11, have_test_x11=yes, have_test_x11=no)
> +PKG_CHECK_MODULES(TEST_XINPUT, [xi >= 1.6], have_test_xinput=yes, have_test_xinput=no)
> +
> +if [test "x$have_test_x11" != xyes]; then
> +    AC_MSG_NOTICE([libX11 not available, skipping input tests])
> +elif [test "x$have_test_xinput" != xyes]; then
> +    AC_MSG_NOTICE([libXi 1.6 not available, skipping input tests])
> +elif [test "x$have_xorg_gtest_evemu" != xyes]; then
> +    AC_MSG_NOTICE([uTouch-Evemu not available, skipping input tests])
> +else
> +    enable_input_tests=yes
> +fi
> +AM_CONDITIONAL(ENABLE_XORG_GTEST_INPUT_TESTS, [test "x$enable_input_tests" = xyes])
> +
>  dnl and the rest of these are generic, so they're in config.h
>  dnl 
>  dnl though, thanks to the passing of some significant amount of time, the
> diff --git a/test/integration/.gitignore b/test/integration/.gitignore
> new file mode 100644
> index 0000000..ab86ebf
> --- /dev/null
> +++ b/test/integration/.gitignore
> @@ -0,0 +1 @@
> +gtest-tests
> diff --git a/test/integration/Makefile.am b/test/integration/Makefile.am
> index e70d642..3b7c858 100644
> --- a/test/integration/Makefile.am
> +++ b/test/integration/Makefile.am
> @@ -1,4 +1,28 @@
> +TESTS =
> +
>  if ENABLE_XORG_GTEST_TESTS
>  include $(top_srcdir)/test/integration/Makefile-xorg-gtest.am
>  check_LIBRARIES = $(XORG_GTEST_BUILD_LIBS)
> +
> +if ENABLE_XORG_GTEST_INPUT_TESTS
> +TESTS += gtest-tests
>  endif
> +endif
> +
> +check_PROGRAMS = $(TESTS)

noinst_PROGRAMS is better here.

> +
> +AM_CPPFLAGS = \
> +	-DDEFAULT_XORG_SERVER=\"$(abs_top_builddir)/hw/xfree86/Xorg\" \
> +	-DTEST_ROOT_DIR=\"$(abs_top_srcdir)/test/integration/\" \
> +	$(GTEST_CPPFLAGS) \
> +	$(XORG_GTEST_CPPFLAGS)
> +
> +AM_CXXFLAGS = $(GTEST_CXXFLAGS) $(XORG_GTEST_CXXFLAGS)
> +
> +gtest_tests_SOURCES = xi2.cpp
> +gtest_tests_LDADD = \
> +	$(TEST_XINPUT_LIBS) \
> +	$(TEST_X11_LIBS) \
> +	$(XORG_GTEST_LIBS) \
> +	$(EVEMU_LIBS) \
> +	$(XORG_GTEST_MAIN_LIBS)
> diff --git a/test/integration/xi2.cpp b/test/integration/xi2.cpp
> new file mode 100644
> index 0000000..68974a9
> --- /dev/null
> +++ b/test/integration/xi2.cpp
> @@ -0,0 +1,194 @@
> +#include <stdexcept>
> +
> +#include <xorg/gtest/xorg-gtest.h>
> +
> +#include <X11/extensions/XInput2.h>
> +
> +namespace {
> +
> +/**
> + * Wait for an event on the X connection.
> + *
> + * param [in] display The X display connection
> + * param [in] timeout The timeout in milliseconds

shouldn't these be @param?

Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net> otherwise

one question that remains though: my impression was that xorg-gtests would
go into the xorg-gtest repo but these are for the xserver repo. What's the
plan here?

wait_for_event on a Display* seems generic enough that I don't quite
understand why this isn't part of the xorg-gtest repo.

Cheers,
  Peter

> + *
> + * @return Whether an event is available
> + */
> +bool wait_for_event(::Display *display, time_t timeout = 1000)
> +{
> +    fd_set fds;
> +    FD_ZERO(&fds);
> +
> +    int display_fd = ConnectionNumber(display);
> +
> +    XSync(display, False);
> +
> +    if (XPending(display))
> +        return true;
> +    else {
> +        FD_SET(display_fd, &fds);
> +
> +        struct timeval timeval = {
> +            static_cast<time_t>(timeout / 1000),
> +            static_cast<time_t>(timeout % 1000),
> +        };
> +
> +        int ret;
> +        if (timeout)
> +            ret = select(display_fd + 1, &fds, NULL, NULL, &timeval);
> +        else
> +            ret = select(display_fd + 1, &fds, NULL, NULL, NULL);
> +
> +        if (ret < 0)
> +            throw std::runtime_error("Failed to select on X fd");
> +
> +        if (ret == 0)
> +            return false;
> +
> +        return XPending(display);
> +    }
> +}
> +
> +/**
> + * Wait for an event of a specific type on the X connection.
> + *
> + * All events preceding the matching event are discarded. If no event was found
> + * before the timeout expires, all events in the queue will have been discarded.
> + *
> + * param [in] display   The X display connection
> + * param [in] type      The X core protocol event type
> + * param [in] extension The X extension opcode of a generic event, or -1 for any
> + *                      generic event
> + * param [in] evtype    The X extension event type of a generic event, or -1 for
> + *                      any event of the given extension
> + * param [in] timeout   The timeout in milliseconds
> + *
> + * @return Whether an event is available
> + */
> +bool wait_for_event_of_type(::Display *display, int type, int extension,
> +                            int evtype, time_t timeout = 1000)
> +{
> +    while (wait_for_event(display)) {
> +        XEvent event;
> +        if (!XPeekEvent(display, &event))
> +            throw std::runtime_error("Failed to peek X event");
> +
> +        if (event.type != type) {
> +            if (XNextEvent(display, &event) != Success)
> +                throw std::runtime_error("Failed to remove X event");
> +            continue;
> +        }
> +
> +        if (event.type != GenericEvent || extension == -1)
> +            return true;
> +
> +        XGenericEvent *generic_event = reinterpret_cast<XGenericEvent*>(&event);
> +
> +        if (generic_event->extension != extension) {
> +            if (XNextEvent(display, &event) != Success)
> +                throw std::runtime_error("Failed to remove X event");
> +            continue;
> +        }
> +
> +        if (evtype == -1 || generic_event->evtype == evtype)
> +            return true;
> +
> +        if (XNextEvent(display, &event) != Success)
> +            throw std::runtime_error("Failed to remove X event");
> +    }
> +}
> +
> +/**
> + * Wait for a specific device to be added to the server.
> + *
> + * param [in] display The X display connection
> + * param [in] name    The name of the device to wait for
> + * param [in] timeout The timeout in milliseconds
> + *
> + * @return Whether the device was added
> + */
> +bool wait_for_device(::Display *display, const std::string &name,
> +                     time_t timeout = 1000)
> +{
> +    int opcode;
> +    int event_start;
> +    int error_start;
> +
> +    if (!XQueryExtension(display, "XInputExtension", &opcode, &event_start,
> +                         &error_start))
> +        throw std::runtime_error("Failed to query XInput extension");
> +
> +    while (wait_for_event_of_type(display, GenericEvent, opcode,
> +                                  XI_HierarchyChanged)) {
> +        XEvent event;
> +        if (XNextEvent(display, &event) != Success)
> +            throw std::runtime_error("Failed to get X event");
> +
> +        XGenericEventCookie *xcookie =
> +            reinterpret_cast<XGenericEventCookie*>(&event.xcookie);
> +        if (!XGetEventData(display, xcookie))
> +            throw std::runtime_error("Failed to get X event data");
> +
> +        XIHierarchyEvent *hierarchy_event =
> +            reinterpret_cast<XIHierarchyEvent*>(xcookie->data);
> +
> +        if (!(hierarchy_event->flags & XISlaveAdded)) {
> +            XFreeEventData(display, xcookie);
> +            continue;
> +        }
> +
> +        bool device_found = false;
> +        for (int i = 0; i < hierarchy_event->num_info; i++) {
> +            if (!(hierarchy_event->info[i].flags & XISlaveAdded))
> +                continue;
> +
> +            int num_devices;
> +            XIDeviceInfo *device_info =
> +                XIQueryDevice(display, hierarchy_event->info[i].deviceid,
> +                              &num_devices);
> +            if (num_devices != 1 || !device_info)
> +                throw std::runtime_error("Failed to query device");
> +
> +            if (name.compare(device_info[0].name) == 0) {
> +                device_found = true;
> +                break;
> +            }
> +        }
> +
> +        XFreeEventData(display, xcookie);
> +
> +        if (device_found)
> +            return true;
> +    }
> +
> +    return false;
> +}
> +
> +}
> +
> +/**
> + * A test fixture for testing the XInput 2.x extension.
> + *
> + * @tparam The XInput 2.x minor version
> + */
> +class XInput2Test : public xorg::testing::Test,
> +                    public ::testing::WithParamInterface<int> {
> +protected:
> +    virtual void SetUp()
> +    {
> +        ASSERT_NO_FATAL_FAILURE(xorg::testing::Test::SetUp());
> +
> +        int event_start;
> +        int error_start;
> +
> +        ASSERT_TRUE(XQueryExtension(Display(), "XInputExtension", &xi2_opcode_,
> +                                    &event_start, &error_start));
> +
> +        int major = 2;
> +        int minor = GetParam();
> +
> +        ASSERT_EQ(Success, XIQueryVersion(Display(), &major, &minor));
> +    }
> +
> +    int xi2_opcode_;
> +};
> -- 
> 1.7.9.5



More information about the xorg-devel mailing list