1a46c0ec8Sopenharmony_ci#!/usr/bin/env python3
2a46c0ec8Sopenharmony_ci#
3a46c0ec8Sopenharmony_ci# This file is formatted with Python Black
4a46c0ec8Sopenharmony_ci#
5a46c0ec8Sopenharmony_ci# Run with pytest
6a46c0ec8Sopenharmony_ci
7a46c0ec8Sopenharmony_cifrom pathlib import Path
8a46c0ec8Sopenharmony_ci
9a46c0ec8Sopenharmony_ciimport configparser
10a46c0ec8Sopenharmony_ciimport os
11a46c0ec8Sopenharmony_ciimport pytest
12a46c0ec8Sopenharmony_ciimport re
13a46c0ec8Sopenharmony_ci
14a46c0ec8Sopenharmony_ci# see the IDs from
15a46c0ec8Sopenharmony_ci# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L772
16a46c0ec8Sopenharmony_ci# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-dj.c#L1826
17a46c0ec8Sopenharmony_cilogitech_receivers = [
18a46c0ec8Sopenharmony_ci    0xC50C,  # USB_DEVICE_ID_S510_RECEIVER
19a46c0ec8Sopenharmony_ci    0xC517,  # USB_DEVICE_ID_S510_RECEIVER_2
20a46c0ec8Sopenharmony_ci    0xC512,  # USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500
21a46c0ec8Sopenharmony_ci    0xC513,  # USB_DEVICE_ID_MX3000_RECEIVER
22a46c0ec8Sopenharmony_ci    0xC51B,  # USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER
23a46c0ec8Sopenharmony_ci    0xC52B,  # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
24a46c0ec8Sopenharmony_ci    0xC52F,  # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER
25a46c0ec8Sopenharmony_ci    0xC532,  # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
26a46c0ec8Sopenharmony_ci    0xC534,  # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2
27a46c0ec8Sopenharmony_ci    0xC539,  # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1
28a46c0ec8Sopenharmony_ci    0xC53F,  # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1
29a46c0ec8Sopenharmony_ci    0xC53A,  # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY
30a46c0ec8Sopenharmony_ci    0xC545,  # Bolt receiver, not listed in the kernel (yet)
31a46c0ec8Sopenharmony_ci    0xC547,  # Bolt receiver, not listed in the kernel (yet)
32a46c0ec8Sopenharmony_ci    0xC548,  # Bolt receiver, not listed in the kernel (yet)
33a46c0ec8Sopenharmony_ci]
34a46c0ec8Sopenharmony_ci
35a46c0ec8Sopenharmony_ci
36a46c0ec8Sopenharmony_cidef quirksdir():
37a46c0ec8Sopenharmony_ci    return Path(os.getenv("MESON_SOURCE_ROOT") or ".") / "quirks"
38a46c0ec8Sopenharmony_ci
39a46c0ec8Sopenharmony_ci
40a46c0ec8Sopenharmony_cidef pytest_generate_tests(metafunc):
41a46c0ec8Sopenharmony_ci    # for any function that takes a "quirksfile" argument return the path to
42a46c0ec8Sopenharmony_ci    # a quirks file
43a46c0ec8Sopenharmony_ci    if "quirksfile" in metafunc.fixturenames:
44a46c0ec8Sopenharmony_ci        metafunc.parametrize("quirksfile", [f for f in quirksdir().glob("*.quirks")])
45a46c0ec8Sopenharmony_ci
46a46c0ec8Sopenharmony_ci
47a46c0ec8Sopenharmony_cidef test_matches_are_valid(quirksfile):
48a46c0ec8Sopenharmony_ci    quirks = configparser.ConfigParser(strict=True)
49a46c0ec8Sopenharmony_ci    # Don't convert to lowercase
50a46c0ec8Sopenharmony_ci    quirks.optionxform = lambda option: option  # type: ignore
51a46c0ec8Sopenharmony_ci    quirks.read(quirksfile)
52a46c0ec8Sopenharmony_ci
53a46c0ec8Sopenharmony_ci    for name, section in filter(lambda n: n != "DEFAULT", quirks.items()):
54a46c0ec8Sopenharmony_ci        bus = section.get("MatchBus")
55a46c0ec8Sopenharmony_ci        if bus is not None:
56a46c0ec8Sopenharmony_ci            assert bus in ("ps2", "usb", "bluetooth", "i2c", "spi")
57a46c0ec8Sopenharmony_ci
58a46c0ec8Sopenharmony_ci        vid = section.get("MatchVendor")
59a46c0ec8Sopenharmony_ci        if vid is not None:
60a46c0ec8Sopenharmony_ci            assert re.match(
61a46c0ec8Sopenharmony_ci                "0x[0-9A-F]{4}", vid
62a46c0ec8Sopenharmony_ci            ), f"{quirksfile}: {name}: {vid} must be uppercase hex (0xAB12)"
63a46c0ec8Sopenharmony_ci
64a46c0ec8Sopenharmony_ci        pid = section.get("MatchProduct")
65a46c0ec8Sopenharmony_ci        if pid is not None:
66a46c0ec8Sopenharmony_ci            assert re.match(
67a46c0ec8Sopenharmony_ci                "0x[0-9A-F]{4}", pid
68a46c0ec8Sopenharmony_ci            ), f"{quirksfile}: {name}: {pid} must be uppercase hex (0xAB12)"
69a46c0ec8Sopenharmony_ci
70a46c0ec8Sopenharmony_ci
71a46c0ec8Sopenharmony_cidef test_match_product_is_not_a_logitech_receiver(quirksfile):
72a46c0ec8Sopenharmony_ci    quirks = configparser.ConfigParser(strict=True)
73a46c0ec8Sopenharmony_ci    # Don't convert to lowercase
74a46c0ec8Sopenharmony_ci    quirks.optionxform = lambda option: option  # type: ignore
75a46c0ec8Sopenharmony_ci    quirks.read(quirksfile)
76a46c0ec8Sopenharmony_ci
77a46c0ec8Sopenharmony_ci    for name, section in filter(lambda n: n != "DEFAULT", quirks.items()):
78a46c0ec8Sopenharmony_ci        vid = int(section.get("MatchVendor", "0x0"), 16)
79a46c0ec8Sopenharmony_ci        if vid == 0x046D:
80a46c0ec8Sopenharmony_ci            pid = int(section.get("MatchProduct", "0x0"), 16)
81a46c0ec8Sopenharmony_ci            assert (
82a46c0ec8Sopenharmony_ci                pid not in logitech_receivers
83a46c0ec8Sopenharmony_ci            ), f"{quirksfile}: {name}: applies to a Logitech Receiver"
84a46c0ec8Sopenharmony_ci
85a46c0ec8Sopenharmony_ci
86a46c0ec8Sopenharmony_cidef main():
87a46c0ec8Sopenharmony_ci    args = [__file__]
88a46c0ec8Sopenharmony_ci    try:
89a46c0ec8Sopenharmony_ci        import xdist  # noqa
90a46c0ec8Sopenharmony_ci
91a46c0ec8Sopenharmony_ci        ncores = os.environ.get("FDO_CI_CONCURRENT", "auto")
92a46c0ec8Sopenharmony_ci        args += ["-n", ncores]
93a46c0ec8Sopenharmony_ci    except ImportError:
94a46c0ec8Sopenharmony_ci        pass
95a46c0ec8Sopenharmony_ci
96a46c0ec8Sopenharmony_ci    return pytest.main(args)
97a46c0ec8Sopenharmony_ci
98a46c0ec8Sopenharmony_ci
99a46c0ec8Sopenharmony_ciif __name__ == "__main__":
100a46c0ec8Sopenharmony_ci    raise SystemExit(main())
101