[PATCH v5 2/5] Add XInput 2.x integration test framework
Peter Hutterer
peter.hutterer at who-t.net
Thu Jun 7 18:09:08 PDT 2012
On Thu, Jun 07, 2012 at 02:01:22PM -0700, Chase Douglas wrote:
> 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.
oh, right. I misread this, it does loop until the enable comes for this
device. that is correct then.
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