| /* |
| * |
| * 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/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/att-storage.h> |
| #include <app/util/attribute-storage.h> |
| #include <app/util/endpoint-config-api.h> |
| #include <app/util/mock/Constants.h> |
| #include <app/util/mock/MockNodeConfig.h> |
| |
| #include <app/AttributeValueEncoder.h> |
| #include <app/ConcreteAttributePath.h> |
| #include <app/EventManagement.h> |
| #include <lib/core/CHIPCore.h> |
| #include <lib/core/CHIPEncoding.h> |
| #include <lib/core/TLVDebug.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/DLLUtil.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| #include <app/util/af-types.h> |
| #include <app/util/attribute-metadata.h> |
| |
| typedef uint8_t EmberAfClusterMask; |
| |
| using namespace chip; |
| using namespace chip::Test; |
| using namespace chip::app; |
| using namespace Clusters::Globals::Attributes; |
| |
| namespace { |
| |
| DataVersion dataVersion = 0; |
| const MockNodeConfig * mockConfig = nullptr; |
| |
| const MockNodeConfig & DefaultMockNodeConfig() |
| { |
| // clang-format off |
| static const MockNodeConfig config({ |
| MockEndpointConfig(kMockEndpoint1, { |
| MockClusterConfig(MockClusterId(1), { |
| ClusterRevision::Id, FeatureMap::Id, |
| }, { |
| MockEventId(1), MockEventId(2), |
| }), |
| MockClusterConfig(MockClusterId(2), { |
| ClusterRevision::Id, FeatureMap::Id, MockAttributeId(1), |
| }), |
| }), |
| MockEndpointConfig(kMockEndpoint2, { |
| MockClusterConfig(MockClusterId(1), { |
| ClusterRevision::Id, FeatureMap::Id, |
| }), |
| MockClusterConfig(MockClusterId(2), { |
| ClusterRevision::Id, FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), |
| }), |
| MockClusterConfig(MockClusterId(3), { |
| ClusterRevision::Id, FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), MockAttributeId(3), |
| }), |
| }), |
| MockEndpointConfig(kMockEndpoint3, { |
| MockClusterConfig(MockClusterId(1), { |
| ClusterRevision::Id, FeatureMap::Id, MockAttributeId(1), |
| }), |
| MockClusterConfig(MockClusterId(2), { |
| ClusterRevision::Id, FeatureMap::Id, MockAttributeId(1), MockAttributeId(2), MockAttributeId(3), MockAttributeId(4), |
| }), |
| MockClusterConfig(MockClusterId(3), { |
| ClusterRevision::Id, FeatureMap::Id, |
| }), |
| MockClusterConfig(MockClusterId(4), { |
| ClusterRevision::Id, FeatureMap::Id, |
| }), |
| }), |
| }); |
| // clang-format on |
| return config; |
| } |
| |
| const MockNodeConfig & GetMockNodeConfig() |
| { |
| return (mockConfig != nullptr) ? *mockConfig : DefaultMockNodeConfig(); |
| } |
| |
| } // namespace |
| |
| namespace chip { |
| namespace Test { |
| |
| const uint16_t mockClusterRevision = 1; |
| const uint32_t mockFeatureMap = 0x1234; |
| const bool mockAttribute1 = true; |
| const int16_t mockAttribute2 = 42; |
| const uint64_t mockAttribute3 = 0xdeadbeef0000cafe; |
| const 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 Test |
| } // namespace chip |
| |
| uint16_t emberAfEndpointCount() |
| { |
| return static_cast<uint16_t>(GetMockNodeConfig().endpoints.size()); |
| } |
| |
| uint16_t emberAfIndexFromEndpoint(EndpointId endpointId) |
| { |
| ptrdiff_t index; |
| auto endpoint = GetMockNodeConfig().endpointById(endpointId, &index); |
| VerifyOrReturnValue(endpoint != nullptr, kEmberInvalidEndpointIndex); |
| return static_cast<uint16_t>(index); |
| } |
| |
| uint8_t emberAfGetClusterCountForEndpoint(EndpointId endpointId) |
| { |
| auto endpoint = GetMockNodeConfig().endpointById(endpointId); |
| VerifyOrReturnValue(endpoint != nullptr, 0); |
| return static_cast<uint8_t>(endpoint->clusters.size()); |
| } |
| |
| const EmberAfAttributeMetadata * emberAfLocateAttributeMetadata(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId) |
| { |
| auto ep = GetMockNodeConfig().endpointById(endpointId); |
| VerifyOrReturnValue(ep != nullptr, nullptr); |
| |
| auto cluster = ep->clusterById(clusterId); |
| VerifyOrReturnValue(cluster != nullptr, nullptr); |
| |
| auto attr = cluster->attributeById(attributeId); |
| VerifyOrReturnValue(attr != nullptr, nullptr); |
| |
| return &attr->attributeMetaData; |
| } |
| |
| const EmberAfCluster * emberAfFindClusterInType(const EmberAfEndpointType * endpointType, ClusterId clusterId, |
| EmberAfClusterMask mask, uint8_t * index) |
| { |
| // This is a copy & paste implementation from ember attribute storage |
| // TODO: this hard-codes ember logic and is duplicated code. |
| uint8_t scopedIndex = 0; |
| |
| for (uint8_t i = 0; i < endpointType->clusterCount; i++) |
| { |
| const EmberAfCluster * cluster = &(endpointType->cluster[i]); |
| |
| if (mask == 0 || ((cluster->mask & mask) != 0)) |
| { |
| if (cluster->clusterId == clusterId) |
| { |
| if (index) |
| { |
| *index = scopedIndex; |
| } |
| |
| return cluster; |
| } |
| |
| scopedIndex++; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server) |
| { |
| return (server) ? emberAfGetClusterCountForEndpoint(endpoint) : 0; |
| } |
| |
| uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpointId, chip::ClusterId clusterId) |
| { |
| auto cluster = GetMockNodeConfig().clusterByIds(endpointId, clusterId); |
| VerifyOrReturnValue(cluster != nullptr, 0); |
| return static_cast<uint16_t>(cluster->attributes.size()); |
| } |
| |
| uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpointId, chip::ClusterId clusterId, |
| chip::AttributeId attributeId) |
| { |
| auto cluster = GetMockNodeConfig().clusterByIds(endpointId, clusterId); |
| VerifyOrReturnValue(cluster != nullptr, kEmberInvalidEndpointIndex); |
| ptrdiff_t index; |
| auto attribute = cluster->attributeById(attributeId, &index); |
| VerifyOrReturnValue(attribute != nullptr, kEmberInvalidEndpointIndex); |
| return static_cast<uint16_t>(index); |
| } |
| |
| bool emberAfContainsAttribute(chip::EndpointId endpoint, chip::ClusterId clusterId, chip::AttributeId attributeId) |
| { |
| return emberAfGetServerAttributeIndexByAttributeId(endpoint, clusterId, attributeId) != kEmberInvalidEndpointIndex; |
| } |
| |
| chip::EndpointId emberAfEndpointFromIndex(uint16_t index) |
| { |
| auto & config = GetMockNodeConfig(); |
| VerifyOrDie(index < config.endpoints.size()); |
| return config.endpoints[index].id; |
| } |
| |
| chip::Optional<chip::ClusterId> emberAfGetNthClusterId(chip::EndpointId endpointId, uint8_t n, bool server) |
| { |
| VerifyOrReturnValue(server, NullOptional); // only server clusters supported |
| auto endpoint = GetMockNodeConfig().endpointById(endpointId); |
| VerifyOrReturnValue(endpoint != nullptr && n < endpoint->clusters.size(), NullOptional); |
| return MakeOptional(endpoint->clusters[n].id); |
| } |
| |
| // Returns number of clusters put into the passed cluster list |
| // for the given endpoint and client/server polarity |
| uint8_t emberAfGetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen, bool server) |
| { |
| uint8_t cluster_count = emberAfClusterCount(endpoint, server); |
| uint8_t i; |
| |
| if (cluster_count > listLen) |
| { |
| cluster_count = listLen; |
| } |
| for (i = 0; i < cluster_count; i++) |
| { |
| clusterList[i] = emberAfGetNthClusterId(endpoint, i, server).Value(); |
| } |
| return cluster_count; |
| } |
| |
| chip::Optional<chip::AttributeId> emberAfGetServerAttributeIdByIndex(chip::EndpointId endpointId, chip::ClusterId clusterId, |
| uint16_t index) |
| { |
| auto cluster = GetMockNodeConfig().clusterByIds(endpointId, clusterId); |
| VerifyOrReturnValue(cluster != nullptr && index < cluster->attributes.size(), NullOptional); |
| return MakeOptional(cluster->attributes[index].id); |
| } |
| |
| uint8_t emberAfClusterIndex(chip::EndpointId endpointId, chip::ClusterId clusterId, EmberAfClusterMask mask) |
| { |
| VerifyOrReturnValue(mask == 0 || (mask & CLUSTER_MASK_SERVER) != 0, UINT8_MAX); // only server clusters supported |
| ptrdiff_t index; |
| auto cluster = GetMockNodeConfig().clusterByIds(endpointId, clusterId, &index); |
| VerifyOrReturnValue(cluster != nullptr, UINT8_MAX); |
| return static_cast<uint8_t>(index); |
| } |
| |
| bool emberAfEndpointIndexIsEnabled(uint16_t index) |
| { |
| return index < GetMockNodeConfig().endpoints.size(); |
| } |
| |
| // This will find the first server that has the clusterId given from the index of endpoint. |
| bool emberAfContainsServerFromIndex(uint16_t index, ClusterId clusterId) |
| { |
| auto config = GetMockNodeConfig(); |
| VerifyOrReturnValue(index < config.endpoints.size(), false); |
| return true; // TODO: TestSceneTable relies on returning true here: https://github.com/project-chip/connectedhomeip/issues/30696 |
| // return config.endpoints[index].clusterById(clusterId) != nullptr; |
| } |
| |
| const EmberAfEndpointType * emberAfFindEndpointType(EndpointId endpointId) |
| { |
| auto endpoint = GetMockNodeConfig().endpointById(endpointId); |
| VerifyOrReturnValue(endpoint != nullptr, nullptr); |
| return endpoint->emberEndpoint(); |
| } |
| |
| const EmberAfCluster * emberAfFindServerCluster(EndpointId endpointId, ClusterId clusterId) |
| { |
| auto cluster = GetMockNodeConfig().clusterByIds(endpointId, clusterId); |
| VerifyOrReturnValue(cluster != nullptr, nullptr); |
| return cluster->emberCluster(); |
| } |
| |
| DataVersion * emberAfDataVersionStorage(const chip::app::ConcreteClusterPath & aConcreteClusterPath) |
| { |
| // shared data version storage |
| return &dataVersion; |
| } |
| |
| void emberAfAttributeChanged(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, |
| AttributesChangedListener * listener) |
| { |
| dataVersion++; |
| listener->MarkDirty(AttributePathParams(endpoint, clusterId, attributeId)); |
| } |
| |
| namespace chip { |
| namespace app { |
| |
| EndpointId EnabledEndpointsWithServerCluster::operator*() const |
| { |
| return emberAfEndpointFromIndex(mEndpointIndex); |
| } |
| |
| EnabledEndpointsWithServerCluster::EnabledEndpointsWithServerCluster(ClusterId clusterId) : |
| mEndpointCount(emberAfEndpointCount()), mClusterId(clusterId) |
| { |
| EnsureMatchingEndpoint(); |
| } |
| |
| EnabledEndpointsWithServerCluster & EnabledEndpointsWithServerCluster::operator++() |
| { |
| ++mEndpointIndex; |
| EnsureMatchingEndpoint(); |
| return *this; |
| } |
| |
| void EnabledEndpointsWithServerCluster::EnsureMatchingEndpoint() |
| { |
| for (; mEndpointIndex < mEndpointCount; ++mEndpointIndex) |
| { |
| if (!emberAfEndpointIndexIsEnabled(mEndpointIndex)) |
| { |
| continue; |
| } |
| |
| if (emberAfContainsServerFromIndex(mEndpointIndex, mClusterId)) |
| { |
| break; |
| } |
| } |
| } |
| |
| } // namespace app |
| |
| namespace Test { |
| |
| void ResetVersion() |
| { |
| dataVersion = 0; |
| } |
| |
| void BumpVersion() |
| { |
| dataVersion++; |
| } |
| |
| DataVersion GetVersion() |
| { |
| return dataVersion; |
| } |
| |
| CHIP_ERROR ReadSingleMockClusterData(FabricIndex aAccessingFabricIndex, const ConcreteAttributePath & aPath, |
| AttributeReportIBs::Builder & aAttributeReports, AttributeEncodeState * apEncoderState) |
| { |
| bool dataExists = |
| (emberAfGetServerAttributeIndexByAttributeId(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId) != UINT16_MAX); |
| |
| ChipLogDetail(DataManagement, "Reading Mock Endpoint %x 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()); |
| ReturnErrorOnFailure(attributeStatus.EndOfAttributeStatusIB()); |
| return attributeReport.EndOfAttributeReportIB(); |
| } |
| |
| // Attribute 4 acts as a large attribute to trigger chunking. |
| if (aPath.mAttributeId == MockAttributeId(4)) |
| { |
| AttributeEncodeState state(apEncoderState); |
| Access::SubjectDescriptor subject; |
| subject.fabricIndex = aAccessingFabricIndex; |
| |
| AttributeValueEncoder valueEncoder(aAttributeReports, subject, aPath, dataVersion, /* aIsFabricFiltered = */ 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(AttributeDataIB::Tag::kData), mockClusterRevision)); |
| break; |
| case Clusters::Globals::Attributes::FeatureMap::Id: |
| ReturnErrorOnFailure(writer->Put(TLV::ContextTag(AttributeDataIB::Tag::kData), mockFeatureMap)); |
| break; |
| case MockAttributeId(1): |
| ReturnErrorOnFailure(writer->Put(TLV::ContextTag(AttributeDataIB::Tag::kData), mockAttribute1)); |
| break; |
| case MockAttributeId(2): |
| ReturnErrorOnFailure(writer->Put(TLV::ContextTag(AttributeDataIB::Tag::kData), mockAttribute2)); |
| break; |
| case MockAttributeId(3): |
| ReturnErrorOnFailure(writer->Put(TLV::ContextTag(AttributeDataIB::Tag::kData), mockAttribute3)); |
| break; |
| default: |
| // The key should found since we have checked above. |
| return CHIP_ERROR_KEY_NOT_FOUND; |
| } |
| |
| ReturnErrorOnFailure(attributeData.EndOfAttributeDataIB()); |
| return attributeReport.EndOfAttributeReportIB(); |
| } |
| |
| void SetMockNodeConfig(const MockNodeConfig & config) |
| { |
| mockConfig = &config; |
| } |
| |
| /// Resets the mock attribute storage to the default configuration. |
| void ResetMockNodeConfig() |
| { |
| mockConfig = nullptr; |
| } |
| |
| } // namespace Test |
| } // namespace chip |