tests: expand coverage of hidpp20

This commit is contained in:
Peter F. Patel-Schneider 2024-03-10 14:49:06 -04:00
parent 03a5ca3d49
commit 4632c46e30
1 changed files with 287 additions and 8 deletions

View File

@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from functools import partial from functools import partial
from typing import Any from typing import Any, Optional
from unittest import mock from unittest import mock
import pytest import pytest
@ -8,13 +8,19 @@ import pytest
from lib.logitech_receiver import hidpp20 from lib.logitech_receiver import hidpp20
from lib.logitech_receiver import hidpp20_constants from lib.logitech_receiver import hidpp20_constants
DEVICE = "test_device"
@dataclass
class Device:
name: str = "TEST DEVICE"
DEVICE = Device
_hidpp20 = hidpp20.Hidpp20() _hidpp20 = hidpp20.Hidpp20()
@dataclass @dataclass
class Response: class Response:
response: str response: Optional[str]
device: Any device: Any
feature: int feature: int
function: int function: int
@ -26,9 +32,7 @@ def feature_request(responses, device, feature, function=0x00, *params, no_reply
r = responses[0] r = responses[0]
responses.pop(0) responses.pop(0)
assert r.device == device assert r.device == device
assert r.feature == feature assert (r.feature, r.function, r.params) == (feature, function, params)
assert r.function == function
assert r.params == params
return bytes.fromhex(r.response) if r.response is not None else None return bytes.fromhex(r.response) if r.response is not None else None
@ -38,6 +42,219 @@ def mock_feature_request():
yield mock_feature_request yield mock_feature_request
def test_get_firmware(mock_feature_request):
responses = [
Response("02FFFF", DEVICE, hidpp20_constants.FEATURE.DEVICE_FW_VERSION, 0x00, ()),
Response("01414243030401000101000102030405", DEVICE, hidpp20_constants.FEATURE.DEVICE_FW_VERSION, 0x10, (0,)),
Response("02414243030401000101000102030405", DEVICE, hidpp20_constants.FEATURE.DEVICE_FW_VERSION, 0x10, (1,)),
]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_firmware(DEVICE)
assert len(result) == 2
assert isinstance(result[0], common.FirmwareInfo)
assert isinstance(result[1], common.FirmwareInfo)
def test_get_ids(mock_feature_request):
responses = [Response("FF12345678000D123456789ABC", DEVICE, hidpp20_constants.FEATURE.DEVICE_FW_VERSION, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
unitId, modelId, tid_map = _hidpp20.get_ids(DEVICE)
assert unitId == "12345678"
assert modelId == "123456789ABC"
assert tid_map == {"btid": "1234", "wpid": "5678", "usbid": "9ABC"}
def test_get_kind(mock_feature_request):
responses = [Response("00", DEVICE, hidpp20_constants.FEATURE.DEVICE_NAME, 0x20, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_kind(DEVICE)
assert result == "keyboard"
assert result == 1
def test_get_name(mock_feature_request):
responses = [
Response("12", DEVICE, hidpp20_constants.FEATURE.DEVICE_NAME, 0x00, ()),
Response("4142434445464748494A4B4C4D4E4F", DEVICE, hidpp20_constants.FEATURE.DEVICE_NAME, 0x10, (0,)),
Response("505152530000000000000000000000", DEVICE, hidpp20_constants.FEATURE.DEVICE_NAME, 0x10, (15,)),
]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_name(DEVICE)
assert result == "ABCDEFGHIJKLMNOPQR"
def test_get_friendly_name(mock_feature_request):
responses = [
Response("12", DEVICE, hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME, 0x00, ()),
Response("004142434445464748494A4B4C4D4E", DEVICE, hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME, 0x10, (0,)),
Response("0E4F50515253000000000000000000", DEVICE, hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME, 0x10, (14,)),
]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_friendly_name(DEVICE)
assert result == "ABCDEFGHIJKLMNOPQR"
def test_get_battery_status(mock_feature_request):
responses = [Response("502000FFFF", DEVICE, hidpp20_constants.FEATURE.BATTERY_STATUS, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
feature, battery = _hidpp20.get_battery_status(DEVICE)
assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS
assert battery.level == 80
assert battery.next_level == 32
assert battery.status == common.Battery.STATUS.discharging
def test_get_battery_voltage(mock_feature_request):
responses = [Response("1000FFFFFF", DEVICE, hidpp20_constants.FEATURE.BATTERY_VOLTAGE, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
feature, battery = _hidpp20.get_battery_voltage(DEVICE)
assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE
assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging
assert battery.voltage == 0x1000
def test_get_battery_unified(mock_feature_request):
responses = [Response("500100FFFF", DEVICE, hidpp20_constants.FEATURE.UNIFIED_BATTERY, 0x10, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
feature, battery = _hidpp20.get_battery_unified(DEVICE)
assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY
assert battery.level == 80
assert battery.status == common.Battery.STATUS.discharging
def test_get_adc_measurement(mock_feature_request):
responses = [Response("100003", DEVICE, hidpp20_constants.FEATURE.ADC_MEASUREMENT, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
feature, battery = _hidpp20.get_adc_measurement(DEVICE)
assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT
assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging
assert battery.voltage == 0x1000
def test_get_battery(mock_feature_request):
responses = [Response("502000FFFF", DEVICE, hidpp20_constants.FEATURE.BATTERY_STATUS, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
feature, battery = _hidpp20.get_battery(DEVICE, hidpp20_constants.FEATURE.BATTERY_STATUS)
assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS
assert battery.level == 80
assert battery.next_level == 32
assert battery.status == common.Battery.STATUS.discharging
def test_get_battery_none(mock_feature_request):
responses = [
Response(None, DEVICE, hidpp20_constants.FEATURE.BATTERY_STATUS, 0x00, ()),
Response(None, DEVICE, hidpp20_constants.FEATURE.BATTERY_VOLTAGE, 0x00, ()),
Response("500100ffff", DEVICE, hidpp20_constants.FEATURE.UNIFIED_BATTERY, 0x10, ()),
]
mock_feature_request.side_effect = partial(feature_request, responses)
feature, battery = _hidpp20.get_battery(DEVICE, None)
assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY
assert battery.level == 80
assert battery.status == common.Battery.STATUS.discharging
# get_keys is complex
# get_remap_keys is comples
# get_gestures is complex
# get_backlight is complex
# get_profiles is complex
def test_get_mouse_pointer_info(mock_feature_request):
responses = [Response("01000A", DEVICE, hidpp20_constants.FEATURE.MOUSE_POINTER, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_mouse_pointer_info(DEVICE)
assert result == {
"dpi": 0x100,
"acceleration": "med",
"suggest_os_ballistics": False,
"suggest_vertical_orientation": True,
}
def test_get_vertical_scrolling_info(mock_feature_request):
responses = [Response("01080C", DEVICE, hidpp20_constants.FEATURE.VERTICAL_SCROLLING, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_vertical_scrolling_info(DEVICE)
assert result == {"roller": "standard", "ratchet": 8, "lines": 12}
def test_get_hi_res_scrolling_info(mock_feature_request):
responses = [Response("0102", DEVICE, hidpp20_constants.FEATURE.HI_RES_SCROLLING, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
mode, resolution = _hidpp20.get_hi_res_scrolling_info(DEVICE)
assert mode == 1
assert resolution == 2
def test_get_pointer_speed_info(mock_feature_request):
responses = [Response("0102", DEVICE, hidpp20_constants.FEATURE.POINTER_SPEED, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_pointer_speed_info(DEVICE)
assert result == 0x0102 / 256
def test_get_lowres_wheel_status(mock_feature_request):
responses = [Response("01", DEVICE, hidpp20_constants.FEATURE.LOWRES_WHEEL, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_lowres_wheel_status(DEVICE)
assert result == "HID++"
def test_get_hires_wheel(mock_feature_request):
responses = [
Response("010C", DEVICE, hidpp20_constants.FEATURE.HIRES_WHEEL, 0x00, ()),
Response("05FF", DEVICE, hidpp20_constants.FEATURE.HIRES_WHEEL, 0x10, ()),
Response("03FF", DEVICE, hidpp20_constants.FEATURE.HIRES_WHEEL, 0x30, ()),
]
mock_feature_request.side_effect = partial(feature_request, responses)
multi, has_invert, has_ratchet, inv, res, target, ratchet = _hidpp20.get_hires_wheel(DEVICE)
assert multi == 1
assert has_invert is True
assert has_ratchet is True
assert inv is True
assert res is False
assert target is True
assert ratchet is True
def test_get_new_fn_inversion(mock_feature_request): def test_get_new_fn_inversion(mock_feature_request):
responses = [Response("0300", DEVICE, hidpp20_constants.FEATURE.NEW_FN_INVERSION, 0x00, ())] responses = [Response("0300", DEVICE, hidpp20_constants.FEATURE.NEW_FN_INVERSION, 0x00, ())]
mock_feature_request.side_effect = partial(feature_request, responses) mock_feature_request.side_effect = partial(feature_request, responses)
@ -49,14 +266,33 @@ def test_get_new_fn_inversion(mock_feature_request):
assert len(responses) == 0 assert len(responses) == 0
@pytest.fixture
def mock_gethostname(mocker):
mocker.patch("socket.gethostname", return_value="getafix.foo.org")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"responses, expected_result", "responses, expected_result",
[ [
([Response(None, DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ())], {}), ([Response(None, DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ())], {}),
([Response("02000002", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ())], {}), ([Response("02000000", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ())], {}),
(
[
Response("03000200", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ()),
Response("FF01FFFF05FFFF", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x10, (0x00,)),
Response("0000414243444500FFFFFFFFFF", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x30, (0x00, 0x00)),
Response("FF01FFFF10FFFF", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x10, (0x01,)),
Response("01004142434445464748494A4B4C4D", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x30, (0x01, 0)),
Response("01134E4F5000FFFFFFFFFFFFFFFFFF", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x30, (0x01, 14)),
Response("03000200", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ()),
Response("000000000008", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x10, (0x0,)),
Response("0208", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x40, (0x0, 0x0, bytearray("getafix", "utf-8"))),
],
{0: (True, "getafix"), 1: (True, "ABCDEFGHIJKLMNO")},
),
], ],
) )
def test_get_host_names(responses, expected_result, mock_feature_request): def test_get_host_names(responses, expected_result, mock_feature_request, mock_gethostname):
mock_feature_request.side_effect = partial(feature_request, responses) mock_feature_request.side_effect = partial(feature_request, responses)
result = _hidpp20.get_host_names(DEVICE) result = _hidpp20.get_host_names(DEVICE)
@ -158,3 +394,46 @@ def test_config_change(mock_feature_request):
assert result == bytes.fromhex("03FFFF") assert result == bytes.fromhex("03FFFF")
assert len(responses) == 0 assert len(responses) == 0
def test_decipher_battery_status():
report = b"\x50\x20\x00\xff\xff"
feature, battery = hidpp20.decipher_battery_status(report)
assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS
assert battery.level == 80
assert battery.next_level == 32
assert battery.status == common.Battery.STATUS.discharging
def test_decipher_battery_voltage():
report = b"\x10\x00\xFF\xff\xff"
feature, battery = hidpp20.decipher_battery_voltage(report)
assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE
assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging
assert battery.voltage == 0x1000
def test_decipher_battery_unified():
report = b"\x50\x01\x00\xff\xff"
feature, battery = hidpp20.decipher_battery_unified(report)
assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY
assert battery.level == 80
assert battery.status == common.Battery.STATUS.discharging
def test_decipher_adc_measurement():
report = b"\x10\x00\x03"
feature, battery = hidpp20.decipher_adc_measurement(report)
assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT
assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging
assert battery.voltage == 0x1000