Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for default pseudo cluster for chip-repl based yamltests #24669

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion src/controller/python/chip/yaml/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@
import stringcase
from chip import ChipDeviceCtrl
from chip.clusters.Attribute import AttributeStatus, SubscriptionTransaction, TypedAttributePath, ValueDecodeFailure
from chip.exceptions import ChipStackError
from chip.yaml.errors import ParsingError, UnexpectedParsingError
from matter_yamltests.pseudo_clusters.clusters.delay_commands import DelayCommands
from matter_yamltests.pseudo_clusters.clusters.log_commands import LogCommands
from matter_yamltests.pseudo_clusters.clusters.system_commands import SystemCommands
from matter_yamltests.pseudo_clusters.pseudo_clusters import PseudoClusters

from .data_model_lookup import *

_PSEUDO_CLUSTERS = PseudoClusters([DelayCommands(), LogCommands(), SystemCommands()])
logger = logging.getLogger('YamlParser')


Expand Down Expand Up @@ -96,6 +102,18 @@ def run_action(self, dev_ctrl: ChipDeviceCtrl) -> _ActionResult:
pass


class DefaultPseudoCluster(BaseAction):
def __init__(self, test_step):
super().__init__(test_step)
self._test_step = test_step
if not _PSEUDO_CLUSTERS.supports(test_step):
raise ParsingError(f'Default cluster {test_step.cluster} {test_step.command}, not supported')

def run_action(self, dev_ctrl: ChipDeviceCtrl) -> _ActionResult:
resp = asyncio.run(_PSEUDO_CLUSTERS.execute(self._test_step))
return _ActionResult(status=_ActionStatus.SUCCESS, response=None)


class InvokeAction(BaseAction):
'''Single invoke action to be executed.'''

Expand Down Expand Up @@ -213,6 +231,13 @@ def run_action(self, dev_ctrl: ChipDeviceCtrl) -> _ActionResult:
fabricFiltered=self._fabric_filtered))
except chip.interaction_model.InteractionModelError as error:
return _ActionResult(status=_ActionStatus.ERROR, response=error)
except ChipStackError as error:
_CHIP_TIMEOUT_ERROR = 50
if error.err == _CHIP_TIMEOUT_ERROR:
return _ActionResult(status=_ActionStatus.ERROR, response=error)
# For now it is unsure if all ChipStackError are supposed to be intentional.
# As a result we simply re-raise the error.
raise error

return self.parse_raw_response(raw_resp)

Expand Down Expand Up @@ -253,8 +278,13 @@ def __init__(self, test_step):
args = test_step.arguments['values']
request_data_as_dict = Converter.convert_list_of_name_value_pair_to_dict(args)

# There's a chance the commissionee may have rebooted before this call here as part of a
# test flow or is just starting out fresh outright. Unless expireExistingSession is
# explicitly set, the default behaviour it to make sure we're not re-using any cached CASE
# sessions that will now be stale and mismatched with the peer, causing subsequent
# interactions to fail.
self._expire_existing_session = request_data_as_dict.get('expireExistingSession', True)
self._node_id = request_data_as_dict['nodeId']
self._expire_existing_session = request_data_as_dict.get('expireExistingSession', False)
if 'timeout' in request_data_as_dict:
# Timeout is provided in seconds we need to conver to milliseconds.
self._timeout_ms = request_data_as_dict['timeout'] * 1000
Expand Down Expand Up @@ -579,6 +609,12 @@ def _commissioner_command_action_factory(self, test_step):
except ParsingError:
return None

def _default_pseudo_cluster(self, test_step):
try:
return DefaultPseudoCluster(test_step)
except ParsingError:
return None

def encode(self, request) -> BaseAction:
action = None
cluster = request.cluster.replace(' ', '').replace('/', '')
Expand All @@ -605,6 +641,10 @@ def encode(self, request) -> BaseAction:
else:
action = self._invoke_action_factory(request, cluster)

if action is None:
# Now we try to create a default pseudo cluster.
action = self._default_pseudo_cluster(request)

if action is None:
logger.warn(f"Failed to parse {request.label}")
return action
Expand All @@ -628,6 +668,10 @@ def decode(self, result: _ActionResult):
decoded_response['error'] = stringcase.snakecase(response.name).upper()
return decoded_response

if isinstance(response, ChipStackError):
decoded_response['error'] = 'FAILURE'
return decoded_response

cluster_name = self._test_spec_definition.get_cluster_name(response.cluster_id)
if cluster_name is None:
raise Exception("Cannot find cluster name for id 0x%0X / %d" % (response.cluster_id, response.cluster_id))
Expand Down