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

Fix issue with chip-repl yamltest running TestClusterComplexTypes.yaml #24502

Merged
merged 6 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
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
5 changes: 4 additions & 1 deletion scripts/tests/chiptest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ def tests_with_command(chip_tool: str, is_manual: bool):
# TODO We will move away from hardcoded list of yamltests to run all file when yamltests
# parser/runner reaches parity with the code gen version.
def _hardcoded_python_yaml_tests():
currently_supported_yaml_tests = ["TestConstraints.yaml"]
currently_supported_yaml_tests = [
"TestClusterComplexTypes.yaml",
"TestConstraints.yaml",
]

for name in currently_supported_yaml_tests:
yaml_test_path = _FindYamlTestPath(name)
Expand Down
8 changes: 4 additions & 4 deletions src/controller/python/chip/ChipDeviceCtrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ def ComputeRoundTripTimeout(self, nodeid, upperLayerProcessingTimeoutMs: int = 0
device.deviceProxy, upperLayerProcessingTimeoutMs))
return res

async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None, timedRequestTimeoutMs: int = None, interactionTimeoutMs: int = None):
async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None, timedRequestTimeoutMs: typing.Union[None, int] = None, interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None):
'''
Send a cluster-object encapsulated command to a node and get returned a future that can be awaited upon to receive the response.
If a valid responseType is passed in, that will be used to deserialize the object. If not, the type will be automatically deduced
Expand All @@ -867,10 +867,10 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.
EndpointId=endpoint,
ClusterId=payload.cluster_id,
CommandId=payload.command_id,
), payload, timedRequestTimeoutMs=timedRequestTimeoutMs, interactionTimeoutMs=interactionTimeoutMs).raise_on_error()
), payload, timedRequestTimeoutMs=timedRequestTimeoutMs, interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs).raise_on_error()
return await future

async def WriteAttribute(self, nodeid: int, attributes: typing.List[typing.Tuple[int, ClusterObjects.ClusterAttributeDescriptor, int]], timedRequestTimeoutMs: int = None, interactionTimeoutMs: int = None):
async def WriteAttribute(self, nodeid: int, attributes: typing.List[typing.Tuple[int, ClusterObjects.ClusterAttributeDescriptor, int]], timedRequestTimeoutMs: typing.Union[None, int] = None, interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None):
'''
Write a list of attributes on a target node.

Expand Down Expand Up @@ -900,7 +900,7 @@ async def WriteAttribute(self, nodeid: int, attributes: typing.List[typing.Tuple
v[0], v[1], v[2], 1, v[1].value))

ClusterAttribute.WriteAttributes(
future, eventLoop, device.deviceProxy, attrs, timedRequestTimeoutMs=timedRequestTimeoutMs, interactionTimeoutMs=interactionTimeoutMs).raise_on_error()
future, eventLoop, device.deviceProxy, attrs, timedRequestTimeoutMs=timedRequestTimeoutMs, interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs).raise_on_error()
return await future

def _parseAttributePathTuple(self, pathTuple: typing.Union[
Expand Down
20 changes: 14 additions & 6 deletions src/controller/python/chip/clusters/Attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import chip.interaction_model
import chip.tlv
import construct
from chip.native import PyChipError
from chip.native import ErrorSDKPart, PyChipError
from rich.pretty import pprint

from .ClusterObjects import Cluster, ClusterAttributeDescriptor, ClusterEvent
Expand Down Expand Up @@ -810,7 +810,11 @@ def _handleDone(self):
# move on, possibly invalidating the provided _event_loop.
#
if self._resultError is not None:
self._future.set_exception(self._resultError.to_exception())
if self._resultError.sdk_part is ErrorSDKPart.IM_GLOBAL_STATUS:
im_status = chip.interaction_model.Status(self._resultError.sdk_code)
self._future.set_exception(chip.interaction_model.InteractionModelError(im_status))
else:
self._future.set_exception(self._resultError.to_exception())
else:
self._future.set_result(self._resultData)

Expand Down Expand Up @@ -910,14 +914,13 @@ def _OnWriteDoneCallback(closure):
closure.handleDone()


def WriteAttributes(future: Future, eventLoop, device, attributes: List[AttributeWriteRequest], timedRequestTimeoutMs: int = None, interactionTimeoutMs: int = None) -> PyChipError:
def WriteAttributes(future: Future, eventLoop, device, attributes: List[AttributeWriteRequest], timedRequestTimeoutMs: Union[None, int] = None, interactionTimeoutMs: Union[None, int] = None, busyWaitMs: Union[None, int] = None) -> PyChipError:
handle = chip.native.GetLibraryHandle()

writeargs = []
for attr in attributes:
if attr.Attribute.must_use_timed_write and timedRequestTimeoutMs is None or timedRequestTimeoutMs == 0:
raise ValueError(
f"Attribute {attr.__class__} must use timed write, please specify a valid timedRequestTimeoutMs value.")
raise chip.interaction_model.InteractionModelError(chip.interaction_model.Status.NeedsTimedInteraction)
path = chip.interaction_model.AttributePathIBstruct.parse(
b'\x00' * chip.interaction_model.AttributePathIBstruct.sizeof())
path.EndpointId = attr.EndpointId
Expand All @@ -935,7 +938,12 @@ def WriteAttributes(future: Future, eventLoop, device, attributes: List[Attribut
ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction))
res = builtins.chipStack.Call(
lambda: handle.pychip_WriteClient_WriteAttributes(
ctypes.py_object(transaction), device, ctypes.c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), ctypes.c_uint16(0 if interactionTimeoutMs is None else interactionTimeoutMs), ctypes.c_size_t(len(attributes)), *writeargs))
ctypes.py_object(transaction), device,
ctypes.c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs),
ctypes.c_uint16(0 if interactionTimeoutMs is None else interactionTimeoutMs),
ctypes.c_uint16(0 if busyWaitMs is None else busyWaitMs),
ctypes.c_size_t(len(attributes)), *writeargs)
)
if not res.is_success:
ctypes.pythonapi.Py_DecRef(ctypes.py_object(transaction))
return res
Expand Down
16 changes: 10 additions & 6 deletions src/controller/python/chip/clusters/Command.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from asyncio.futures import Future
from ctypes import CFUNCTYPE, c_char_p, c_size_t, c_uint8, c_uint16, c_uint32, c_void_p, py_object
from dataclasses import dataclass
from typing import Type
from typing import Type, Union

import chip.exceptions
import chip.interaction_model
Expand Down Expand Up @@ -142,7 +142,7 @@ def _OnCommandSenderDoneCallback(closure):
ctypes.pythonapi.Py_DecRef(ctypes.py_object(closure))


def SendCommand(future: Future, eventLoop, responseType: Type, device, commandPath: CommandPath, payload: ClusterCommand, timedRequestTimeoutMs: int = None, interactionTimeoutMs: int = None) -> PyChipError:
def SendCommand(future: Future, eventLoop, responseType: Type, device, commandPath: CommandPath, payload: ClusterCommand, timedRequestTimeoutMs: Union[None, int] = None, interactionTimeoutMs: Union[None, int] = None, busyWaitMs: Union[None, int] = None) -> PyChipError:
''' Send a cluster-object encapsulated command to a device and does the following:
- On receipt of a successful data response, returns the cluster-object equivalent through the provided future.
- None (on a successful response containing no data)
Expand All @@ -158,17 +158,21 @@ def SendCommand(future: Future, eventLoop, responseType: Type, device, commandPa
if (responseType is not None) and (not issubclass(responseType, ClusterCommand)):
raise ValueError("responseType must be a ClusterCommand or None")
if payload.must_use_timed_invoke and timedRequestTimeoutMs is None or timedRequestTimeoutMs == 0:
raise ValueError(
f"Command {payload.__class__} must use timed invoke, please specify a valid timedRequestTimeoutMs value")
raise chip.interaction_model.InteractionModelError(chip.interaction_model.Status.NeedsTimedInteraction)

handle = chip.native.GetLibraryHandle()
transaction = AsyncCommandTransaction(future, eventLoop, responseType)

payloadTLV = payload.ToTLV()
ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction))
return builtins.chipStack.Call(
lambda: handle.pychip_CommandSender_SendCommand(ctypes.py_object(
transaction), device, c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), commandPath.EndpointId, commandPath.ClusterId, commandPath.CommandId, payloadTLV, len(payloadTLV), ctypes.c_uint16(0 if interactionTimeoutMs is None else interactionTimeoutMs)))
lambda: handle.pychip_CommandSender_SendCommand(
ctypes.py_object(transaction), device,
c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), commandPath.EndpointId,
commandPath.ClusterId, commandPath.CommandId, payloadTLV, len(payloadTLV),
ctypes.c_uint16(0 if interactionTimeoutMs is None else interactionTimeoutMs),
ctypes.c_uint16(0 if busyWaitMs is None else busyWaitMs),
))


def Init():
Expand Down
9 changes: 7 additions & 2 deletions src/controller/python/chip/clusters/attribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ struct __attribute__((packed)) PyReadAttributeParams

// Encodes n attribute write requests, follows 3 * n arguments, in the (AttributeWritePath*=void *, uint8_t*, size_t) order.
PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * device, uint16_t timedWriteTimeoutMs,
uint16_t interactionTimeoutMs, size_t n, ...);
uint16_t interactionTimeoutMs, uint16_t busyWaitMs, size_t n, ...);
PyChipError pychip_ReadClient_ReadAttributes(void * appContext, ReadClient ** pReadClient, ReadClientCallback ** pCallback,
DeviceProxy * device, uint8_t * readParamsBuf, size_t n, size_t total, ...);
}
Expand Down Expand Up @@ -323,7 +323,7 @@ void pychip_ReadClient_InitCallbacks(OnReadAttributeDataCallback onReadAttribute
}

PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * device, uint16_t timedWriteTimeoutMs,
uint16_t interactionTimeoutMs, size_t n, ...)
uint16_t interactionTimeoutMs, uint16_t busyWaitMs, size_t n, ...)
{
CHIP_ERROR err = CHIP_NO_ERROR;

Expand Down Expand Up @@ -370,6 +370,11 @@ PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy *
client.release();
callback.release();

if (busyWaitMs)
{
usleep(busyWaitMs * 1000);
}

exit:
va_end(args);
return ToPyChipError(err);
Expand Down
11 changes: 9 additions & 2 deletions src/controller/python/chip/clusters/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ using PyObject = void *;
extern "C" {
PyChipError pychip_CommandSender_SendCommand(void * appContext, DeviceProxy * device, uint16_t timedRequestTimeoutMs,
chip::EndpointId endpointId, chip::ClusterId clusterId, chip::CommandId commandId,
const uint8_t * payload, size_t length, uint16_t interactionTimeoutMs);
const uint8_t * payload, size_t length, uint16_t interactionTimeoutMs,
uint16_t busyWaitMs);
}

namespace chip {
Expand Down Expand Up @@ -126,7 +127,8 @@ void pychip_CommandSender_InitCallbacks(OnCommandSenderResponseCallback onComman

PyChipError pychip_CommandSender_SendCommand(void * appContext, DeviceProxy * device, uint16_t timedRequestTimeoutMs,
chip::EndpointId endpointId, chip::ClusterId clusterId, chip::CommandId commandId,
const uint8_t * payload, size_t length, uint16_t interactionTimeoutMs)
const uint8_t * payload, size_t length, uint16_t interactionTimeoutMs,
uint16_t busyWaitMs)
{
CHIP_ERROR err = CHIP_NO_ERROR;

Expand Down Expand Up @@ -161,6 +163,11 @@ PyChipError pychip_CommandSender_SendCommand(void * appContext, DeviceProxy * de
sender.release();
callback.release();

if (busyWaitMs)
{
usleep(busyWaitMs * 1000);
}

exit:
return ToPyChipError(err);
}
Expand Down
15 changes: 10 additions & 5 deletions src/controller/python/chip/yaml/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
UnexpectedParsingError: Raised if there is an unexpected parsing error.
'''
super().__init__(test_step.label, test_step.identity)
self._busy_wait_ms = test_step.busy_wait_ms
self._command_name = stringcase.pascalcase(test_step.command)
self._cluster = cluster
self._interation_timeout_ms = test_step.timed_interaction_timeout_ms
Expand Down Expand Up @@ -142,7 +143,8 @@ def run_action(self, dev_ctrl: ChipDeviceCtrl) -> _ActionResult:
try:
resp = asyncio.run(dev_ctrl.SendCommand(
self._node_id, self._endpoint, self._request_object,
timedRequestTimeoutMs=self._interation_timeout_ms))
timedRequestTimeoutMs=self._interation_timeout_ms,
busyWaitMs=self._busy_wait_ms))
except chip.interaction_model.InteractionModelError as error:
return _ActionResult(status=_ActionStatus.ERROR, response=error)

Expand Down Expand Up @@ -357,8 +359,10 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
'''
super().__init__(test_step.label, test_step.identity)
self._attribute_name = stringcase.pascalcase(test_step.attribute)
self._busy_wait_ms = test_step.busy_wait_ms
self._cluster = cluster
self._endpoint = test_step.endpoint
self._interation_timeout_ms = test_step.timed_interaction_timeout_ms
self._node_id = test_step.node_id
self._request_object = None

Expand Down Expand Up @@ -389,10 +393,11 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
def run_action(self, dev_ctrl: ChipDeviceCtrl) -> _ActionResult:
try:
resp = asyncio.run(
dev_ctrl.WriteAttribute(self._node_id, [(self._endpoint, self._request_object)]))
except chip.interaction_model.InteractionModelError:
# TODO Should we be doing the same thing as InvokeAction on InteractionModelError?
raise
dev_ctrl.WriteAttribute(self._node_id, [(self._endpoint, self._request_object)],
timedRequestTimeoutMs=self._interation_timeout_ms,
busyWaitMs=self._busy_wait_ms))
except chip.interaction_model.InteractionModelError as error:
return _ActionResult(status=_ActionStatus.ERROR, response=error)
if len(resp) == 1 and isinstance(resp[0], AttributeStatus):
if resp[0].Status == chip.interaction_model.Status.Success:
return _ActionResult(status=_ActionStatus.SUCCESS, response=None)
Expand Down
10 changes: 6 additions & 4 deletions src/controller/python/test/test_scripts/cluster_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,9 @@ async def TestTimedRequest(cls, devCtrl):
req = Clusters.UnitTesting.Commands.TimedInvokeRequest()
await devCtrl.SendCommand(nodeid=NODE_ID, endpoint=1, payload=req)
raise AssertionError("The command invoke should be rejected.")
except ValueError:
pass
except chip.interaction_model.InteractionModelError as ex:
if ex.status != chip.interaction_model.Status.NeedsTimedInteraction:
raise AssertionError("The command invoke was expected to error with NeedsTimedInteraction.")

logger.info(
"4: Writing TestCluster-TimedWriteBoolean without timedRequestTimeoutMs should be rejected")
Expand All @@ -455,8 +456,9 @@ async def TestTimedRequest(cls, devCtrl):
True)),
])
raise AssertionError("The write request should be rejected.")
except ValueError:
pass
except chip.interaction_model.InteractionModelError as ex:
if ex.status != chip.interaction_model.Status.NeedsTimedInteraction:
raise AssertionError("The write attribute was expected to error with NeedsTimedInteraction.")

@ classmethod
@ base.test_case
Expand Down