## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License along ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """The tests work by creating a faked device (from the hidpp module) that uses provided data as responses to HID++ commands. The device uses some methods from the real device to set up data structures that are needed for some tests. """ from dataclasses import dataclass from struct import pack from typing import Any from typing import Optional import pytest from logitech_receiver import common from logitech_receiver import hidpp20 from logitech_receiver import settings_templates from logitech_receiver import special_keys from . import hidpp # TODO DpiSlidingXY, MouseGesturesXY, SpeedChange # TODO Gesture2Gestures, Gesture2Divert, Gesture2Params, MKeyLEDs, Equalizer # future per-key lighting @dataclass class RegisterTest: sclass: Any initial_value: Any = False write_value: Any = True write_params: str = "" tests = [ [ RegisterTest(settings_templates.RegisterHandDetection, False, True, [b"\x00\x00\x00"]), hidpp.Response("000030", 0x8101), # keyboard_hand_detection hidpp.Response("000000", 0x8001, "000000"), ], [ RegisterTest(settings_templates.RegisterHandDetection, True, False, [b"\x00\x00\x30"]), hidpp.Response("000000", 0x8101), # keyboard_hand_detection hidpp.Response("000030", 0x8001, "000030"), ], [ RegisterTest(settings_templates.RegisterSmoothScroll, False, True, [b"\x40"]), hidpp.Response("00", 0x8101), # mouse_button_flags hidpp.Response("40", 0x8001, "40"), ], [ RegisterTest(settings_templates.RegisterSideScroll, True, False, [b"\x00"]), hidpp.Response("02", 0x8101), # mouse_button_flags hidpp.Response("00", 0x8001, "00"), ], [ RegisterTest(settings_templates.RegisterFnSwap, False, True, [b"\x00\x01"]), hidpp.Response("0000", 0x8109), # keyboard_fn_swap hidpp.Response("0001", 0x8009, "0001"), ], [ RegisterTest( settings_templates._PerformanceMXDpi, common.NamedInt(0x88, "800"), common.NamedInt(0x89, "900"), [b"\x89"] ), hidpp.Response("88", 0x8163), # mouse_dpi hidpp.Response("89", 0x8063, "89"), ], ] @pytest.mark.parametrize("test", tests) def test_register_template(test, mocker): device = hidpp.Device(protocol=1.0, responses=test[1:]) spy_request = mocker.spy(device, "request") setting = test[0].sclass.build(device) value = setting.read(cached=False) cached_value = setting.read(cached=True) write_value = setting.write(test[0].write_value) assert setting is not None assert value == test[0].initial_value assert cached_value == test[0].initial_value assert write_value == test[0].write_value spy_request.assert_called_with(test[0].sclass.register + 0x8000, *test[0].write_params) @dataclass class FeatureTest: sclass: Any initial_value: Any = False write_value: Any = True write_fnid: int = 0x10 write_params: str = "" no_reply: Optional[bool] = False tests = [ [ FeatureTest(settings_templates.K375sFnSwap, False, True, 0x10, "FF01"), hidpp.Response("060001", 0x0000, "40A3"), # K375_FN_INVERSION hidpp.Response("FF0001", 0x0600, "FF"), hidpp.Response("FF0101", 0x0610, "FF01"), ], [ FeatureTest(settings_templates.FnSwap, True, False, 0x10, "00"), hidpp.Response("040001", 0x0000, "40A0"), # FN_INVERSION hidpp.Response("01", 0x0400), hidpp.Response("00", 0x0410, "00"), ], [ FeatureTest(settings_templates.NewFnSwap, True, False, 0x10, "00"), hidpp.Response("040001", 0x0000, "40A2"), # NEW_FN_INVERSION hidpp.Response("01", 0x0400), hidpp.Response("00", 0x0410, "00"), ], [ FeatureTest(settings_templates.Backlight, 0, 5, 0x10, "05"), hidpp.Response("060001", 0x0000, "1981"), # BACKLIGHT hidpp.Response("00", 0x0600), hidpp.Response("05", 0x0610, "05"), ], [ FeatureTest(settings_templates.Backlight2DurationHandsOut, 80, 160, 0x10, "0118FF00200040006000", None), hidpp.Response("040003", 0x0000, "1982"), # BACKLIGHT2 hidpp.Response("011830000000100040006000", 0x0400), hidpp.Response("0118FF00200040006000", 0x0410, "0118FF00200040006000"), ], [ FeatureTest(settings_templates.Backlight2DurationHandsIn, 320, 160, 0x10, "0118FF00200020006000", None), hidpp.Response("040003", 0x0000, "1982"), # BACKLIGHT2 hidpp.Response("011830000000200040006000", 0x0400), hidpp.Response("0118FF00200020006000", 0x0410, "0118FF00200020006000"), ], [ FeatureTest(settings_templates.Backlight2DurationPowered, 480, 80, 0x10, "0118FF00200040001000", None), hidpp.Response("040003", 0x0000, "1982"), # BACKLIGHT2 hidpp.Response("011830000000200040006000", 0x0400), hidpp.Response("0118FF00200040001000", 0x0410, "0118FF00200040001000"), ], [ FeatureTest(settings_templates.Backlight3, 0x50, 0x70, 0x20, "007009"), hidpp.Response("040001", 0x0000, "1983"), # BACKLIGHT3 hidpp.Response("50", 0x0410), hidpp.Response("70", 0x0420, "007009"), ], [ FeatureTest(settings_templates.HiResScroll, True, False, 0x10, "00"), hidpp.Response("040001", 0x0000, "2120"), # HI_RES_SCROLLING hidpp.Response("01", 0x0400), hidpp.Response("00", 0x0410, "00"), ], [ FeatureTest(settings_templates.LowresMode, False, True, 0x10, "01"), hidpp.Response("040001", 0x0000, "2130"), # LOWRES_WHEEL hidpp.Response("00", 0x0400), hidpp.Response("01", 0x0410, "01"), ], [ FeatureTest(settings_templates.HiresSmoothInvert, True, False, 0x20, "02"), hidpp.Response("040001", 0x0000, "2121"), # HIRES_WHEEL hidpp.Response("06", 0x0410), hidpp.Response("02", 0x0420, "02"), ], [ FeatureTest(settings_templates.HiresSmoothResolution, True, False, 0x20, "04"), hidpp.Response("040001", 0x0000, "2121"), # HIRES_WHEEL hidpp.Response("06", 0x0410), hidpp.Response("04", 0x0420, "04"), ], [ FeatureTest(settings_templates.HiresMode, False, True, 0x20, "07"), hidpp.Response("040001", 0x0000, "2121"), # HIRES_WHEEL hidpp.Response("06", 0x0410), hidpp.Response("07", 0x0420, "07"), ], [ FeatureTest(settings_templates.PointerSpeed, 0x0100, 0x0120, 0x10, "0120"), hidpp.Response("040001", 0x0000, "2205"), # POINTER_SPEED hidpp.Response("0100", 0x0400), hidpp.Response("0120", 0x0410, "0120"), ], [ FeatureTest(settings_templates.ThumbMode, True, False, 0x20, "0000"), hidpp.Response("040001", 0x0000, "2150"), # THUMB_WHEEL hidpp.Response("0100", 0x0410), hidpp.Response("0000", 0x0420, "0000"), ], [ FeatureTest(settings_templates.ThumbInvert, False, True, 0x20, "0101"), hidpp.Response("040001", 0x0000, "2150"), # THUMB_WHEEL hidpp.Response("0100", 0x0410), hidpp.Response("0101", 0x0420, "0101"), ], [ FeatureTest(settings_templates.DivertCrown, False, True, 0x20, "02"), hidpp.Response("040001", 0x0000, "4600"), # CROWN hidpp.Response("01", 0x0410), hidpp.Response("02", 0x0420, "02"), ], [ FeatureTest(settings_templates.CrownSmooth, True, False, 0x20, "0002"), hidpp.Response("040001", 0x0000, "4600"), # CROWN hidpp.Response("0001", 0x0410), hidpp.Response("0002", 0x0420, "0002"), ], [ FeatureTest(settings_templates.DivertGkeys, False, True, 0x20, "01"), hidpp.Response("040001", 0x0000, "8010"), # GKEY hidpp.Response("01", 0x0420, "01"), ], [ FeatureTest(settings_templates.ScrollRatchet, 2, 1, 0x10, "01"), hidpp.Response("040001", 0x0000, "2110"), # SMART_SHIFT hidpp.Response("02", 0x0400), hidpp.Response("01", 0x0410, "01"), ], [ FeatureTest(settings_templates.SmartShift, 1, 10, 0x10, "000A"), hidpp.Response("040001", 0x0000, "2110"), # SMART_SHIFT hidpp.Response("0100", 0x0400), hidpp.Response("000A", 0x0410, "000A"), ], [ FeatureTest(settings_templates.SmartShift, 5, 50, 0x10, "00FF"), hidpp.Response("040001", 0x0000, "2110"), # SMART_SHIFT hidpp.Response("0005", 0x0400), hidpp.Response("00FF", 0x0410, "00FF"), ], [ FeatureTest(settings_templates.SmartShiftEnhanced, 5, 50, 0x20, "00FF"), hidpp.Response("040001", 0x0000, "2111"), # SMART_SHIFT_ENHANCED hidpp.Response("0005", 0x0410), hidpp.Response("00FF", 0x0420, "00FF"), ], [ FeatureTest(settings_templates.DisableKeyboardKeys, {1: True, 8: True}, {1: False, 8: True}, 0x20, "08"), hidpp.Response("040003", 0x0000, "4521"), # KEYBOARD_DISABLE_KEYS hidpp.Response("09", 0x0400), hidpp.Response("09", 0x0410), hidpp.Response("08", 0x0420, "08"), ], [ FeatureTest(settings_templates.DualPlatform, 0, 1, 0x20, "01"), hidpp.Response("040003", 0x0000, "4530"), # DUALPLATFORM hidpp.Response("00", 0x0400), hidpp.Response("01", 0x0420, "01"), ], [ FeatureTest(settings_templates.MRKeyLED, False, True, 0x00, "01"), hidpp.Response("040003", 0x0000, "8030"), # MR hidpp.Response("01", 0x0400, "01"), ], [ FeatureTest(settings_templates.Sidetone, 5, 0xA, 0x10, "0A"), hidpp.Response("040003", 0x0000, "8300"), # SIDETONE hidpp.Response("05", 0x0400), hidpp.Response("0A", 0x0410, "0A"), ], [ FeatureTest(settings_templates.ADCPower, 5, 0xA, 0x20, "0A"), hidpp.Response("040003", 0x0000, "1F20"), # ADC_MEASUREMENT hidpp.Response("05", 0x0410), hidpp.Response("0A", 0x0420, "0A"), ], [ FeatureTest(settings_templates.LEDControl, 0, 1, 0x80, "01"), hidpp.Response("040003", 0x0000, "8070"), # COLOR_LED_EFFECTS hidpp.Response("00", 0x0470), hidpp.Response("01", 0x0480, "01"), ], [ FeatureTest( settings_templates.LEDZoneSetting, hidpp20.LEDEffectSetting(ID=3, intensity=0x50, period=0x100), hidpp20.LEDEffectSetting(ID=3, intensity=0x50, period=0x101), 0x30, "000000000000000101500000", ), hidpp.Response("040001", 0x0000, "8070"), # COLOR_LED_EFFECTS hidpp.Response("0100000001", 0x0400), hidpp.Response("00000102", 0x0410, "00FF00"), hidpp.Response("0000000300040005", 0x0420, "000000"), hidpp.Response("0001000B00080009", 0x0420, "000100"), hidpp.Response("000000000000010050", 0x04E0, "00"), hidpp.Response("000000000000000101500000", 0x0430, "000000000000000101500000"), ], [ FeatureTest(settings_templates.RGBControl, 0, 1, 0x50, "0101"), hidpp.Response("040003", 0x0000, "8071"), # RGB_EFFECTS hidpp.Response("0000", 0x0450), hidpp.Response("010100", 0x0450, "0101"), ], [ FeatureTest( settings_templates.RGBEffectSetting, hidpp20.LEDEffectSetting(ID=3, intensity=0x50, period=0x100), hidpp20.LEDEffectSetting(ID=2, color=0x505050, speed=0x50), 0x10, "00015050505000000000000001", ), hidpp.Response("040003", 0x0000, "8071"), # RGB_EFFECTS hidpp.Response("FFFF0100000001", 0x0400, "FFFF00"), hidpp.Response("0000000102", 0x0400, "00FF00"), hidpp.Response("0000000300040005", 0x0400, "000000"), hidpp.Response("0001000200080009", 0x0400, "000100"), hidpp.Response("000000000000010050", 0x04E0, "00"), hidpp.Response("00015050505000000000000001", 0x0410, "00015050505000000000000001"), ], [ FeatureTest( settings_templates.RGBEffectSetting, None, hidpp20.LEDEffectSetting(ID=3, intensity=0x60, period=0x101), 0x10, "00000000000000010160000001", ), hidpp.Response("040003", 0x0000, "8071"), # RGB_EFFECTS hidpp.Response("FFFF0100000001", 0x0400, "FFFF00"), hidpp.Response("0000000102", 0x0400, "00FF00"), hidpp.Response("0000000300040005", 0x0400, "000000"), hidpp.Response("0001000200080009", 0x0400, "000100"), hidpp.Response("00000000000000010160000001", 0x0410, "00000000000000010160000001"), ], [ FeatureTest( settings_templates.RGBEffectSetting, None, hidpp20.LEDEffectSetting(ID=3, intensity=0x60, period=0x101), 0x10, "00020000000000010160000001", ), hidpp.Response("0A0000", 0x0000, "8071"), # RGB_EFFECTS hidpp.Response("FF000200020004000000000000000000", 0x0A00, "FFFF00"), hidpp.Response("00000002040000000000000000000000", 0x0A00, "00FF00"), hidpp.Response("00000000000000000000000000000000", 0x0A00, "000000"), hidpp.Response("00010001000000000000000000000000", 0x0A00, "000100"), hidpp.Response("00020003C00503E00000000000000000", 0x0A00, "000200"), hidpp.Response("0003000AC0011E0B0000000000000000", 0x0A00, "000300"), hidpp.Response("01000001070000000000000000000000", 0x0A00, "01FF00"), hidpp.Response("01000000000000000000000000000000", 0x0A00, "010000"), hidpp.Response("01010001000000000000000000000000", 0x0A00, "010100"), hidpp.Response("0102000AC0011E0B0000000000000000", 0x0A00, "010200"), hidpp.Response("01030003C00503E00000000000000000", 0x0A00, "010300"), hidpp.Response("01040004DCE1001E0000000000000000", 0x0A00, "010400"), hidpp.Response("0105000B000000320000000000000000", 0x0A00, "010500"), hidpp.Response("0106000C001B02340000000000000000", 0x0A00, "010600"), hidpp.Response("00020000000000010160000001", 0x0A10, "00020000000000010160000001"), ], ] @pytest.mark.parametrize("test", tests) def test_simple_template(test, mocker): device = hidpp.Device(responses=test[1:]) spy_feature_request = mocker.spy(device, "feature_request") setting = settings_templates.check_feature(device, test[0].sclass) if isinstance(setting, list): setting = setting[0] value = setting.read(cached=False) cached_value = setting.read(cached=True) write_value = setting.write(test[0].write_value) assert setting is not None assert value == test[0].initial_value assert cached_value == test[0].initial_value assert write_value == test[0].write_value params = bytes.fromhex(test[0].write_params) no_reply = {"no_reply": test[0].no_reply} if test[0].no_reply is not None else {} spy_feature_request.assert_called_with(test[0].sclass.feature, test[0].write_fnid, params, **no_reply) @pytest.fixture def mock_gethostname(mocker): mocker.patch("socket.gethostname", return_value="ABCDEF.foo.org") tests = [ [ FeatureTest(settings_templates.Backlight2, 0xFF, 0x00, 0x10, "0102ff00000000000000", None), common.NamedInts(Disabled=0xFF, Enabled=0x00), hidpp.Response("040001", 0x0000, "1982"), # BACKLIGHT2 hidpp.Response("000201000000000000000000", 0x0400), hidpp.Response("010201", 0x0410, "0102ff00000000000000"), ], [ FeatureTest(settings_templates.Backlight2, 0x03, 0xFF, 0x10, "0018ff00000000000000", None), common.NamedInts(Disabled=0xFF, Automatic=0x01, Manual=0x03), hidpp.Response("040001", 0x0000, "1982"), # BACKLIGHT2 hidpp.Response("011838000000000000000000", 0x0400), hidpp.Response("001801", 0x0410, "0018ff00000000000000"), ], [ FeatureTest(settings_templates.Backlight2Level, 0, 3, 0x10, "0118ff03000000000000", None), [0, 4], hidpp.Response("040003", 0x0000, "1982"), # BACKLIGHT2 hidpp.Response("011830000000000000000000", 0x0400), hidpp.Response("01180103000000000000", 0x0410, "0118ff03000000000000"), hidpp.Response("05", 0x0420), ], [ FeatureTest(settings_templates.Backlight2Level, 0, 2, 0x10, "0118ff02000000000000", None), [0, 4], hidpp.Response("040003", 0x0000, "1982"), # BACKLIGHT2 hidpp.Response("011830000000000000000000", 0x0400), hidpp.Response("01180102000000000000", 0x0410, "0118ff02000000000000"), hidpp.Response("05", 0x0420), ], [ FeatureTest(settings_templates.OnboardProfiles, 2, 1, 0x30, "0001", None), common.NamedInts(**{"Disabled": 0, "Profile 1": 1, "Profile 2": 2}), hidpp.Response("0C0003", 0x0000, "8100"), # ONBOARD_PROFILES hidpp.Response("00010100000201FFFFFFFFFFFFFFFFFF", 0x0C50, "00000000"), hidpp.Response("000201FFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000004"), hidpp.Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000008"), hidpp.Response("01", 0x0C20), hidpp.Response("0002", 0x0C40), hidpp.Response("01", 0x0C10, "01"), hidpp.Response("0001", 0x0C30, "0001"), ], [ FeatureTest(settings_templates.OnboardProfiles, 1, 0, 0x10, "02", None), common.NamedInts(**{"Disabled": 0, "Profile 1": 1, "Profile 2": 2}), hidpp.Response("0C0003", 0x0000, "8100"), # ONBOARD_PROFILES hidpp.Response("00010100000201FFFFFFFFFFFFFFFFFF", 0x0C50, "00000000"), hidpp.Response("000201FFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000004"), hidpp.Response("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0x0C50, "00000008"), hidpp.Response("01", 0x0C20), hidpp.Response("0001", 0x0C40), hidpp.Response("02", 0x0C10, "02"), ], [ FeatureTest(settings_templates.ReportRate, 1, 5, 0x20, "05"), common.NamedInts(**{"1ms": 1, "2ms": 2, "5ms": 5, "6ms": 6}), hidpp.Response("0C0003", 0x0000, "8060"), # REPORT_RATE hidpp.Response("33", 0x0C00), hidpp.Response("01", 0x0C10), hidpp.Response("05", 0x0C20, "05"), ], [ FeatureTest(settings_templates.ExtendedReportRate, 1, 5, 0x30, "05"), common.NamedInts(**{"8ms": 0, "4ms": 1, "500us": 4, "250us": 5}), hidpp.Response("0C0003", 0x0000, "8061"), # EXTENDED_REPORT_RATE hidpp.Response("33", 0x0C10), hidpp.Response("01", 0x0C20), hidpp.Response("05", 0x0C30, "05"), ], # [ # FeatureTest(settings_templates.AdjustableDpi, 800, 400, 0x30, "000190"), # common.NamedInts.list([400, 800, 1600]), # hidpp.Response("040003", 0x0000, "2201"), # ADJUSTABLE_DPI # hidpp.Response("000190032006400000", 0x0410, "000000"), # hidpp.Response("000320", 0x0420), # hidpp.Response("000190", 0x0430, "000190"), # ], # [ # FeatureTest(settings_templates.AdjustableDpi, 256, 512, 0x30, "000200"), # common.NamedInts.list([256, 512]), # hidpp.Response("040003", 0x0000, "2201"), # ADJUSTABLE_DPI # hidpp.Response("000100e10002000000", 0x0410, "000000"), # hidpp.Response("000100", 0x0420), # hidpp.Response("000200", 0x0430, "000200"), # ], # [ # FeatureTest(settings_templates.ExtendedAdjustableDpi, 256, 512, 0x60, "000200"), # common.NamedInts.list([256, 512]), # hidpp.Response("090000", 0x0000, "2202"), # EXTENDED_ADJUSTABLE_DPI # hidpp.Response("000000", 0x0910, "00"), # no y direction # hidpp.Response("0000000100e10002000000", 0x0920, "000000"), # hidpp.Response("000100", 0x0950), # hidpp.Response("000200", 0x0960, "000200"), # ], # [ # FeatureTest(settings_templates.ExtendedAdjustableDpi, 0x64, 0x164, 0x60, "0001640164"), # common.NamedInts.list([0x064, 0x074, 0x084, 0x0A4, 0x0C4, 0x0E4, 0x0124, 0x0164, 0x01C4]), # hidpp.Response("090000", 0x0000, "2202"), # EXTENDED_ADJUSTABLE_DPI # hidpp.Response("000001", 0x0910, "00"), # supports y direction # hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000000"), # hidpp.Response("000001E4E0400124E0400164E06001C4", 0x0920, "000001"), # hidpp.Response("00000000000000000000000000000000", 0x0920, "000002"), # hidpp.Response("000064", 0x0950), # hidpp.Response("0001640164", 0x0960, "0001640164"), # ], [ FeatureTest(settings_templates.Multiplatform, 0, 1, 0x30, "FF01"), common.NamedInts(**{"MacOS 0.1-0.5": 0, "iOS 0.1-0.7": 1, "Linux 0.2-0.9": 2, "Windows 0.3-0.9": 3}), hidpp.Response("040003", 0x0000, "4531"), # MULTIPLATFORM hidpp.Response("020004000001", 0x0400), hidpp.Response("00FF200000010005", 0x0410, "00"), hidpp.Response("01FF400000010007", 0x0410, "01"), hidpp.Response("02FF040000020009", 0x0410, "02"), hidpp.Response("03FF010000030009", 0x0410, "03"), hidpp.Response("FF01", 0x0430, "FF01"), ], [ FeatureTest(settings_templates.ChangeHost, 1, 0, 0x10, "00", True), common.NamedInts(**{"1:ABCDEF": 0, "2:GHIJKL": 1}), hidpp.Response("040003", 0x0000, "1814"), # CHANGE_HOST hidpp.Response("050003", 0x0000, "1815"), # HOSTS_INFO hidpp.Response("01000200", 0x0500), hidpp.Response("000100000600", 0x0510, "00"), hidpp.Response("000041424344454600", 0x0530, "0000"), hidpp.Response("000100000600", 0x0510, "01"), hidpp.Response("00004748494A4B4C00", 0x0530, "0100"), hidpp.Response("0201", 0x0400), hidpp.Response(True, 0x0410, "00"), ], [ FeatureTest(settings_templates.BrightnessControl, 0x10, 0x20, 0x20, "0020", False), [0, 80], hidpp.Response("040003", 0x0000, "8040"), # BRIGHTNESS_CONTROL hidpp.Response("00505100000000", 0x0400), # 0 to 80, all acceptable, no separate on/off hidpp.Response("10", 0x0410), # brightness 16 hidpp.Response("0020", 0x0420, "0020"), # set brightness 32 ], [ FeatureTest(settings_templates.BrightnessControl, 0x10, 0x00, 0x20, "0000", False), [0, 80], hidpp.Response("040003", 0x0000, "8040"), # BRIGHTNESS_CONTROL hidpp.Response("00505104000000", 0x0400), # 0 to 80, all acceptable, separate on/off hidpp.Response("10", 0x0410), # brightness 16 hidpp.Response("01", 0x0430), # on hidpp.Response("00", 0x0440), # set off hidpp.Response("0000", 0x0420, "0000"), # set brightness 0 ], [ FeatureTest(settings_templates.BrightnessControl, 0x00, 0x20, 0x20, "0020", False), [0, 80], hidpp.Response("040003", 0x0000, "8040"), # BRIGHTNESS_CONTROL hidpp.Response("00505104000000", 0x0400), # 0 to 80, all acceptable, separate on/off hidpp.Response("10", 0x0410), # brightness 16 hidpp.Response("00", 0x0430), # off hidpp.Response("01", 0x0440), # set on hidpp.Response("0020", 0x0420, "0020"), # set brightness 32 ], [ FeatureTest(settings_templates.BrightnessControl, 0x20, 0x08, 0x40, "00", False), [0, 80], hidpp.Response("040003", 0x0000, "8040"), # BRIGHTNESS_CONTROL hidpp.Response("00504104001000", 0x0400), # 16 to 80, all acceptable, separate on/off hidpp.Response("20", 0x0410), # brightness 32 hidpp.Response("01", 0x0430), # on hidpp.Response("00", 0x0440, "00"), # set off ], ] @pytest.mark.parametrize("test", tests) def test_variable_template(test, mocker, mock_gethostname): device = hidpp.Device(responses=test[2:]) spy_feature_request = mocker.spy(device, "feature_request") setting = settings_templates.check_feature(device, test[0].sclass) value = setting.read(cached=False) cached_value = setting.read(cached=True) write_value = setting.write(test[0].write_value) assert setting is not None if isinstance(test[1], common.NamedInts): assert len(setting.choices) == len(test[1]) for setting_choice, expected_choice in zip(setting.choices, test[1]): assert setting_choice == expected_choice if isinstance(test[1], list): assert setting._validator.min_value == test[1][0] assert setting._validator.max_value == test[1][1] assert value == test[0].initial_value assert cached_value == test[0].initial_value assert write_value == test[0].write_value params = bytes.fromhex(test[0].write_params) no_reply = {"no_reply": test[0].no_reply} if test[0].no_reply is not None else {} spy_feature_request.assert_called_with(test[0].sclass.feature, test[0].write_fnid, params, **no_reply) responses_reprog_controls = [ hidpp.Response("050001", 0x0000, "1B04"), # REPROG_CONTROLS_V4 hidpp.Response("03", 0x0500), hidpp.Response("00500038010001010400000000000000", 0x0510, "00"), # left button hidpp.Response("00510039010001010400000000000000", 0x0510, "01"), # right button hidpp.Response("00C4009D310003070500000000000000", 0x0510, "02"), # smart shift hidpp.Response("00500000000000000000000000000000", 0x0520, "0050"), # left button current hidpp.Response("00510000500000000000000000000000", 0x0520, "0051"), # right button current hidpp.Response("00C40000000000000000000000000000", 0x0520, "00C4"), # smart shift current hidpp.Response("0051000051", 0x0530, "0051000051"), # right button set ] responses_remappable_action = responses_reprog_controls + [ hidpp.Response("040001", 0x0000, "1C00"), # PERSISTENT_REMAPPABLE_ACTION hidpp.Response("0041", 0x0400), hidpp.Response("0201", 0x0410), hidpp.Response("02", 0x0400), hidpp.Response("0050", 0x0420, "00FF"), # left button hidpp.Response("0051", 0x0420, "01FF"), # right button hidpp.Response("0050000100500000", 0x0430, "0050FF"), # left button current hidpp.Response("0051000100500001", 0x0430, "0051FF"), # right button current hidpp.Response("0051FF01005100", 0x0440, "0051FF01005100"), # right button set ] tests = [ [ FeatureTest( settings_templates.ReprogrammableKeys, {0x50: 0x50, 0x51: 0x50, 0xC4: 0xC4}, {0x51: 0x51}, 0x30, "0051000051" ), { common.NamedInt(0x50, "Left Button"): [0x50, 0x51], common.NamedInt(0x51, "Right Button"): [0x51, 0x50], common.NamedInt(0xC4, "Smart Shift"): [0xC4, 0x50, 0x51], }, ] + responses_reprog_controls, [ FeatureTest(settings_templates.DivertKeys, {0xC4: 0}, {0xC4: 1}, 0x30, "00C4030000"), {common.NamedInt(0xC4, "Smart Shift"): [0, 1, 2]}, ] + responses_reprog_controls + [hidpp.Response("00C4030000", 0x0530, "00C4030000")], [ FeatureTest( settings_templates.PersistentRemappableAction, {80: 16797696, 81: 16797696}, {0x51: 16797952}, 0x40, "0051FF01005100", ), { common.NamedInt(80, "Left Button"): special_keys.KEYS_KEYS_CONSUMER, common.NamedInt(81, "Right Button"): special_keys.KEYS_KEYS_CONSUMER, }, ] + responses_remappable_action, [ FeatureTest(settings_templates.PerKeyLighting, {0x01: 0xFFFFFF, 0x02: 0xFFFFFF}, {0x02: 0xFF0000}, 0x70, "00"), { common.NamedInt(1, "1"): special_keys.COLORS, common.NamedInt(2, "2"): special_keys.COLORS, common.NamedInt(9, "9"): special_keys.COLORS, common.NamedInt(10, "10"): special_keys.COLORS, common.NamedInt(113, "113"): special_keys.COLORS, common.NamedInt(114, "114"): special_keys.COLORS, }, hidpp.Response("040001", 0x0000, "8081"), # PER_KEY_LIGHTING_V2 hidpp.Response("00000606000000000000000000000000", 0x0400, "0000"), hidpp.Response("00000600000000000000000000000000", 0x0400, "0001"), hidpp.Response("00000000000000000000000000000000", 0x0400, "0002"), hidpp.Response("02FF0000", 0x0410, "02FF0000"), hidpp.Response("00", 0x0470, "00"), ], ] @pytest.mark.parametrize("test", tests) def test_key_template(test, mocker): device = hidpp.Device(responses=test[2:]) spy_feature_request = mocker.spy(device, "feature_request") setting = settings_templates.check_feature(device, test[0].sclass) assert setting is not None if isinstance(setting, list): setting = setting[0] assert len(setting.choices) == len(test[1]) for k, v in setting.choices.items(): assert len(v) == len(test[1][k]) for setting_key, test_key in zip(v, test[1][k]): assert setting_key == test_key value = setting.read(cached=False) for k, v in test[0].initial_value.items(): assert value[k] == v setting.write(setting._value) for key, value in test[0].write_value.items(): write_value = setting.write_key_value(key, value) assert write_value == value assert spy_feature_request.call_args_list[-1][0][0] == test[0].sclass.feature assert spy_feature_request.call_args_list[-1][0][1] == test[0].write_fnid param = b"".join(pack("B", p) if isinstance(p, int) else p for p in spy_feature_request.call_args_list[-1][0][2:]) assert param == bytes.fromhex(test[0].write_params) tests = [ # needs settings to be set up!! [ FeatureTest(settings_templates.SpeedChange, 0, 0xED, 0x60, "000200"), common.NamedInts(**{"Off": 0, "DPI Change": 0xED}), hidpp.Response("040001", 0x0000, "2205"), # POINTER_SPEED hidpp.Response("0100", 0x0400), hidpp.Response("0120", 0x0410, "0120"), hidpp.Response("050001", 0x0000, "1B04"), # REPROG_CONTROLS_V4 hidpp.Response("01", 0x0500), hidpp.Response("00ED009D310003070500000000000000", 0x0510, "00"), # DPI Change hidpp.Response("00ED0000000000000000000000000000", 0x0520, "00ED"), # DPI Change current ], ] @pytest.mark.parametrize("test", tests) def XX_action_template(test, mocker): # needs settings to be set up!! device = hidpp.Device(responses=test[2:]) spy_feature_request = mocker.spy(device, "feature_request") setting = settings_templates.check_feature(device, test[0].sclass) value = setting.read(cached=False) cached_value = setting.read(cached=True) write_value = setting.write(test[0].write_value) assert setting is not None if isinstance(test[1], common.NamedInts): assert len(setting.choices) == len(test[1]) for setting_choice, expected_choice in zip(setting.choices, test[1]): assert setting_choice == expected_choice if isinstance(test[1], list): assert setting._validator.min_value == test[1][0] assert setting._validator.max_value == test[1][1] assert value == test[0].initial_value assert cached_value == test[0].initial_value assert write_value == test[0].write_value params = bytes.fromhex(test[0].write_params) no_reply = {"no_reply": test[0].no_reply} if test[0].no_reply is not None else {} spy_feature_request.assert_called_with(test[0].sclass.feature, test[0].write_fnid, params, **no_reply)