refactor: replace ERROR NamedInts by IntEnum (#2645)
* 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) * refactor(solaar/cli./probe.py): create unit tests for run function before replacing ERROR NamedInts by IntEnum (focusing on the call order when receiving errors) * refactor(solaar/cli./probe.py): refactor register processing to handle short and long registers in a single loop structure for improved readability and reduced code duplication. * refactor(logitech_receiver/hidpp10_constants.py): replace ERROR NamedInt by IntEnum. * refactor(logitech_receiver/hidpp10_constants.py): distinguish hidpp10 and hidpp20 errors in the code for readibility. * refactor(logitech_receiver/hidpp20_constants.py): replace ERROR NamedInt by IntEnum. * refactor(logitech_receiver/hidpp20_constants.py): replace ERROR NamedInt by IntEnum. (fix problem with | operator when typing with python 3.8) * feature(hide on startup option): Visual test (not binded yet) DRAFT * refactor(solaar/cli./probe.py): create unit tests for run function before replacing ERROR NamedInts by IntEnum (focusing on the call order when receiving errors) * refactor(solaar/cli./probe.py): refactor register processing to handle short and long registers in a single loop structure for improved readability and reduced code duplication. * refactor(logitech_receiver/hidpp10_constants.py): replace ERROR NamedInt by IntEnum. * refactor(logitech_receiver/hidpp20_constants.py): replace ERROR NamedInt by IntEnum. * refactor(logitech_receiver/hidpp20_constants.py): replace ERROR NamedInt by IntEnum. (fix problem with | operator when typing with python 3.8) * feature(hide on startup option): Visual test (not binded yet) DRAFT * Merge: Refactor: hidpp20 to use enum * Merge: Refactor: hidpp20 to use enum (fix test) --------- Co-authored-by: some_developer <some.developper.44@gmail.com>
This commit is contained in:
parent
c90146df31
commit
a19461b29d
|
|
@ -35,10 +35,10 @@ from . import base_usb
|
||||||
from . import common
|
from . import common
|
||||||
from . import descriptors
|
from . import descriptors
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
from . import hidpp10_constants
|
|
||||||
from . import hidpp20_constants
|
|
||||||
from .common import LOGITECH_VENDOR_ID
|
from .common import LOGITECH_VENDOR_ID
|
||||||
from .common import BusID
|
from .common import BusID
|
||||||
|
from .hidpp10_constants import ErrorCode as Hidpp10ErrorCode
|
||||||
|
from .hidpp20_constants import ErrorCode as Hidpp20ErrorCode
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
import gi
|
import gi
|
||||||
|
|
@ -583,9 +583,9 @@ def request(
|
||||||
devnumber,
|
devnumber,
|
||||||
request_id,
|
request_id,
|
||||||
error,
|
error,
|
||||||
hidpp10_constants.ERROR[error],
|
Hidpp10ErrorCode(error),
|
||||||
)
|
)
|
||||||
return hidpp10_constants.ERROR[error] if return_error else None
|
return Hidpp10ErrorCode(error) if return_error else None
|
||||||
if reply_data[:1] == b"\xff" and reply_data[1:3] == request_data[:2]:
|
if reply_data[:1] == b"\xff" and reply_data[1:3] == request_data[:2]:
|
||||||
# a HID++ 2.0 feature call returned with an error
|
# a HID++ 2.0 feature call returned with an error
|
||||||
error = ord(reply_data[3:4])
|
error = ord(reply_data[3:4])
|
||||||
|
|
@ -595,7 +595,7 @@ def request(
|
||||||
devnumber,
|
devnumber,
|
||||||
request_id,
|
request_id,
|
||||||
error,
|
error,
|
||||||
hidpp20_constants.ErrorCode(error),
|
Hidpp20ErrorCode(error),
|
||||||
)
|
)
|
||||||
raise exceptions.FeatureCallError(
|
raise exceptions.FeatureCallError(
|
||||||
number=devnumber,
|
number=devnumber,
|
||||||
|
|
@ -676,15 +676,12 @@ def ping(handle, devnumber, long_message: bool = False):
|
||||||
and reply_data[1:3] == request_data[:2]
|
and reply_data[1:3] == request_data[:2]
|
||||||
): # error response
|
): # error response
|
||||||
error = ord(reply_data[3:4])
|
error = ord(reply_data[3:4])
|
||||||
if error == hidpp10_constants.ERROR.invalid_SubID__command:
|
if error == Hidpp10ErrorCode.INVALID_SUB_ID_COMMAND:
|
||||||
# a valid reply from a HID++ 1.0 device
|
# a valid reply from a HID++ 1.0 device
|
||||||
return 1.0
|
return 1.0
|
||||||
if (
|
if error in [Hidpp10ErrorCode.RESOURCE_ERROR, Hidpp10ErrorCode.CONNECTION_REQUEST_FAILED]:
|
||||||
error == hidpp10_constants.ERROR.resource_error
|
|
||||||
or error == hidpp10_constants.ERROR.connection_request_failed
|
|
||||||
):
|
|
||||||
return # device unreachable
|
return # device unreachable
|
||||||
if error == hidpp10_constants.ERROR.unknown_device: # no paired device with that number
|
if error == Hidpp10ErrorCode.UNKNOWN_DEVICE: # no paired device with that number
|
||||||
logger.error("(%s) device %d error on ping request: unknown device", handle, devnumber)
|
logger.error("(%s) device %d error on ping request: unknown device", handle, devnumber)
|
||||||
raise exceptions.NoSuchDevice(number=devnumber, request=request_id)
|
raise exceptions.NoSuchDevice(number=devnumber, request=request_id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,20 +87,20 @@ NOTIFICATION_FLAG = NamedInts(
|
||||||
threed_gesture=0x000001,
|
threed_gesture=0x000001,
|
||||||
)
|
)
|
||||||
|
|
||||||
ERROR = NamedInts(
|
|
||||||
invalid_SubID__command=0x01,
|
class ErrorCode(IntEnum):
|
||||||
invalid_address=0x02,
|
INVALID_SUB_ID_COMMAND = 0x01
|
||||||
invalid_value=0x03,
|
INVALID_ADDRESS = 0x02
|
||||||
connection_request_failed=0x04,
|
INVALID_VALUE = 0x03
|
||||||
too_many_devices=0x05,
|
CONNECTION_REQUEST_FAILED = 0x04
|
||||||
already_exists=0x06,
|
TOO_MANY_DEVICES = 0x05
|
||||||
busy=0x07,
|
ALREADY_EXISTS = 0x06
|
||||||
unknown_device=0x08,
|
BUSY = 0x07
|
||||||
resource_error=0x09,
|
UNKNOWN_DEVICE = 0x08
|
||||||
request_unavailable=0x0A,
|
RESOURCE_ERROR = 0x09
|
||||||
unsupported_parameter_value=0x0B,
|
REQUEST_UNAVAILABLE = 0x0A
|
||||||
wrong_pin_code=0x0C,
|
UNSUPPORTED_PARAMETER_VALUE = 0x0B
|
||||||
)
|
WRONG_PIN_CODE = 0x0C
|
||||||
|
|
||||||
|
|
||||||
class PairingError(IntEnum):
|
class PairingError(IntEnum):
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
from logitech_receiver import base
|
from logitech_receiver import base
|
||||||
from logitech_receiver import hidpp10_constants
|
|
||||||
from logitech_receiver.common import strhex
|
from logitech_receiver.common import strhex
|
||||||
|
from logitech_receiver.hidpp10_constants import ErrorCode
|
||||||
from logitech_receiver.hidpp10_constants import Registers
|
from logitech_receiver.hidpp10_constants import Registers
|
||||||
|
|
||||||
from solaar.cli.show import _print_device
|
from solaar.cli.show import _print_device
|
||||||
|
|
@ -95,31 +95,18 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
for reg in range(0, 0xFF):
|
for reg in range(0, 0xFF):
|
||||||
last = None
|
for offset, reg_type in [(0x00, "Short"), (0x200, "Long")]:
|
||||||
for sub in range(0, 0xFF):
|
last = None
|
||||||
rgst = base.request(receiver.handle, 0xFF, 0x8100 | reg, sub, return_error=True)
|
for sub in range(0, 0xFF):
|
||||||
if isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_address:
|
rgst = base.request(receiver.handle, 0xFF, 0x8100 | (offset + reg), sub, return_error=True)
|
||||||
break
|
if isinstance(rgst, int) and rgst == ErrorCode.INVALID_ADDRESS:
|
||||||
elif isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_value:
|
break
|
||||||
continue
|
elif isinstance(rgst, int) and rgst == ErrorCode.INVALID_VALUE:
|
||||||
else:
|
continue
|
||||||
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
|
else:
|
||||||
print(
|
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
|
||||||
" Register Short %#04x %#04x: %s"
|
print(
|
||||||
% (reg, sub, "0x" + strhex(rgst) if isinstance(rgst, bytes) else str(rgst))
|
" Register %s %#04x %#04x: %s"
|
||||||
)
|
% (reg_type, reg, sub, "0x" + strhex(rgst) if isinstance(rgst, bytes) else str(rgst))
|
||||||
last = rgst
|
)
|
||||||
last = None
|
last = rgst
|
||||||
for sub in range(0, 0xFF):
|
|
||||||
rgst = base.request(receiver.handle, 0xFF, 0x8100 | (0x200 + reg), sub, return_error=True)
|
|
||||||
if isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_address:
|
|
||||||
break
|
|
||||||
elif isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_value:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
|
|
||||||
print(
|
|
||||||
" Register Long %#04x %#04x: %s"
|
|
||||||
% (reg, sub, "0x" + strhex(rgst) if isinstance(rgst, bytes) else str(rgst))
|
|
||||||
)
|
|
||||||
last = rgst
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -9,7 +10,8 @@ from logitech_receiver import base
|
||||||
from logitech_receiver import exceptions
|
from logitech_receiver import exceptions
|
||||||
from logitech_receiver.base import HIDPP_SHORT_MESSAGE_ID
|
from logitech_receiver.base import HIDPP_SHORT_MESSAGE_ID
|
||||||
from logitech_receiver.base import request
|
from logitech_receiver.base import request
|
||||||
from logitech_receiver.hidpp10_constants import ERROR
|
from logitech_receiver.hidpp10_constants import ErrorCode as Hidpp10Error
|
||||||
|
from logitech_receiver.hidpp20_constants import ErrorCode as Hidpp20Error
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
@ -125,12 +127,14 @@ def test_get_next_sw_id():
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"prefix, error_code, return_error, raise_exception",
|
"prefix, error_code, return_error, raise_exception",
|
||||||
[
|
[
|
||||||
(b"\x8f", ERROR.invalid_SubID__command, False, False),
|
(b"\x8f", Hidpp10Error.INVALID_SUB_ID_COMMAND, False, False),
|
||||||
(b"\x8f", ERROR.invalid_SubID__command, True, False),
|
(b"\x8f", Hidpp10Error.INVALID_SUB_ID_COMMAND, True, False),
|
||||||
(b"\xff", ERROR.invalid_SubID__command, False, True),
|
(b"\xff", Hidpp20Error.UNKNOWN, False, True),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_request_errors(prefix: bytes, error_code: ERROR, return_error: bool, raise_exception: bool):
|
def test_request_errors(
|
||||||
|
prefix: bytes, error_code: Union[Hidpp10Error, Hidpp20Error], return_error: bool, raise_exception: bool
|
||||||
|
):
|
||||||
handle = 0
|
handle = 0
|
||||||
device_number = 66
|
device_number = 66
|
||||||
|
|
||||||
|
|
@ -160,13 +164,13 @@ def test_request_errors(prefix: bytes, error_code: ERROR, return_error: bool, ra
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"simulated_error, expected_result",
|
"simulated_error, expected_result",
|
||||||
[
|
[
|
||||||
(ERROR.invalid_SubID__command, 1.0),
|
(Hidpp10Error.INVALID_SUB_ID_COMMAND, 1.0),
|
||||||
(ERROR.resource_error, None),
|
(Hidpp10Error.RESOURCE_ERROR, None),
|
||||||
(ERROR.connection_request_failed, None),
|
(Hidpp10Error.CONNECTION_REQUEST_FAILED, None),
|
||||||
(ERROR.unknown_device, exceptions.NoSuchDevice),
|
(Hidpp10Error.UNKNOWN_DEVICE, exceptions.NoSuchDevice),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_ping_errors(simulated_error: ERROR, expected_result):
|
def test_ping_errors(simulated_error: Hidpp10Error, expected_result):
|
||||||
handle = 1
|
handle = 1
|
||||||
device_number = 1
|
device_number = 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from logitech_receiver.hidpp10_constants import ErrorCode
|
||||||
|
from logitech_receiver.hidpp10_constants import Registers
|
||||||
|
from solaar.cli.probe import run
|
||||||
|
|
||||||
|
|
||||||
|
# Mock receiver class
|
||||||
|
class MockReceiver:
|
||||||
|
handle = 1
|
||||||
|
isDevice = False
|
||||||
|
|
||||||
|
def read_register(self, register, *args):
|
||||||
|
return 0 if register == Registers.RECEIVER_INFO else b"\x01\x03"
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_register_errors():
|
||||||
|
mock_args = mock.Mock()
|
||||||
|
mock_args.receiver = False
|
||||||
|
|
||||||
|
mock_receiver = MockReceiver()
|
||||||
|
|
||||||
|
# Define expected addresses to be called in order
|
||||||
|
expected_addresses = []
|
||||||
|
|
||||||
|
for reg in range(0, 0xFF):
|
||||||
|
expected_addresses.append((0x8100 | reg, 0)) # First short call, returns invalid_value (continue)
|
||||||
|
expected_addresses.append((0x8100 | reg, 1)) # Second short call, returns invalid_address (stop here)
|
||||||
|
|
||||||
|
expected_addresses.append((0x8100 | (0x200 + reg), 0)) # First long call, returns invalid_value (continue)
|
||||||
|
expected_addresses.append((0x8100 | (0x200 + reg), 1)) # Second long call, returns invalid_address (stop here)
|
||||||
|
|
||||||
|
# To record the actual addresses called
|
||||||
|
called_addresses = []
|
||||||
|
|
||||||
|
def mock_base_request(handle, devnumber, reg, sub, return_error=False):
|
||||||
|
called_addresses.append((reg, sub))
|
||||||
|
if sub == 0:
|
||||||
|
return ErrorCode.INVALID_VALUE
|
||||||
|
elif sub == 1:
|
||||||
|
return ErrorCode.INVALID_ADDRESS
|
||||||
|
return b"\x01\x02"
|
||||||
|
|
||||||
|
with mock.patch("logitech_receiver.base.request", side_effect=mock_base_request), mock.patch(
|
||||||
|
"solaar.cli.probe._print_receiver", return_value=None
|
||||||
|
):
|
||||||
|
# Call the run function with mocked receivers and args (passing real find_receiver function)
|
||||||
|
run([mock_receiver], mock_args, None, None)
|
||||||
|
|
||||||
|
# Evaluate that the addresses called match the expected addresses
|
||||||
|
assert (
|
||||||
|
called_addresses == expected_addresses
|
||||||
|
), f"Called addresses {called_addresses} do not match expected {expected_addresses}"
|
||||||
Loading…
Reference in New Issue