diff --git a/tests/logitech_receiver/test_hidpp20_simple.py b/tests/logitech_receiver/test_hidpp20_simple.py new file mode 100644 index 0000000000..1039431315 --- /dev/null +++ b/tests/logitech_receiver/test_hidpp20_simple.py @@ -0,0 +1,158 @@ +from dataclasses import dataclass +from functools import partial +from typing import Any +from unittest import mock + +import pytest + +from lib.logitech_receiver import hidpp20, hidpp20_constants + +DEVICE = "test_device" + + +@dataclass +class Response: + response: str + device: Any + feature: int + function: int + params: Any + no_reply: bool = False + + +def feature_request(responses, device, feature, function=0x00, *params, no_reply=False): + r = responses[0] + responses.pop(0) + assert r.device == device + assert r.feature == feature + assert r.function == function + assert r.params == params + return bytes.fromhex(r.response) if r.response is not None else None + + +@pytest.fixture +def mock_feature_request(): + with mock.patch("lib.logitech_receiver.hidpp20.feature_request", return_value=None) as mock_feature_request: + yield mock_feature_request + + +def test_get_new_fn_inversion(mock_feature_request): + responses = [Response("0300", DEVICE, hidpp20_constants.FEATURE.NEW_FN_INVERSION, 0x00, ())] + mock_feature_request.side_effect = partial(feature_request, responses) + + result = hidpp20.get_new_fn_inversion(DEVICE) + + assert result == (True, False) + assert mock_feature_request.call_count == 1 + assert len(responses) == 0 + + +@pytest.mark.parametrize( + "responses, expected_result", + [ + ([Response(None, DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ())], {}), + ([Response("02000002", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ())], {}), + ], +) +def test_get_host_names(responses, expected_result, mock_feature_request): + mock_feature_request.side_effect = partial(feature_request, responses) + + result = hidpp20.get_host_names(DEVICE) + + assert result == expected_result + assert len(responses) == 0 + + +@pytest.mark.parametrize( + "responses, expected_result", + [ + ([Response(None, DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ())], None), + ( + [ + Response("03000002", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ()), + Response("000000000008", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x10, (0x2,)), + Response("0208", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x40, (0x2, 0x0, bytearray("THIS IS A LONG", "utf-8"))), + ], + True, + ), + ( + [ + Response("03000002", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x00, ()), + Response("000000000014", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x10, (0x2,)), + Response("020E", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x40, (0x2, 0, bytearray("THIS IS A LONG", "utf-8"))), + Response("0214", DEVICE, hidpp20.FEATURE.HOSTS_INFO, 0x40, (0x2, 14, bytearray(" HOST NAME", "utf-8"))), + ], + True, + ), + ], +) +def test_set_host_name(responses, expected_result, mock_feature_request): + mock_feature_request.side_effect = partial(feature_request, responses) + + result = hidpp20.set_host_name(DEVICE, "THIS IS A LONG HOST NAME") + + assert result == expected_result + assert len(responses) == 0 + + +def test_get_onboard_mode(mock_feature_request): + responses = [Response("03FFFFFFFF", DEVICE, hidpp20_constants.FEATURE.ONBOARD_PROFILES, 0x20, ())] + mock_feature_request.side_effect = partial(feature_request, responses) + + result = hidpp20.get_onboard_mode(DEVICE) + + assert result == 0x3 + assert mock_feature_request.call_count == 1 + assert mock_feature_request.call_args[0] == (DEVICE, hidpp20_constants.FEATURE.ONBOARD_PROFILES, 0x20) + + +def test_set_onboard_mode(mock_feature_request): + responses = [Response("03FFFFFFFF", DEVICE, hidpp20_constants.FEATURE.ONBOARD_PROFILES, 0x10, (0x3,))] + mock_feature_request.side_effect = partial(feature_request, responses) + + res = hidpp20.set_onboard_mode(DEVICE, 0x3) + + assert mock_feature_request.call_count == 1 + assert res is not None + + +@pytest.mark.parametrize( + "responses, expected_result", + [ + ([Response("03FFFF", DEVICE, hidpp20.FEATURE.REPORT_RATE, 0x10, ())], "3ms"), + ( + [ + Response(None, DEVICE, hidpp20.FEATURE.REPORT_RATE, 0x10, ()), + Response("04FFFF", DEVICE, hidpp20.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE, 0x20, ()), + ], + "500us", + ), + ], +) +def test_get_polling_rate(responses, expected_result, mock_feature_request): + mock_feature_request.side_effect = partial(feature_request, responses) + + result = hidpp20.get_polling_rate(DEVICE) + + assert result == expected_result + assert len(responses) == 0 + + +def test_get_remaining_pairing(mock_feature_request): + responses = [Response("03FFFF", None, hidpp20.FEATURE.REMAINING_PAIRING, 0x0, ())] + mock_feature_request.side_effect = partial(feature_request, responses) + + result = hidpp20.get_remaining_pairing(None) + + assert result == 0x03 + assert len(responses) == 0 + + +def test_config_change(mock_feature_request): + responses = [Response("03FFFF", None, hidpp20.FEATURE.CONFIG_CHANGE, 0x0, (0x2,))] + mock_feature_request.side_effect = partial(feature_request, responses) + + result = hidpp20.config_change(None, 0x2) + + assert result == bytes.fromhex("03FFFF") + assert len(responses) == 0 diff --git a/tests/test_profiles.py b/tests/test_profiles.py deleted file mode 100644 index d06b3d0a7a..0000000000 --- a/tests/test_profiles.py +++ /dev/null @@ -1,93 +0,0 @@ -import logitech_receiver.hidpp20 as hidpp20 -import pytest - - -def test_led_setting_bytes(): - ebytes = bytes.fromhex("0A01020300500407000000") - - setting = hidpp20.LEDEffectSetting.from_bytes(ebytes) - - assert setting.ID == 0x0A - assert setting.color == 0x010203 - assert setting.period == 0x0050 - assert setting.form == 0x04 - assert setting.intensity == 0x07 - - bytes_out = setting.to_bytes() - - assert ebytes == bytes_out - - -def test_button_bytes_1(): - bbytes = bytes.fromhex("8000FFFF") - - button = hidpp20.Button.from_bytes(bbytes) - - assert button.behavior == 0x8 - assert button.type == 0x00 - - bytes_out = button.to_bytes() - - assert bbytes == bytes_out - - -def test_button_bytes_2(): - bbytes = bytes.fromhex("900aFF00") - - button = hidpp20.Button.from_bytes(bbytes) - - assert button.behavior == 0x9 - - bytes_out = button.to_bytes() - - assert bbytes == bytes_out - - -def test_button_bytes_3(): - bbytes = bytes.fromhex("80020454") - - button = hidpp20.Button.from_bytes(bbytes) - - assert button.behavior == 0x8 - assert button.modifiers == 0x04 - - bytes_out = button.to_bytes() - - assert bbytes == bytes_out - - -@pytest.fixture -def profile_bytes(): - return bytes.fromhex( - "01010290018003000700140028FFFFFF" - "FFFF0000000000000000000000000000" - "8000FFFF900aFF00800204548000FFFF" - "900aFF00800204548000FFFF900aFF00" - "800204548000FFFF900aFF0080020454" - "8000FFFF900aFF00800204548000FFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "54004500370000000000000000000000" - "00000000000000000000000000000000" - "00000000000000000000000000000000" - "0A01020300500407000000FFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFF7C81" - ) - - -def test_profile_bytes(profile_bytes): - pbytes = profile_bytes - profile = hidpp20.OnboardProfile.from_bytes(2, 1, 16, 0, pbytes) - - assert profile.sector == 2 - assert profile.resolutions == [0x0190, 0x0380, 0x0700, 0x1400, 0x2800] - assert profile.buttons[0].to_bytes() == bytes.fromhex("8000FFFF") - assert profile.lighting[0].to_bytes() == bytes.fromhex("0A01020300500407000000") - assert profile.name == "TE7" - - bytes_out = profile.to_bytes(255) - - assert pbytes == bytes_out