summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2018-11-01 09:16:03 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2018-11-07 05:03:52 +0000
commit5cd27b070e13e942cbb80d031b4f0bc211e562b0 (patch)
treef0c2db55b801dd370880235f8106e89d4738d8b3
parent36af7d312b5b10790bd19e3066eb1cd4e4e5f6d4 (diff)
tools: add a test for tool option parsing
We don't check for correctness in the output as such, just that whatever combination of cmdline arguments still works/doesn't work. This is the scaffolding and a few tests, but needs to be filled in, especially for libinput measure and for some more complex combinations. valgrind: requires one more python-related suppression gitlab-ci: requires another environment variable so we know to skip the --device tests (udev will time out on those) meson: skip the test run in release builds, we pass the full path to the built libinput tool but rely on the subtool lookup that won't work in a release build Fixes #174 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--meson.build20
-rw-r--r--test/valgrind.suppressions6
-rwxr-xr-xtools/test-tool-option-parsing.py220
4 files changed, 242 insertions, 6 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 53ecf10c..eec286b3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -56,6 +56,8 @@ variables:
FREEBSD_DOCKER_IMAGE: $CI_REGISTRY/libinput/$CI_PROJECT_NAME/freebsd/11.2
# Until we have a VM with full access, we cannot run the test suite runner
SKIP_LIBINPUT_TEST_SUITE_RUNNER: 1
+ # udev isn't available/working properly in the containers
+ UDEV_NOT_AVAILABLE: 1
# When using docker-in-docker (dind), it's wise to use the overlayfs driver
# for improved performance.
DOCKER_DRIVER: overlay2
diff --git a/meson.build b/meson.build
index c9f78144..321e625a 100644
--- a/meson.build
+++ b/meson.build
@@ -577,12 +577,12 @@ endif
libinput_sources = [ 'tools/libinput-tool.c' ]
-executable('libinput',
- libinput_sources,
- dependencies : deps_tools,
- include_directories : [includes_src, includes_include],
- install : true
- )
+libinput_tool = executable('libinput',
+ libinput_sources,
+ dependencies : deps_tools,
+ include_directories : [includes_src, includes_include],
+ install : true
+ )
configure_file(input : 'tools/libinput.man',
output : 'libinput.1',
configuration : man_config,
@@ -598,6 +598,14 @@ executable('ptraccel-debug',
install : false
)
+# Don't run the test during a release build because we rely on the magic
+# subtool lookup
+if get_option('buildtype') == 'debug' or get_option('buildtype') == 'debugoptimized'
+ test('tool-option-parsing',
+ find_program('tools/test-tool-option-parsing.py'),
+ args : [libinput_tool.full_path()])
+endif
+
# the libinput tools check whether we execute from the builddir, this is
# the test to verify that lookup. We test twice, once as normal test
# run from the builddir, once after copying to /tmp
diff --git a/test/valgrind.suppressions b/test/valgrind.suppressions
index 6a4f4271..5e6add52 100644
--- a/test/valgrind.suppressions
+++ b/test/valgrind.suppressions
@@ -70,3 +70,9 @@
...
fun:execute_command
}
+{
+ python:Py_GetProgramFullPath
+ Memcheck:Cond
+ ...
+ fun:Py_GetProgramFullPath
+}
diff --git a/tools/test-tool-option-parsing.py b/tools/test-tool-option-parsing.py
new file mode 100755
index 00000000..b6f845e8
--- /dev/null
+++ b/tools/test-tool-option-parsing.py
@@ -0,0 +1,220 @@
+#!/usr/bin/python3
+# vim: set expandtab shiftwidth=4:
+# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
+#
+# Copyright © 2018 Red Hat, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import argparse
+import os
+import unittest
+import sys
+import subprocess
+import time
+
+
+class TestLibinputTool(unittest.TestCase):
+ libinput_tool = 'libinput'
+ subtool = None
+
+ def run_command(self, args):
+ args = [self.libinput_tool] + args
+ if self.subtool is not None:
+ args.insert(1, self.subtool)
+
+ with subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
+ time.sleep(0.1)
+ p.send_signal(2)
+ p.wait()
+ return p.returncode, p.stdout.read().decode('UTF-8'), p.stderr.read().decode('UTF-8')
+
+ def run_command_success(self, args):
+ rc, stdout, stderr = self.run_command(args)
+ # if we're running as user, we might fail the command but we should
+ # never get rc 2 (invalid usage)
+ self.assertIn(rc, [0, 1])
+
+ def run_command_unrecognised_option(self, args):
+ rc, stdout, stderr = self.run_command(args)
+ self.assertEqual(rc, 2)
+ self.assertTrue(stdout.startswith('Usage') or stdout == '')
+ self.assertIn('unrecognized option', stderr)
+
+ def run_command_missing_arg(self, args):
+ rc, stdout, stderr = self.run_command(args)
+ self.assertEqual(rc, 2)
+ self.assertTrue(stdout.startswith('Usage') or stdout == '')
+ self.assertIn('requires an argument', stderr)
+
+ def run_command_unrecognised_tool(self, args):
+ rc, stdout, stderr = self.run_command(args)
+ self.assertEqual(rc, 2)
+ self.assertTrue(stdout.startswith('Usage') or stdout == '')
+ self.assertIn('is not a libinput command', stderr)
+
+
+class TestLibinputCommand(TestLibinputTool):
+ subtool = None
+
+ def test_help(self):
+ rc, stdout, stderr = self.run_command(['--help'])
+ self.assertEqual(rc, 0)
+ self.assertTrue(stdout.startswith('Usage:'))
+ self.assertEqual(stderr, '')
+
+ def test_version(self):
+ rc, stdout, stderr = self.run_command(['--version'])
+ self.assertEqual(rc, 0)
+ self.assertTrue(stdout.startswith('1'))
+ self.assertEqual(stderr, '')
+
+ def test_invalid_arguments(self):
+ self.run_command_unrecognised_option(['--banana'])
+ self.run_command_unrecognised_option(['--foo'])
+ self.run_command_unrecognised_option(['--quiet'])
+ self.run_command_unrecognised_option(['--verbose'])
+ self.run_command_unrecognised_option(['--quiet', 'foo'])
+
+ def test_invalid_tools(self):
+ self.run_command_unrecognised_tool(['foo'])
+ self.run_command_unrecognised_tool(['debug'])
+ self.run_command_unrecognised_tool(['foo', '--quiet'])
+
+
+class TestToolWithOptions(object):
+ options = {
+ 'pattern': ['sendevents'],
+ # enable/disable options
+ 'enable-disable': [
+ 'tap',
+ 'drag',
+ 'drag-lock',
+ 'middlebutton',
+ 'natural-scrolling',
+ 'left-handed',
+ 'dwt'
+ ],
+ # options with distinct values
+ 'enums': {
+ 'set-click-method': ['none', 'clickfinger', 'buttonareas'],
+ 'set-scroll-method': ['none', 'twofinger', 'edge', 'button'],
+ 'set-profile': ['adaptive', 'flat'],
+ 'set-tap-map': ['lrm', 'lmr'],
+ },
+ # options with a range
+ 'ranges': {
+ 'set-speed': (float, -1.0, +1.0),
+ }
+ }
+
+ def test_udev_seat(self):
+ self.run_command_missing_arg(['--udev'])
+ self.run_command_success(['--udev', 'seat0'])
+ self.run_command_success(['--udev', 'seat1'])
+
+ @unittest.skipIf(os.environ.get('UDEV_NOT_AVAILABLE'), "udev required")
+ def test_device(self):
+ self.run_command_missing_arg(['--device'])
+ self.run_command_success(['--device', '/dev/input/event0'])
+ self.run_command_success(['--device', '/dev/input/event1'])
+
+ def test_options_pattern(self):
+ for option in self.options['pattern']:
+ self.run_command_success(['--disable-{}'.format(option), '*'])
+ self.run_command_success(['--disable-{}'.format(option), 'abc*'])
+
+ def test_options_enable_disable(self):
+ for option in self.options['enable-disable']:
+ self.run_command_success(['--enable-{}'.format(option)])
+ self.run_command_success(['--disable-{}'.format(option)])
+
+ def test_options_enums(self):
+ for option, values in self.options['enums'].items():
+ for v in values:
+ self.run_command_success(['--{}'.format(option), v])
+ self.run_command_success(['--{}={}'.format(option, v)])
+
+ def test_options_ranges(self):
+ for option, values in self.options['ranges'].items():
+ range_type, minimum, maximum = values
+ self.assertEqual(range_type, float)
+ step = (maximum - minimum)/10.0
+ value = minimum
+ while value < maximum:
+ self.run_command_success(['--{}'.format(option), str(value)])
+ self.run_command_success(['--{}={}'.format(option, value)])
+ value += step
+ self.run_command_success(['--{}'.format(option), str(maximum)])
+ self.run_command_success(['--{}={}'.format(option, maximum)])
+
+
+class TestDebugEvents(TestToolWithOptions, TestLibinputTool):
+ subtool = 'debug-events'
+
+ def test_verbose_quiet(self):
+ rc, stdout, stderr = self.run_command(['--verbose'])
+ self.assertEqual(rc, 0)
+ rc, stdout, stderr = self.run_command(['--quiet'])
+ self.assertEqual(rc, 0)
+ rc, stdout, stderr = self.run_command(['--verbose', '--quiet'])
+ self.assertEqual(rc, 0)
+ rc, stdout, stderr = self.run_command(['--quiet', '--verbose'])
+ self.assertEqual(rc, 0)
+
+ def test_invalid_arguments(self):
+ self.run_command_unrecognised_option(['--banana'])
+ self.run_command_unrecognised_option(['--foo'])
+ self.run_command_unrecognised_option(['--version'])
+
+
+class TestDebugGUI(TestToolWithOptions, TestLibinputTool):
+ subtool = 'debug-gui'
+
+ @classmethod
+ def setUpClass(cls):
+ if not os.getenv('DISPLAY') and not os.getenv('WAYLAND_DISPLAY'):
+ raise unittest.SkipTest()
+
+ def test_verbose_quiet(self):
+ rc, stdout, stderr = self.run_command(['--verbose'])
+ self.assertEqual(rc, 0)
+
+ def test_invalid_arguments(self):
+ self.run_command_unrecognised_option(['--quiet'])
+ self.run_command_unrecognised_option(['--banana'])
+ self.run_command_unrecognised_option(['--foo'])
+ self.run_command_unrecognised_option(['--version'])
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Verify a libinput tool\'s option parsing')
+ parser.add_argument('tool_path', metavar='/path/to/builddir/libinput',
+ type=str, nargs='?',
+ help='Path to the libinput tool in the builddir')
+ parser.add_argument('--verbose', action='store_true')
+ args = parser.parse_args()
+ if args.tool_path is not None:
+ TestLibinputTool.libinput_tool = args.tool_path
+ verbosity = 1
+ if args.verbose:
+ verbosity = 3
+ del sys.argv[1:]
+ unittest.main(verbosity=verbosity)