Skip to content

Commit

Permalink
[matter_yaml] Add a yaml test that exercise anyOf, EqualityCommands a…
Browse files Browse the repository at this point in the history
…nd runIf (#26567)

* [matter_yamltests] Add anyOf constraint supports

* [matter_yamltest] Add EqualityCommands pseudo cluster

* [matter_yamltest] Add a runIf keyword to enable/disable a step based on a boolean variable

* Add a yaml test that exercise anyOf, EqualityCommands and runIf
  • Loading branch information
vivien-apple authored and pull[bot] committed Dec 15, 2023
1 parent 11c7bc5 commit e328965
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 2 deletions.
23 changes: 23 additions & 0 deletions scripts/py_matter_yamltests/matter_yamltests/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ def __init__(self, context, reason):
super().__init__(context, 'notValue', reason)


class ConstraintAnyOfError(ConstraintCheckError):
def __init__(self, context, reason):
super().__init__(context, 'anyOf', reason)


class BaseConstraint(ABC):
'''Constraint Interface'''

Expand Down Expand Up @@ -185,6 +190,8 @@ def _raise_error(self, reason):
raise ConstraintMaxValueError(self._context, reason)
elif isinstance(self, _ConstraintNotValue):
raise ConstraintNotValueError(self._context, reason)
elif isinstance(self, _ConstraintAnyOf):
raise ConstraintAnyOfError(self._context, reason)
else:
# This should not happens.
raise ConstraintParseError(f'Unknown constraint instance.')
Expand Down Expand Up @@ -740,6 +747,18 @@ def get_reason(self, value, value_type_name) -> str:
return f'The response value "{value}" should differs from the constraint.'


class _ConstraintAnyOf(BaseConstraint):
def __init__(self, context, any_of):
super().__init__(context, types=[], is_null_allowed=True)
self._any_of = any_of

def check_response(self, value, value_type_name) -> bool:
return value in self._any_of

def get_reason(self, value, value_type_name) -> str:
return f'The response value "{value}" is not a value from {self._any_of}.'


def get_constraints(constraints: dict) -> list[BaseConstraint]:
_constraints = []
context = constraints
Expand Down Expand Up @@ -792,6 +811,9 @@ def get_constraints(constraints: dict) -> list[BaseConstraint]:
elif 'notValue' == constraint:
_constraints.append(_ConstraintNotValue(
context, constraint_value))
elif 'anyOf' == constraint:
_constraints.append(_ConstraintAnyOf(
context, constraint_value))
else:
raise ConstraintParseError(f'Unknown constraint type:{constraint}')

Expand All @@ -816,6 +838,7 @@ def is_typed_constraint(constraint: str):
'hasMasksSet': False,
'hasMasksClear': False,
'notValue': True,
'anyOf': True,
}

is_typed = constraints.get(constraint)
Expand Down
5 changes: 4 additions & 1 deletion scripts/py_matter_yamltests/matter_yamltests/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def __init__(self, test: dict, config: dict, definitions: SpecDefinitions, pics_
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')
self.run_if = _value_or_none(test, 'runIf')

self.is_attribute = self.__is_attribute_command()
self.is_event = self.__is_event_command()
Expand Down Expand Up @@ -477,6 +478,8 @@ def __init__(self, test: _TestStepWithPlaceholders, step_index: int, runtime_con
self._update_placeholder_values(self.responses)
self._test.node_id = self._config_variable_substitution(
self._test.node_id)
self._test.run_if = self._config_variable_substitution(
self._test.run_if)
self._test.event_number = self._config_variable_substitution(
self._test.event_number)
test.update_arguments(self.arguments)
Expand All @@ -492,7 +495,7 @@ def is_enabled(self):

@property
def is_pics_enabled(self):
return self._test.is_pics_enabled
return self._test.is_pics_enabled and (self._test.run_if is None or self._test.run_if)

@property
def is_attribute(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#
# 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 ..pseudo_cluster import PseudoCluster

_DEFINITION = '''<?xml version="1.0"?>
<configurator>
<cluster>
<name>EqualityCommands</name>
<code>0xFFF1FD08</code>
<command source="client" code="0" name="BooleanEquals" response="EqualityResponse">
<arg name="Value1" type="boolean"/>
<arg name="Value2" type="boolean"/>
</command>
<command source="client" code="1" name="SignedNumberEquals" response="EqualityResponse">
<arg name="Value1" type="int64s"/>
<arg name="Value2" type="int64s"/>
</command>
<command source="client" code="2" name="UnsignedNumberEquals" response="EqualityResponse">
<arg name="Value1" type="int64u"/>
<arg name="Value2" type="int64u"/>
</command>
<command source="server" code="254" name="EqualityResponse">
<arg name="Equals" type="bool"/>
</command>
</cluster>
</configurator>
'''


def Compare(request):
value1 = None
value2 = None

for argument in request.arguments['values']:
if argument['name'] == 'Value1':
value1 = argument['value']
elif argument['name'] == 'Value2':
value2 = argument['value']

return value1 == value2


class EqualityCommands(PseudoCluster):
name = 'EqualityCommands'
definition = _DEFINITION

async def BooleanEquals(self, request):
return {'value': {'Equals': Compare(request)}}

async def SignedNumberEquals(self, request):
return {'value': {'Equals': Compare(request)}}

async def UnsignedNumberEquals(self, request):
return {'value': {'Equals': Compare(request)}}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .clusters.commissioner_commands import CommissionerCommands
from .clusters.delay_commands import DelayCommands
from .clusters.discovery_commands import DiscoveryCommands
from .clusters.equality_commands import EqualityCommands
from .clusters.log_commands import LogCommands
from .clusters.system_commands import SystemCommands
from .pseudo_cluster import PseudoCluster
Expand Down Expand Up @@ -51,6 +52,7 @@ def get_default_pseudo_clusters() -> PseudoClusters:
CommissionerCommands(),
DelayCommands(),
DiscoveryCommands(),
EqualityCommands(),
LogCommands(),
SystemCommands()
]
Expand Down
4 changes: 3 additions & 1 deletion scripts/py_matter_yamltests/matter_yamltests/yaml_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __check_test_step(self, content):
'label': str,
'identity': str,
'nodeId': int,
'runIf': str, # Should be a variable.
'groupId': int,
'endpoint': int,
'cluster': str,
Expand Down Expand Up @@ -194,7 +195,8 @@ def __check_test_step_response_value_constraints(self, content):
'excludes': list,
'hasMasksSet': list,
'hasMasksClear': list,
'notValue': (type(None), bool, str, int, float, list, dict)
'notValue': (type(None), bool, str, int, float, list, dict),
'anyOf': list
}

self.__check(content, schema)
Expand Down
1 change: 1 addition & 0 deletions scripts/tests/chiptest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def _GetInDevelopmentTests() -> Set[str]:
"""
return {
"Test_AddNewFabricFromExistingFabric.yaml", # chip-repl does not support GetCommissionerRootCertificate and IssueNocChain command
"TestEqualities.yaml", # chip-repl does not support pseudo-cluster commands that return a value
"TestClientMonitoringCluster.yaml" # Client Monitoring Tests need a rework after the XML update
}

Expand Down
96 changes: 96 additions & 0 deletions src/app/tests/suites/TestEqualities.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# 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.

name: Test for conditional steps using the runIf keyword

config:
nodeId: 0x12344321
cluster: "EqualityCommands"
endpoint: 1
expectedValue: 20003
unexpectedValue: 20004

tests:
- label: "Wait for the commissioned device to be retrieved"
cluster: "DelayCommands"
command: "WaitForCommissionee"
arguments:
values:
- name: "nodeId"
value: nodeId

- label: "Send a command with a vendor_id and enum"
cluster: "Unit Testing"
command: "TestEnumsRequest"
arguments:
values:
- name: "arg1"
value: expectedValue
- name: "arg2"
value: 1
response:
values:
- name: "arg1"
saveAs: Arg1Value
constraints:
anyOf: [expectedValue, unexpectedValue]

- label:
"Compute the result of comparing Arg1Value to 20003 and save the
result as a variable for later use"
cluster: "EqualityCommands"
command: "UnsignedNumberEquals"
arguments:
values:
- name: "Value1"
value: Arg1Value
- name: "Value2"
value: expectedValue
response:
- values:
- name: "Equals"
value: true
saveAs: IsExpectedValue

- label:
"Compute the result of comparing Arg1Value to 20004 and save the
result as a variable for later use"
cluster: "EqualityCommands"
command: "UnsignedNumberEquals"
arguments:
values:
- name: "Value1"
value: Arg1Value
- name: "Value2"
value: unexpectedValue
response:
- values:
- name: "Equals"
value: false
saveAs: IsUnexpectedValue

- label: "Use runIf to skip this step"
runIf: IsUnexpectedValue
cluster: "EqualityCommands"
command: "BooleanEquals"
arguments:
values:
- name: "Value1"
value: true
- name: "Value2"
value: false
response:
- values:
- name: "Equals"
value: true # 'true' such that if the step is executed it will result in an error.

0 comments on commit e328965

Please sign in to comment.