Skip to content

Commit

Permalink
change pairing error values to intenums
Browse files Browse the repository at this point in the history
* refactoring(logitech_receiver/notifications): change to enums PairingError and BoltPairingError

* refactoring(logitech_receiver/notifications): change to enums PairingError and BoltPairingError (Fix pre-commit checks)

* refactor(logitech_receiver/base.py): create unit tests for ping function before replacing ERRORNamedInts by IntEnum

* refactor(logitech_receiver/base.py): create unit tests for request function before replacing ERROR NamedInts by IntEnum

* refactor(logitech_receiver/base.py): create unit tests for ping function before replacing ERRORNamedInts by IntEnum (add exclusion for macOS)

* refactor(logitech_receiver/base.py): create unit tests for ping function before replacing ERRORNamedInts by IntEnum (fix for python < 3.10)
  • Loading branch information
rloutrel authored Oct 23, 2024
1 parent 2185a83 commit 79ffbda
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 9 deletions.
13 changes: 11 additions & 2 deletions lib/logitech_receiver/hidpp10_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,17 @@
wrong_pin_code=0x0C,
)

PAIRING_ERRORS = NamedInts(device_timeout=0x01, device_not_supported=0x02, too_many_devices=0x03, sequence_timeout=0x06)
BOLT_PAIRING_ERRORS = NamedInts(device_timeout=0x01, failed=0x02)

class PairingError(IntEnum):
DEVICE_TIMEOUT = 0x01
DEVICE_NOT_SUPPORTED = 0x02
TOO_MANY_DEVICES = 0x03
SEQUENCE_TIMEOUT = 0x06


class BoltPairingError(IntEnum):
DEVICE_TIMEOUT = 0x01
FAILED = 0x02


class Registers(IntEnum):
Expand Down
6 changes: 3 additions & 3 deletions lib/logitech_receiver/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def _process_receiver_notification(receiver: Receiver, hidpp_notification: HIDPP
receiver.pairing.new_device = None
pair_error = ord(hidpp_notification.data[:1])
if pair_error:
receiver.pairing.error = error_string = hidpp10_constants.PAIRING_ERRORS[pair_error]
receiver.pairing.error = error_string = hidpp10_constants.PairingError(pair_error)
receiver.pairing.new_device = None
logger.warning("pairing error %d: %s", pair_error, error_string)
receiver.changed(reason=reason)
Expand All @@ -110,7 +110,7 @@ def _process_receiver_notification(receiver: Receiver, hidpp_notification: HIDPP
receiver.pairing.device_passkey = None
discover_error = ord(hidpp_notification.data[:1])
if discover_error:
receiver.pairing.error = discover_string = hidpp10_constants.BOLT_PAIRING_ERRORS[discover_error]
receiver.pairing.error = discover_string = hidpp10_constants.BoltPairingError(discover_error)
logger.warning("bolt discovering error %d: %s", discover_error, discover_string)
receiver.changed(reason=reason)
return True
Expand Down Expand Up @@ -150,7 +150,7 @@ def _process_receiver_notification(receiver: Receiver, hidpp_notification: HIDPP
elif hidpp_notification.address == 0x02 and not pair_error:
receiver.pairing.new_device = receiver.register_new_device(hidpp_notification.data[7])
if pair_error:
receiver.pairing.error = error_string = hidpp10_constants.BOLT_PAIRING_ERRORS[pair_error]
receiver.pairing.error = error_string = hidpp10_constants.BoltPairingError(pair_error)
receiver.pairing.new_device = None
logger.warning("pairing error %d: %s", pair_error, error_string)
receiver.changed(reason=reason)
Expand Down
75 changes: 75 additions & 0 deletions tests/logitech_receiver/test_base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import struct
import sys

from unittest import mock

import pytest

from logitech_receiver import base
from logitech_receiver import exceptions
from logitech_receiver.base import HIDPP_SHORT_MESSAGE_ID
from logitech_receiver.base import request
from logitech_receiver.hidpp10_constants import ERROR


@pytest.mark.parametrize(
Expand Down Expand Up @@ -111,3 +120,69 @@ def test_get_next_sw_id():

assert res1 == 2
assert res2 == 3


@pytest.mark.parametrize(
"prefix, error_code, return_error, raise_exception",
[
(b"\x8f", ERROR.invalid_SubID__command, False, False),
(b"\x8f", ERROR.invalid_SubID__command, True, False),
(b"\xff", ERROR.invalid_SubID__command, False, True),
],
)
def test_request_errors(prefix: bytes, error_code: ERROR, return_error: bool, raise_exception: bool):
handle = 0
device_number = 66

next_sw_id = 0x02
reply_data_sw_id = struct.pack("!H", 0x0000 | next_sw_id)

with mock.patch(
"logitech_receiver.base._read",
return_value=(HIDPP_SHORT_MESSAGE_ID, device_number, prefix + reply_data_sw_id + struct.pack("B", error_code)),
), mock.patch("logitech_receiver.base._skip_incoming", return_value=None), mock.patch(
"logitech_receiver.base.write", return_value=None
), mock.patch("logitech_receiver.base._get_next_sw_id", return_value=next_sw_id):
if raise_exception:
with pytest.raises(exceptions.FeatureCallError) as context:
request(handle, device_number, next_sw_id, return_error=return_error)
assert context.value.number == device_number
assert context.value.request == next_sw_id
assert context.value.error == error_code
assert context.value.params == b""

else:
result = request(handle, device_number, next_sw_id, return_error=return_error)
assert result == (error_code if return_error else None)


@pytest.mark.skipif(sys.platform == "darwin", reason="Test only runs on Linux")
@pytest.mark.parametrize(
"simulated_error, expected_result",
[
(ERROR.invalid_SubID__command, 1.0),
(ERROR.resource_error, None),
(ERROR.connection_request_failed, None),
(ERROR.unknown_device, exceptions.NoSuchDevice),
],
)
def test_ping_errors(simulated_error: ERROR, expected_result):
handle = 1
device_number = 1

next_sw_id = 0x05
reply_data_sw_id = struct.pack("!H", 0x0010 | next_sw_id)

with mock.patch(
"logitech_receiver.base._read",
return_value=(HIDPP_SHORT_MESSAGE_ID, device_number, b"\x8f" + reply_data_sw_id + bytes([simulated_error])),
), mock.patch("logitech_receiver.base._get_next_sw_id", return_value=next_sw_id):
if isinstance(expected_result, type) and issubclass(expected_result, Exception):
with pytest.raises(expected_result) as context:
base.ping(handle=handle, devnumber=device_number)
assert context.value.number == device_number
assert context.value.request == struct.unpack("!H", reply_data_sw_id)[0]

else:
result = base.ping(handle=handle, devnumber=device_number)
assert result == expected_result
14 changes: 10 additions & 4 deletions tests/logitech_receiver/test_notifications.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import pytest

from logitech_receiver import hidpp10_constants
from logitech_receiver import notifications
from logitech_receiver.base import HIDPPNotification
from logitech_receiver.common import Notification
from logitech_receiver.hidpp10_constants import BoltPairingError
from logitech_receiver.hidpp10_constants import PairingError
from logitech_receiver.hidpp10_constants import Registers
from logitech_receiver.receiver import Receiver

Expand All @@ -24,8 +26,12 @@ def request(self, handle, devnumber, request_id, *params, **kwargs):
@pytest.mark.parametrize(
"sub_id, notification_data, expected_error, expected_new_device",
[
(Registers.DISCOVERY_STATUS_NOTIFICATION, b"\x01", "device_timeout", None),
(Registers.PAIRING_STATUS_NOTIFICATION, b"\x02", "failed", None),
(Registers.DISCOVERY_STATUS_NOTIFICATION, b"\x01", BoltPairingError.DEVICE_TIMEOUT, None),
(Registers.PAIRING_STATUS_NOTIFICATION, b"\x02", BoltPairingError.FAILED, None),
(Notification.PAIRING_LOCK, b"\x01", PairingError.DEVICE_TIMEOUT, None),
(Notification.PAIRING_LOCK, b"\x02", PairingError.DEVICE_NOT_SUPPORTED, None),
(Notification.PAIRING_LOCK, b"\x03", PairingError.TOO_MANY_DEVICES, None),
(Notification.PAIRING_LOCK, b"\x06", PairingError.SEQUENCE_TIMEOUT, None),
],
)
def test_process_receiver_notification(sub_id, notification_data, expected_error, expected_new_device):
Expand All @@ -35,5 +41,5 @@ def test_process_receiver_notification(sub_id, notification_data, expected_error
result = notifications._process_receiver_notification(receiver, notification)

assert result
assert receiver.pairing.error == hidpp10_constants.BOLT_PAIRING_ERRORS[expected_error]
assert receiver.pairing.error == expected_error
assert receiver.pairing.new_device is expected_new_device

0 comments on commit 79ffbda

Please sign in to comment.