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 145 146 147 148 149 150 151 152 153 154
|
#!/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 os
from pathlib import Path
import pytest
import logging
import sys
import subprocess
import shutil
@pytest.fixture(scope="session", autouse=True)
def systemd_reload():
"""Make sure our hwdb and udev rules are up-to-date"""
try:
hwdb = os.environ.get("LIBWACOM_HWDB_FILE")
target = Path("/etc/udev/hwdb.d/99-libwacom-pytest.hwdb")
target.parent.mkdir(exist_ok=True, parents=True)
if hwdb:
shutil.copyfile(hwdb, target)
else:
import warnings
warnings.warn("LIBWACOM_HWDB_FILE is not set, using already installed hwdb")
subprocess.run(["systemd-hwdb", "update"], check=True)
yield
if hwdb:
os.unlink(target)
subprocess.run(["systemd-hwdb", "update"], check=True)
except (IOError, FileNotFoundError, subprocess.CalledProcessError) as e:
# If any of the commands above are not found (most likely the system
# simply does not use systemd), just skip.
logging.critical(f"{e}")
pytest.skip(f"Skipping test: {e}")
except Exception as e:
logging.critical(f"{e}")
pytest.fail(f"Aborting test: {e}")
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
try:
vid = int(vid, 16)
pid = int(pid, 16)
except ValueError as e:
print(f"Invalid vid/pid in {match} in {f}", file=sys.stderr)
raise e
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, ids=[t.name for t in tablets])
@pytest.mark.skipif(sys.platform != "linux", reason="This test requires udev")
def test_hwdb_files(tablet):
# Note: the actual name doesn't really matter, all our hwdb files use either "*"
# or "* Finger", etc. It does matter for that "Finger" suffix though.
query = f"libwacom:name:{tablet.name}:input:b{tablet.bus:04X}v{tablet.vid:04X}p{tablet.pid:04X}"
logging.debug(query)
r = subprocess.run(
["systemd-hwdb", "query", query], check=True, capture_output=True
)
stdout = r.stdout.decode("utf-8").strip()
assert stdout, f"No output recorded for query {query}"
logging.debug(stdout)
props = {}
for line in filter(lambda line: len(line) > 1, stdout.split("\n")):
print(line)
k, v = line.split("=")
props[k] = v
assert "ID_INPUT" in props
assert props["ID_INPUT"] == "1"
assert "ID_INPUT_TABLET" in props
assert props["ID_INPUT_TABLET"] == "1"
if "ID_INPUT_JOYSTICK" not in props:
assert props["ID_INPUT_JOYSTICK"] == "0"
if "Finger" in tablet.name:
if tablet.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 tablet.name:
if "Wacom Bamboo Pad" not in tablet.name or "Pad Pad" in tablet.name:
assert "ID_INPUT_TABLET_PAD" in props
|