blob: 1276db759fc37b1bf6708a12521f44cdde7d6d0c [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* 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.
*/
/**
* @file
* This file contains the mock implementation for the generated attribute-storage.cpp
* - It contains three endpoints, 0xFFFE, 0xFFFD, 0xFFFC
* - It contains four clusters: 0xFFF1'0001 to 0xFFF1'0004
* - All cluster has two global attribute (0x0000'FFFC, 0x0000'FFFD)
* - Some clusters has some cluster-specific attributes, with 0xFFF1 prefix.
*
* Note: The ember's attribute-storage.cpp will include some app specific generated files. So we cannot use it directly. This
* might be fixed with a mock endpoint-config.h
*/
#include <app-common/zap-generated/att-storage.h>
#include <app-common/zap-generated/attribute-type.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app/MessageDef/AttributeDataIB.h>
#include <app/MessageDef/AttributeReportIB.h>
#include <app/MessageDef/AttributeStatusIB.h>
#include <app/util/mock/Constants.h>
#include <app/AttributeAccessInterface.h>
#include <app/ConcreteAttributePath.h>
#include <app/EventManagement.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/core/CHIPTLVDebug.hpp>
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/UnitTestRegistration.h>
#include <lib/support/logging/CHIPLogging.h>
#include <app/util/attribute-metadata.h>
typedef uint8_t EmberAfClusterMask;
using namespace chip;
using namespace chip::Test;
using namespace chip::app;
namespace {
DataVersion dataVersion = 0;
EndpointId endpoints[] = { kMockEndpoint1, kMockEndpoint2, kMockEndpoint3 };
uint16_t clusterIndex[] = { 0, 2, 5 };
uint8_t clusterCount[] = { 2, 3, 4 };
ClusterId clusters[] = { MockClusterId(1), MockClusterId(2), MockClusterId(1), MockClusterId(2), MockClusterId(3),
MockClusterId(1), MockClusterId(2), MockClusterId(3), MockClusterId(4) };
uint16_t attributeIndex[] = { 0, 2, 5, 7, 11, 16, 19, 25, 27 };
uint16_t attributeCount[] = { 2, 3, 2, 4, 5, 3, 6, 2, 2 };
AttributeId attributes[] = {
// clang-format off
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id,
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1),
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id,
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), MockAttributeId(2),
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), MockAttributeId(3),
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1),
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), MockAttributeId(3), MockAttributeId(4),
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id,
Clusters::Globals::Attributes::ClusterRevision::Id, Clusters::Globals::Attributes::FeatureMap::Id
// clang-format on
};
uint16_t mockClusterRevision = 1;
uint32_t mockFeatureMap = 0x1234;
bool mockAttribute1 = true;
int16_t mockAttribute2 = 42;
uint64_t mockAttribute3 = 0xdeadbeef0000cafe;
uint8_t mockAttribute4[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
};
} // namespace
uint16_t emberAfEndpointCount()
{
return ArraySize(endpoints);
}
uint16_t emberAfIndexFromEndpoint(chip::EndpointId endpoint)
{
static_assert(ArraySize(endpoints) < UINT16_MAX, "Need to be able to return endpoint index as a 16-bit value.");
for (size_t i = 0; i < ArraySize(endpoints); i++)
{
if (endpoints[i] == endpoint)
{
return static_cast<uint16_t>(i);
}
}
return UINT16_MAX;
}
uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server)
{
for (size_t i = 0; i < ArraySize(endpoints); i++)
{
if (endpoints[i] == endpoint)
{
return clusterCount[i];
}
}
return 0;
}
uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster)
{
uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
uint8_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true);
for (uint8_t i = 0; i < clusterCountOnEndpoint; i++)
{
if (clusters[i + clusterIndex[endpointIndex]] == cluster)
{
return attributeCount[i + clusterIndex[endpointIndex]];
}
}
return 0;
}
uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster,
chip::AttributeId attributeId)
{
uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
uint8_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true);
for (uint8_t i = 0; i < clusterCountOnEndpoint; i++)
{
if (clusters[i + clusterIndex[endpointIndex]] == cluster)
{
uint16_t clusterAttributeOffset = attributeIndex[i + clusterIndex[endpointIndex]];
for (uint16_t j = 0; j < emberAfGetServerAttributeCount(endpoint, cluster); j++)
{
if (attributes[clusterAttributeOffset + j] == attributeId)
{
return j;
}
}
break;
}
}
return UINT16_MAX;
}
bool emberAfContainsAttribute(chip::EndpointId endpoint, chip::ClusterId clusterId, chip::AttributeId attributeId)
{
return !(emberAfGetServerAttributeIndexByAttributeId(endpoint, clusterId, attributeId) == UINT16_MAX);
}
chip::EndpointId emberAfEndpointFromIndex(uint16_t index)
{
VerifyOrDie(index < ArraySize(endpoints));
return endpoints[index];
}
chip::Optional<chip::ClusterId> emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server)
{
if (n >= emberAfClusterCount(endpoint, server))
{
return chip::Optional<chip::ClusterId>::Missing();
}
return chip::Optional<chip::ClusterId>(clusters[clusterIndex[emberAfIndexFromEndpoint(endpoint)] + n]);
}
chip::Optional<chip::AttributeId> emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster,
uint16_t index)
{
uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
uint8_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true);
for (uint8_t i = 0; i < clusterCountOnEndpoint; i++)
{
if (clusters[i + clusterIndex[endpointIndex]] == cluster)
{
uint16_t clusterAttributeOffset = attributeIndex[i + clusterIndex[endpointIndex]];
if (index < emberAfGetServerAttributeCount(endpoint, cluster))
{
return Optional<AttributeId>(attributes[clusterAttributeOffset + index]);
}
break;
}
}
return Optional<AttributeId>::Missing();
}
uint8_t emberAfClusterIndex(chip::EndpointId endpoint, chip::ClusterId cluster, EmberAfClusterMask mask)
{
uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
uint8_t clusterCountOnEndpoint = emberAfClusterCount(endpoint, true);
for (uint8_t i = 0; i < clusterCountOnEndpoint; i++)
{
if (clusters[i + clusterIndex[endpointIndex]] == cluster)
{
return i;
}
}
return UINT8_MAX;
}
bool emberAfEndpointIndexIsEnabled(uint16_t index)
{
return index < ArraySize(endpoints);
}
// This duplication of basic utilities is really unfortunate, but we can't link
// to the normal attribute-storage.cpp because we redefine some of its symbols
// above.
bool emberAfIsStringAttributeType(EmberAfAttributeType attributeType)
{
return (attributeType == ZCL_OCTET_STRING_ATTRIBUTE_TYPE || attributeType == ZCL_CHAR_STRING_ATTRIBUTE_TYPE);
}
bool emberAfIsLongStringAttributeType(EmberAfAttributeType attributeType)
{
return (attributeType == ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE || attributeType == ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE);
}
// And we don't have a good way to link to message.cpp either.
uint8_t emberAfStringLength(const uint8_t * buffer)
{
// The first byte specifies the length of the string. A length of 0xFF means
// the string is invalid and there is no character data.
return (buffer[0] == 0xFF ? 0 : buffer[0]);
}
uint16_t emberAfLongStringLength(const uint8_t * buffer)
{
// The first two bytes specify the length of the long string. A length of
// 0xFFFF means the string is invalid and there is no character data.
uint16_t length = Encoding::LittleEndian::Get16(buffer);
return (length == 0xFFFF ? 0 : length);
}
namespace chip {
namespace app {
AttributeAccessInterface * GetAttributeAccessOverride(EndpointId aEndpointId, ClusterId aClusterId)
{
return nullptr;
}
} // namespace app
namespace Test {
void BumpVersion()
{
dataVersion++;
}
DataVersion GetVersion()
{
return dataVersion;
}
CHIP_ERROR ReadSingleMockClusterData(FabricIndex aAccessingFabricIndex, const ConcreteAttributePath & aPath,
AttributeReportIBs::Builder & aAttributeReports,
AttributeValueEncoder::AttributeEncodeState * apEncoderState)
{
bool dataExists =
(emberAfGetServerAttributeIndexByAttributeId(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId) != UINT16_MAX);
ChipLogDetail(DataManagement, "Reading Mock Endpoint %" PRIx32 "Mock Cluster %" PRIx32 ", Field %" PRIx32 " is dirty",
aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
if (!dataExists)
{
AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport();
ReturnErrorOnFailure(aAttributeReports.GetError());
AttributeStatusIB::Builder & attributeStatus = attributeReport.CreateAttributeStatus();
ReturnErrorOnFailure(attributeReport.GetError());
AttributePathIB::Builder & attributePath = attributeStatus.CreatePath();
ReturnErrorOnFailure(attributeStatus.GetError());
attributePath.Endpoint(aPath.mEndpointId).Cluster(aPath.mClusterId).Attribute(aPath.mAttributeId).EndOfAttributePathIB();
ReturnErrorOnFailure(attributePath.GetError());
StatusIB::Builder & errorStatus = attributeStatus.CreateErrorStatus();
ReturnErrorOnFailure(attributeStatus.GetError());
errorStatus.EncodeStatusIB(StatusIB(Protocols::InteractionModel::Status::UnsupportedAttribute));
ReturnErrorOnFailure(errorStatus.GetError());
attributeStatus.EndOfAttributeStatusIB();
ReturnErrorOnFailure(attributeStatus.GetError());
return attributeReport.EndOfAttributeReportIB().GetError();
}
// Attribute 4 acts as a large attribute to trigger chunking.
if (aPath.mAttributeId == MockAttributeId(4))
{
AttributeValueEncoder::AttributeEncodeState state =
(apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState);
AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, dataVersion, false, state);
CHIP_ERROR err = valueEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR {
for (int i = 0; i < 6; i++)
{
ReturnErrorOnFailure(encoder.Encode(chip::ByteSpan(mockAttribute4, sizeof(mockAttribute4))));
}
return CHIP_NO_ERROR;
});
if (apEncoderState != nullptr)
{
*apEncoderState = valueEncoder.GetState();
}
return err;
}
AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport();
ReturnErrorOnFailure(aAttributeReports.GetError());
AttributeDataIB::Builder & attributeData = attributeReport.CreateAttributeData();
ReturnErrorOnFailure(attributeReport.GetError());
attributeData.DataVersion(dataVersion);
AttributePathIB::Builder & attributePath = attributeData.CreatePath();
ReturnErrorOnFailure(attributeData.GetError());
attributePath.Endpoint(aPath.mEndpointId).Cluster(aPath.mClusterId).Attribute(aPath.mAttributeId).EndOfAttributePathIB();
ReturnErrorOnFailure(attributePath.GetError());
TLV::TLVWriter * writer = attributeData.GetWriter();
switch (aPath.mAttributeId)
{
case Clusters::Globals::Attributes::ClusterRevision::Id:
ReturnErrorOnFailure(writer->Put(TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), mockClusterRevision));
break;
case Clusters::Globals::Attributes::FeatureMap::Id:
ReturnErrorOnFailure(writer->Put(TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), mockFeatureMap));
break;
case MockAttributeId(1):
ReturnErrorOnFailure(writer->Put(TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), mockAttribute1));
break;
case MockAttributeId(2):
ReturnErrorOnFailure(writer->Put(TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), mockAttribute2));
break;
case MockAttributeId(3):
ReturnErrorOnFailure(writer->Put(TLV::ContextTag(to_underlying(AttributeDataIB::Tag::kData)), mockAttribute3));
break;
default:
// The key should found since we have checked above.
return CHIP_ERROR_KEY_NOT_FOUND;
}
attributeData.EndOfAttributeDataIB();
ReturnErrorOnFailure(attributeData.GetError());
return attributeReport.EndOfAttributeReportIB().GetError();
}
} // namespace Test
} // namespace chip