Skip to content

Commit ecfb0e2

Browse files
ReneJosefsencecilleAliTalebVeluxgemini-code-assist[bot]
authored
IDM-14.1: Add closures sem tags test (project-chip#41382)
* IDM-14.1: Add closures test * bot * Add to test * linter * oh bot...I fell for your tricks and you deceived me * Add test step to verify semantic tags on Closure device types * restyle * Fixed syntax error introduced by mistake and added helper function to check tags on endpoints * Applied suggestion to improve get_namespace_id * Apply suggestion from @AliTalebVelux Co-authored-by: Ali Taleb <[email protected]> * Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Added use of max_num_tags_allowed_namespace and named arguments for check_tags_on_endpoint * Added named arguments for the missed check_tags_on_endpoint call --------- Co-authored-by: cecille <[email protected]> Co-authored-by: Ali Taleb <[email protected]> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 148a908 commit ecfb0e2

File tree

5 files changed

+330
-10
lines changed

5 files changed

+330
-10
lines changed

src/python_testing/TC_DeviceConformance.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,25 @@ def steps_TC_IDM_14_1(self):
8787
* Time Synchronization
8888
* TLS Certificate Management
8989
* TLS Client Management
90-
""", "No root-node-restricted clusters appear on non-root endpoints")]
90+
""", "No root-node-restricted clusters appear on non-root endpoints"),
91+
TestStep(2, "Ensure the complex device type composition and conformance rules related to closure device types are met",
92+
"Closure cluster device type rules are met"),
93+
TestStep(3, "Ensure the rules related to semantic tags for the closure device types are met",
94+
"Closure semantic tags rules are met")
95+
]
9196

9297
def test_TC_IDM_14_1(self):
9398
self.step(0) # wildcard read - done in setup
9499
self.step(1)
95100
problems = self.check_root_node_restricted_clusters()
101+
self.step(2)
102+
problems.extend(self.check_closure_restricted_clusters())
103+
self.step(3)
104+
problems.extend(self.check_closure_restricted_sem_tags())
105+
96106
if problems:
97107
self.problems.extend(problems)
98-
self.fail_current_test("One or more root-node-restricted clusters appear on non-root-node endpoints")
108+
self.fail_current_test("One or more device conformance violations were found")
99109

100110
def steps_TC_DESC_2_3(self):
101111
return [TestStep(0, "TH performs a wildcard read of all attributes on all endpoints on the device"),

src/python_testing/matter_testing_infrastructure/matter/testing/problem_notices.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,13 @@ def __str__(self):
118118
class DeviceTypePathLocation:
119119
device_type_id: int
120120
cluster_id: Optional[int] = None
121+
endpoint_id: Optional[int] = None
121122

122123
def __str__(self):
123-
msg = f'\n DeviceType: 0x{self.device_type_id:04X}'
124+
msg = ""
125+
if self.endpoint_id is not None:
126+
msg += f'\n Endpoint ID: {self.endpoint_id}'
127+
msg += f'\n DeviceType: 0x{self.device_type_id:04X}'
124128
if self.cluster_id:
125129
msg += f'\n ClusterID: 0x{self.cluster_id:04X}'
126130
return msg

src/python_testing/test_testing/DeviceConformanceTests.py

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#
1717

1818

19-
from typing import Callable
19+
from typing import Callable, Optional
2020

2121
import matter.clusters as Clusters
2222
from matter.testing.basic_composition import BasicCompositionTests
@@ -27,7 +27,8 @@
2727
device_type_id_type, is_valid_device_type_id)
2828
from matter.testing.problem_notices import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, DeviceTypePathLocation,
2929
ProblemNotice, ProblemSeverity)
30-
from matter.testing.spec_parsing import CommandType, XmlDeviceType, XmlDeviceTypeClusterRequirements
30+
from matter.testing.spec_parsing import (CommandType, PrebuiltDataModelDirectory, XmlDeviceType, XmlDeviceTypeClusterRequirements,
31+
build_xml_device_types, build_xml_namespaces)
3132
from matter.tlv import uint
3233

3334

@@ -76,8 +77,10 @@ async def setup_class_helper(self):
7677
await super().setup_class_helper()
7778
self.build_spec_xmls()
7879

79-
def _get_device_type_id(self, device_type_name: str) -> int:
80-
id = [id for id, dt in self.xml_device_types.items() if dt.name.lower() == device_type_name.lower()]
80+
def _get_device_type_id(self, device_type_name: str, xml_device_types: Optional[dict[uint, XmlDeviceType]] = None) -> int:
81+
if xml_device_types is None:
82+
xml_device_types = self.xml_device_types
83+
id = [id for id, dt in xml_device_types.items() if dt.name.lower() == device_type_name.lower()]
8184
if len(id) != 1:
8285
raise KeyError(f"Unable to find {device_type_name} device type")
8386
return id[0]
@@ -494,3 +497,82 @@ def check_root_node_restricted_clusters(self) -> list[ProblemNotice]:
494497
problems.append(ProblemNotice("TC-IDM-14.1", location=ClusterPathLocation(endpoint_id=endpoint_id, cluster_id=cluster.id),
495498
severity=ProblemSeverity.ERROR, problem=f"Root-node-restricted cluster {cluster} appears on non-root-node endpoint"))
496499
return problems
500+
501+
def check_closure_restricted_clusters(self) -> list[ProblemNotice]:
502+
# This is a test that is SPECIFIC to the 1.5 spec, and thus we need the 1.5 spec information specifically
503+
# to assess the revisions.
504+
one_five_device_types, _ = build_xml_device_types(PrebuiltDataModelDirectory.k1_5)
505+
# TODO: change this once https://github.com/project-chip/matter-test-scripts/issues/689 is implemented
506+
507+
window_covering_id = self._get_device_type_id('Window Covering', one_five_device_types)
508+
closure_id = self._get_device_type_id('Closure', one_five_device_types)
509+
closure_panel_id = self._get_device_type_id('Closure Panel', one_five_device_types)
510+
restricted_device_type_ids = [window_covering_id, closure_id, closure_panel_id]
511+
512+
problems = []
513+
for endpoint_id, endpoint in self.endpoints.items():
514+
device_types = endpoint[Clusters.Descriptor][Clusters.Descriptor.Attributes.DeviceTypeList]
515+
have_closure = Clusters.ClosureControl in endpoint.keys() or Clusters.ClosureDimension in endpoint.keys()
516+
have_window_covering = Clusters.WindowCovering in endpoint.keys()
517+
518+
if have_closure and have_window_covering:
519+
for dt in device_types:
520+
device_type_id = dt.deviceType
521+
if device_type_id in restricted_device_type_ids and dt.revision <= one_five_device_types[device_type_id].revision:
522+
problems.append(ProblemNotice("TC-IDM-14.1", location=DeviceTypePathLocation(endpoint_id=endpoint_id, device_type_id=device_type_id), severity=ProblemSeverity.ERROR,
523+
problem=f"Endpoint with device type {one_five_device_types[device_type_id].name} has both window covering and closure clusters"))
524+
return problems
525+
526+
def check_closure_restricted_sem_tags(self) -> list[ProblemNotice]:
527+
# This is a test that is SPECIFIC to the 1.5 spec, and thus we need the 1.5 spec information specifically
528+
# to assess the revisions.
529+
one_five_device_types, _ = build_xml_device_types(PrebuiltDataModelDirectory.k1_5)
530+
one_five_namespaces, _ = build_xml_namespaces(PrebuiltDataModelDirectory.k1_5)
531+
# TODO: change this once https://github.com/project-chip/matter-test-scripts/issues/689 is implemented
532+
533+
def get_namespace_id(name: str) -> uint:
534+
ids = [id for id, xml in one_five_namespaces.items() if xml.name.lower() == name.lower()]
535+
if len(ids) != 1:
536+
raise ValueError(f"Unable to find unique namespace for '{name}'")
537+
return ids[0]
538+
539+
closure_id = self._get_device_type_id('Closure', one_five_device_types)
540+
closure_panel_id = self._get_device_type_id('Closure Panel', one_five_device_types)
541+
closure_namespace_id = get_namespace_id('Closure')
542+
closure_panel_namespace_id = get_namespace_id('Closure Panel')
543+
544+
def check_tags_on_endpoint(device_type_id: int, allowed_namespace: int, max_num_tags_allowed_namespace: int, disallowed_namespace: int):
545+
tag_list = endpoint[Clusters.Descriptor][Clusters.Descriptor.Attributes.TagList]
546+
allowed_tag_count = 0
547+
548+
for tag in tag_list:
549+
if tag.namespaceID == disallowed_namespace:
550+
problems.append(ProblemNotice("TC-IDM-14.1", location=DeviceTypePathLocation(endpoint_id=endpoint_id, device_type_id=device_type_id), severity=ProblemSeverity.ERROR,
551+
problem=f"Endpoint with device type {one_five_device_types[device_type_id].name} has semantic tag {tag.tag} from namespace {one_five_namespaces[disallowed_namespace].name}"))
552+
elif tag.namespaceID == allowed_namespace:
553+
allowed_tag_count += 1
554+
555+
if allowed_tag_count == 0:
556+
problems.append(ProblemNotice("TC-IDM-14.1", location=DeviceTypePathLocation(endpoint_id=endpoint_id, device_type_id=device_type_id), severity=ProblemSeverity.ERROR,
557+
problem=f"Endpoint with device type {one_five_device_types[device_type_id].name} is missing a {one_five_namespaces[allowed_namespace].name} namespace tag"))
558+
elif allowed_tag_count > max_num_tags_allowed_namespace:
559+
problems.append(ProblemNotice("TC-IDM-14.1", location=DeviceTypePathLocation(endpoint_id=endpoint_id, device_type_id=device_type_id), severity=ProblemSeverity.ERROR,
560+
problem=f"Endpoint with device type {one_five_device_types[device_type_id].name} has multiple {one_five_namespaces[allowed_namespace].name} namespace tags"))
561+
562+
problems = []
563+
for endpoint_id, endpoint in self.endpoints.items():
564+
# If a Cloure or Closure Panel does not implement TagList, this is also invalid but is verified by another IDM test,
565+
if Clusters.Descriptor.Attributes.TagList not in endpoint[Clusters.Descriptor]:
566+
continue
567+
568+
device_types = endpoint[Clusters.Descriptor][Clusters.Descriptor.Attributes.DeviceTypeList]
569+
570+
for dt in device_types:
571+
if dt.deviceType == closure_id:
572+
check_tags_on_endpoint(device_type_id=closure_id, allowed_namespace=closure_namespace_id,
573+
max_num_tags_allowed_namespace=1, disallowed_namespace=closure_panel_namespace_id)
574+
elif dt.deviceType == closure_panel_id:
575+
check_tags_on_endpoint(device_type_id=closure_panel_id, allowed_namespace=closure_panel_namespace_id,
576+
max_num_tags_allowed_namespace=1, disallowed_namespace=closure_namespace_id)
577+
578+
return problems

0 commit comments

Comments
 (0)