[PATCH 2/2] Add test for XIQueryPointer button mask when physical touch is active
Peter Hutterer
peter.hutterer at who-t.net
Tue May 1 22:21:33 PDT 2012
On Wed, Apr 25, 2012 at 06:15:51PM -0700, Chase Douglas wrote:
> Signed-off-by: Chase Douglas <chase.douglas at 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.
I don't think I'm really qualified to review that properly, I'm not too
familiar with the xorg-gtest.
one comment: this patch should probably be split into the actual
XIQueryPointer part and the misc helper functions beeing added.
> 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
I really recommend renaming these as touch_1_begin and touch_1_end. 1 could
otherwise refer to buttons as well.
> 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;
> +
this could be simplified with XCheckTypedEvent
> +
> + 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));
> +
odd choice of line breaking. makes the code harder to read, IMO.
> + mask.deviceid = XIAllMasterDevices;
> + XIClearMask(mask.mask, XI_HierarchyChanged);
> + XISetMask(mask.mask, XI_ButtonPress);
> +
> + ASSERT_EQ(Success,
> + XISelectEvents(Display(), DefaultRootWindow(Display()), &mask,
> + 1));
as above
rest looks ok (only skimmed it).
Cheers,
Peter
> +
> + 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
>
More information about the xorg-devel
mailing list