Skip to content

Commit

Permalink
Add support for contains/excludes for struct types. (#29643)
Browse files Browse the repository at this point in the history
Also makes it possible to use a variable for nodeId.
  • Loading branch information
bzbarsky-apple committed Oct 11, 2023
1 parent 5647e0a commit e8f516a
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 19 deletions.
56 changes: 47 additions & 9 deletions scripts/py_matter_yamltests/matter_yamltests/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -689,20 +689,53 @@ def get_reason(self, value, value_type_name) -> str:
return f'The response value ({value}) should be lower or equal to the constraint but {value} > {self._max_value}.'


def _values_match(expected_value, received_value):
# TODO: This is a copy of _response_value_validation over in parser.py,
# but with the recursive calls renamed.
if isinstance(expected_value, list):
if len(expected_value) != len(received_value):
return False

for index, expected_item in enumerate(expected_value):
received_item = received_value[index]
if not _values_match(expected_item, received_item):
return False
return True
elif isinstance(expected_value, dict):
for key, expected_item in expected_value.items():
received_item = received_value.get(key)
if not _values_match(expected_item, received_item):
return False
return True
else:
return expected_value == received_value


class _ConstraintContains(BaseConstraint):
def __init__(self, context, contains):
super().__init__(context, types=[list])
self._contains = contains

def _find_missing_values(self, expected_values, received_values):
# Make a copy of received_values, so that we can remove things from the
# list as they match up against our expected values.
received_values = list(received_values)
missing_values = []
for expected_value in expected_values:
for index, received_value in enumerate(received_values):
if _values_match(expected_value, received_value):
# We've used up this received value
del received_values[index]
break
else:
missing_values.append(expected_value)
return missing_values

def check_response(self, value, value_type_name) -> bool:
return set(self._contains).issubset(value)
return len(self._find_missing_values(self._contains, value)) == 0

def get_reason(self, value, value_type_name) -> str:
expected_values = []

for expected_value in self._contains:
if expected_value not in value:
expected_values.append(expected_value)
expected_values = self._find_missing_values(self._contains, value)

return f'The response ({value}) is missing {expected_values}.'

Expand All @@ -713,14 +746,19 @@ def __init__(self, context, excludes):
self._excludes = excludes

def check_response(self, value, value_type_name) -> bool:
return set(self._excludes).isdisjoint(value)
for expected_value in self._excludes:
for received_value in value:
if _values_match(expected_value, received_value):
return False
return True

def get_reason(self, value, value_type_name) -> str:
unexpected_values = []

for unexpected_value in self._excludes:
if unexpected_value in value:
unexpected_values.append(unexpected_value)
for received_value in value:
if _values_match(unexpected_value, received_value):
unexpected_values.append(unexpected_value)

return f'The response ({value}) contains {unexpected_values}.'

Expand Down
2 changes: 2 additions & 0 deletions scripts/py_matter_yamltests/matter_yamltests/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,8 @@ def __init__(self, test: _TestStepWithPlaceholders, step_index: int, runtime_con
self._test.endpoint)
self._test.group_id = self._config_variable_substitution(
self._test.group_id)
self._test.node_id = self._config_variable_substitution(
self._test.node_id)
test.update_arguments(self.arguments)
test.update_responses(self.responses)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def __check_test_step(self, config: dict, content):
schema = {
'label': str,
'identity': str,
'nodeId': int,
'nodeId': (int, str), # Can be a variable.
'runIf': str, # Should be a variable.
'groupId': (int, str), # Can be a variable.
'endpoint': (int, str), # Can be a variable
Expand Down
18 changes: 17 additions & 1 deletion scripts/py_matter_yamltests/test_yaml_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ def test_key_tests_step_int_keys(self):
content = ('tests:\n'
' - {key}: {value}')
keys = [
'nodeId',
'minInterval',
'maxInterval',
'timedInteractionTimeoutMs',
Expand Down Expand Up @@ -340,6 +339,23 @@ def test_key_tests_step_group_id_key(self):
x = content.format(value=value)
self.assertRaises(TestStepInvalidTypeError, load, x)

def test_key_tests_step_node_id_key(self):
load = YamlLoader().load

content = ('tests:\n'
' - nodeId: {value}')

_, _, _, _, tests = load(content.format(value=1))
self.assertEqual(tests, [{'nodeId': 1}])

_, _, _, _, tests = load(content.format(value='TestKey'))
self.assertEqual(tests, [{'nodeId': 'TestKey'}])

wrong_values = self._get_wrong_values([str, int], spaces=6)
for value in wrong_values:
x = content.format(value=value)
self.assertRaises(TestStepInvalidTypeError, load, x)

def test_key_tests_step_event_number_key(self):
load = YamlLoader().load

Expand Down
95 changes: 91 additions & 4 deletions src/app/tests/suites/Test_AddNewFabricFromExistingFabric.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ name: Test
config:
nodeId: 0x12344321
endpoint: 0
nodeId2:
type: node_id
defaultValue: 0x43211234

tests:
- label: "Wait for the alpha device to be retrieved "
Expand Down Expand Up @@ -91,7 +94,7 @@ tests:
- name: "Elements"
value: NOCSRElements
- name: "nodeId"
value: 0x43211234
value: nodeId2
response:
values:
- name: "NOC"
Expand Down Expand Up @@ -130,11 +133,27 @@ tests:
value: 0

- label: "Send Commissioning Complete command from beta"
nodeId: 0x43211234
nodeId: nodeId2
identity: "beta"
cluster: "General Commissioning"
command: "CommissioningComplete"

- label: "Read alpha fabric index"
command: "readAttribute"
cluster: "Operational Credentials"
attribute: "CurrentFabricIndex"
response:
saveAs: AlphaFabricIndex

- label: "Read beta fabric index"
identity: "beta"
nodeId: nodeId2
command: "readAttribute"
cluster: "Operational Credentials"
attribute: "CurrentFabricIndex"
response:
saveAs: BetaFabricIndex

- label: "Read the fabrics list again from alpha"
command: "readAttribute"
cluster: "Operational Credentials"
Expand All @@ -144,13 +163,81 @@ tests:
constraints:
type: list

- label: "Read the fabrics list again from alpha without fabric-filtering"
command: "readAttribute"
cluster: "Operational Credentials"
attribute: "Fabrics"
fabricFiltered: false
response:
value:
[
{ Label: "", NodeID: nodeId, FabricIndex: AlphaFabricIndex },
{ Label: "", NodeID: nodeId2, FabricIndex: BetaFabricIndex },
]
constraints:
type: list

- label:
"Read the fabrics list again from alpha without fabric-filtering,
checking contains constraint"
command: "readAttribute"
cluster: "Operational Credentials"
attribute: "Fabrics"
fabricFiltered: false
response:
constraints:
type: list
# Flip the order to test the constraint better.
contains:
[
{
Label: "",
NodeID: nodeId2,
FabricIndex: BetaFabricIndex,
},
{
Label: "",
NodeID: nodeId,
FabricIndex: AlphaFabricIndex,
},
]
excludes:
[
{
Label: "",
NodeID: nodeId,
FabricIndex: BetaFabricIndex,
},
{
Label: "",
NodeID: nodeId2,
FabricIndex: AlphaFabricIndex,
},
]

- label: "Read the fabrics list from beta this time"
identity: "beta"
nodeId: 0x43211234
nodeId: nodeId2
command: "readAttribute"
cluster: "Operational Credentials"
attribute: "Fabrics"
response:
value: [{ Label: "", NodeID: nodeId2 }]
constraints:
type: list

- label: "Read the fabrics list from beta without fabric-filtering"
identity: "beta"
nodeId: nodeId2
command: "readAttribute"
cluster: "Operational Credentials"
attribute: "Fabrics"
fabricFiltered: false
response:
value: [{ Label: "", NodeID: 0x43211234 }]
value:
[
{ Label: "", NodeID: nodeId, FabricIndex: AlphaFabricIndex },
{ Label: "", NodeID: nodeId2, FabricIndex: BetaFabricIndex },
]
constraints:
type: list
4 changes: 2 additions & 2 deletions src/app/tests/suites/certification/Test_TC_WAKEONLAN_1_5.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ tests:
response:
constraints:
type: list
contains: [65528, 65529, 65530, 65531, 65531, 65533]
contains: [65528, 65529, 65530, 65531, 65532, 65533]

- label: "Step 3a: Read the global attribute: AttributeList"
PICS: "!PICS_EVENT_LIST_ENABLED"
Expand All @@ -65,7 +65,7 @@ tests:
response:
constraints:
type: list
contains: [65528, 65529, 65531, 65531, 65533]
contains: [65528, 65529, 65531, 65532, 65533]

- label: "Step 3b: Read the optional attribute(MACAddress) in AttributeList"
PICS: WAKEONLAN.S.A0000
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e8f516a

Please sign in to comment.