[PATCH v5 2/5] Add XInput 2.x integration test framework

Chase Douglas chase.douglas at canonical.com
Thu Jun 7 14:01:22 PDT 2012


On 06/04/2012 09:41 PM, Peter Hutterer wrote:
> On Fri, May 25, 2012 at 09:06:09AM -0700, Chase Douglas wrote:
>> Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
>> Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
>> ---
>>  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 ba10b2e..0c511b8 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -2161,6 +2161,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 2028d35..ff9789e 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
>>  noinst_LIBRARIES = $(XORG_GTEST_BUILD_LIBS)
>> +
>> +if ENABLE_XORG_GTEST_INPUT_TESTS
>> +TESTS += gtest-tests
>>  endif
>> +endif
>> +
>> +noinst_PROGRAMS = $(TESTS)
>> +
>> +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..d3c3a30
>> --- /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
>> + *
>> + * @return Whether an event is available
>> + */
>> +bool WaitForEvent(::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 WaitForEventOfType(::Display *display, int type, int extension, int evtype,
>> +                        time_t timeout = 1000)
>> +{
>> +    while (WaitForEvent(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 WaitForDevice(::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 (WaitForEventOfType(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;
>> +        }
> 
> I didn't actually hit this but while debugging something else here I
> realised there's a race condition here. the way devices are enabled, you'll
> see the XISlaveAdded before the device is enabled. In the unlikely case that
> this test is too fast, you may be sending events before the server will take
> them, causing them to be discarded. 
> this will result in heisenbugs as some tests may arbitrarily fail.
> 
> simply changing this to XIDeviceEnabled should do the job, though you'll
> need some more exception handling in case you get the enabled for other
> devices, I suspect.
> 
> follow-up patch for this is fine with me.

I went ahead and changed this from XISlaveAdded to XIDeviceEnabled both
above and below. I think your concern about exception handling in case
other devices are enabled instead is properly handled by the logic below.

> Cheers,
>   Peter
> 
>> +
>> +        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
>>
> _______________________________________________
> xorg-devel at lists.x.org: X.Org development
> Archives: http://lists.x.org/archives/xorg-devel
> Info: http://lists.x.org/mailman/listinfo/xorg-devel
> 



More information about the xorg-devel mailing list