-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PICS checker test implementation (#30970)
* PICS checker test implementation * Address review comments * Address review comments * Remove tests for case-insensitive pics - we don't want this * Fix case-insensitive pics and add test * Fix pics case in test
- Loading branch information
Showing
7 changed files
with
194 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# | ||
# Copyright (c) 2023 Project CHIP Authors | ||
# All rights reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
import math | ||
|
||
import chip.clusters as Clusters | ||
from basic_composition_support import BasicCompositionTests | ||
from global_attribute_ids import GlobalAttributeIds | ||
from matter_testing_support import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, FeaturePathLocation, | ||
MatterBaseTest, async_test_body, default_matter_test_main) | ||
from mobly import asserts | ||
from spec_parsing_support import build_xml_clusters | ||
|
||
|
||
def attribute_pics(pics_base: str, id: int) -> str: | ||
return f'{pics_base}.S.A{id:04x}' | ||
|
||
|
||
def accepted_cmd_pics(pics_base: str, id: int) -> str: | ||
return f'{pics_base}.S.C{id:02x}.Rsp' | ||
|
||
|
||
def generated_cmd_pics(pics_base: str, id: int) -> str: | ||
return f'{pics_base}.S.C{id:02x}.Tx' | ||
|
||
|
||
def feature_pics(pics_base: str, bit: int) -> str: | ||
return f'{pics_base}.S.F{bit:02x}' | ||
|
||
|
||
class TC_PICS_Checker(MatterBaseTest, BasicCompositionTests): | ||
@async_test_body | ||
async def setup_class(self): | ||
super().setup_class() | ||
await self.setup_class_helper(False) | ||
# build_xml_cluster returns a list of issues found when paring the XML | ||
# Problems in the XML shouldn't cause test failure, but we want them recorded | ||
# so they are added to the list of problems that get output when the test set completes. | ||
self.xml_clusters, self.problems = build_xml_clusters() | ||
|
||
def _check_and_record_errors(self, location, required, pics): | ||
if required and not self.check_pics(pics): | ||
self.record_error("PICS check", location=location, | ||
problem=f"An element found on the device, but the corresponding PICS {pics} was not found in pics list") | ||
self.success = False | ||
elif not required and self.check_pics(pics): | ||
self.record_error("PICS check", location=location, problem=f"PICS {pics} found in PICS list, but not on device") | ||
self.success = False | ||
|
||
def _add_pics_for_lists(self, cluster_id: int, attribute_id_of_element_list: GlobalAttributeIds) -> None: | ||
try: | ||
if attribute_id_of_element_list == GlobalAttributeIds.ATTRIBUTE_LIST_ID: | ||
all_spec_elements_to_check = Clusters.ClusterObjects.ALL_ATTRIBUTES[cluster_id] | ||
pics_mapper = attribute_pics | ||
elif attribute_id_of_element_list == GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID: | ||
all_spec_elements_to_check = Clusters.ClusterObjects.ALL_ACCEPTED_COMMANDS[cluster_id] | ||
pics_mapper = accepted_cmd_pics | ||
|
||
elif attribute_id_of_element_list == GlobalAttributeIds.GENERATED_COMMAND_LIST_ID: | ||
all_spec_elements_to_check = Clusters.ClusterObjects.ALL_GENERATED_COMMANDS[cluster_id] | ||
pics_mapper = generated_cmd_pics | ||
else: | ||
asserts.fail("add_pics_for_list function called for non-list attribute") | ||
except KeyError: | ||
# This cluster does not have any of this element type | ||
return | ||
|
||
for element_id in all_spec_elements_to_check: | ||
if element_id > 0xF000: | ||
# No pics for global elements | ||
continue | ||
pics = pics_mapper(self.xml_clusters[cluster_id].pics, element_id) | ||
|
||
if cluster_id not in self.endpoint.keys(): | ||
# This cluster is not on this endpoint | ||
required = False | ||
elif element_id in self.endpoint[cluster_id][attribute_id_of_element_list]: | ||
# Cluster and element are on the endpoint | ||
required = True | ||
else: | ||
# Cluster is on the endpoint but the element is not in the list | ||
required = False | ||
|
||
if attribute_id_of_element_list == GlobalAttributeIds.ATTRIBUTE_LIST_ID: | ||
location = AttributePathLocation(endpoint_id=self.endpoint_id, cluster_id=cluster_id, attribute_id=element_id) | ||
else: | ||
location = CommandPathLocation(endpoint_id=self.endpoint_id, cluster_id=cluster_id, command_id=element_id) | ||
|
||
self._check_and_record_errors(location, required, pics) | ||
|
||
def test_TC_pics_checker(self): | ||
self.endpoint_id = self.matter_test_config.endpoint | ||
self.endpoint = self.endpoints_tlv[self.endpoint_id] | ||
self.success = True | ||
|
||
for cluster_id, cluster in Clusters.ClusterObjects.ALL_CLUSTERS.items(): | ||
# Data model XML is used to get the PICS code for this cluster. If we don't know the PICS | ||
# code, we can't evaluate the PICS list. Clusters that are present on the device but are | ||
# not present in the spec are checked in the IDM tests. | ||
if cluster_id not in self.xml_clusters or self.xml_clusters[cluster_id].pics is None: | ||
continue | ||
|
||
# Ensure the PICS.S code is correctly marked | ||
pics_cluster = f'{self.xml_clusters[cluster_id].pics}.S' | ||
location = ClusterPathLocation(endpoint_id=self.endpoint_id, cluster_id=cluster_id) | ||
self._check_and_record_errors(location, cluster_id in self.endpoint, pics_cluster) | ||
|
||
self._add_pics_for_lists(cluster_id, GlobalAttributeIds.ATTRIBUTE_LIST_ID) | ||
self._add_pics_for_lists(cluster_id, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID) | ||
self._add_pics_for_lists(cluster_id, GlobalAttributeIds.GENERATED_COMMAND_LIST_ID) | ||
|
||
try: | ||
cluster_features = cluster.Bitmaps.Feature | ||
except AttributeError: | ||
# cluster has no features | ||
continue | ||
|
||
pics_base = self.xml_clusters[cluster_id].pics | ||
try: | ||
feature_map = self.endpoint[cluster_id][GlobalAttributeIds.FEATURE_MAP_ID] | ||
except KeyError: | ||
feature_map = 0 | ||
|
||
for feature_mask in cluster_features: | ||
# Codegen in python uses feature masks (0x01, 0x02, 0x04 etc.) | ||
# PICS uses the mask bit number (1, 2, 3) | ||
# Convert the mask to a bit number so we can check the PICS. | ||
feature_bit = int(math.log2(feature_mask)) | ||
pics = feature_pics(pics_base, feature_bit) | ||
if feature_mask & feature_map: | ||
required = True | ||
else: | ||
required = False | ||
|
||
try: | ||
location = FeaturePathLocation(endpoint_id=self.endpoint_id, cluster_id=cluster_id, | ||
feature_code=self.xml_clusters[cluster_id].features[feature_mask].code) | ||
except KeyError: | ||
location = ClusterPathLocation(endpoint_id=self.endpoint_id, cluster_id=cluster_id) | ||
self._check_and_record_errors(location, required, pics) | ||
|
||
if not self.success: | ||
self.fail_current_test("At least one PICS error was found for this endpoint") | ||
|
||
|
||
if __name__ == "__main__": | ||
default_matter_test_main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters