1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
#!/usr/bin/env python3
#
# This test will only work where /dev/uinput is available.
# This test will reload the hwdb and udev rules
#
# Execute via pytest, it will:
# - load all data files and extract the matches
# - create a uinput device for each match
# - check if that device has the udev properties set we expect
import configparser
import libevdev
import os
from pathlib import Path
import pyudev
import pytest
import time
import logging
import sys
@pytest.fixture(scope='session', autouse=True)
def systemd_reload():
'''Make sure our hwdb and udev rules are up-to-date'''
import subprocess
try:
subprocess.run(['systemd-hwdb', 'update'])
subprocess.run(['systemctl', 'daemon-reload'])
except FileNotFoundError:
# If any of the commands above are not found (most likely the system
# simply does not use systemd), just skip.
raise pytest.skip()
def pytest_generate_tests(metafunc):
# for any function that takes a "tablet" argument return a Tablet object
# filled with exactly one DeviceMatch from the list of all .tablet files
# in the data dir. Where the tablet also has touch/buttons generate an
# extra Finger or Pad device
if 'tablet' in metafunc.fixturenames:
datadir = Path(os.getenv('MESON_SOURCE_ROOT') or '.') / 'data'
tablets = []
for f in datadir.glob('*.tablet'):
config = configparser.ConfigParser()
config.read(f)
name = config['Device']['Name']
want_pad = config['Device'].get('Buttons', 0)
want_finger = config['Features'].get('Touch') == 'true'
integrated_in = config['Device'].get('IntegratedIn', '').split(';')
is_touchscreen = set(integrated_in) & set(['Display', 'System'])
for match in config['Device']['DeviceMatch'].split(';'):
if not match or match == 'generic':
continue
bus, vid, pid = match.split(':')[:3] # skip the name part of the match
if bus not in ['usb', 'bluetooth']:
continue
vid = int(vid, 16)
pid = int(pid, 16)
if bus == 'usb':
bus = 0x3
elif bus == 'bluetooth':
bus = 0x5
class Tablet(object):
def __init__(self, name, bus, vid, pid, is_touchscreen=False):
self.name = name
self.bus = bus
self.vid = vid
self.pid = pid
self.is_touchscreen = is_touchscreen
tablets.append(Tablet(name, bus, vid, pid))
if want_pad:
tablets.append(Tablet(name + ' Pad', bus, vid, pid))
if want_finger:
tablets.append(Tablet(name + ' Finger', bus, vid, pid, is_touchscreen))
# our tablets list now becomes the list of arguments passed to the
# test functions taking a 'tablet' argument - one-by-one. So where
# tablets contains 10 entries, our test function will be called 10
# times.
metafunc.parametrize('tablet', tablets)
@pytest.fixture
def uinput(tablet):
dev = libevdev.Device()
dev.name = tablet.name
dev.id = {
'vendor': tablet.vid,
'product': tablet.pid,
'bustype': tablet.bus
}
# Our rules match on pid/vid, so purposely make this look like a
# non-tablet to verify that our rules apply anyway and not others
dev.enable(libevdev.EV_REL.REL_X)
dev.enable(libevdev.EV_REL.REL_Y)
dev.enable(libevdev.EV_KEY.BTN_LEFT)
dev.enable(libevdev.EV_KEY.BTN_RIGHT)
try:
uinput = dev.create_uinput_device()
# We'll need the is_touchscreen later, so let's hide it in the
# uinput device to pass it to the actual test
try:
uinput.is_touchscreen = tablet.is_touchscreen
except AttributeError:
pass
time.sleep(0.3)
return uinput
except OSError:
raise pytest.skip()
@pytest.mark.skipif(sys.platform != 'linux', reason='This test requires udev')
def test_hwdb_files(uinput):
logging.debug('{:04x}:{:04x} {}'.format(uinput.id['vendor'], uinput.id['product'], uinput.name))
udev = pyudev.Context()
dev = pyudev.Devices.from_device_file(udev, uinput.devnode)
props = list(dev.properties) # convert to list for better error messages
assert 'ID_INPUT' in props
assert dev.properties['ID_INPUT'] == '1'
assert 'ID_INPUT_TABLET' in props
assert dev.properties['ID_INPUT_TABLET'] == '1'
assert 'ID_INPUT_JOYSTICK' not in props
if 'Finger' in uinput.name:
if uinput.is_touchscreen:
assert 'ID_INPUT_TOUCHSCREEN' in props
else:
assert 'ID_INPUT_TOUCHPAD' in props
# For the Wacom Bamboo Pad we check for "Pad Pad" in the device name
if 'Pad' in uinput.name:
if 'Wacom Bamboo Pad' not in uinput.name or 'Pad Pad' in uinput.name:
assert 'ID_INPUT_TABLET_PAD' in props
|