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 f395df13674a7d..7371fb6b0fa3bb 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 @@ -234,10 +234,36 @@ def __get_command_name(self, request): return command_name, command_specifier def __get_arguments(self, request): + # chip-tool expects a json encoded string that contains both mandatory and optional arguments for the target command. + # + # Those arguments are either top level properties of the request object or under the 'arguments' property. + # + # Usually if an argument is used by multiple commands (e.g: 'endpoint', 'min-interval', 'commissioner-name') it is represented as + # a top level property of the request. + # Otherwise if the argument is a command specific argument, it can be retrieved as a member of the 'arguments' property. + # + # As an example, the following test step: + # + # - label: "Send Test Add Arguments Command" + # nodeId: 0x12344321 + # endpoint: 1 + # cluster: Unit Testing + # command: TestAddArguments + # identity: beta + # arguments: + # values: + # - name: arg1 + # value: 3 + # - name: arg2 + # value: 4 + # + # Will be translated to: + # destination-id": "0x12344321", "endpoint-id-ignored-for-group-commands": "1", "arg1":"3", "arg2":"17", "commissioner-name": "beta" arguments = '' arguments = self.__maybe_add_destination(arguments, request) arguments = self.__maybe_add_endpoint(arguments, request) arguments = self.__maybe_add_command_arguments(arguments, request) + arguments = self.__maybe_add_data_version(arguments, request) arguments = self.__maybe_add( arguments, request.min_interval, "min-interval") arguments = self.__maybe_add( @@ -304,6 +330,25 @@ def __maybe_add_command_arguments(self, rv, request): return rv + def __maybe_add_data_version(self, rv, request): + if request.data_version is None: + return rv + + value = '' + if type(request.data_version) is list: + for index, version in enumerate(request.data_version): + value += str(version) + if index != len(request.data_version) - 1: + value += ',' + else: + value = request.data_version + + if rv: + rv += ', ' + rv += f'"data-version":"{value}"' + + return rv + def __get_argument_name(self, request, entry): cluster_name = request.cluster command_name = request.command diff --git a/scripts/py_matter_yamltests/matter_yamltests/parser.py b/scripts/py_matter_yamltests/matter_yamltests/parser.py index 3190ffc94c3680..f7b5de016fc9cd 100644 --- a/scripts/py_matter_yamltests/matter_yamltests/parser.py +++ b/scripts/py_matter_yamltests/matter_yamltests/parser.py @@ -201,6 +201,8 @@ def __init__(self, test: dict, config: dict, definitions: SpecDefinitions, pics_ self.max_interval = _value_or_none(test, 'maxInterval') self.timed_interaction_timeout_ms = _value_or_none( test, 'timedInteractionTimeoutMs') + self.data_version = _value_or_none( + test, 'dataVersion') self.busy_wait_ms = _value_or_none(test, 'busyWaitMs') self.wait_for = _value_or_none(test, 'wait') self.event_number = _value_or_none(test, 'eventNumber') @@ -412,7 +414,8 @@ def _convert_single_value_to_values(self, container): # members of the 'values' array which is what is used for other tests. value = {} - known_keys_to_copy = ['value', 'constraints', 'saveAs'] + known_keys_to_copy = ['value', 'constraints', + 'saveAs', 'saveDataVersionAs'] known_keys_to_allow = ['error', 'clusterError'] for key, item in list(container.items()): @@ -473,6 +476,8 @@ def _update_with_definition(self, container: dict, mapping_type): item_value, mapping) elif key == 'saveAs' and type(item_value) is str and item_value not in self._parsing_config_variable_storage: self._parsing_config_variable_storage[item_value] = None + elif key == 'saveDataVersionAs' and type(item_value) is str and item_value not in self._parsing_config_variable_storage: + self._parsing_config_variable_storage[item_value] = None elif key == 'constraints': for constraint, constraint_value in item_value.items(): # Only apply update_value_with_definition to constraints that have a value that depends on @@ -563,6 +568,8 @@ def __init__(self, test: _TestStepWithPlaceholders, step_index: int, runtime_con if test.is_pics_enabled: self._update_placeholder_values(self.arguments) self._update_placeholder_values(self.responses) + self._test.data_version = self._config_variable_substitution( + self._test.data_version) self._test.node_id = self._config_variable_substitution( self._test.node_id) self._test.run_if = self._config_variable_substitution( @@ -654,6 +661,10 @@ def max_interval(self): def timed_interaction_timeout_ms(self): return self._test.timed_interaction_timeout_ms + @property + def data_version(self): + return self._test.data_version + @property def busy_wait_ms(self): return self._test.busy_wait_ms @@ -702,7 +713,10 @@ def post_process_response(self, received_responses): expected_response, received_response, result) self._response_constraints_validation( expected_response, received_response, result) - self._maybe_save_as(expected_response, received_response, result) + self._maybe_save_as('saveAs', 'value', + expected_response, received_response, result) + self._maybe_save_as('saveDataVersionAs', 'dataVersion', + expected_response, received_response, result) # An empty response array in a test step (responses: []) implies that the test step does expect a response # but without any associated value. @@ -936,17 +950,17 @@ def _response_constraints_validation(self, expected_response, received_response, e.update_context(expected_response, self.step_index) result.error(check_type, error_failure, e) - def _maybe_save_as(self, expected_response, received_response, result): + def _maybe_save_as(self, key: str, default_target: str, expected_response, received_response, result): check_type = PostProcessCheckType.SAVE_AS_VARIABLE error_success = 'The test save the value "{value}" as {name}.' error_name_does_not_exist = 'The test expects a value named "{name}" but it does not exists in the response."' for value in expected_response['values']: - if 'saveAs' not in value: + if key not in value: continue - received_value = received_response.get('value') - if not self.is_attribute and not self.is_event: + received_value = received_response.get(default_target) + if not self.is_attribute and not self.is_event and not (self.command in ANY_COMMANDS_LIST): expected_name = value.get('name') if received_value is None or expected_name not in received_value: result.error(check_type, error_name_does_not_exist.format( @@ -956,7 +970,7 @@ def _maybe_save_as(self, expected_response, received_response, result): received_value = received_value.get( expected_name) if received_value else None - save_as = value.get('saveAs') + save_as = value.get(key) self._runtime_config_variable_storage[save_as] = received_value result.success(check_type, error_success.format( value=received_value, name=save_as)) diff --git a/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py b/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py index a8cf54172b0593..f806127c96f7d1 100644 --- a/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py +++ b/scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py @@ -105,6 +105,7 @@ def __check_test_step(self, content): 'minInterval': int, 'maxInterval': int, 'timedInteractionTimeoutMs': int, + 'dataVersion': (list, int, str), # Can be a variable 'busyWaitMs': int, 'wait': str, } @@ -166,7 +167,8 @@ def __check_test_step_response_value(self, content, allow_name_key=False): 'error': str, 'clusterError': int, 'constraints': dict, - 'saveAs': str + 'saveAs': str, + 'saveDataVersionAs': str, } if allow_name_key: