Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TC-IDM-10.2: Add check for MACL #35086

Merged
merged 3 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ jobs:
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestIdChecks.py'
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestSpecParsingDeviceType.py'
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestConformanceSupport.py'
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestConformanceTest.py'
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestChoiceConformanceSupport.py'
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_IDM_10_4.py'
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_SC_7_1.py'
Expand Down
23 changes: 23 additions & 0 deletions src/python_testing/TC_DeviceConformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ async def setup_class_helper(self):
self.xml_device_types, problems = build_xml_device_types()
self.problems.extend(problems)

def _get_device_type_id(self, device_type_name: str) -> int:
id = [id for id, dt in self.xml_device_types.items() if dt.name.lower() == device_type_name.lower()]
if len(id) != 1:
self.fail_current_test(f"Unable to find {device_type_name} device type")
return id[0]

def _has_nim(self):
nim_id = self._get_device_type_id('network infrastructure manager')
cecille marked this conversation as resolved.
Show resolved Hide resolved
for endpoint in self.endpoints_tlv.values():
desc = Clusters.Descriptor
device_types = [dt.deviceType for dt in endpoint[desc.id][desc.Attributes.DeviceTypeList.attribute_id]]
if nim_id in device_types:
# TODO: it's unclear if this needs to be present on every endpoint. Right now, this assumes one is sufficient.
cecille marked this conversation as resolved.
Show resolved Hide resolved
return True
return False

def check_conformance(self, ignore_in_progress: bool, is_ci: bool):
problems = []
success = True
Expand Down Expand Up @@ -125,6 +141,13 @@ def record_warning(location, problem):
for f in feature_masks:
location = AttributePathLocation(endpoint_id=endpoint_id, cluster_id=cluster_id,
attribute_id=GlobalAttributeIds.FEATURE_MAP_ID)
if cluster_id == Clusters.AccessControl.id and f == Clusters.AccessControl.Bitmaps.Feature.kManagedDevice:
# Managed ACL is treated as a special case because it is only allowed if other endpoints support NIM and disallowed otherwise.
if not self._has_nim():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we will likely add to this list in the future, shouldn't this be self._has_allowed_MACL()?

Do we have hard coding of the device library rules in the other scripts?

record_error(
location=location, problem="MACL feature is disallowed if the network infrastructure manager device type is not present")
cecille marked this conversation as resolved.
Show resolved Hide resolved
continue

if f not in self.xml_clusters[cluster_id].features.keys():
record_error(location=location, problem=f'Unknown feature with mask 0x{f:02x}')
continue
Expand Down
131 changes: 131 additions & 0 deletions src/python_testing/TestConformanceTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#
# Copyright (c) 2024 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.
#

from typing import Any

import chip.clusters as Clusters
from conformance_support import ConformanceDecision
from global_attribute_ids import GlobalAttributeIds
from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main
from mobly import asserts
from spec_parsing_support import build_xml_clusters, build_xml_device_types
from TC_DeviceConformance import DeviceConformanceTests


def is_mandatory(conformance):
return conformance(0, [], []).decision == ConformanceDecision.MANDATORY


class TestConformanceSupport(MatterBaseTest, DeviceConformanceTests):
def setup_class(self):
self.xml_clusters, self.problems = build_xml_clusters()
self.xml_device_types, problems = build_xml_device_types()
self.problems.extend(problems)

def _create_minimal_cluster(self, cluster_id: int) -> dict[int, Any]:
attrs = {}
attrs[GlobalAttributeIds.FEATURE_MAP_ID] = 0

mandatory_attributes = [id for id, a in self.xml_clusters[cluster_id].attributes.items() if is_mandatory(a.conformance)]
for m in mandatory_attributes:
# dummy versions - we're not using the values in this test
attrs[m] = 0
attrs[GlobalAttributeIds.ATTRIBUTE_LIST_ID] = mandatory_attributes
mandatory_accepted_commands = [id for id, a in self.xml_clusters[cluster_id].accepted_commands.items()
if is_mandatory(a.conformance)]
attrs[GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID] = mandatory_accepted_commands
mandatory_generated_commands = [id for id, a in self.xml_clusters[cluster_id].generated_commands.items()
if is_mandatory(a.conformance)]
attrs[GlobalAttributeIds.GENERATED_COMMAND_LIST_ID] = mandatory_generated_commands
attrs[GlobalAttributeIds.CLUSTER_REVISION_ID] = self.xml_clusters[cluster_id].revision
return attrs

def _create_minimal_dt(self, device_type_id: int) -> dict[int, dict[int, Any]]:
''' Creates the internals of an endpoint_tlv with the minimal set of clusters, with the minimal set of attributes and commands. Global attributes only.
Does NOT take into account overrides yet.
'''
endpoint_tlv = {}
required_servers = [id for id, c in self.xml_device_types[device_type_id].server_clusters.items()
if is_mandatory(c.conformance)]
required_clients = [id for id, c in self.xml_device_types[device_type_id].client_clusters.items()
if is_mandatory(c.conformance)]
device_type_revision = self.xml_device_types[device_type_id].revision

for s in required_servers:
endpoint_tlv[s] = self._create_minimal_cluster(s)

# Descriptor
attr = Clusters.Descriptor.Attributes
attrs = {}
attrs[attr.FeatureMap.attribute_id] = 0
attrs[attr.AcceptedCommandList.attribute_id] = []
attrs[attr.GeneratedCommandList.attribute_id] = []
attrs[attr.ClusterRevision.attribute_id] = self.xml_clusters[Clusters.Descriptor.id].revision
attrs[attr.DeviceTypeList.attribute_id] = [
Clusters.Descriptor.Structs.DeviceTypeStruct(deviceType=device_type_id, revision=device_type_revision)]
attrs[attr.ServerList.attribute_id] = required_servers
attrs[attr.ClientList.attribute_id] = required_clients
attrs[attr.PartsList.attribute_id] = []
attrs[attr.AttributeList.attribute_id] = []
attrs[attr.AttributeList.attribute_id] = list(attrs.keys())

endpoint_tlv[Clusters.Descriptor.id] = attrs
return endpoint_tlv

def add_macl(self, root_endpoint: dict[int, dict[int, Any]]):
ac = Clusters.AccessControl
root_endpoint[ac.id][ac.Attributes.FeatureMap.attribute_id] = ac.Bitmaps.Feature.kManagedDevice
root_endpoint[ac.id][ac.Attributes.Arl.attribute_id] = []
root_endpoint[ac.id][ac.Attributes.CommissioningARL.attribute_id] = []
root_endpoint[ac.id][ac.Attributes.AttributeList.attribute_id].extend([
ac.Attributes.Arl.attribute_id, ac.Attributes.CommissioningARL.attribute_id])
root_endpoint[ac.id][ac.Attributes.AcceptedCommandList.attribute_id].append(ac.Commands.ReviewFabricRestrictions.command_id)
root_endpoint[ac.id][ac.Attributes.GeneratedCommandList.attribute_id].append(
ac.Commands.ReviewFabricRestrictionsResponse.command_id)

@ async_test_body
cecille marked this conversation as resolved.
Show resolved Hide resolved
cecille marked this conversation as resolved.
Show resolved Hide resolved
async def test_macl_handling(self):
nim_id = self._get_device_type_id('network infrastructure manager')
root_node_id = self._get_device_type_id('root node')
on_off_id = self._get_device_type_id('On/Off Light')

root = self._create_minimal_dt(device_type_id=root_node_id)
nim = self._create_minimal_dt(device_type_id=nim_id)
self.endpoints_tlv = {0: root, 1: nim}
asserts.assert_true(self._has_nim(), "Did not find NIM in generated device")

success, problems = self.check_conformance(ignore_in_progress=False, is_ci=False)
self.problems.extend(problems)
asserts.assert_true(success, "Unexpected failure parsing minimal dt")

self.add_macl(root)
# A MACL is allowed when there is a NIM, so this should succeed as well
success, problems = self.check_conformance(ignore_in_progress=False, is_ci=False)
self.problems.extend(problems)
asserts.assert_true(success, "Unexpected failure with NIM and MACL")

# A MACL is not allowed when there is no NIM
self.endpoints_tlv[1] = self._create_minimal_dt(device_type_id=on_off_id)
success, problems = self.check_conformance(ignore_in_progress=False, is_ci=False)
self.problems.extend(problems)
asserts.assert_false(success, "Unexpected success with On/Off and MACL")

# TODO: what happens if there is a NIM and a non-NIM endpoint?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is allowed. The ARL entries would only be allowed on the clusters of the NIM endpoint

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where in the spec is this stated? The ACL cluster is node scopes so it applies to all the EP's on the device and I have not found any such restrictions mentioned in the spec?



if __name__ == "__main__":
default_matter_test_main()
1 change: 1 addition & 0 deletions src/python_testing/execute_python_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def main(search_directory, env_file):
"TestChoiceConformanceSupport.py",
"TC_DEMTestBase.py",
"choice_conformance_support.py",
"TestConformanceTest.py", # Unit test of the conformance test (TC_DeviceConformance) - does not run against an app.
"TestIdChecks.py",
"TestSpecParsingDeviceType.py",
"TestMatterTestingSupport.py",
Expand Down
Loading