Skip to content

Commit

Permalink
python_testing: Add Checks for Command IDs (project-chip#36018)
Browse files Browse the repository at this point in the history
* python_testing: Add Checks for Command IDs

This PR adds checks and unit tests for the command ID ranges defined in Table 79 of the spec.
These checks will be used on the various Python tests, for example,
TC_DeviceBasicComposition.py.

* Restyled by isort

* python_testing: Update command id error msg

This PR uses the newly added command id check.
It updates the error msg to indicate if it's a "Test Vendor" id.

* fix merge conflicts

* Restyled by isort

---------

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
soares-sergio and restyled-commits authored Oct 23, 2024
1 parent a7a5004 commit 614a086
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 5 deletions.
5 changes: 3 additions & 2 deletions src/python_testing/TC_DeviceBasicComposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@
from chip.clusters.ClusterObjects import ClusterAttributeDescriptor, ClusterObjectFieldDescriptor
from chip.interaction_model import InteractionModelError, Status
from chip.testing.basic_composition import BasicCompositionTests
from chip.testing.global_attribute_ids import AttributeIdType, ClusterIdType, GlobalAttributeIds, attribute_id_type, cluster_id_type
from chip.testing.global_attribute_ids import (AttributeIdType, ClusterIdType, CommandIdType, GlobalAttributeIds, attribute_id_type,
cluster_id_type, command_id_type)
from chip.testing.matter_testing import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, MatterBaseTest, TestStep,
async_test_body, default_matter_test_main)
from chip.testing.taglist_and_topology_test import (create_device_type_list_for_root, create_device_type_lists,
Expand Down Expand Up @@ -503,7 +504,7 @@ class RequiredMandatoryAttribute:
location = CommandPathLocation(endpoint_id=endpoint_id, cluster_id=cluster_id, command_id=bad_cmd_id)
vendor_id = get_vendor_id(bad_cmd_id)
self.record_error(self.get_test_name(
), location=location, problem=f'Command 0x{bad_cmd_id:08x} with bad prefix 0x{vendor_id:04x} in cluster 0x{cluster_id:08x}', spec_location='Manufacturer Extensible Identifier (MEI)')
), location=location, problem=f'Command 0x{bad_cmd_id:08x} with bad prefix 0x{vendor_id:04x} in cluster 0x{cluster_id:08x}' + (' (Test Vendor)' if command_id_type(bad_cmd_id) == CommandIdType.kTest else ''), spec_location='Manufacturer Extensible Identifier (MEI)')
success = False

self.print_step(7, "Validate that none of the MEI global attribute IDs contain values outside of the allowed suffix range")
Expand Down
71 changes: 68 additions & 3 deletions src/python_testing/TestIdChecks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
# limitations under the License.
#

from chip.testing.global_attribute_ids import (AttributeIdType, ClusterIdType, DeviceTypeIdType, attribute_id_type, cluster_id_type,
device_type_id_type, is_valid_attribute_id, is_valid_cluster_id,
is_valid_device_type_id)
from chip.testing.global_attribute_ids import (AttributeIdType, ClusterIdType, CommandIdType, DeviceTypeIdType, attribute_id_type,
cluster_id_type, command_id_type, device_type_id_type, is_valid_attribute_id,
is_valid_cluster_id, is_valid_command_id, is_valid_device_type_id)
from chip.testing.matter_testing import MatterBaseTest, default_matter_test_main
from mobly import asserts

Expand Down Expand Up @@ -210,6 +210,71 @@ def check_all_bad(id):
for id in prefix_bad:
check_all_bad(id)

def test_command_ids(self):
standard_global_good = [0x0000_00E0, 0x0000_00FF, 0x0000_00E1, 0x0000_00FE]
standard_global_bad = [0x0000_01E0, 0x0000_0FFF, 0x0000_AAE1, 0x0000_BBFE, 0x0000_FFFF]
scoped_non_global_good = [0x0000_0000, 0x0000_00DF, 0x0000_0001]
scoped_non_global_bad = [0x0000_0F00, 0x0000_01DF, 0x0000_0F01]
manufacturer_good = [0x0001_0000, 0x0001_00FF, 0xFFF0_0000, 0xFFF0_00FF, 0x0001_00FE]
manufacturer_bad = [0x0001_0A00, 0x0001_0BFF, 0x0001_FFFF, 0xFFF0_0C00, 0xFFF0_D0FF, 0x0001_F0FE]
test_good = [0xFFF1_0000, 0xFFF1_00E0, 0xFFF1_00FF, 0xFFF4_0000, 0xFFF4_00E0, 0xFFF4_00FF]
test_bad = [0xFFF1_5000, 0xFFF1_F000, 0xFFF1_FFFF, 0xFFF4_5000, 0xFFF4_F000, 0xFFF4_FFFF]
prefix_bad = [0xFFF5_0000, 0xFFF5_4FFF, 0xFFF5_5000, 0xFFF5_F000, 0xFFF5_FFFF]

def check_standard_global(id):
id_type = command_id_type(id)
msg = f"Incorrect command range assessment, expecting standard global {id:08x}, type = {id_type}"
asserts.assert_equal(id_type, CommandIdType.kStandardGlobal, msg)
asserts.assert_true(is_valid_command_id(id_type, allow_test=True), msg)
asserts.assert_true(is_valid_command_id(id_type, allow_test=False), msg)

def check_scoped_non_global(id):
id_type = command_id_type(id)
msg = f"Incorrect command range assessment, expecting scoped non-global {id:08x}, type = {id_type}"
asserts.assert_equal(id_type, CommandIdType.kScopedNonGlobal, msg)
asserts.assert_true(is_valid_command_id(id_type, allow_test=True), msg)
asserts.assert_true(is_valid_command_id(id_type, allow_test=False), msg)

def check_manufacturer(id):
id_type = command_id_type(id)
msg = f"Incorrect command range assessment, expecting manufacturer {id:08x}, type = {id_type}"
asserts.assert_equal(id_type, CommandIdType.kManufacturer, msg)
asserts.assert_true(is_valid_command_id(id_type, allow_test=True), msg)
asserts.assert_true(is_valid_command_id(id_type, allow_test=False), msg)

def check_test(id):
id_type = command_id_type(id)
msg = f"Incorrect command range assessment, expecting test {id:08x}, type = {id_type}"
asserts.assert_equal(id_type, CommandIdType.kTest, msg)
asserts.assert_true(is_valid_command_id(id_type, allow_test=True), msg)
asserts.assert_false(is_valid_command_id(id_type, allow_test=False), msg)

def check_all_bad(id):
id_type = command_id_type(id)
msg = f"Incorrect command range assessment, expecting invalid {id:08x}, type = {id_type}"
asserts.assert_equal(id_type, CommandIdType.kInvalid, msg)
asserts.assert_false(is_valid_command_id(id_type, allow_test=True), msg)
asserts.assert_false(is_valid_command_id(id_type, allow_test=False), msg)

for id in standard_global_good:
check_standard_global(id)
for id in standard_global_bad:
check_all_bad(id)
for id in scoped_non_global_good:
check_scoped_non_global(id)
for id in scoped_non_global_bad:
check_all_bad(id)
for id in manufacturer_good:
check_manufacturer(id)
for id in manufacturer_bad:
check_all_bad(id)
for id in test_good:
check_test(id)
for id in test_bad:
check_all_bad(id)
for id in prefix_bad:
check_all_bad(id)


if __name__ == "__main__":
default_matter_test_main()
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ class AttributeIdType(Enum):
kManufacturer = auto(),
kTest = auto(),


class CommandIdType(Enum):
kInvalid = auto()
kStandardGlobal = auto(),
kScopedNonGlobal = auto(),
kManufacturer = auto(),
kTest = auto(),


# ID helper classes - this allows us to use the values from the prefix and suffix table directly
# because the class handles the non-inclusive range.

Expand Down Expand Up @@ -92,6 +101,9 @@ def __contains__(self, id: int):
CLUSTER_ID_MANUFACTURER_RANGE_SUFFIX = SuffixIdRange(0xFC00, 0xFFFE)
ATTRIBUTE_ID_GLOBAL_RANGE_SUFFIX = SuffixIdRange(0xF000, 0xFFFE)
ATTRIBUTE_ID_NON_GLOBAL_RANGE_SUFFIX = SuffixIdRange(0x0000, 0x4FFF)
COMMAND_ID_GLOBAL_STANDARD_SUFFIX = SuffixIdRange(0x00E0, 0x00FF)
COMMAND_ID_NON_GLOBAL_SCOPED_SUFFIX = SuffixIdRange(0x0000, 0x00DF)
COMMAND_ID_SUFFIX = SuffixIdRange(0x0000, 0x00FF)


def device_type_id_type(id: int) -> DeviceTypeIdType:
Expand Down Expand Up @@ -145,3 +157,22 @@ def is_valid_attribute_id(id_type: AttributeIdType, allow_test: bool = False):
if allow_test:
valid.append(AttributeIdType.kTest)
return id_type in valid


def command_id_type(id: int) -> CommandIdType:
if id in STANDARD_PREFIX and id in COMMAND_ID_GLOBAL_STANDARD_SUFFIX:
return CommandIdType.kStandardGlobal
if id in STANDARD_PREFIX and id in COMMAND_ID_NON_GLOBAL_SCOPED_SUFFIX:
return CommandIdType.kScopedNonGlobal
if id in MANUFACTURER_PREFIX and id in COMMAND_ID_SUFFIX:
return CommandIdType.kManufacturer
if id in TEST_PREFIX and id in COMMAND_ID_SUFFIX:
return CommandIdType.kTest
return CommandIdType.kInvalid


def is_valid_command_id(id_type: CommandIdType, allow_test: bool = False):
valid = [CommandIdType.kStandardGlobal, CommandIdType.kScopedNonGlobal, CommandIdType.kManufacturer]
if allow_test:
valid.append(CommandIdType.kTest)
return id_type in valid

0 comments on commit 614a086

Please sign in to comment.