blob: d7f076b471173d930a0d7470b8dc0779f151191e [file] [log] [blame]
#
# 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()