From 658b80db429b9ff0e73620cb5e5caf5df46389ee Mon Sep 17 00:00:00 2001 From: sgfeniex Date: Fri, 8 Nov 2024 15:50:59 -0600 Subject: [PATCH] feat: Enumeration Management Group --- smp/enumeration_management.py | 109 ++++++++++++++++++++++ smp/header.py | 8 ++ tests/test_enumeration_management.py | 130 +++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 smp/enumeration_management.py create mode 100644 tests/test_enumeration_management.py diff --git a/smp/enumeration_management.py b/smp/enumeration_management.py new file mode 100644 index 0000000..1d7bca6 --- /dev/null +++ b/smp/enumeration_management.py @@ -0,0 +1,109 @@ +"""The Simple Management Protocol (SMP) Enumeration Management group.""" + +from enum import IntEnum, unique +from typing import Tuple + +import smp.error as smperr +import smp.header as smphdr +import smp.message as smpmsg + + +class GroupCountRequest(smpmsg.ReadRequest): + """Read the number of SMP server groups.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.GROUP_COUNT + + +class GroupCountResponse(smpmsg.ReadResponse): + """SMP group count response.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.GROUP_COUNT + + count: int + + +class ListOfGroupsRequest(smpmsg.ReadRequest): + """List the available SMP groups.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.LIST_OF_GROUPS + + +class ListOfGroupsResponse(smpmsg.ReadResponse): + """SMP group list response.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.LIST_OF_GROUPS + + groups: Tuple[int] + + +class GroupIdRequest(smpmsg.ReadRequest): + """List a SMP group by index.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.GROUP_ID + + index: int | None = None + + +class GroupIdResponse(smpmsg.ReadResponse): + """SMP group at index response.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.GROUP_ID + + group: int + end: bool | None = None + + +class GroupDetailsRequest(smpmsg.ReadRequest): + """List a SMP group by index.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.GROUP_ID + + groups: Tuple[int, ...] + + +class GroupDetailsResponse(smpmsg.ReadResponse): + """SMP group details response.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + _COMMAND_ID = smphdr.CommandId.EnumManagement.GROUP_ID + + groups: Tuple[Tuple[int, str, int]] # group_id, group_name, group_handlers + + +@unique +class ENUM_MGMT_ERR(IntEnum): + """Return codes for the enumeration management group.""" + + OK = 0 + """No error, this is implied if there is no ret value in the response.""" + + UNKNOWN = 1 + """Unknown error occurred.""" + + ERR_TOO_MANY_GROUP_ENTRIES = 2 + """Too many entries were provided.""" + + ERR_INSUFFICIENT_HEAP_FOR_ENTRIES = 3 + """Insufficient heap memory to store entry data.""" + + ENUM_MGMT_ERR_INDEX_TOO_LARGE = 4 + """Provided index is larger than the number of supported groups.""" + + +class EnumManagementErrorV1(smperr.ErrorV1): + """Error response to a enumeration management command.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT + + +class EnumManagementErrorV2(smperr.ErrorV2[ENUM_MGMT_ERR]): + """Error response to a enumeration management command.""" + + _GROUP_ID = smphdr.GroupId.ENUM_MANAGEMENT diff --git a/smp/header.py b/smp/header.py index b79101c..551ffe6 100644 --- a/smp/header.py +++ b/smp/header.py @@ -56,6 +56,13 @@ class FileManagement(IntEnum): SUPPORTED_FILE_HASH_CHECKSUM_TYPES = 3 FILE_CLOSE = 4 + @unique + class EnumManagement(IntEnum): + GROUP_COUNT = 0 + LIST_OF_GROUPS = 1 + GROUP_ID = 2 + GROUP_DETAILS = 3 + @unique class ZephyrManagement(IntEnum): ERASE_STORAGE = 0 @@ -80,6 +87,7 @@ class GroupId(IntEnum): TEST_CRASH = 7 FILE_MANAGEMENT = 8 SHELL_MANAGEMENT = 9 + ENUM_MANAGEMENT = 10 ZEPHYR_MANAGEMENT = 63 diff --git a/tests/test_enumeration_management.py b/tests/test_enumeration_management.py new file mode 100644 index 0000000..2346983 --- /dev/null +++ b/tests/test_enumeration_management.py @@ -0,0 +1,130 @@ +"""Test the SMP Enumeration Management group.""" + +from __future__ import annotations + +from typing import Any, Dict, Type, TypeVar + +import cbor2 + +from smp import enumeration_management as smpenum +from smp import header as smphdr +from smp import message as smpmsg +from tests.helpers import make_assert_header + +T = TypeVar("T", bound=smpmsg._MessageBase) + + +def _do_test( + msg: Type[T], + op: smphdr.OP, + command_id: smphdr.CommandId.EnumManagement, + data: Dict[str, Any], +) -> T: + cbor = cbor2.dumps(data, canonical=True) + assert_header = make_assert_header(smphdr.GroupId.ENUM_MANAGEMENT, op, command_id, len(cbor)) + + def _assert_common(r: smpmsg._MessageBase) -> None: + assert_header(r) + for k, v in data.items(): + assert v == getattr(r, k) + assert cbor == r.BYTES[8:] + + r = msg(**data) + + _assert_common(r) # serialize + _assert_common(msg.loads(r.BYTES)) # deserialize + + return r + + +def test_GroupCountRequest() -> None: + _do_test( + smpenum.GroupCountRequest, + smphdr.OP.READ, + smphdr.CommandId.EnumManagement.GROUP_COUNT, + {}, + ) + + +def test_GroupCountResponse() -> None: + r = _do_test( + smpenum.GroupCountResponse, + smphdr.OP.READ_RSP, + smphdr.CommandId.EnumManagement.GROUP_COUNT, + {"count": 2}, + ) + assert r.count == 2 + + +def test_ListOfGroupsRequest() -> None: + _do_test( + smpenum.ListOfGroupsRequest, + smphdr.OP.READ, + smphdr.CommandId.EnumManagement.LIST_OF_GROUPS, + {}, + ) + + +def test_ListOfGroupsResponse() -> None: + r = _do_test( + smpenum.ListOfGroupsResponse, + smphdr.OP.READ_RSP, + smphdr.CommandId.EnumManagement.LIST_OF_GROUPS, + {"groups": (2, 5, 15)}, + ) + assert r.groups == (2, 5, 15) + + +def test_GroupIdRequest() -> None: + _do_test( + smpenum.GroupIdRequest, + smphdr.OP.READ, + smphdr.CommandId.EnumManagement.GROUP_ID, + {"index": (1)}, + ) + + _do_test( + smpenum.GroupIdRequest, + smphdr.OP.READ, + smphdr.CommandId.EnumManagement.GROUP_ID, + {"index": None}, + ) + + +def test_GroupIdResponse() -> None: + r = _do_test( + smpenum.GroupIdResponse, + smphdr.OP.READ_RSP, + smphdr.CommandId.EnumManagement.GROUP_ID, + {"group": 2}, + ) + assert r.group == 2 + assert not r.end + + r = _do_test( + smpenum.GroupIdResponse, + smphdr.OP.READ_RSP, + smphdr.CommandId.EnumManagement.GROUP_ID, + {"group": 15, "end": True}, + ) + assert r.group == 15 + assert r.end + + +def test_GroupDetailsRequest() -> None: + _do_test( + smpenum.GroupDetailsRequest, + smphdr.OP.READ, + smphdr.CommandId.EnumManagement.GROUP_DETAILS, + {"groups": (2, 5, 15)}, + ) + + +def test_GroupDetailsResponse() -> None: + r = _do_test( + smpenum.GroupDetailsResponse, + smphdr.OP.READ_RSP, + smphdr.CommandId.EnumManagement.GROUP_DETAILS, + {"groups": ((2, "group2", 2), (5, "group5", 5), (15, "group15", 15))}, + ) + assert r.groups == ((2, "group2", 2), (5, "group5", 5), (15, "group15", 15))