From 3896c7436c84e98536dedd29827798fe6aaa9795 Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Mon, 9 Jan 2023 15:28:02 +0000 Subject: [PATCH] Couple of quick fixes to yamltests parser These fixes are needed for multi admin support. They were missed when doing some refactoring in the yamltests parser. --- .../matter_yamltests/fixes.py | 9 +- .../matter_yamltests/parser.py | 95 ++++++++++++------- 2 files changed, 68 insertions(+), 36 deletions(-) diff --git a/scripts/py_matter_yamltests/matter_yamltests/fixes.py b/scripts/py_matter_yamltests/matter_yamltests/fixes.py index b0a3f428895f0d..c073b923e163c1 100644 --- a/scripts/py_matter_yamltests/matter_yamltests/fixes.py +++ b/scripts/py_matter_yamltests/matter_yamltests/fixes.py @@ -96,7 +96,8 @@ def try_add_yaml_support_for_scientific_notation_without_dot(loader): # accessory. But this state may not exist in the runner (as in it prevent to have multiple node ids # associated to a fabric...) so the 'nodeId' needs to be added back manually. def try_update_yaml_node_id_test_runner_state(tests, config): - identities = {'alpha': None if 'nodeId' not in config else config['nodeId']} + identities = { + 'alpha': None if 'nodeId' not in config else config['nodeId']} for test in tests: if not test.is_enabled: @@ -106,9 +107,9 @@ def try_update_yaml_node_id_test_runner_state(tests, config): if test.cluster == 'CommissionerCommands': if test.command == 'PairWithCode': - for item in test.arguments['values']: + for item in test.arguments_with_placeholders['values']: if item['name'] == 'nodeId': identities[identity] = item['value'] elif identity is not None and identity in identities: - nodeId = identities[identity] - test.nodeId = nodeId + node_id = identities[identity] + test.node_id = node_id diff --git a/scripts/py_matter_yamltests/matter_yamltests/parser.py b/scripts/py_matter_yamltests/matter_yamltests/parser.py index d58d56e3c43bd8..bf23433e7ec0d3 100644 --- a/scripts/py_matter_yamltests/matter_yamltests/parser.py +++ b/scripts/py_matter_yamltests/matter_yamltests/parser.py @@ -206,7 +206,8 @@ def __init__(self, test: dict, config: dict, definitions): self.fabric_filtered = _value_or_none(test, 'fabricFiltered') self.min_interval = _value_or_none(test, 'minInterval') self.max_interval = _value_or_none(test, 'maxInterval') - self.timed_interaction_timeout_ms = _value_or_none(test, 'timedInteractionTimeoutMs') + self.timed_interaction_timeout_ms = _value_or_none( + test, 'timedInteractionTimeoutMs') self.busy_wait_ms = _value_or_none(test, 'busyWaitMs') self.is_attribute = self.command in _ATTRIBUTE_COMMANDS @@ -215,8 +216,10 @@ def __init__(self, test: dict, config: dict, definitions): self.arguments_with_placeholders = _value_or_none(test, 'arguments') self.response_with_placeholders = _value_or_none(test, 'response') - _check_valid_keys(self.arguments_with_placeholders, _TEST_ARGUMENTS_SECTION) - _check_valid_keys(self.response_with_placeholders, _TEST_RESPONSE_SECTION) + _check_valid_keys(self.arguments_with_placeholders, + _TEST_ARGUMENTS_SECTION) + _check_valid_keys(self.response_with_placeholders, + _TEST_RESPONSE_SECTION) self._convert_single_value_to_values(self.arguments_with_placeholders) self._convert_single_value_to_values(self.response_with_placeholders) @@ -225,20 +228,26 @@ def __init__(self, test: dict, config: dict, definitions): response_mapping = None if self.is_attribute: - attribute = definitions.get_attribute_by_name(self.cluster, self.attribute) + attribute = definitions.get_attribute_by_name( + self.cluster, self.attribute) if attribute: attribute_mapping = self._as_mapping(definitions, self.cluster, attribute.definition.data_type.name) argument_mapping = attribute_mapping response_mapping = attribute_mapping else: - command = definitions.get_command_by_name(self.cluster, self.command) + command = definitions.get_command_by_name( + self.cluster, self.command) if command: - argument_mapping = self._as_mapping(definitions, self.cluster, command.input_param) - response_mapping = self._as_mapping(definitions, self.cluster, command.output_param) + argument_mapping = self._as_mapping( + definitions, self.cluster, command.input_param) + response_mapping = self._as_mapping( + definitions, self.cluster, command.output_param) - self._update_with_definition(self.arguments_with_placeholders, argument_mapping) - self._update_with_definition(self.response_with_placeholders, response_mapping) + self._update_with_definition( + self.arguments_with_placeholders, argument_mapping) + self._update_with_definition( + self.response_with_placeholders, response_mapping) # This performs a very basic sanity parse time check of constrains. This parsing happens # again inside post processing response since at that time we will have required variables @@ -281,7 +290,8 @@ def _as_mapping(self, definitions, cluster_name, target_name): if hasattr(element, 'base_type'): target_name = element.base_type.lower() elif hasattr(element, 'fields'): - target_name = {f.name: self._as_mapping(definitions, cluster_name, f.data_type.name) for f in element.fields} + target_name = {f.name: self._as_mapping( + definitions, cluster_name, f.data_type.name) for f in element.fields} elif target_name: target_name = target_name.lower() @@ -296,7 +306,8 @@ def _update_with_definition(self, container: dict, mapping_type): mapping = mapping_type if self.is_attribute else mapping_type[value['name']] if key == 'value': - value[key] = self._update_value_with_definition(item_value, mapping) + value[key] = self._update_value_with_definition( + 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 == 'constraints': @@ -321,7 +332,8 @@ def _update_value_with_definition(self, value, mapping_type): rv[key] = value[key] # int64u else: mapping = mapping_type[key] - rv[key] = self._update_value_with_definition(value[key], mapping) + rv[key] = self._update_value_with_definition( + value[key], mapping) return rv if type(value) is list: return [self._update_value_with_definition(entry, mapping_type) for entry in value] @@ -331,7 +343,8 @@ def _update_value_with_definition(self, value, mapping_type): if value is not None and value not in self._parsing_config_variable_storage: if mapping_type == 'int64u' or mapping_type == 'int64s' or mapping_type == 'bitmap64' or mapping_type == 'epoch_us': value = fixes.try_apply_yaml_cpp_longlong_limitation_fix(value) - value = fixes.try_apply_yaml_unrepresentable_integer_for_javascript_fixes(value) + value = fixes.try_apply_yaml_unrepresentable_integer_for_javascript_fixes( + value) elif mapping_type == 'single' or mapping_type == 'double': value = fixes.try_apply_yaml_float_written_as_strings(value) elif mapping_type == 'octet_string' or mapping_type == 'long_octet_string': @@ -464,7 +477,7 @@ def _response_error_validation(self, response, result): error_success = 'The test expects the "{error}" error which occured successfully.' error_success_no_error = 'The test expects no error and no error occurred.' error_wrong_error = 'The test expects the "{error}" error but the "{value}" error occured.' - error_unexpected_error = 'The test expects no error but the "{value}" error occured.' + error_unexpected_error = 'The test expects no error but the "{error}" error occured.' error_unexpected_success = 'The test expects the "{error}" error but no error occured.' expected_error = self.response.get('error') if self.response else None @@ -478,14 +491,17 @@ def _response_error_validation(self, response, result): received_error = response.get('error') if expected_error and received_error and expected_error == received_error: - result.success(check_type, error_success.format(error=expected_error)) + result.success(check_type, error_success.format( + error=expected_error)) elif expected_error and received_error: result.error(check_type, error_wrong_error.format( error=expected_error, value=received_error)) elif expected_error and not received_error: - result.error(check_type, error_unexpected_success.format(error=expected_error)) + result.error(check_type, error_unexpected_success.format( + error=expected_error)) elif not expected_error and received_error: - result.error(check_type, error_unexpected_error.format(error=received_error)) + result.error(check_type, error_unexpected_error.format( + error=received_error)) elif not expected_error and not received_error: result.success(check_type, error_success_no_error) else: @@ -503,12 +519,14 @@ def _response_cluster_error_validation(self, response, result): if expected_error: if received_error and expected_error == received_error: - result.success(check_type, error_success.format(error=expected_error)) + result.success(check_type, error_success.format( + error=expected_error)) elif received_error: result.error(check_type, error_wrong_error.format( error=expected_error, value=received_error)) else: - result.error(check_type, error_unexpected_success.format(error=expected_error)) + result.error(check_type, error_unexpected_success.format( + error=expected_error)) else: # Nothing is logged here to not be redundant with the generic error checking code. pass @@ -528,10 +546,12 @@ def _response_values_validation(self, response, result): if not self.is_attribute: expected_name = value.get('name') if expected_name not in received_value: - result.error(check_type, error_name_does_not_exist.format(name=expected_name)) + result.error(check_type, error_name_does_not_exist.format( + name=expected_name)) continue - received_value = received_value.get(expected_name) if received_value else None + received_value = received_value.get( + expected_name) if received_value else None # TODO Supports Array/List. See an exemple of failure in TestArmFailSafe.yaml expected_value = value.get('value') @@ -557,10 +577,12 @@ def _response_constraints_validation(self, response, result): if not self.is_attribute: expected_name = value.get('name') if expected_name not in received_value: - result.error(check_type, error_name_does_not_exist.format(name=expected_name)) + result.error(check_type, error_name_does_not_exist.format( + name=expected_name)) continue - received_value = received_value.get(expected_name) if received_value else None + received_value = received_value.get( + expected_name) if received_value else None constraints = get_constraints(value['constraints']) if all([constraint.is_met(received_value) for constraint in constraints]): @@ -583,14 +605,17 @@ def _maybe_save_as(self, response, result): if not self.is_attribute: expected_name = value.get('name') if expected_name not in received_value: - result.error(check_type, error_name_does_not_exist.format(name=expected_name)) + result.error(check_type, error_name_does_not_exist.format( + name=expected_name)) continue - received_value = received_value.get(expected_name) if received_value else None + received_value = received_value.get( + expected_name) if received_value else None save_as = value.get('saveAs') self._runtime_config_variable_storage[save_as] = received_value - result.success(check_type, error_success.format(value=received_value, name=save_as)) + result.success(check_type, error_success.format( + value=received_value, name=save_as)) def _update_placeholder_values(self, container): if not container: @@ -600,7 +625,8 @@ def _update_placeholder_values(self, container): for idx, item in enumerate(values): if 'value' in item: - values[idx]['value'] = self._config_variable_substitution(item['value']) + values[idx]['value'] = self._config_variable_substitution( + item['value']) if 'constraints' in item: for constraint, constraint_value in item['constraints'].items(): @@ -615,7 +641,8 @@ def _config_variable_substitution(self, value): elif type(value) is dict: mapped_value = {} for key in value: - mapped_value[key] = self._config_variable_substitution(value[key]) + mapped_value[key] = self._config_variable_substitution( + value[key]) return mapped_value elif type(value) is str: # For most tests, a single config variable is used and it can be replaced as in. @@ -669,7 +696,8 @@ def __init__(self, parsing_config_variable_storage: dict, definitions, tests: di fixes.try_update_yaml_node_id_test_runner_state( enabled_tests, self._parsing_config_variable_storage) - self._runtime_config_variable_storage = copy.deepcopy(parsing_config_variable_storage) + self._runtime_config_variable_storage = copy.deepcopy( + parsing_config_variable_storage) self._tests = enabled_tests self._index = 0 self.count = len(self._tests) @@ -692,7 +720,8 @@ def __init__(self, test_file, pics_file, definitions): # TODO Needs supports for PICS file with open(test_file) as f: loader = yaml.FullLoader - loader = fixes.try_add_yaml_support_for_scientific_notation_without_dot(loader) + loader = fixes.try_add_yaml_support_for_scientific_notation_without_dot( + loader) data = yaml.load(f, Loader=loader) _check_valid_keys(data, _TESTS_SECTION) @@ -700,10 +729,12 @@ def __init__(self, test_file, pics_file, definitions): self.name = _value_or_none(data, 'name') self.PICS = _value_or_none(data, 'PICS') - self._parsing_config_variable_storage = _value_or_none(data, 'config') + self._parsing_config_variable_storage = _value_or_none( + data, 'config') tests = _value_or_none(data, 'tests') - self.tests = YamlTests(self._parsing_config_variable_storage, definitions, tests) + self.tests = YamlTests( + self._parsing_config_variable_storage, definitions, tests) def update_config(self, key, value): self._parsing_config_variable_storage[key] = value