blob: 069badcbb4bc0ac01ee81f10fad4c2be6206e0d3 [file] [log] [blame] [edit]
#
# Copyright (c) 2025 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 chip.clusters as Clusters
from chip.testing.conformance import ConformanceDecision, ConformanceException
from chip.testing.global_attribute_ids import is_standard_attribute_id
from chip.testing.matter_testing import MatterBaseTest, default_matter_test_main
from chip.testing.spec_parsing import PrebuiltDataModelDirectory, build_xml_clusters, dm_from_spec_version
from chip.tlv import uint
from mobly import asserts, signals
from TC_DeviceConformance import DeviceConformanceTests
class TestSpecParsingSelection(MatterBaseTest, DeviceConformanceTests):
def setup_class(self):
# Overriding the DeviceConformanceTest setup_class so we don't go out to a real device
pass
def test_dm_from_spec_version(self):
asserts.assert_equal(dm_from_spec_version(0x01030000), PrebuiltDataModelDirectory.k1_3,
"Incorrect directory selected for 1.3 with patch 0")
asserts.assert_equal(dm_from_spec_version(0x01030100), PrebuiltDataModelDirectory.k1_3,
"Incorrect directory selected for 1.3 with patch 1")
asserts.assert_equal(dm_from_spec_version(0x01040100), PrebuiltDataModelDirectory.k1_4_1,
"Incorrect directory selected for 1.4.1")
asserts.assert_equal(dm_from_spec_version(0x01040100), PrebuiltDataModelDirectory.k1_4_1,
"Incorrect directory selected for 1.4.1")
asserts.assert_equal(dm_from_spec_version(0x01040200), PrebuiltDataModelDirectory.k1_4_2,
"Incorrect directory selected for 1.4.2")
# We don't have data model files for 1.2, so these should error
with asserts.assert_raises(ConformanceException, "Expected assertion was not raised for spec version 1.2"):
dm_from_spec_version(0x01020000)
# Any dot release besides 0, 1 and 2 for 1.4 should error
with asserts.assert_raises(ConformanceException, "Data model incorrectly identified for 1.4.2"):
dm_from_spec_version(0x01040300)
with asserts.assert_raises(ConformanceException, "Data model incorrectly identified for 1.4.FF"):
dm_from_spec_version(0x0104FF00)
# Any dot release besides 0 for 1.5 should error
with asserts.assert_raises(ConformanceException, "Data model incorrectly identified for 1.5.1"):
dm_from_spec_version(0x01050100)
with asserts.assert_raises(ConformanceException, "Data model incorrectly identified for 1.5.FF"):
dm_from_spec_version(0x0105FF00)
# Any value with stuff in reserved should error
with asserts.assert_raises(ConformanceException, "Error not returned for specification revision with non-zero reserved values"):
dm_from_spec_version(0x01030001)
with asserts.assert_raises(ConformanceException, "Error not returned for specification revision with non-zero reserved values"):
dm_from_spec_version(0x01040001)
with asserts.assert_raises(ConformanceException, "Error not returned for specification revision with non-zero reserved values"):
dm_from_spec_version(0x01040101)
with asserts.assert_raises(ConformanceException, "Error not returned for specification revision with non-zero reserved values"):
dm_from_spec_version(0x01050001)
def _create_device(self, spec_version: uint, tc_enabled: bool):
# Build at 1.4.1 so we can have TC info
xml_clusters, _ = build_xml_clusters(PrebuiltDataModelDirectory.k1_4_1)
gc_feature_map = Clusters.GeneralCommissioning.Bitmaps.Feature.kTermsAndConditions if tc_enabled else 0
def create_cluster_globals(cluster, feature_map):
spec_attributes = xml_clusters[cluster.id].attributes
spec_accepted_commands = xml_clusters[cluster.id].accepted_commands
spec_generated_commands = xml_clusters[cluster.id].generated_commands
# Build just the lists - basic composition checks the wildcard against the lists, conformance just uses lists
attributes = [id for id, a in spec_attributes.items() if a.conformance(
feature_map, [], []).decision == ConformanceDecision.MANDATORY]
accepted_commands = [id for id, c in spec_accepted_commands.items() if c.conformance(
feature_map, [], []).decision == ConformanceDecision.MANDATORY]
generated_commands = [id for id, c in spec_generated_commands.items() if c.conformance(
feature_map, [], []).decision == ConformanceDecision.MANDATORY]
attr = cluster.Attributes
resp = {}
non_global_attrs = [a for a in attributes if is_standard_attribute_id(a)]
for attribute_id in non_global_attrs:
# We don't use the values in these tests, set them all to 0. The types are wrong, but it shouldn't matter
resp[Clusters.ClusterObjects.ALL_ATTRIBUTES[cluster.id][attribute_id]] = 0
resp[attr.AttributeList] = attributes
resp[attr.AcceptedCommandList] = accepted_commands
resp[attr.GeneratedCommandList] = generated_commands
resp[attr.FeatureMap] = feature_map
resp[attr.ClusterRevision] = xml_clusters[cluster.id].revision
return resp
def get_tlv(resp):
# This only works because there are no structs in here.
# structs need special handling. Beware.
return {k.attribute_id: v for k, v in resp.items()}
gc_resp = create_cluster_globals(Clusters.GeneralCommissioning, gc_feature_map)
bi_resp = create_cluster_globals(Clusters.BasicInformation, 0)
bi_resp[Clusters.BasicInformation.Attributes.SpecificationVersion] = spec_version
self.endpoints = {0: {Clusters.GeneralCommissioning: gc_resp, Clusters.BasicInformation: bi_resp}}
self.endpoints_tlv = {0: {Clusters.GeneralCommissioning.id: get_tlv(
gc_resp), Clusters.BasicInformation.id: get_tlv(bi_resp)}}
def _run_conformance_against_device(self, spec_version: uint, tc_enabled: bool, expect_success_conformance: bool, expect_success_revisions: bool):
self._create_device(spec_version, tc_enabled)
# build the spec XMLs for the stated version
self.build_spec_xmls()
success, problems = self.check_conformance(ignore_in_progress=False, is_ci=False, allow_provisional=False)
problem_strs = [str(p) for p in problems]
problem_str = "\n".join(problem_strs)
asserts.assert_equal(success, expect_success_conformance,
f"Improper conformance result for spec version {spec_version:08X}, TC: {tc_enabled} problems: {problem_str}")
success, problems = self.check_revisions(ignore_in_progress=False)
asserts.assert_equal(success, expect_success_revisions,
f"Improper revision result for spec version {spec_version:08X}, TC: {tc_enabled} problems: {problems}")
def test_conformance(self):
# 1.4 is OK if TC is off
self._run_conformance_against_device(0x01040000, False, expect_success_conformance=True, expect_success_revisions=True)
# 1.4.1 is OK if TC is off
self._run_conformance_against_device(0x01040100, False, expect_success_conformance=True, expect_success_revisions=True)
# 1.4.1 is OK if TC is on
self._run_conformance_against_device(0x01040100, True, expect_success_conformance=True, expect_success_revisions=True)
# 1.4 is NOT OK if TC is on
self._run_conformance_against_device(0x01040000, True, expect_success_conformance=False, expect_success_revisions=True)
# Check that we get a test failure on a bad spec revision
self._create_device(0xFFFFFFFF, False)
with asserts.assert_raises(signals.TestFailure, "Exception not properly raised for bad spec type"):
self.build_spec_xmls()
if __name__ == "__main__":
default_matter_test_main()