[CI] Automate Test_TC_IDM_1_2.yaml (#28071)
* [chiptool.py] Allow '*' to be used as a top level endpoint value
* [matter_yamltests] Add saveResponseAs step level keyword to save the whole response
* [matter_yamltests] Add an optional definitions parameters for the methods of pseudo clusters
* [matter_yamltests] Add WildcardResponseExtractorCluster
* [CI] Automate Test_TC_IDM_1_2.yaml
diff --git a/examples/chip-tool/py_matter_chip_tool_adapter/matter_chip_tool_adapter/encoder.py b/examples/chip-tool/py_matter_chip_tool_adapter/matter_chip_tool_adapter/encoder.py
index 8340289..2c02811 100644
--- a/examples/chip-tool/py_matter_chip_tool_adapter/matter_chip_tool_adapter/encoder.py
+++ b/examples/chip-tool/py_matter_chip_tool_adapter/matter_chip_tool_adapter/encoder.py
@@ -309,6 +309,8 @@
endpoint_argument_name = 'endpoint-id-ignored-for-group-commands'
endpoint_argument_value = request.endpoint
+ if endpoint_argument_value == '*':
+ endpoint_argument_value = 0xFFFF
if (request.is_attribute and not request.command == "writeAttribute") or request.is_event or (request.command in _ANY_COMMANDS_LIST and not request.command == "WriteById"):
endpoint_argument_name = 'endpoint-ids'
diff --git a/scripts/py_matter_yamltests/matter_yamltests/parser.py b/scripts/py_matter_yamltests/matter_yamltests/parser.py
index 82b798a..0709067 100644
--- a/scripts/py_matter_yamltests/matter_yamltests/parser.py
+++ b/scripts/py_matter_yamltests/matter_yamltests/parser.py
@@ -208,6 +208,7 @@
self.wait_for = _value_or_none(test, 'wait')
self.event_number = _value_or_none(test, 'eventNumber')
self.run_if = _value_or_none(test, 'runIf')
+ self.save_response_as = _value_or_none(test, 'saveResponseAs')
self.is_attribute = self.__is_attribute_command()
self.is_event = self.__is_event_command()
@@ -695,6 +696,9 @@
if not isinstance(received_responses, list):
received_responses = [received_responses]
+ if self._test.save_response_as:
+ self._runtime_config_variable_storage[self._test.save_response_as] = received_responses
+
if self.wait_for is not None:
self._response_cluster_wait_validation(received_responses, result)
return result
@@ -1112,6 +1116,7 @@
tests
)
self.timeout = config['timeout']
+ self.definitions = parser_config.definitions
def __apply_config_override(self, config, config_override):
for key, value in config_override.items():
diff --git a/scripts/py_matter_yamltests/matter_yamltests/pseudo_clusters/pseudo_clusters.py b/scripts/py_matter_yamltests/matter_yamltests/pseudo_clusters/pseudo_clusters.py
index e194615..7ab24f0 100644
--- a/scripts/py_matter_yamltests/matter_yamltests/pseudo_clusters/pseudo_clusters.py
+++ b/scripts/py_matter_yamltests/matter_yamltests/pseudo_clusters/pseudo_clusters.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import inspect
from typing import List
from .clusters.commissioner_commands import CommissionerCommands
@@ -33,12 +34,16 @@
def add(self, cluster: PseudoCluster):
self.clusters.append(cluster)
- async def execute(self, request):
+ async def execute(self, request, definitions=None):
status = {'error': 'FAILURE'}
command = self.__get_command(request)
if command:
- status = await command(request)
+ if 'definitions' in inspect.signature(command).parameters:
+ status = await command(request, definitions)
+ else:
+ status = await command(request)
+
# If the command does not returns an error, it is considered a success.
if status is None:
status = {}
diff --git a/scripts/py_matter_yamltests/matter_yamltests/runner.py b/scripts/py_matter_yamltests/matter_yamltests/runner.py
index 08de8d7..c819eea 100644
--- a/scripts/py_matter_yamltests/matter_yamltests/runner.py
+++ b/scripts/py_matter_yamltests/matter_yamltests/runner.py
@@ -186,7 +186,7 @@
start = time.time()
if config.pseudo_clusters.supports(request):
- responses, logs = await config.pseudo_clusters.execute(request)
+ responses, logs = await config.pseudo_clusters.execute(request, parser.definitions)
else:
encoded_request = config.adapter.encode(request)
encoded_response = await self.execute(encoded_request)
diff --git a/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py b/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py
index 50a9217..ecdc7af 100644
--- a/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py
+++ b/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py
@@ -103,6 +103,7 @@
'PICS': str,
'arguments': dict,
'response': (dict, list, str), # Can be a variable
+ 'saveResponseAs': str,
'minInterval': int,
'maxInterval': int,
'timeout': int,
diff --git a/scripts/tests/chiptest/__init__.py b/scripts/tests/chiptest/__init__.py
index 87c308c..26aed1d 100644
--- a/scripts/tests/chiptest/__init__.py
+++ b/scripts/tests/chiptest/__init__.py
@@ -148,6 +148,7 @@
"Test_TC_SMCO_2_4.yaml", # chip-repl does not support timeout (07/20/2023)
"Test_TC_SMCO_2_5.yaml", # chip-repl does not support timeout (07/20/2023)
"Test_TC_SMCO_2_6.yaml", # chip-repl does not support timeout (07/20/2023)
+ "Test_TC_IDM_1_2.yaml", # chip-repl does not support AnyCommands (19/07/2023)
}
diff --git a/scripts/tests/yaml/extensions/wildcard_response_extractor_cluster.py b/scripts/tests/yaml/extensions/wildcard_response_extractor_cluster.py
new file mode 100644
index 0000000..a0e34cc
--- /dev/null
+++ b/scripts/tests/yaml/extensions/wildcard_response_extractor_cluster.py
@@ -0,0 +1,128 @@
+#
+# Copyright (c) 2023 Project CHIP Authors
+#
+# 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 matter_yamltests.pseudo_clusters.pseudo_cluster import PseudoCluster
+
+_VALUE_ARGUMENT_NAME = 'Value'
+_CLUSTERS_ARGUMENT_NAME = 'Clusters'
+
+
+class wildcard_response_extractor_cluster(PseudoCluster):
+ name = 'WildcardResponseExtractorCluster'
+
+ async def GetDefaultEndPointForClusters(self, request, definitions):
+ entries = self.__get_argument(request, _VALUE_ARGUMENT_NAME)
+ clusters = self.__get_argument(request, _CLUSTERS_ARGUMENT_NAME)
+ if entries is None or clusters is None:
+ return {'error': 'INVALID_ARGUMENT'}
+
+ results = {}
+
+ for cluster in clusters:
+ cluster_id = definitions.get_cluster_id_by_name(cluster)
+ results[cluster] = None
+
+ for entry in entries:
+ server_list = entry.get('value')
+ if cluster_id in server_list:
+ results[cluster] = entry.get('endpoint')
+ break
+
+ return {'value': results}
+
+ async def GetUnsupportedCluster(self, request):
+ entries = self.__get_argument(request, _VALUE_ARGUMENT_NAME)
+ if entries is None:
+ return {'error': 'INVALID_ARGUMENT'}
+
+ cluster_ids = []
+ for entry in entries:
+ server_list = entry.get('value')
+ for cluster_id in server_list:
+ if cluster_id not in cluster_ids:
+ cluster_ids.append(cluster_id)
+
+ unsupported_cluster = None
+ for cluster_code in range(0xFFFFFFFF):
+ if cluster_code not in cluster_ids:
+ unsupported_cluster = f'{cluster_code:#0{10}x}'
+ break
+
+ return {'value': {'UnsupportedCluster': unsupported_cluster}}
+
+ async def GetUnsupportedCommand(self, request):
+ entries = self.__get_argument(request, _VALUE_ARGUMENT_NAME)
+ if entries is None:
+ return {'error': 'INVALID_ARGUMENT'}
+
+ command_ids = []
+ for entry in entries:
+ commands_list = entry.get('value')
+ for command_id in commands_list:
+ if command_id not in command_ids:
+ command_ids.append(command_id)
+
+ unsupported_command = None
+ for command_code in range(0xFFFFFFFF):
+ if command_code not in command_ids:
+ unsupported_command = f'{command_code:#0{10}x}'
+ break
+
+ return {'value': {'UnsupportedCommand': unsupported_command}}
+
+ async def GetUnsupportedEndPoint(self, request):
+ entries = self.__get_argument(request, _VALUE_ARGUMENT_NAME)
+ if entries is None:
+ return {'error': 'INVALID_ARGUMENT'}
+
+ endpoint_ids = []
+ for entry in entries:
+ parts_list = entry.get('value')
+ for endpoint_id in parts_list:
+ if endpoint_id not in endpoint_ids:
+ endpoint_ids.append(endpoint_id)
+
+ # Add the endpoint id of the response if needed.
+ endpoint_id = entry.get('endpoint')
+ if endpoint_id not in endpoint_ids:
+ endpoint_ids.append(endpoint_id)
+
+ unsupported_endpoint = None
+ for endpoint_code in range(0xFFFF):
+ if endpoint_code not in endpoint_ids:
+ unsupported_endpoint = endpoint_code
+ break
+
+ return {'value': {'UnsupportedEndPoint': unsupported_endpoint}}
+
+ def __get_argument(self, request, argument_name):
+ arguments = request.arguments.get('values')
+ if arguments is None:
+ return None
+
+ if not type(arguments) is list:
+ return None
+
+ for argument in arguments:
+ name = argument.get('name')
+ value = argument.get('value')
+ if name is None or value is None:
+ return None
+
+ if name == argument_name:
+ return value
+
+ return None
diff --git a/src/app/tests/suites/certification/Test_TC_IDM_1_2.yaml b/src/app/tests/suites/certification/Test_TC_IDM_1_2.yaml
new file mode 100644
index 0000000..ff7e3d0
--- /dev/null
+++ b/src/app/tests/suites/certification/Test_TC_IDM_1_2.yaml
@@ -0,0 +1,384 @@
+# Copyright (c) 2021-2023 Project CHIP Authors
+#
+# 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.
+
+name: 3.1.2. [TC-IDM-1.2] Invoke Response Action from DUT to TH - [{DUT_Server}]
+
+PICS:
+ - MCORE.IDM.S
+
+config:
+ nodeId: 0x12344321
+ InvokeRequestMessage.Cluster: 0x00000006 # OnOff
+ InvokeRequestMessage.Command: 0x00000000 # Off
+ InvokeRequestMessage.EndPoint: 1
+ InvokeRequestMessage.Payload: {}
+
+tests:
+ - label: "Wait for the commissioned device to be retrieved"
+ cluster: "DelayCommands"
+ command: "WaitForCommissionee"
+ arguments:
+ values:
+ - name: "nodeId"
+ value: nodeId
+
+ #### Test Setup
+
+ - label: "Get the list of available endpoints on the device"
+ cluster: "Descriptor"
+ command: "readAttribute"
+ attribute: "PartsList"
+ endpoint: 0
+ saveResponseAs: AvailableDeviceEndPoints
+
+ - label:
+ "Get an unsupported endpoint id from the list of available device
+ endpoints"
+ cluster: WildcardResponseExtractorCluster
+ command: GetUnsupportedEndPoint
+ arguments:
+ values:
+ - name: Value
+ value: AvailableDeviceEndPoints
+ response:
+ values:
+ - name: UnsupportedEndPoint
+ saveAs: PIXIT.IDM.UnsupportedEndPoint
+
+ - label: "Get the list of available clusters on the device (all endpoints)"
+ cluster: "Descriptor"
+ command: "readAttribute"
+ attribute: "ServerList"
+ endpoint: "*"
+ saveResponseAs: AvailableDeviceClusters
+
+ - label:
+ "Extract the endpoint values for the AccessControl,
+ GeneralCommissioning, AdministratorCommissioning and
+ OperationalCredentials cluster"
+ cluster: WildcardResponseExtractorCluster
+ command: GetDefaultEndPointForClusters
+ arguments:
+ values:
+ - name: Value
+ value: AvailableDeviceClusters
+ - name: Clusters
+ value:
+ [
+ "AccessControl",
+ "GeneralCommissioning",
+ "AdministratorCommissioning",
+ "OperationalCredentials",
+ ]
+ response:
+ values:
+ - name: AccessControl
+ saveAs: PIXIT.Cluster.AccessControl.EndPoint
+ - name: GeneralCommissioning
+ saveAs: PIXIT.Cluster.GeneralCommissioning.EndPoint
+ - name: AdministratorCommissioning
+ saveAs: PIXIT.Cluster.AdministratorCommissioning.EndPoint
+ - name: OperationalCredentials
+ saveAs: PIXIT.Cluster.OperationalCredentials.EndPoint
+
+ - label:
+ "Get an unsupported cluster id from the list of available device
+ clusters"
+ cluster: WildcardResponseExtractorCluster
+ command: GetUnsupportedCluster
+ arguments:
+ values:
+ - name: Value
+ value: AvailableDeviceClusters
+ response:
+ values:
+ - name: UnsupportedCluster
+ saveAs: PIXIT.IDM.UnsupportedCluster
+
+ - label:
+ "Get the list of available cluster commands on the device (all
+ endpoints)"
+ cluster: "AnyCommands"
+ command: "ReadById"
+ endpoint: "*"
+ arguments:
+ values:
+ - name: "ClusterId"
+ value: "*"
+ - name: "AttributeId"
+ value: 0x0000FFF9
+ saveResponseAs: AvailableDeviceCommands
+
+ - label:
+ "Get an unsupported command id from the list of available device
+ commands"
+ cluster: WildcardResponseExtractorCluster
+ command: GetUnsupportedCommand
+ arguments:
+ values:
+ - name: Value
+ value: AvailableDeviceCommands
+ response:
+ values:
+ - name: UnsupportedCommand
+ saveAs: PIXIT.IDM.UnsupportedCommand
+
+ - label: "Read the fabric index from the alpha fabric"
+ cluster: "Operational Credentials"
+ command: "readAttribute"
+ endpoint: PIXIT.Cluster.OperationalCredentials.EndPoint
+ attribute: "CurrentFabricIndex"
+ response:
+ saveAs: alphaIndex
+
+ - label: "Read the commissioner node ID from the alpha fabric"
+ cluster: "CommissionerCommands"
+ command: "GetCommissionerNodeId"
+ response:
+ values:
+ - name: "nodeId"
+ saveAs: commissionerNodeIdAlpha
+
+ ##### Test Implementation
+
+ - label:
+ "TH sends the Invoke Request Message to the DUT with the path that
+ indicates a specific endpoint that is unsupported."
+ cluster: "AnyCommands"
+ command: "CommandById"
+ endpoint: PIXIT.IDM.UnsupportedEndPoint
+ arguments:
+ values:
+ - name: "ClusterId"
+ value: InvokeRequestMessage.Cluster
+ - name: "CommandId"
+ value: InvokeRequestMessage.Command
+ - name: "Payload"
+ value: InvokeRequestMessage.Payload
+ response:
+ error: UNSUPPORTED_ENDPOINT
+
+ - label:
+ "TH sends the Invoke Request Message to the DUT with the path that
+ indicates a specific cluster that is unsupported."
+ cluster: "AnyCommands"
+ command: "CommandById"
+ endpoint: InvokeRequestMessage.EndPoint
+ arguments:
+ values:
+ - name: "ClusterId"
+ value: PIXIT.IDM.UnsupportedCluster
+ - name: "CommandId"
+ value: InvokeRequestMessage.Command
+ - name: "Payload"
+ value: InvokeRequestMessage.Payload
+ response:
+ error: UNSUPPORTED_CLUSTER
+
+ - label:
+ "TH sends the Invoke Request Message to the DUT with the path that
+ indicates a specific command that is unsupported."
+ cluster: "AnyCommands"
+ command: "CommandById"
+ endpoint: InvokeRequestMessage.EndPoint
+ arguments:
+ values:
+ - name: "ClusterId"
+ value: InvokeRequestMessage.Cluster
+ - name: "CommandId"
+ value: PIXIT.IDM.UnsupportedCommand
+ - name: "Payload"
+ value: InvokeRequestMessage.Payload
+ response:
+ error: UNSUPPORTED_COMMAND
+
+ - label:
+ "Setup the TH such that it should not have the privilege for the
+ cluster in the path."
+ cluster: "AccessControl"
+ command: "writeAttribute"
+ attribute: "ACL"
+ endpoint: PIXIT.Cluster.AccessControl.EndPoint
+ arguments:
+ value:
+ [
+ {
+ "fabricIndex": alphaIndex,
+ "Privilege": 5,
+ "AuthMode": 2,
+ "Subjects": [commissionerNodeIdAlpha],
+ "Targets":
+ [
+ {
+ "Cluster": 31,
+ "Endpoint": 0,
+ "DeviceType": null,
+ },
+ ],
+ },
+ ]
+
+ - label:
+ "TH sends the Invoke Request Message to the DUT with a valid
+ CommandDataIB"
+ cluster: "AnyCommands"
+ command: "CommandById"
+ endpoint: InvokeRequestMessage.EndPoint
+ arguments:
+ values:
+ - name: "ClusterId"
+ value: InvokeRequestMessage.Cluster
+ - name: "CommandId"
+ value: InvokeRequestMessage.Command
+ - name: "Payload"
+ value: InvokeRequestMessage.Payload
+ response:
+ error: UNSUPPORTED_ACCESS
+
+ - label:
+ "TH sends the Invoke Request Message to the DUT with a valid and
+ fabric-scoped CommandDataIB"
+ cluster: "General Commissioning"
+ command: "CommissioningComplete"
+ endpoint: PIXIT.Cluster.GeneralCommissioning.EndPoint
+ response:
+ error: UNSUPPORTED_ACCESS
+
+ - label:
+ "Setup the TH such that it should have the privilege for the cluster
+ in the path."
+ cluster: "AccessControl"
+ command: "writeAttribute"
+ endpoint: PIXIT.Cluster.AccessControl.EndPoint
+ attribute: "ACL"
+ arguments:
+ value:
+ [
+ {
+ "fabricIndex": alphaIndex,
+ "Privilege": 5,
+ "AuthMode": 2,
+ "Subjects": [commissionerNodeIdAlpha],
+ "Targets": null,
+ },
+ ]
+
+ - label:
+ "(OPTIONAL) TH sends the Invoke Request Message to the DUT with the
+ command which requires a data response to be sent back."
+ cluster: "General Commissioning"
+ command: "ArmFailSafe"
+ endpoint: PIXIT.Cluster.GeneralCommissioning.EndPoint
+ arguments:
+ values:
+ - name: "ExpiryLengthSeconds"
+ value: 1000
+ - name: "Breadcrumb"
+ value: 1
+ response:
+ values:
+ - name: "ErrorCode"
+ value: 0
+
+ - label:
+ "TH sends the Invoke Request Message to the DUT with a valid
+ CommandDataIB and SuppressResponse set to True"
+ disabled: true
+ verification: |
+ Out of Scope for V1.0
+ https://github.com/project-chip/connectedhomeip/issues/8043
+ cluster: InvokeRequestMessage.Cluster
+ command: InvokeRequestMessage.Command
+ endpoint: InvokeRequestMessage.EndPoint
+ arguments:
+ values:
+ - name: "Payload"
+ value: PIXIT.InvokeRequestMessage.Payload
+
+ - label:
+ "Setup the TH such that it should not have the privilege for the
+ cluster in the path."
+ cluster: "AccessControl"
+ command: "writeAttribute"
+ attribute: "ACL"
+ endpoint: PIXIT.Cluster.AccessControl.EndPoint
+ arguments:
+ value:
+ [
+ {
+ "fabricIndex": alphaIndex,
+ "Privilege": 5,
+ "AuthMode": 2,
+ "Subjects": [commissionerNodeIdAlpha],
+ "Targets":
+ [
+ {
+ "Cluster": 31,
+ "Endpoint": 0,
+ "DeviceType": null,
+ },
+ ],
+ },
+ ]
+
+ - label:
+ "TH sends a Invoke Request Message to the DUT with the TimedRequest
+ set as TRUE.(There should be no previous Timed Invoke action.)"
+ cluster: "AnyCommands"
+ command: "CommandById"
+ endpoint: InvokeRequestMessage.EndPoint
+ timedInteractionTimeoutMs: 1000
+ arguments:
+ values:
+ - name: "ClusterId"
+ value: InvokeRequestMessage.Cluster
+ - name: "CommandId"
+ value: InvokeRequestMessage.Command
+ - name: "Payload"
+ value: InvokeRequestMessage.Payload
+ response:
+ error: UNSUPPORTED_ACCESS
+
+ - label:
+ "Setup the TH such that it should have the privilege for the cluster
+ in the path."
+ cluster: "AccessControl"
+ command: "writeAttribute"
+ endpoint: PIXIT.Cluster.AccessControl.EndPoint
+ attribute: "ACL"
+ arguments:
+ value:
+ [
+ {
+ "fabricIndex": alphaIndex,
+ "Privilege": 5,
+ "AuthMode": 2,
+ "Subjects": [commissionerNodeIdAlpha],
+ "Targets": null,
+ },
+ ]
+
+ - label:
+ "TH sends Invoke Request Message to the DUT with the command in the
+ path that requires a Timed Invoke transaction to invoke and this
+ action is not part of a Timed Invoke transaction"
+ cluster: "AdministratorCommissioning"
+ command: "OpenBasicCommissioningWindow"
+ endpoint: PIXIT.Cluster.AdministratorCommissioning.EndPoint
+ arguments:
+ values:
+ - name: "CommissioningTimeout"
+ value: 180
+ response:
+ error: NEEDS_TIMED_INTERACTION