tools/replay: check the recorded udev properties against the local properties
authorPeter Hutterer <peter.hutterer@who-t.net>
Wed, 9 Sep 2020 00:50:42 +0000 (10:50 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Fri, 11 Sep 2020 04:36:03 +0000 (04:36 +0000)
Where a device is replayed locally for testing, its udev properties should
match the recorded properties. Otherwise the testing results will not be
reliable.

The exception here is the device group which we currently don't set for
emulated devices and even if we did, it may intentionally differ anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
tools/libinput-replay

index 401dbbc3740a6fd35aa43a13ab148c92087a1d74..556cf4810a8a2bd5ca5c6396d4e0e43ce62337e0 100755 (executable)
@@ -33,6 +33,7 @@ from pathlib import Path
 try:
     import libevdev
     import yaml
+    import pyudev
 except ModuleNotFoundError as e:
     print('Error: {}'.format(e), file=sys.stderr)
     print('One or more python modules are missing. Please install those '
@@ -60,6 +61,41 @@ def fetch(yaml, key):
         raise YamlException('Failed to get \'{}\' from recording.'.format(key))
 
 
+def check_udev_properties(yaml_data, uinput):
+    '''
+    Compare the properties our new uinput device has with the ones from the
+    recording and ring the alarm bell if one of them is off.
+    '''
+    yaml_udev_section = fetch(yaml_data, 'udev')
+    yaml_udev_props = fetch(yaml_udev_section, 'properties')
+    yaml_props = {k: v for (k, v) in [prop.split('=', maxsplit=1) for prop in yaml_udev_props]}
+    try:
+        # We don't assign this one to virtual devices
+        del yaml_props['LIBINPUT_DEVICE_GROUP']
+    except KeyError:
+        pass
+
+    # give udev some time to catch up
+    time.sleep(0.2)
+    context = pyudev.Context()
+    udev_device = pyudev.Devices.from_device_file(context, uinput.devnode)
+    for name, value in udev_device.properties.items():
+        if name in yaml_props:
+            if yaml_props[name] != value:
+                error(f'Warning: udev property mismatch: recording has {name}={yaml_props[name]}, device has {name}={value}')
+            del yaml_props[name]
+        else:
+            # The list of properties we add to the recording, see libinput-record.c
+            prefixes = ("ID_INPUT", "LIBINPUT", "EVDEV_ABS", "MOUSE_DPI", "POINTINGSTICK_")
+            for prefix in prefixes:
+                if name.startswith(prefix):
+                    error(f'Warning: unexpected property: {name}={value}')
+
+    # the ones we found above were removed from the dict
+    for name, value in yaml_props.items():
+        error(f'Warning: device is missing recorded udev property: {name}={value}')
+
+
 def create(device):
     evdev = fetch(device, 'evdev')
 
@@ -95,6 +131,9 @@ def create(device):
         d.enable(libevdev.propbit(prop))
 
     uinput = d.create_uinput_device()
+
+    check_udev_properties(device, uinput)
+
     return uinput