Signed-off-by: Chase Douglas <chase.doug...@canonical.com> --- The functions to wait for specific events should probably be moved to xorg-gtest eventually, but I want to make sure they are generic enough for all use cases before doing that.
configure.ac | 14 + test/integration/.gitignore | 1 + test/integration/Makefile.am | 24 ++ .../recordings/ntrig_dell_xt2/1_begin.record | 11 + .../recordings/ntrig_dell_xt2/1_end.record | 3 + .../recordings/ntrig_dell_xt2/device.prop | 32 +++ test/integration/xi2.cpp | 267 ++++++++++++++++++++ 7 files changed, 352 insertions(+), 0 deletions(-) create mode 100644 test/integration/.gitignore create mode 100644 test/integration/recordings/ntrig_dell_xt2/1_begin.record create mode 100644 test/integration/recordings/ntrig_dell_xt2/1_end.record create mode 100644 test/integration/recordings/ntrig_dell_xt2/device.prop 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) + +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/recordings/ntrig_dell_xt2/1_begin.record b/test/integration/recordings/ntrig_dell_xt2/1_begin.record new file mode 100644 index 0000000..28a849b --- /dev/null +++ b/test/integration/recordings/ntrig_dell_xt2/1_begin.record @@ -0,0 +1,11 @@ +E: 1327542640.244087 0003 0000 2745 +E: 1327542640.244089 0003 0001 1639 +E: 1327542640.244090 0003 0035 2745 +E: 1327542640.244091 0003 0036 1639 +E: 1327542640.244092 0003 0034 0 +E: 1327542640.244093 0003 0030 468 +E: 1327542640.244094 0003 0031 306 +E: 1327542640.244095 0000 0002 0 +E: 1327542640.244251 0001 014d 1 +E: 1327542640.244251 0001 014a 1 +E: 1327542640.244253 0000 0000 0 diff --git a/test/integration/recordings/ntrig_dell_xt2/1_end.record b/test/integration/recordings/ntrig_dell_xt2/1_end.record new file mode 100644 index 0000000..cd6a9d9 --- /dev/null +++ b/test/integration/recordings/ntrig_dell_xt2/1_end.record @@ -0,0 +1,3 @@ +E: 1327542642.244253 0001 014d 0 +E: 1327542642.244253 0001 014a 0 +E: 1327542642.244253 0000 0000 0 diff --git a/test/integration/recordings/ntrig_dell_xt2/device.prop b/test/integration/recordings/ntrig_dell_xt2/device.prop new file mode 100644 index 0000000..2738c04 --- /dev/null +++ b/test/integration/recordings/ntrig_dell_xt2/device.prop @@ -0,0 +1,32 @@ +N: N-Trig MultiTouch (Virtual Test Device) +I: 0003 1b96 0001 0110 +P: 00 00 00 00 00 00 00 00 +B: 00 0b 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 04 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 02 00 00 00 00 00 00 00 00 +B: 03 03 00 00 00 00 01 73 00 +B: 04 00 00 00 00 00 00 00 00 +B: 05 00 00 00 00 00 00 00 00 +B: 11 00 00 00 00 00 00 00 00 +B: 12 00 00 00 00 00 00 00 00 +B: 15 00 00 00 00 00 00 00 00 +B: 15 00 00 00 00 00 00 00 00 +A: 00 0 9600 75 0 +A: 01 0 7200 78 0 +A: 28 0 255 0 0 +A: 30 0 9600 200 0 +A: 31 0 7200 150 0 +A: 34 0 1 0 0 +A: 35 0 9600 75 0 +A: 36 0 7200 78 0 diff --git a/test/integration/xi2.cpp b/test/integration/xi2.cpp new file mode 100644 index 0000000..ea2f783 --- /dev/null +++ b/test/integration/xi2.cpp @@ -0,0 +1,267 @@ +#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 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. + * + * 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_; +}; + +/** + * XIQueryPointer for XInput 2.1 and earlier should report the first button + * pressed if a touch is physically active. For XInput 2.2 and later clients, + * the first button should not be reported. + */ +TEST_P(XInput2Test, XIQueryPointerTouchscreen) +{ + XIEventMask mask; + mask.deviceid = XIAllDevices; + mask.mask_len = XIMaskLen(XI_HierarchyChanged); + mask.mask = reinterpret_cast<unsigned char*>( + calloc(XIMaskLen(XI_HierarchyChanged), 1)); + XISetMask(mask.mask, XI_HierarchyChanged); + + ASSERT_EQ(Success, + XISelectEvents(Display(), DefaultRootWindow(Display()), &mask, + 1)); + + mask.deviceid = XIAllMasterDevices; + XIClearMask(mask.mask, XI_HierarchyChanged); + XISetMask(mask.mask, XI_ButtonPress); + + ASSERT_EQ(Success, + XISelectEvents(Display(), DefaultRootWindow(Display()), &mask, + 1)); + + free(mask.mask); + + XFlush(Display()); + + xorg::testing::evemu::Device device( + TEST_ROOT_DIR "recordings/ntrig_dell_xt2/device.prop"); + + ASSERT_TRUE(wait_for_device(Display(), + "N-Trig MultiTouch (Virtual Test Device)")); + + device.Play(TEST_ROOT_DIR "recordings/ntrig_dell_xt2/1_begin.record"); + + ASSERT_TRUE(wait_for_event_of_type(Display(), GenericEvent, xi2_opcode_, + XI_ButtonPress)); + + XEvent event; + ASSERT_EQ(Success, XNextEvent(Display(), &event)); + + XGenericEventCookie *xcookie = &event.xcookie; + ASSERT_TRUE(XGetEventData(Display(), xcookie)); + + XIDeviceEvent *device_event = + reinterpret_cast<XIDeviceEvent*>(xcookie->data); + + Window root; + Window child; + double root_x; + double root_y; + double win_x; + double win_y; + XIButtonState buttons; + XIModifierState modifiers; + XIGroupState group; + ASSERT_TRUE(XIQueryPointer(Display(), device_event->deviceid, + DefaultRootWindow(Display()), &root, &child, + &root_x, &root_y, &win_x, &win_y, &buttons, + &modifiers, &group)); + + /* Test if button 1 is pressed */ + ASSERT_GE(buttons.mask_len, XIMaskLen(1)); + if (GetParam() < 2) + EXPECT_TRUE(XIMaskIsSet(buttons.mask, 0)); + else + EXPECT_FALSE(XIMaskIsSet(buttons.mask, 0)); + + XFreeEventData(Display(), xcookie); +} + +INSTANTIATE_TEST_CASE_P(, XInput2Test, ::testing::Range(0, 3)); -- 1.7.9.1 _______________________________________________ xorg-devel@lists.x.org: X.Org development Archives: http://lists.x.org/archives/xorg-devel Info: http://lists.x.org/mailman/listinfo/xorg-devel