Skip to content

Commit

Permalink
Switch YAML tests to using templated ReadAttribute. (#11607)
Browse files Browse the repository at this point in the history
This will let us handle reading nullables (and structs once we can
have struct-typed attributes) propely.
  • Loading branch information
bzbarsky-apple authored and pull[bot] committed Nov 15, 2021
1 parent 03f6d7c commit 45e7101
Show file tree
Hide file tree
Showing 8 changed files with 5,479 additions and 4,943 deletions.
11 changes: 11 additions & 0 deletions examples/chip-tool/templates/partials/test_cluster.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ class {{filename}}: public TestCommand
{{#unless isWait}}
{{#unless isCommand}}
{{#unless isWriteAttribute}}
{{#unless isReadAttribute}}
chip::Callback::Callback<void (*) ({{>failureArguments}})> {{>failureCallback}} { {{>failureResponse}}, this };
chip::Callback::Callback<void (*) ({{>successArguments}})> {{>successCallback}} { {{>successResponse}}, this };
{{/unless}}
{{/unless}}
{{/unless}}
{{/unless}}
{{/unless}}
{{/chip_tests_items}}

{{#chip_tests_items}}
Expand All @@ -78,6 +80,11 @@ class {{filename}}: public TestCommand
{
(static_cast<{{filename}} *>(context))->OnFailureResponse_{{index}}(chip::to_underlying(status));
}
{{else if isReadAttribute}}
static void {{>failureResponse}}(void * context, EmberAfStatus status)
{
(static_cast<{{filename}} *>(context))->OnFailureResponse_{{index}}(chip::to_underlying(status));
}
{{else}}
static void {{>failureResponse}}({{> failureArguments}})
{
Expand Down Expand Up @@ -160,6 +167,10 @@ class {{filename}}: public TestCommand
{{#*inline "failureResponse"}}OnFailureCallback_{{index}}{{/inline}}
{{#*inline "successResponse"}}OnSuccessCallback_{{index}}{{/inline}}
{{#if async}}ReturnErrorOnFailure({{else}}return {{/if}}cluster.WriteAttribute<chip::app::Clusters::{{asUpperCamelCase cluster}}::Attributes::{{asUpperCamelCase attribute}}::TypeInfo>({{#chip_tests_item_parameters}}{{asLowerCamelCase name}}Argument, {{/chip_tests_item_parameters}}this, {{>successResponse}}, {{>failureResponse}}){{#if async}}){{/if}};
{{else if isReadAttribute}}
{{#*inline "failureResponse"}}OnFailureCallback_{{index}}{{/inline}}
{{#*inline "successResponse"}}OnSuccessCallback_{{index}}{{/inline}}
{{#if async}}ReturnErrorOnFailure({{else}}return {{/if}}cluster.ReadAttribute<chip::app::Clusters::{{asUpperCamelCase cluster}}::Attributes::{{asUpperCamelCase attribute}}::TypeInfo>({{#chip_tests_item_parameters}}{{asLowerCamelCase name}}Argument, {{/chip_tests_item_parameters}}this, {{>successResponse}}, {{>failureResponse}}){{#if async}}){{/if}};
{{else}}
{{~#*inline "commandName"}}{{asUpperCamelCase commandName}}{{#if isAttribute}}Attribute{{asUpperCamelCase attribute}}{{/if}}{{/inline}}
{{#if async}}ReturnErrorOnFailure({{else}}return {{/if}}cluster.{{>commandName}}({{>successCallback}}.Cancel(){{#unless isWaitForReport}}, {{>failureCallback}}.Cancel(){{/unless}}{{#chip_tests_item_parameters}}, {{asLowerCamelCase name}}Argument{{/chip_tests_item_parameters}}){{#if async}}){{/if}};
Expand Down
2 changes: 1 addition & 1 deletion src/app/tests/suites/TestDescriptorCluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ tests:
command: "readAttribute"
attribute: "Device List"
response:
value: [{ deviceTypeId: 1, revision: 1 }]
value: [{ type: 0, revision: 1 }]

- label: "Read attribute Server list"
command: "readAttribute"
Expand Down
2 changes: 2 additions & 0 deletions src/app/zap-templates/templates/app/cluster-objects.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,11 @@ namespace {{asUpperCamelCase label}} {
{{#if entryType}}
using Type = {{zapTypeToEncodableClusterObjectType entryType forceNotOptional=true}};
using DecodableType = {{zapTypeToDecodableClusterObjectType entryType forceNotOptional=true}};
using DecodableArgType = {{zapTypeToDecodableClusterObjectType entryType forceNotOptional=true isArgument=true}};
{{else}}
using Type = {{zapTypeToEncodableClusterObjectType type forceNotOptional=true}};
using DecodableType = {{zapTypeToDecodableClusterObjectType type forceNotOptional=true}};
using DecodableArgType = {{zapTypeToDecodableClusterObjectType type forceNotOptional=true isArgument=true}};
{{/if}}

static constexpr ClusterId GetClusterId() { return Clusters::{{asUpperCamelCase parent.name}}::Id; }
Expand Down
40 changes: 40 additions & 0 deletions src/controller/CHIPCluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <app/DeviceProxy.h>
#include <app/util/error-mapping.h>
#include <controller/InvokeInteraction.h>
#include <controller/ReadInteraction.h>
#include <controller/WriteInteraction.h>

namespace chip {
Expand All @@ -40,6 +41,9 @@ using CommandResponseSuccessCallback = void(void * context, const T & responseOb
using CommandResponseFailureCallback = void(void * context, EmberAfStatus status);
using WriteResponseSuccessCallback = void (*)(void * context);
using WriteResponseFailureCallback = void (*)(void * context, EmberAfStatus status);
template <typename T>
using ReadResponseSuccessCallback = void (*)(void * context, T responseData);
using ReadResponseFailureCallback = void (*)(void * context, EmberAfStatus status);

class DLL_EXPORT ClusterBase
{
Expand Down Expand Up @@ -102,6 +106,42 @@ class DLL_EXPORT ClusterBase
failureCb);
}

/**
* Read an attribute and get a type-safe callback with the attribute value.
*/
template <typename AttributeInfo>
CHIP_ERROR ReadAttribute(void * context, ReadResponseSuccessCallback<typename AttributeInfo::DecodableArgType> successCb,
ReadResponseFailureCallback failureCb)
{
return ReadAttribute<typename AttributeInfo::DecodableType, typename AttributeInfo::DecodableArgType>(
context, AttributeInfo::GetClusterId(), AttributeInfo::GetAttributeId(), successCb, failureCb);
}

template <typename DecodableType, typename DecodableArgType>
CHIP_ERROR ReadAttribute(void * context, ClusterId clusterId, AttributeId attributeId,
ReadResponseSuccessCallback<DecodableArgType> successCb, ReadResponseFailureCallback failureCb)
{
VerifyOrReturnError(mDevice != nullptr, CHIP_ERROR_INCORRECT_STATE);

auto onSuccessCb = [context, successCb](const app::ConcreteAttributePath & commandPath, const DecodableType & aData) {
if (successCb != nullptr)
{
successCb(context, aData);
}
};

auto onFailureCb = [context, failureCb](const app::ConcreteAttributePath * commandPath, app::StatusIB status,
CHIP_ERROR aError) {
if (failureCb != nullptr)
{
failureCb(context, app::ToEmberAfStatus(status.mStatus));
}
};

return Controller::ReadAttribute<DecodableType>(mDevice->GetExchangeManager(), mDevice->GetSecureSession().Value(),
mEndpoint, clusterId, attributeId, onSuccessCb, onFailureCb);
}

protected:
ClusterBase(uint16_t cluster) : mClusterId(cluster) {}

Expand Down
29 changes: 23 additions & 6 deletions src/controller/ReadInteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ namespace Controller {
* GetAttributeId() methods is expected to work.
*
*/
template <typename AttributeTypeInfo>

/**
* To avoid instantiating all the complicated code on a per-attribute basis, we
* have a helper that's just templated on the type.
*/
template <typename DecodableAttributeType>
CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * aExchangeMgr, const SessionHandle sessionHandle, EndpointId endpointId,
typename TypedReadCallback<AttributeTypeInfo>::OnSuccessCallbackType onSuccessCb,
typename TypedReadCallback<AttributeTypeInfo>::OnErrorCallbackType onErrorCb)
ClusterId clusterId, AttributeId attributeId,
typename TypedReadCallback<DecodableAttributeType>::OnSuccessCallbackType onSuccessCb,
typename TypedReadCallback<DecodableAttributeType>::OnErrorCallbackType onErrorCb)
{
app::AttributePathParams attributePath(endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId());
app::AttributePathParams attributePath(endpointId, clusterId, attributeId);
app::ReadPrepareParams readParams(sessionHandle);
app::ReadClient * readClient = nullptr;
app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
Expand All @@ -50,11 +56,12 @@ CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * aExchangeMgr, const Sessio
readParams.mpAttributePathParamsList = &attributePath;
readParams.mAttributePathParamsListSize = 1;

auto onDone = [](app::ReadClient * apReadClient, TypedReadCallback<AttributeTypeInfo> * callback) {
auto onDone = [](app::ReadClient * apReadClient, TypedReadCallback<DecodableAttributeType> * callback) {
chip::Platform::Delete(callback);
};

auto callback = chip::Platform::MakeUnique<TypedReadCallback<AttributeTypeInfo>>(onSuccessCb, onErrorCb, onDone);
auto callback = chip::Platform::MakeUnique<TypedReadCallback<DecodableAttributeType>>(clusterId, attributeId, onSuccessCb,
onErrorCb, onDone);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);

ReturnErrorOnFailure(engine->NewReadClient(&readClient, app::ReadClient::InteractionType::Read, callback.get()));
Expand All @@ -75,5 +82,15 @@ CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * aExchangeMgr, const Sessio
return err;
}

template <typename AttributeTypeInfo>
CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * aExchangeMgr, const SessionHandle sessionHandle, EndpointId endpointId,
typename TypedReadCallback<typename AttributeTypeInfo::DecodableType>::OnSuccessCallbackType onSuccessCb,
typename TypedReadCallback<typename AttributeTypeInfo::DecodableType>::OnErrorCallbackType onErrorCb)
{
return ReadAttribute<typename AttributeTypeInfo::DecodableType>(aExchangeMgr, sessionHandle, endpointId,
AttributeTypeInfo::GetClusterId(),
AttributeTypeInfo::GetAttributeId(), onSuccessCb, onErrorCb);
}

} // namespace Controller
} // namespace chip
18 changes: 10 additions & 8 deletions src/controller/TypedReadCallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,31 @@ namespace Controller {
* 2. Automatic decoding of attribute data provided in the TLVReader by InteractionModelDelegate::OnReportData into a decoded
* cluster object.
*/
template <typename AttributeTypeInfo>
template <typename DecodableAttributeType>
class TypedReadCallback final : public app::ReadClient::Callback
{
public:
using OnSuccessCallbackType =
std::function<void(const app::ConcreteAttributePath & aPath, const typename AttributeTypeInfo::DecodableType & aData)>;
std::function<void(const app::ConcreteAttributePath & aPath, const DecodableAttributeType & aData)>;
using OnErrorCallbackType = std::function<void(const app::ConcreteAttributePath * aPath,
Protocols::InteractionModel::Status aIMStatus, CHIP_ERROR aError)>;
using OnDoneCallbackType = std::function<void(app::ReadClient * client, TypedReadCallback * callback)>;

TypedReadCallback(OnSuccessCallbackType aOnSuccess, OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone) :
mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone)
TypedReadCallback(ClusterId aClusterId, AttributeId aAttributeId, OnSuccessCallbackType aOnSuccess,
OnErrorCallbackType aOnError, OnDoneCallbackType aOnDone) :
mClusterId(aClusterId),
mAttributeId(aAttributeId), mOnSuccess(aOnSuccess), mOnError(aOnError), mOnDone(aOnDone)
{}

private:
void OnAttributeData(const app::ReadClient * apReadClient, const app::ConcreteAttributePath & aPath, TLV::TLVReader * apData,
const app::StatusIB & status) override
{
CHIP_ERROR err = CHIP_NO_ERROR;
typename AttributeTypeInfo::DecodableType value;
DecodableAttributeType value;

VerifyOrExit(status.mStatus == Protocols::InteractionModel::Status::Success, err = CHIP_ERROR_IM_STATUS_CODE_RECEIVED);
VerifyOrExit(aPath.mClusterId == AttributeTypeInfo::GetClusterId() &&
aPath.mAttributeId == AttributeTypeInfo::GetAttributeId(),
CHIP_ERROR_SCHEMA_MISMATCH);
VerifyOrExit(aPath.mClusterId == mClusterId && aPath.mAttributeId == mAttributeId, err = CHIP_ERROR_SCHEMA_MISMATCH);
VerifyOrExit(apData != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);

err = app::DataModel::Decode(*apData, value);
Expand Down Expand Up @@ -90,6 +90,8 @@ class TypedReadCallback final : public app::ReadClient::Callback

void OnDone(app::ReadClient * apReadClient) override { mOnDone(apReadClient, this); }

ClusterId mClusterId;
AttributeId mAttributeId;
OnSuccessCallbackType mOnSuccess;
OnErrorCallbackType mOnError;
OnDoneCallbackType mOnDone;
Expand Down
Loading

0 comments on commit 45e7101

Please sign in to comment.