Skip to content

Commit

Permalink
Switch YAML tests to using templated ReadAttribute.
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 committed Nov 9, 2021
1 parent 89898f8 commit 755acc0
Show file tree
Hide file tree
Showing 7 changed files with 5,450 additions and 4,915 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: 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 All @@ -66,6 +70,42 @@ class DLL_EXPORT ClusterBase
CHIP_ERROR WriteAttribute(const typename AttributeInfo::Type & requestData, void * context,
WriteResponseSuccessCallback successCb, WriteResponseFailureCallback 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>>(endpointId, clusterId, 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, 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 755acc0

Please sign in to comment.