diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 19cf8e8bd910b0..060556001e98c7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -296,6 +296,7 @@ jobs: ':(exclude)src/app/util/attribute-table.cpp' \ ':(exclude)src/app/util/attribute-table.h' \ ':(exclude)src/app/util/ember-compatibility-functions.cpp' \ + ':(exclude)src/app/util/mock/CodegenEmberMocks.cpp' \ ':(exclude)src/app/zap-templates/templates/app/attributes/Accessors-src.zapt' \ ':(exclude)zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp' \ && exit 1 || exit 0 diff --git a/scripts/build/builders/host.py b/scripts/build/builders/host.py index 79d6b849a32aeb..50695772fff354 100644 --- a/scripts/build/builders/host.py +++ b/scripts/build/builders/host.py @@ -454,6 +454,10 @@ def __init__(self, root, runner, app: HostApp, board=HostBoard.NATIVE, if self.app == HostApp.SIMULATED_APP2: self.extra_gn_options.append('chip_tests_zap_config="app2"') + if self.app in {HostApp.JAVA_MATTER_CONTROLLER, HostApp.KOTLIN_MATTER_CONTROLLER}: + # TODO: controllers depending on a datamodel is odd. For now fix compile dependencies on ember. + self.extra_gn_options.append('chip_use_data_model_interface="disabled"') + if self.app == HostApp.TESTS and fuzzing_type != HostFuzzingType.NONE: self.build_command = 'fuzz_tests' diff --git a/src/app/AttributePathExpandIterator-Checked.cpp b/src/app/AttributePathExpandIterator-Checked.cpp new file mode 100644 index 00000000000000..3d0d660edcf1eb --- /dev/null +++ b/src/app/AttributePathExpandIterator-Checked.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 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. + */ + +#include "lib/support/logging/TextOnlyLogging.h" +#include + +namespace chip { +namespace app { +AttributePathExpandIteratorChecked::AttributePathExpandIteratorChecked(InteractionModel::DataModel * dataModel, + SingleLinkedListNode * attributePath) : + mDataModelIterator(dataModel, attributePath), + mEmberIterator(dataModel, attributePath) +{ + CheckOutputsIdentical("Constructor"); +} + +bool AttributePathExpandIteratorChecked::Next() +{ + bool dmResult = mDataModelIterator.Next(); + bool emResult = mEmberIterator.Next(); + + CheckOutputsIdentical("Next"); + + VerifyOrDie(dmResult == emResult); + + return emResult; +} + +bool AttributePathExpandIteratorChecked::Get(ConcreteAttributePath & aPath) +{ + CheckOutputsIdentical("Get"); + return mEmberIterator.Get(aPath); +} + +void AttributePathExpandIteratorChecked::ResetCurrentCluster() +{ + mDataModelIterator.ResetCurrentCluster(); + mEmberIterator.ResetCurrentCluster(); + + CheckOutputsIdentical("ResetCurrentCluster"); +} + +void AttributePathExpandIteratorChecked::ResetTo(SingleLinkedListNode * paths) + +{ + mDataModelIterator.ResetTo(paths); + mEmberIterator.ResetTo(paths); + CheckOutputsIdentical("ResetTo"); +} + +void AttributePathExpandIteratorChecked::CheckOutputsIdentical(const char * msg) +{ + ConcreteAttributePath dmPath; + ConcreteAttributePath emPath; + + bool dmResult = mDataModelIterator.Get(dmPath); + bool emResult = mEmberIterator.Get(emPath); + + if (dmResult == emResult) + { + // We check for: + // - either failed result (in which case path should not matter) + // - or exact match of paths on success + // + // NOTE: extra logic because mExpanded is NOT considered in operator== (ugly...) + if ((dmResult == false) || ((dmPath == emPath) && (dmPath.mExpanded == emPath.mExpanded))) + { + // outputs are identical. All is good + return; + } + } + + ChipLogProgress(Test, "Different paths in DM vs EMBER (%d and %d) in %s", dmResult, emResult, msg); + ChipLogProgress(Test, " DM PATH: 0x%X/" ChipLogFormatMEI "/" ChipLogFormatMEI " (%s)", dmPath.mEndpointId, + ChipLogValueMEI(dmPath.mClusterId), ChipLogValueMEI(dmPath.mAttributeId), + dmPath.mExpanded ? "EXPANDED" : "NOT expanded"); + ChipLogProgress(Test, " EMBER PATH: 0x%X/" ChipLogFormatMEI "/" ChipLogFormatMEI " (%s)", emPath.mEndpointId, + ChipLogValueMEI(emPath.mClusterId), ChipLogValueMEI(emPath.mAttributeId), + emPath.mExpanded ? "EXPANDED" : "NOT expanded"); + + chipDie(); +} + +} // namespace app +} // namespace chip diff --git a/src/app/AttributePathExpandIterator-Checked.h b/src/app/AttributePathExpandIterator-Checked.h new file mode 100644 index 00000000000000..e1611b3288ee18 --- /dev/null +++ b/src/app/AttributePathExpandIterator-Checked.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 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. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace app { + +class AttributePathExpandIteratorChecked +{ +public: + AttributePathExpandIteratorChecked(InteractionModel::DataModel * dataModel, + SingleLinkedListNode * attributePath); + + bool Next(); + bool Get(ConcreteAttributePath & aPath); + void ResetCurrentCluster(); + void ResetTo(SingleLinkedListNode * paths); + +private: + AttributePathExpandIteratorDataModel mDataModelIterator; + AttributePathExpandIteratorEmber mEmberIterator; + + void CheckOutputsIdentical(const char * msg); +}; + +} // namespace app +} // namespace chip diff --git a/src/app/AttributePathExpandIterator-DataModel.cpp b/src/app/AttributePathExpandIterator-DataModel.cpp new file mode 100644 index 00000000000000..92af8b50ca6730 --- /dev/null +++ b/src/app/AttributePathExpandIterator-DataModel.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2024 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. + */ +#include "lib/support/CodeUtils.h" +#include +#include + +using namespace chip::app::InteractionModel; + +namespace chip { +namespace app { + +AttributePathExpandIteratorDataModel::AttributePathExpandIteratorDataModel( + InteractionModel::DataModel * dataModel, SingleLinkedListNode * attributePath) : + mDataModel(dataModel), + mpAttributePath(attributePath), mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId) + +{ + mOutputPath.mExpanded = true; // this is reset in 'next' if needed + + // Make the iterator ready to emit the first valid path in the list. + // TODO: the bool return value here is completely unchecked + Next(); +} + +bool AttributePathExpandIteratorDataModel::IsValidAttributeId(AttributeId attributeId) +{ + switch (attributeId) + { + case Clusters::Globals::Attributes::GeneratedCommandList::Id: + case Clusters::Globals::Attributes::AcceptedCommandList::Id: +#if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE + case Clusters::Globals::Attributes::EventList::Id: +#endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE + case Clusters::Globals::Attributes::AttributeList::Id: + return true; + default: + break; + } + + const ConcreteAttributePath attributePath(mOutputPath.mEndpointId, mOutputPath.mClusterId, attributeId); + return mDataModel->GetAttributeInfo(attributePath).has_value(); +} + +std::optional AttributePathExpandIteratorDataModel::NextAttributeId() +{ + if (mOutputPath.mAttributeId == kInvalidAttributeId) + { + if (mpAttributePath->mValue.HasWildcardAttributeId()) + { + AttributeEntry entry = mDataModel->FirstAttribute(mOutputPath); + return entry.IsValid() // + ? entry.path.mAttributeId // + : Clusters::Globals::Attributes::GeneratedCommandList::Id; // + } + + // We allow fixed attribute IDs if and only if they are valid: + // - they may be GLOBAL attributes OR + // - they are valid attributes for this cluster + if (IsValidAttributeId(mpAttributePath->mValue.mAttributeId)) + { + return mpAttributePath->mValue.mAttributeId; + } + + return std::nullopt; + } + + // advance the existing attribute id if it can be advanced + VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardAttributeId(), std::nullopt); + + // Ensure (including ordering) that GlobalAttributesNotInMetadata is reported as needed + for (unsigned i = 0; i < ArraySize(GlobalAttributesNotInMetadata); i++) + { + if (GlobalAttributesNotInMetadata[i] != mOutputPath.mAttributeId) + { + continue; + } + + unsigned nextAttributeIndex = i + 1; + if (nextAttributeIndex < ArraySize(GlobalAttributesNotInMetadata)) + { + return GlobalAttributesNotInMetadata[nextAttributeIndex]; + } + + // reached the end of global attributes + return std::nullopt; + } + + AttributeEntry entry = mDataModel->NextAttribute(mOutputPath); + if (entry.IsValid()) + { + return entry.path.mAttributeId; + } + + // Finished the data model, start with global attributes + static_assert(ArraySize(GlobalAttributesNotInMetadata) > 0); + return GlobalAttributesNotInMetadata[0]; +} + +std::optional AttributePathExpandIteratorDataModel::NextClusterId() +{ + + if (mOutputPath.mClusterId == kInvalidClusterId) + { + if (mpAttributePath->mValue.HasWildcardClusterId()) + { + ClusterEntry entry = mDataModel->FirstCluster(mOutputPath.mEndpointId); + return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt; + } + + // only return a cluster if it is valid + const ConcreteClusterPath clusterPath(mOutputPath.mEndpointId, mpAttributePath->mValue.mClusterId); + if (!mDataModel->GetClusterInfo(clusterPath).has_value()) + { + return std::nullopt; + } + + return mpAttributePath->mValue.mClusterId; + } + + VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardClusterId(), std::nullopt); + + ClusterEntry entry = mDataModel->NextCluster(mOutputPath); + return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt; +} + +std::optional AttributePathExpandIteratorDataModel::NextEndpointId() +{ + if (mOutputPath.mEndpointId == kInvalidEndpointId) + { + if (mpAttributePath->mValue.HasWildcardEndpointId()) + { + EndpointId id = mDataModel->FirstEndpoint(); + return (id != kInvalidEndpointId) ? std::make_optional(id) : std::nullopt; + } + + return mpAttributePath->mValue.mEndpointId; + } + + VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardEndpointId(), std::nullopt); + + EndpointId id = mDataModel->NextEndpoint(mOutputPath.mEndpointId); + return (id != kInvalidEndpointId) ? std::make_optional(id) : std::nullopt; +} + +void AttributePathExpandIteratorDataModel::ResetCurrentCluster() +{ + // If this is a null iterator, or the attribute id of current cluster info is not a wildcard attribute id, then this function + // will do nothing, since we won't be expanding the wildcard attribute ids under a cluster. + VerifyOrReturn(mpAttributePath != nullptr && mpAttributePath->mValue.HasWildcardAttributeId()); + + // Reset path expansion to ask for the first attribute of the current cluster + mOutputPath.mAttributeId = kInvalidAttributeId; + mOutputPath.mExpanded = true; // we know this is a wildcard attribute + Next(); +} + +bool AttributePathExpandIteratorDataModel::AdvanceOutputPath() +{ + if (!mpAttributePath->mValue.IsWildcardPath()) + { + if (mOutputPath.mEndpointId != kInvalidEndpointId) + { + return false; // cannot expand non-wildcard path + } + + mOutputPath.mEndpointId = mpAttributePath->mValue.mEndpointId; + mOutputPath.mClusterId = mpAttributePath->mValue.mClusterId; + mOutputPath.mAttributeId = mpAttributePath->mValue.mAttributeId; + mOutputPath.mExpanded = false; + return true; + } + + while (true) + { + if (mOutputPath.mClusterId != kInvalidClusterId) + { + + std::optional nextAttribute = NextAttributeId(); + if (nextAttribute.has_value()) + { + mOutputPath.mAttributeId = *nextAttribute; + return true; + } + } + + // no valid attribute, try to advance the cluster, see if a suitable one exists + if (mOutputPath.mEndpointId != kInvalidEndpointId) + { + std::optional nextCluster = NextClusterId(); + if (nextCluster.has_value()) + { + mOutputPath.mClusterId = *nextCluster; + mOutputPath.mAttributeId = kInvalidAttributeId; // restarts attributes + continue; + } + } + + // no valid cluster, try advance the endpoint, see if a suitable on exists + std::optional nextEndpoint = NextEndpointId(); + if (nextEndpoint.has_value()) + { + mOutputPath.mEndpointId = *nextEndpoint; + mOutputPath.mClusterId = kInvalidClusterId; // restarts clusters + continue; + } + return false; + } +} + +bool AttributePathExpandIteratorDataModel::Next() +{ + while (mpAttributePath != nullptr) + { + if (AdvanceOutputPath()) + { + return true; + } + mpAttributePath = mpAttributePath->mpNext; + mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId); + mOutputPath.mExpanded = true; // this is reset to false on advancement if needed + } + + mOutputPath = ConcreteReadAttributePath(); + return false; +} + +} // namespace app +} // namespace chip diff --git a/src/app/AttributePathExpandIterator-DataModel.h b/src/app/AttributePathExpandIterator-DataModel.h new file mode 100644 index 00000000000000..546151c7dd2806 --- /dev/null +++ b/src/app/AttributePathExpandIterator-DataModel.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024 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. + */ +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace app { + +/** + * AttributePathExpandIteratorDataModel is used to iterate over a linked list of AttributePathParams-s. + * The AttributePathExpandIteratorDataModel is copiable, however, the given cluster info must be valid when calling Next(). + * + * AttributePathExpandIteratorDataModel will expand attribute paths with wildcards, and only emit existing paths for + * AttributePathParams with wildcards. For AttributePathParams with a concrete path (i.e. does not contain wildcards), + * AttributePathExpandIteratorDataModel will emit them as-is. + * + * The typical use of AttributePathExpandIteratorDataModel may look like: + * ConcreteAttributePath path; + * for (AttributePathExpandIteratorDataModel iterator(AttributePathParams); iterator.Get(path); iterator.Next()) {...} + * + * The iterator does not copy the given AttributePathParams, The given AttributePathParams must be valid when using the iterator. + * If the set of endpoints, clusters, or attributes that are supported changes, AttributePathExpandIteratorDataModel must be + * reinitialized. + * + * A initialized iterator will return the first valid path, no need to call Next() before calling Get() for the first time. + * + * Note: The Next() and Get() are two separate operations by design since a possible call of this iterator might be: + * - Get() + * - Chunk full, return + * - In a new chunk, Get() + * + * TODO: The AttributePathParams may support a group id, the iterator should be able to call group data provider to expand the group + * id. + */ +class AttributePathExpandIteratorDataModel +{ +public: + AttributePathExpandIteratorDataModel(InteractionModel::DataModel * dataModel, + SingleLinkedListNode * attributePath); + + /** + * Proceed the iterator to the next attribute path in the given cluster info. + * + * Returns false if AttributePathExpandIteratorDataModeDataModel has exhausted all paths in the given AttributePathParams list. + */ + bool Next(); + + /** + * Fills the aPath with the path the iterator currently points to. + * Returns false if the iterator is not pointing to a valid path (i.e. it has exhausted the cluster info). + */ + bool Get(ConcreteAttributePath & aPath) + { + aPath = mOutputPath; + return (mpAttributePath != nullptr); + } + + /** + * Reset the iterator to the beginning of current cluster if we are in the middle of expanding a wildcard attribute id for some + * cluster. + * + * When attributes are changed in the middle of expanding a wildcard attribute, we need to reset the iterator, to provide the + * client with a consistent state of the cluster. + */ + void ResetCurrentCluster(); + + /** Start iterating over the given `paths` */ + inline void ResetTo(SingleLinkedListNode * paths) + { + *this = AttributePathExpandIteratorDataModel(mDataModel, paths); + } + +private: + InteractionModel::DataModel * mDataModel; + SingleLinkedListNode * mpAttributePath; + ConcreteAttributePath mOutputPath; + + /// Move to the next endpoint/cluster/attribute triplet that is valid given + /// the current mOutputPath and mpAttributePath + /// + /// returns true if such a next value was found. + bool AdvanceOutputPath(); + + /// Get the next attribute ID in mOutputPath(endpoint/cluster) if one is available. + /// Will start from the beginning if current mOutputPath.mAttributeId is kInvalidAttributeId + /// + /// Respects path expansion/values in mpAttributePath + /// + /// Handles Global attributes (which are returned at the end) + std::optional NextAttributeId(); + + /// Get the next cluster ID in mOutputPath(endpoint) if one is available. + /// Will start from the beginning if current mOutputPath.mClusterId is kInvalidClusterId + /// + /// Respects path expansion/values in mpAttributePath + std::optional NextClusterId(); + + /// Get the next endpoint ID in mOutputPath if one is available. + /// Will start from the beginning if current mOutputPath.mEndpointId is kInvalidEndpointId + /// + /// Respects path expansion/values in mpAttributePath + std::optional NextEndpointId(); + + /// Checks if the given attributeId is valid for the current mOutputPath(endpoint/cluster) + /// + /// Meaning that it is known to the data model OR it is a always-there global attribute. + bool IsValidAttributeId(AttributeId attributeId); +}; + +} // namespace app +} // namespace chip diff --git a/src/app/AttributePathExpandIterator.cpp b/src/app/AttributePathExpandIterator-Ember.cpp similarity index 92% rename from src/app/AttributePathExpandIterator.cpp rename to src/app/AttributePathExpandIterator-Ember.cpp index 9be7ab495a4267..abaa621cb736ca 100644 --- a/src/app/AttributePathExpandIterator.cpp +++ b/src/app/AttributePathExpandIterator-Ember.cpp @@ -16,7 +16,7 @@ * limitations under the License. */ -#include +#include #include #include @@ -53,9 +53,10 @@ extern bool emberAfEndpointIndexIsEnabled(uint16_t index); namespace chip { namespace app { -AttributePathExpandIterator::AttributePathExpandIterator(SingleLinkedListNode * aAttributePath) +AttributePathExpandIteratorEmber::AttributePathExpandIteratorEmber(InteractionModel::DataModel *, + SingleLinkedListNode * aAttributePath) : + mpAttributePath(aAttributePath) { - mpAttributePath = aAttributePath; // Reset iterator state mEndpointIndex = UINT16_MAX; @@ -72,7 +73,7 @@ AttributePathExpandIterator::AttributePathExpandIterator(SingleLinkedListNodempNext, mEndpointIndex = UINT16_MAX)) { diff --git a/src/app/AttributePathExpandIterator-Ember.h b/src/app/AttributePathExpandIterator-Ember.h new file mode 100644 index 00000000000000..ef359fc21b60fa --- /dev/null +++ b/src/app/AttributePathExpandIterator-Ember.h @@ -0,0 +1,136 @@ +/* + * + * 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 + * Defines an iterator for iterating all possible paths from a list of AttributePathParams-s according to spec section 8.9.2.2 + * (Valid Attribute Paths) + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { + +/** + * AttributePathExpandIteratorEmber is used to iterate over a linked list of AttributePathParams-s. + * The AttributePathExpandIteratorEmber is copiable, however, the given cluster info must be valid when calling Next(). + * + * AttributePathExpandIteratorEmber will expand attribute paths with wildcards, and only emit existing paths for AttributePathParams + * with wildcards. For AttributePathParams with a concrete path (i.e. does not contain wildcards), AttributePathExpandIteratorEmber + * will emit them as-is. + * + * The typical use of AttributePathExpandIteratorEmber may look like: + * ConcreteAttributePath path; + * for (AttributePathExpandIteratorEmber iterator(AttributePathParams); iterator.Get(path); iterator.Next()) {...} + * + * The iterator does not copy the given AttributePathParams, The given AttributePathParams must be valid when using the iterator. + * If the set of endpoints, clusters, or attributes that are supported changes, AttributePathExpandIteratorEmber must be + * reinitialized. + * + * A initialized iterator will return the first valid path, no need to call Next() before calling Get() for the first time. + * + * Note: The Next() and Get() are two separate operations by design since a possible call of this iterator might be: + * - Get() + * - Chunk full, return + * - In a new chunk, Get() + * + * TODO: The AttributePathParams may support a group id, the iterator should be able to call group data provider to expand the group + * id. + */ +class AttributePathExpandIteratorEmber +{ +public: + AttributePathExpandIteratorEmber(InteractionModel::DataModel *, // datamodel is NOT used by this class + SingleLinkedListNode * aAttributePath); + + /** + * Proceed the iterator to the next attribute path in the given cluster info. + * + * Returns false if AttributePathExpandIteratorEmber has exhausted all paths in the given AttributePathParams list. + */ + bool Next(); + + /** + * Fills the aPath with the path the iterator currently points to. + * Returns false if the iterator is not pointing to a valid path (i.e. it has exhausted the cluster info). + */ + bool Get(ConcreteAttributePath & aPath) + { + aPath = mOutputPath; + return (mpAttributePath != nullptr); // still handling some path + } + + /** + * Reset the iterator to the beginning of current cluster if we are in the middle of expanding a wildcard attribute id for some + * cluster. + * + * When attributes are changed in the middle of expanding a wildcard attribute, we need to reset the iterator, to provide the + * client with a consistent state of the cluster. + */ + void ResetCurrentCluster(); + + /** Start iterating over the given `paths` */ + inline void ResetTo(SingleLinkedListNode * paths) + { + *this = AttributePathExpandIteratorEmber(nullptr /* data model is not used */, paths); + } + +private: + SingleLinkedListNode * mpAttributePath; + + ConcreteAttributePath mOutputPath; + + uint16_t mEndpointIndex, mEndEndpointIndex; + uint16_t mAttributeIndex, mEndAttributeIndex; + + // Note: should use decltype(EmberAfEndpointType::clusterCount) here, but af-types is including app specific generated files. + uint8_t mClusterIndex, mEndClusterIndex; + // For dealing with global attributes that are not part of the attribute + // metadata. + uint8_t mGlobalAttributeIndex, mGlobalAttributeEndIndex; + + /** + * Prepare*IndexRange will update mBegin*Index and mEnd*Index variables. + * If AttributePathParams contains a wildcard field, it will set mBegin*Index to 0 and mEnd*Index to count. + * Or it will set mBegin*Index to the index of the Endpoint/Cluster/Attribute, and mEnd*Index to mBegin*Index + 1. + * + * If the Endpoint/Cluster/Attribute does not exist, mBegin*Index will be UINT*_MAX, and mEnd*Inde will be 0. + * + * The index can be used with emberAfEndpointFromIndex, emberAfGetNthClusterId and emberAfGetServerAttributeIdByIndex. + */ + void PrepareEndpointIndexRange(const AttributePathParams & aAttributePath); + void PrepareClusterIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId); + void PrepareAttributeIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId, ClusterId aClusterId); +}; +} // namespace app +} // namespace chip diff --git a/src/app/AttributePathExpandIterator.h b/src/app/AttributePathExpandIterator.h index f11e6793822f84..fae69fe7995d4e 100644 --- a/src/app/AttributePathExpandIterator.h +++ b/src/app/AttributePathExpandIterator.h @@ -15,121 +15,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** - * @file - * Defines an iterator for iterating all possible paths from a list of AttributePathParams-s according to spec section 8.9.2.2 - * (Valid Attribute Paths) - */ - #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include + +#if CHIP_CONFIG_USE_EMBER_DATA_MODEL && CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +#include +#else +#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +#include +#else +#include +#endif // CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +#endif // CHIP_CONFIG_USE_EMBER_DATA_MODEL && CHIP_CONFIG_USE_DATA_MODEL_INTERFACE namespace chip { namespace app { -/** - * AttributePathExpandIterator is used to iterate over a linked list of AttributePathParams-s. - * The AttributePathExpandIterator is copiable, however, the given cluster info must be valid when calling Next(). - * - * AttributePathExpandIterator will expand attribute paths with wildcards, and only emit existing paths for AttributePathParams with - * wildcards. For AttributePathParams with a concrete path (i.e. does not contain wildcards), AttributePathExpandIterator will emit - * them as-is. - * - * The typical use of AttributePathExpandIterator may look like: - * ConcreteAttributePath path; - * for (AttributePathExpandIterator iterator(AttributePathParams); iterator.Get(path); iterator.Next()) {...} - * - * The iterator does not copy the given AttributePathParams, The given AttributePathParams must be valid when using the iterator. - * If the set of endpoints, clusters, or attributes that are supported changes, AttributePathExpandIterator must be reinitialized. - * - * A initialized iterator will return the first valid path, no need to call Next() before calling Get() for the first time. - * - * Note: The Next() and Get() are two separate operations by design since a possible call of this iterator might be: - * - Get() - * - Chunk full, return - * - In a new chunk, Get() - * - * TODO: The AttributePathParams may support a group id, the iterator should be able to call group data provider to expand the group - * id. - */ -class AttributePathExpandIterator -{ -public: - AttributePathExpandIterator(SingleLinkedListNode * aAttributePath); - - /** - * Proceed the iterator to the next attribute path in the given cluster info. - * - * Returns false if AttributePathExpandIterator has exhausted all paths in the given AttributePathParams list. - */ - bool Next(); - - /** - * Fills the aPath with the path the iterator currently points to. - * Returns false if the iterator is not pointing to a valid path (i.e. it has exhausted the cluster info). - */ - bool Get(ConcreteAttributePath & aPath) - { - aPath = mOutputPath; - return Valid(); - } - - /** - * Reset the iterator to the beginning of current cluster if we are in the middle of expanding a wildcard attribute id for some - * cluster. - * - * When attributes are changed in the middle of expanding a wildcard attribute, we need to reset the iterator, to provide the - * client with a consistent state of the cluster. - */ - void ResetCurrentCluster(); - - /** - * Returns if the iterator is valid (not exhausted). An iterator is exhausted if and only if: - * - Next() is called after iterating last path. - * - Iterator is initialized with a null AttributePathParams. - */ - inline bool Valid() const { return mpAttributePath != nullptr; } - -private: - SingleLinkedListNode * mpAttributePath; - - ConcreteAttributePath mOutputPath; - - uint16_t mEndpointIndex, mEndEndpointIndex; - uint16_t mAttributeIndex, mEndAttributeIndex; - - // Note: should use decltype(EmberAfEndpointType::clusterCount) here, but af-types is including app specific generated files. - uint8_t mClusterIndex, mEndClusterIndex; - // For dealing with global attributes that are not part of the attribute - // metadata. - uint8_t mGlobalAttributeIndex, mGlobalAttributeEndIndex; +#if CHIP_CONFIG_USE_EMBER_DATA_MODEL && CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +using AttributePathExpandIterator = ::chip::app::AttributePathExpandIteratorChecked; +#else +#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +using AttributePathExpandIterator = ::chip::app::AttributePathExpandIteratorDataModel; +#else +using AttributePathExpandIterator = ::chip::app::AttributePathExpandIteratorEmber; +#endif // CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +#endif // CHIP_CONFIG_USE_EMBER_DATA_MODEL && CHIP_CONFIG_USE_DATA_MODEL_INTERFACE - /** - * Prepare*IndexRange will update mBegin*Index and mEnd*Index variables. - * If AttributePathParams contains a wildcard field, it will set mBegin*Index to 0 and mEnd*Index to count. - * Or it will set mBegin*Index to the index of the Endpoint/Cluster/Attribute, and mEnd*Index to mBegin*Index + 1. - * - * If the Endpoint/Cluster/Attribute does not exist, mBegin*Index will be UINT*_MAX, and mEnd*Inde will be 0. - * - * The index can be used with emberAfEndpointFromIndex, emberAfGetNthClusterId and emberAfGetServerAttributeIdByIndex. - */ - void PrepareEndpointIndexRange(const AttributePathParams & aAttributePath); - void PrepareClusterIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId); - void PrepareAttributeIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId, ClusterId aClusterId); -}; } // namespace app } // namespace chip diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 666c452b09dc3a..0ca8c63d37bb62 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -58,6 +58,16 @@ declare_args() { chip_im_static_global_interaction_model_engine = current_os != "linux" && current_os != "mac" && current_os != "ios" && current_os != "android" + + # Data model interface usage: + # - disabled: does not use data model interface at all + # - check: runs BOTH datamodel and non-data-model (if possible) functionality and compares results + # - enabled: runs only the data model interface (does not use the legacy code) + if (current_os == "linux") { + chip_use_data_model_interface = "check" + } else { + chip_use_data_model_interface = "disabled" + } } buildconfig_header("app_buildconfig") { @@ -80,6 +90,27 @@ buildconfig_header("app_buildconfig") { "CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP=${chip_enable_busy_handling_for_operational_session_setup}", ] + if (chip_use_data_model_interface == "disabled") { + defines += [ + "CHIP_CONFIG_USE_DATA_MODEL_INTERFACE=0", + "CHIP_CONFIG_USE_EMBER_DATA_MODEL=1", + ] + } else if (chip_use_data_model_interface == "check") { + defines += [ + "CHIP_CONFIG_USE_DATA_MODEL_INTERFACE=1", + "CHIP_CONFIG_USE_EMBER_DATA_MODEL=1", + ] + } else { + # only one of disabled/check/enabled must be used + assert(chip_use_data_model_interface == "enabled", + "chip_use_data_model_interface must use a supported value") + + defines += [ + "CHIP_CONFIG_USE_DATA_MODEL_INTERFACE=1", + "CHIP_CONFIG_USE_EMBER_DATA_MODEL=0", + ] + } + visibility = [ ":app_config" ] } @@ -212,6 +243,8 @@ static_library("interaction-model") { ":paths", ":subscription-info-provider", "${chip_root}/src/app/MessageDef", + "${chip_root}/src/app/codegen-data-model:instance-header", + "${chip_root}/src/app/data-model-interface", "${chip_root}/src/app/icd/server:icd-server-config", "${chip_root}/src/app/icd/server:manager", "${chip_root}/src/app/icd/server:observer", @@ -377,7 +410,6 @@ static_library("app") { output_name = "libCHIPDataModel" sources = [ - "AttributePathExpandIterator.cpp", "AttributePathExpandIterator.h", "AttributePersistenceProvider.h", "ChunkedWriteCallback.cpp", @@ -426,6 +458,32 @@ static_library("app") { "${chip_root}/src/system", ] + if (chip_use_data_model_interface == "disabled") { + sources += [ + "AttributePathExpandIterator-Ember.cpp", + "AttributePathExpandIterator-Ember.h", + "AttributePathExpandIterator.h", + ] + } else if (chip_use_data_model_interface == "check") { + sources += [ + "AttributePathExpandIterator-Checked.cpp", + "AttributePathExpandIterator-Checked.h", + "AttributePathExpandIterator-DataModel.cpp", + "AttributePathExpandIterator-DataModel.h", + "AttributePathExpandIterator-Ember.cpp", + "AttributePathExpandIterator-Ember.h", + "AttributePathExpandIterator.h", + ] + public_deps += [ "${chip_root}/src/app/data-model-interface" ] + } else { # enabled + sources += [ + "AttributePathExpandIterator-DataModel.cpp", + "AttributePathExpandIterator-DataModel.h", + "AttributePathExpandIterator.h", + ] + public_deps += [ "${chip_root}/src/app/data-model-interface" ] + } + if (chip_enable_read_client) { sources += [ "BufferedReadCallback.cpp", diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 132e8cdf108a72..cb595e5bf239d1 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -41,6 +41,10 @@ #include #include +#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +#include +#endif + namespace chip { namespace app { @@ -482,7 +486,7 @@ CHIP_ERROR InteractionModelEngine::ParseAttributePaths(const Access::SubjectDesc if (paramsList.mValue.IsWildcardPath()) { - AttributePathExpandIterator pathIterator(¶msList); + AttributePathExpandIterator pathIterator(GetDataModel(), ¶msList); ConcreteAttributePath readPath; // The definition of "valid path" is "path exists and ACL allows access". The "path exists" part is handled by @@ -845,7 +849,8 @@ Protocols::InteractionModel::Status InteractionModelEngine::OnReadInitialRequest // We have already reserved enough resources for read requests, and have granted enough resources for current subscriptions, so // we should be able to allocate resources requested by this request. - ReadHandler * handler = mReadHandlers.CreateObject(*this, apExchangeContext, aInteractionType, mReportScheduler); + ReadHandler * handler = + mReadHandlers.CreateObject(*this, apExchangeContext, aInteractionType, mReportScheduler, GetDataModel()); if (handler == nullptr) { ChipLogProgress(InteractionModel, "no resource for %s interaction", @@ -1788,6 +1793,15 @@ CommandHandlerInterface * InteractionModelEngine::FindCommandHandler(EndpointId return nullptr; } +InteractionModel::DataModel * InteractionModelEngine::GetDataModel() const +{ +#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE + // TODO: this should be temporary, we should fully inject the data model + VerifyOrReturnValue(mDataModel != nullptr, CodegenDataModelInstance()); +#endif + return mDataModel; +} + void InteractionModelEngine::OnTimedInteractionFailed(TimedHandler * apTimedHandler) { mTimedHandlers.ReleaseObject(apTimedHandler); diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 69c565254f96f0..e2d299cce67280 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -411,6 +412,8 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, } #endif + InteractionModel::DataModel * GetDataModel() const; + private: friend class reporting::Engine; friend class TestCommandInteraction; @@ -450,9 +453,9 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, * * */ - static CHIP_ERROR ParseAttributePaths(const Access::SubjectDescriptor & aSubjectDescriptor, - AttributePathIBs::Parser & aAttributePathListParser, bool & aHasValidAttributePath, - size_t & aRequestedAttributePathCount); + CHIP_ERROR ParseAttributePaths(const Access::SubjectDescriptor & aSubjectDescriptor, + AttributePathIBs::Parser & aAttributePathListParser, bool & aHasValidAttributePath, + size_t & aRequestedAttributePathCount); /** * This parses the event path list to ensure it is well formed. If so, for each path in the list, it will expand to a list @@ -699,6 +702,8 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, CASESessionManager * mpCASESessionMgr = nullptr; SubscriptionResumptionStorage * mpSubscriptionResumptionStorage = nullptr; + + InteractionModel::DataModel * mDataModel = nullptr; }; } // namespace app diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 487acd27ed883e..3e182bb9428a5f 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -22,6 +22,7 @@ * */ +#include "data-model-interface/DataModel.h" #include #include #include @@ -53,9 +54,9 @@ uint16_t ReadHandler::GetPublisherSelectedIntervalLimit() } ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, - InteractionType aInteractionType, Observer * observer) : - mExchangeCtx(*this), - mManagementCallback(apCallback) + InteractionType aInteractionType, Observer * observer, InteractionModel::DataModel * apDataModel) : + mAttributePathExpandIterator(apDataModel, nullptr), + mExchangeCtx(*this), mManagementCallback(apCallback) { VerifyOrDie(apExchangeContext != nullptr); @@ -79,8 +80,8 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeCon } #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS -ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer) : - mExchangeCtx(*this), mManagementCallback(apCallback) +ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer, InteractionModel::DataModel * apDataModel) : + mAttributePathExpandIterator(apDataModel, nullptr), mExchangeCtx(*this), mManagementCallback(apCallback) { mInteractionType = InteractionType::Subscribe; mFlags.ClearAll(); @@ -509,8 +510,8 @@ CHIP_ERROR ReadHandler::ProcessAttributePaths(AttributePathIBs::Parser & aAttrib if (CHIP_END_OF_TLV == err) { mManagementCallback.GetInteractionModelEngine()->RemoveDuplicateConcreteAttributePath(mpAttributePathList); - mAttributePathExpandIterator = AttributePathExpandIterator(mpAttributePathList); - err = CHIP_NO_ERROR; + mAttributePathExpandIterator.ResetTo(mpAttributePathList); + err = CHIP_NO_ERROR; } return err; } @@ -850,7 +851,7 @@ void ReadHandler::PersistSubscription() void ReadHandler::ResetPathIterator() { - mAttributePathExpandIterator = AttributePathExpandIterator(mpAttributePathList); + mAttributePathExpandIterator.ResetTo(mpAttributePathList); mAttributeEncoderState.Reset(); } diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index c973432953275b..0f6b3fada87572 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -212,7 +212,7 @@ class ReadHandler : public Messaging::ExchangeDelegate * */ ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType, - Observer * observer); + Observer * observer, InteractionModel::DataModel * apDataModel); #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS /** @@ -222,7 +222,7 @@ class ReadHandler : public Messaging::ExchangeDelegate * The callback passed in has to outlive this handler object. * */ - ReadHandler(ManagementCallback & apCallback, Observer * observer); + ReadHandler(ManagementCallback & apCallback, Observer * observer, InteractionModel::DataModel * apDataModel); #endif const SingleLinkedListNode * GetAttributePathList() const { return mpAttributePathList; } @@ -500,7 +500,7 @@ class ReadHandler : public Messaging::ExchangeDelegate /// @param aFlag Flag to clear void ClearStateFlag(ReadHandlerFlags aFlag); - AttributePathExpandIterator mAttributePathExpandIterator = AttributePathExpandIterator(nullptr); + AttributePathExpandIterator mAttributePathExpandIterator; // The current generation of the reporting engine dirty set the last time we were notified that a path we're interested in was // marked dirty. @@ -582,5 +582,6 @@ class ReadHandler : public Messaging::ExchangeDelegate // TODO (#27675): Merge all observers into one and that one will dispatch the callbacks to the right place. Observer * mObserver = nullptr; }; + } // namespace app } // namespace chip diff --git a/src/app/SubscriptionResumptionSessionEstablisher.cpp b/src/app/SubscriptionResumptionSessionEstablisher.cpp index 6e016684410fc8..11da3c3d496f92 100644 --- a/src/app/SubscriptionResumptionSessionEstablisher.cpp +++ b/src/app/SubscriptionResumptionSessionEstablisher.cpp @@ -17,6 +17,7 @@ #include #include +#include namespace chip { namespace app { @@ -103,7 +104,8 @@ void SubscriptionResumptionSessionEstablisher::HandleDeviceConnected(void * cont ChipLogProgress(InteractionModel, "no resource for subscription resumption"); return; } - ReadHandler * readHandler = imEngine->mReadHandlers.CreateObject(*imEngine, imEngine->GetReportScheduler()); + ReadHandler * readHandler = + imEngine->mReadHandlers.CreateObject(*imEngine, imEngine->GetReportScheduler(), imEngine->GetDataModel()); if (readHandler == nullptr) { // TODO - Should we keep the subscription here? diff --git a/src/app/chip_data_model.cmake b/src/app/chip_data_model.cmake index 2d4149b66f6a8a..eac3b492c0eeff 100644 --- a/src/app/chip_data_model.cmake +++ b/src/app/chip_data_model.cmake @@ -17,6 +17,7 @@ set(CHIP_APP_BASE_DIR ${CMAKE_CURRENT_LIST_DIR}) include("${CHIP_ROOT}/build/chip/chip_codegen.cmake") +include("${CHIP_ROOT}/src/app/codegen-data-model/model.cmake") # Configure ${APP_TARGET} with source files associated with ${CLUSTER} cluster # @@ -132,6 +133,8 @@ function(chip_configure_data_model APP_TARGET) target_include_directories(${APP_TARGET} ${SCOPE} "${APP_TEMPLATES_GEN_DIR}") add_dependencies(${APP_TARGET} ${APP_TARGET}-zapgen) + # TODO: source for codedgen_data_model ??? + target_sources(${APP_TARGET} ${SCOPE} ${CHIP_APP_BASE_DIR}/../../zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp ${CHIP_APP_BASE_DIR}/../../zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -149,6 +152,7 @@ function(chip_configure_data_model APP_TARGET) ${CHIP_APP_BASE_DIR}/util/generic-callback-stubs.cpp ${CHIP_APP_BASE_DIR}/util/privilege-storage.cpp ${CHIP_APP_BASE_DIR}/util/util.cpp + ${CODEGEN_DATA_MODEL_SOURCES} ${APP_GEN_FILES} ${APP_TEMPLATES_GEN_FILES} ) diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 6b37e857c6b2d8..90b5bd1cfec3f3 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -15,6 +15,7 @@ import("//build_overrides/build.gni") import("//build_overrides/chip.gni") import("${chip_root}/build/chip/chip_codegen.gni") +import("${chip_root}/src/app/codegen-data-model/model.gni") import("${chip_root}/src/app/common_flags.gni") import("${chip_root}/src/platform/python.gni") @@ -201,6 +202,7 @@ template("chip_data_model") { "${_app_root}/util/privilege-storage.cpp", "${chip_root}/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp", ] + sources += codegen_data_model_SOURCES if (!chip_build_controller_dynamic_server) { sources += [ @@ -421,6 +423,7 @@ template("chip_data_model") { "${chip_root}/src/lib/support", "${chip_root}/src/protocols/secure_channel", ] + public_deps += codegen_data_model_PUBLIC_DEPS if (is_server) { public_deps += [ "${chip_root}/src/app/server" ] diff --git a/src/app/codegen-data-model/BUILD.gn b/src/app/codegen-data-model/BUILD.gn index cc856539d001e7..955f76c091c0bd 100644 --- a/src/app/codegen-data-model/BUILD.gn +++ b/src/app/codegen-data-model/BUILD.gn @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import("//build_overrides/chip.gni") + # This source set is TIGHLY coupled with code-generated data models # as generally implemented by `src/app/util` # @@ -25,7 +26,20 @@ import("//build_overrides/chip.gni") # CodegenDataModel_Write.cpp # EmberMetadata.cpp # EmberMetadata.h +# Instance.cpp # # The above list of files exists to satisfy the "dependency linter" # since those files should technically be "visible to gn" even though we # are supposed to go through model.gni constants + +source_set("instance-header") { + # TODO: this is a workaround until we have actual injection in our code. The issue is that + # DataModel is not currently injected and InteractionModelEngine needs a default + # + # The default should be code-generated data models, however that depends on code + # generation and is application specific. + # + # This only declares that the instance method is provided by something (that something + # generally being unit tests or data_model.gni/data_model.cmake files) + sources = [ "Instance.h" ] +} diff --git a/src/app/codegen-data-model/CodegenDataModel_Read.cpp b/src/app/codegen-data-model/CodegenDataModel_Read.cpp index 127e0e7ad3866a..3e1f246cb3a2f9 100644 --- a/src/app/codegen-data-model/CodegenDataModel_Read.cpp +++ b/src/app/codegen-data-model/CodegenDataModel_Read.cpp @@ -121,8 +121,8 @@ static_assert(sizeof(LongPascalString::LengthType) == 2); /// Given a ByteSpan containing data from ember, interpret it /// as a span of type OUT (i.e. ByteSpan or CharSpan) given a ENCODING /// where ENCODING is Short or Long pascal strings. -template -std::optional ExtractEmberString(ByteSpan data) +template +std::optional ExtractEmberString(ByteSpan data) { constexpr size_t kLengthTypeSize = sizeof(typename ENCODING::LengthType); VerifyOrDie(kLengthTypeSize <= data.size()); @@ -134,7 +134,7 @@ std::optional ExtractEmberString(ByteSpan data) } VerifyOrDie(len + sizeof(len) <= data.size()); - return std::make_optional(reinterpret_cast(data.data() + kLengthTypeSize), len); + return std::make_optional(reinterpret_cast(data.data() + kLengthTypeSize), len); } /// Encode a value inside `encoder` diff --git a/src/app/codegen-data-model/Instance.cpp b/src/app/codegen-data-model/Instance.cpp new file mode 100644 index 00000000000000..98eeeb604503b9 --- /dev/null +++ b/src/app/codegen-data-model/Instance.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 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. + */ +#include +#include + +namespace chip { +namespace app { + +InteractionModel::DataModel * CodegenDataModelInstance() +{ + static CodegenDataModel gCodegenModel; + return &gCodegenModel; +} + +} // namespace app +} // namespace chip diff --git a/src/app/codegen-data-model/Instance.h b/src/app/codegen-data-model/Instance.h new file mode 100644 index 00000000000000..4c7530981920c1 --- /dev/null +++ b/src/app/codegen-data-model/Instance.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 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. + */ +#pragma once + +#include + +namespace chip { +namespace app { + +InteractionModel::DataModel * CodegenDataModelInstance(); + +} // namespace app +} // namespace chip diff --git a/src/app/codegen-data-model/model.cmake b/src/app/codegen-data-model/model.cmake new file mode 100644 index 00000000000000..73b54247a81033 --- /dev/null +++ b/src/app/codegen-data-model/model.cmake @@ -0,0 +1,26 @@ +# Copyright (c) 2024 Project CHIP Authors +# +# 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. +set(BASE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# If you change this list, please ALSO CHANGE model.gni +SET(CODEGEN_DATA_MODEL_SOURCES + "${BASE_DIR}/CodegenDataModel.cpp" + "${BASE_DIR}/CodegenDataModel.h" + "${BASE_DIR}/CodegenDataModel_Read.cpp" + "${BASE_DIR}/CodegenDataModel_Write.cpp" + "${BASE_DIR}/EmberMetadata.cpp" + "${BASE_DIR}/EmberMetadata.h" + "${BASE_DIR}/Instance.cpp" +) + diff --git a/src/app/codegen-data-model/model.gni b/src/app/codegen-data-model/model.gni index a2abf6377c07a4..25f50571223971 100644 --- a/src/app/codegen-data-model/model.gni +++ b/src/app/codegen-data-model/model.gni @@ -25,15 +25,17 @@ import("//build_overrides/chip.gni") # be cleanly built as a stand-alone and instead have to be imported as part of # a different data model or compilation unit. codegen_data_model_SOURCES = [ - "${chip_root}/src/app/codegen-data-model/CodegenDataModel.h", "${chip_root}/src/app/codegen-data-model/CodegenDataModel.cpp", + "${chip_root}/src/app/codegen-data-model/CodegenDataModel.h", "${chip_root}/src/app/codegen-data-model/CodegenDataModel_Read.cpp", "${chip_root}/src/app/codegen-data-model/CodegenDataModel_Write.cpp", "${chip_root}/src/app/codegen-data-model/EmberMetadata.cpp", "${chip_root}/src/app/codegen-data-model/EmberMetadata.h", + "${chip_root}/src/app/codegen-data-model/Instance.cpp", ] codegen_data_model_PUBLIC_DEPS = [ "${chip_root}/src/app/common:attribute-type", "${chip_root}/src/app/data-model-interface", + "${chip_root}/src/app/codegen-data-model:instance-header", ] diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 3ca229a6751132..0f0694dcc51574 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -55,6 +55,7 @@ source_set("binding-test-srcs") { ] public_deps = [ + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/lib/core", ] @@ -102,6 +103,7 @@ source_set("power-cluster-test-srcs") { public_deps = [ "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/lib/core", ] @@ -122,6 +124,7 @@ source_set("scenes-table-test-srcs") { public_deps = [ "${chip_root}/src/app", "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/lib/core", ] @@ -146,6 +149,7 @@ source_set("app-test-stubs") { public_configs = [ "${chip_root}/src/lib/support/pw_log_chip:config" ] public_deps = [ + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", @@ -206,9 +210,11 @@ chip_test_suite("tests") { ":thread-network-directory-test-srcs", ":time-sync-data-provider-test-srcs", "${chip_root}/src/app", + "${chip_root}/src/app/codegen-data-model:instance-header", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/icd/client:manager", "${chip_root}/src/app/tests:helpers", + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/lib/core", "${chip_root}/src/lib/core:string-builder-adapters", diff --git a/src/app/tests/TestAttributePathExpandIterator.cpp b/src/app/tests/TestAttributePathExpandIterator.cpp index 032581bff16f8a..236203098c8fb8 100644 --- a/src/app/tests/TestAttributePathExpandIterator.cpp +++ b/src/app/tests/TestAttributePathExpandIterator.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -132,7 +133,7 @@ TEST(TestAttributePathExpandIterator, TestAllWildcard) size_t index = 0; - for (app::AttributePathExpandIterator iter(&clusInfo); iter.Get(path); iter.Next()) + for (app::AttributePathExpandIterator iter(CodegenDataModelInstance(), &clusInfo); iter.Get(path); iter.Next()) { ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); @@ -156,7 +157,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardEndpoint) size_t index = 0; - for (app::AttributePathExpandIterator iter(&clusInfo); iter.Get(path); iter.Next()) + for (app::AttributePathExpandIterator iter(CodegenDataModelInstance(), &clusInfo); iter.Get(path); iter.Next()) { ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); @@ -183,7 +184,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardCluster) size_t index = 0; - for (app::AttributePathExpandIterator iter(&clusInfo); iter.Get(path); iter.Next()) + for (app::AttributePathExpandIterator iter(CodegenDataModelInstance(), &clusInfo); iter.Get(path); iter.Next()) { ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); @@ -210,7 +211,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardClusterGlobalAttributeNotInMet size_t index = 0; - for (app::AttributePathExpandIterator iter(&clusInfo); iter.Get(path); iter.Next()) + for (app::AttributePathExpandIterator iter(CodegenDataModelInstance(), &clusInfo); iter.Get(path); iter.Next()) { ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); @@ -244,7 +245,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardAttribute) size_t index = 0; - for (app::AttributePathExpandIterator iter(&clusInfo); iter.Get(path); iter.Next()) + for (app::AttributePathExpandIterator iter(CodegenDataModelInstance(), &clusInfo); iter.Get(path); iter.Next()) { ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); @@ -269,7 +270,7 @@ TEST(TestAttributePathExpandIterator, TestNoWildcard) size_t index = 0; - for (app::AttributePathExpandIterator iter(&clusInfo); iter.Get(path); iter.Next()) + for (app::AttributePathExpandIterator iter(CodegenDataModelInstance(), &clusInfo); iter.Get(path); iter.Next()) { ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); @@ -413,7 +414,7 @@ TEST(TestAttributePathExpandIterator, TestMultipleClusInfo) size_t index = 0; - for (app::AttributePathExpandIterator iter(&clusInfo1); iter.Get(path); iter.Next()) + for (app::AttributePathExpandIterator iter(CodegenDataModelInstance(), &clusInfo1); iter.Get(path); iter.Next()) { ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId, ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId)); diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp index f724f1921ef81d..db79875b010f48 100644 --- a/src/app/tests/TestInteractionModelEngine.cpp +++ b/src/app/tests/TestInteractionModelEngine.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -264,8 +265,9 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); // Create and setup readHandler 1 - ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject( - nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler()); + ReadHandler * readHandler1 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Verify that Bob still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); @@ -311,14 +313,15 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription // Create readHandler 1 engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, - reporting::GetDefaultReportScheduler()); + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Verify that Bob still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); // Create and setup readHandler 2 - ReadHandler * readHandler2 = engine->GetReadHandlerPool().CreateObject( - nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler()); + ReadHandler * readHandler2 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Verify that Bob still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); @@ -368,12 +371,14 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId)); // Create and setup readHandler 1 - ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject( - nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler()); + ReadHandler * readHandler1 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Create and setup readHandler 2 - ReadHandler * readHandler2 = engine->GetReadHandlerPool().CreateObject( - nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler()); + ReadHandler * readHandler2 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Verify that Bob still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); @@ -449,19 +454,21 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription // Create and setup readHandler 1-1 engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx11, ReadHandler::InteractionType::Subscribe, - reporting::GetDefaultReportScheduler()); + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Create and setup readHandler 1-2 - ReadHandler * readHandler12 = engine->GetReadHandlerPool().CreateObject( - nullCallback, exchangeCtx12, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler()); + ReadHandler * readHandler12 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx12, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Create and setup readHandler 2-1 engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx21, ReadHandler::InteractionType::Subscribe, - reporting::GetDefaultReportScheduler()); + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Create and setup readHandler 2-2 - ReadHandler * readHandler22 = engine->GetReadHandlerPool().CreateObject( - nullCallback, exchangeCtx22, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler()); + ReadHandler * readHandler22 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx22, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Verify that both Alice and Bob have no active subscriptions EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); @@ -531,8 +538,9 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription ASSERT_TRUE(exchangeCtx); // Create readHandler - ReadHandler * readHandler = engine->GetReadHandlerPool().CreateObject( - nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler()); + ReadHandler * readHandler = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); // Verify there are not active subscriptions EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, valideSubjectId)); diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index 81c66e45f40999..57d23d1eb0db81 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -18,17 +18,17 @@ #include -#include "app/tests/test-interaction-model-api.h" -#include "lib/support/CHIPMem.h" #include #include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -469,7 +470,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestReadHandler) { Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); - ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); GenerateReportData(reportDatabuf, ReportType::kValid, false /* aSuppressResponse*/); EXPECT_EQ(readHandler.SendReportData(std::move(reportDatabuf), false), CHIP_ERROR_INCORRECT_STATE); @@ -624,7 +626,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestReadHandlerInvalidAttributePath) { Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); - ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); GenerateReportData(reportDatabuf, ReportType::kValid, false /* aSuppressResponse*/); EXPECT_EQ(readHandler.SendReportData(std::move(reportDatabuf), false), CHIP_ERROR_INCORRECT_STATE); @@ -1349,7 +1352,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestProcessSubscribeRequest) Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); writer.Init(std::move(subscribeRequestbuf)); EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR); @@ -1408,7 +1412,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestSupMaxInt Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); writer.Init(std::move(subscribeRequestbuf)); EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR); @@ -1474,7 +1479,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestInfMaxInt Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); writer.Init(std::move(subscribeRequestbuf)); EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR); @@ -1540,7 +1546,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestSupMinInt Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); writer.Init(std::move(subscribeRequestbuf)); EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR); @@ -1606,7 +1613,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestMaxMinInt Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); writer.Init(std::move(subscribeRequestbuf)); EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR); @@ -1670,7 +1678,8 @@ TEST_F_FROM_FIXTURE(TestReadInteraction, TestICDProcessSubscribeRequestInvalidId Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler, + CodegenDataModelInstance()); writer.Init(std::move(subscribeRequestbuf)); EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR); diff --git a/src/app/tests/TestReportScheduler.cpp b/src/app/tests/TestReportScheduler.cpp index bd8cd11041dacf..f9ff58eac9e017 100644 --- a/src/app/tests/TestReportScheduler.cpp +++ b/src/app/tests/TestReportScheduler.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -274,8 +275,8 @@ TEST_F_FROM_FIXTURE(TestReportScheduler, TestReadHandlerList) for (size_t i = 0; i < kNumMaxReadHandlers; i++) { - ReadHandler * readHandler = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); + ReadHandler * readHandler = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &sScheduler, CodegenDataModelInstance()); sScheduler.OnSubscriptionEstablished(readHandler); ASSERT_NE(nullptr, readHandler); ASSERT_NE(nullptr, sScheduler.FindReadHandlerNode(readHandler)); @@ -338,20 +339,20 @@ TEST_F_FROM_FIXTURE(TestReportScheduler, TestReportTiming) // Dirty read handler, will be triggered at min interval // Test OnReadHandler created - ReadHandler * readHandler1 = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); + ReadHandler * readHandler1 = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &sScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler1, &sScheduler, 1, 2)); readHandler1->ForceDirtyState(); // Clean read handler, will be triggered at max interval - ReadHandler * readHandler2 = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); + ReadHandler * readHandler2 = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &sScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler2, &sScheduler, 0, 3)); // Clean read handler, will be triggered at max interval, but will be cancelled before - ReadHandler * readHandler3 = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); + ReadHandler * readHandler3 = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &sScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler3, &sScheduler, 0, 3)); // Confirms that none of the ReadHandlers are currently reportable @@ -404,8 +405,8 @@ TEST_F_FROM_FIXTURE(TestReportScheduler, TestObserverCallbacks) // Initialize mock timestamp sTestTimerDelegate.SetMockSystemTimestamp(Milliseconds64(0)); - ReadHandler * readHandler = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); + ReadHandler * readHandler = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &sScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler, &sScheduler, 1, 2)); @@ -480,14 +481,14 @@ TEST_F_FROM_FIXTURE(TestReportScheduler, TestSynchronizedScheduler) // Initialize the mock system time sTestTimerSynchronizedDelegate.SetMockSystemTimestamp(System::Clock::Milliseconds64(0)); - ReadHandler * readHandler1 = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); + ReadHandler * readHandler1 = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &syncScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler1, &syncScheduler, 0, 2)); ReadHandlerNode * node1 = syncScheduler.FindReadHandlerNode(readHandler1); - ReadHandler * readHandler2 = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); + ReadHandler * readHandler2 = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &syncScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler2, &syncScheduler, 1, 3)); ReadHandlerNode * node2 = syncScheduler.FindReadHandlerNode(readHandler2); @@ -614,8 +615,8 @@ TEST_F_FROM_FIXTURE(TestReportScheduler, TestSynchronizedScheduler) // Wait for 1 second, nothing should happen here sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000)); - ReadHandler * readHandler3 = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); + ReadHandler * readHandler3 = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &syncScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler3, &syncScheduler, 2, 3)); ReadHandlerNode * node3 = syncScheduler.FindReadHandlerNode(readHandler3); @@ -668,8 +669,8 @@ TEST_F_FROM_FIXTURE(TestReportScheduler, TestSynchronizedScheduler) EXPECT_EQ(syncScheduler.mNextReportTimestamp, node1->GetMaxTimestamp()); // Now simulate a new readHandler being added with a max forcing a conflict - ReadHandler * readHandler4 = - readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); + ReadHandler * readHandler4 = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, + &syncScheduler, CodegenDataModelInstance()); EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler4, &syncScheduler, 0, 1)); ReadHandlerNode * node4 = syncScheduler.FindReadHandlerNode(readHandler4); diff --git a/src/app/tests/TestReportingEngine.cpp b/src/app/tests/TestReportingEngine.cpp index f0d3b0cc60d935..4468eeaaf12ade 100644 --- a/src/app/tests/TestReportingEngine.cpp +++ b/src/app/tests/TestReportingEngine.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -170,7 +171,7 @@ TEST_F_FROM_FIXTURE(TestReportingEngine, TestBuildAndSendSingleReportData) EXPECT_EQ(readRequestBuilder.GetError(), CHIP_NO_ERROR); EXPECT_EQ(writer.Finalize(&readRequestbuf), CHIP_NO_ERROR); app::ReadHandler readHandler(dummy, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + app::reporting::GetDefaultReportScheduler(), CodegenDataModelInstance()); readHandler.OnInitialRequest(std::move(readRequestbuf)); EXPECT_EQ(InteractionModelEngine::GetInstance()->GetReportingEngine().BuildAndSendSingleReportData(&readHandler), diff --git a/src/app/tests/integration/BUILD.gn b/src/app/tests/integration/BUILD.gn index 6acd4ccdd8d6af..88b3eccc14b6f5 100644 --- a/src/app/tests/integration/BUILD.gn +++ b/src/app/tests/integration/BUILD.gn @@ -44,6 +44,7 @@ executable("chip-im-initiator") { deps = [ ":common", "${chip_root}/src/app", + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", @@ -67,6 +68,7 @@ executable("chip-im-responder") { deps = [ ":common", "${chip_root}/src/app", + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", diff --git a/src/app/util/mock/BUILD.gn b/src/app/util/mock/BUILD.gn index 59ab39947711c2..fa57cf4e9e71e9 100644 --- a/src/app/util/mock/BUILD.gn +++ b/src/app/util/mock/BUILD.gn @@ -14,6 +14,8 @@ import("//build_overrides/chip.gni") +import("${chip_root}/src/app/codegen-data-model/model.gni") + config("mock_include") { include_dirs = [ "include" ] } @@ -39,3 +41,19 @@ source_set("mock_ember") { cflags = [ "-Wconversion" ] } + +# Include a runnable codegen data model that is based on the mocks +# defined in mock_ember. +source_set("mock_codegen_data_model") { + sources = codegen_data_model_SOURCES + public_deps = codegen_data_model_PUBLIC_DEPS + + sources += [ + "${chip_root}/src/app/util/ember-global-attribute-access-interface.cpp", + "${chip_root}/src/app/util/ember-io-storage.cpp", + "CodegenEmberMocks.cpp", + ] + + public_configs = [ ":mock_include" ] + public_deps += [ ":mock_ember" ] +} diff --git a/src/app/util/mock/CodegenEmberMocks.cpp b/src/app/util/mock/CodegenEmberMocks.cpp new file mode 100644 index 00000000000000..e1f0b2436084a7 --- /dev/null +++ b/src/app/util/mock/CodegenEmberMocks.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 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. + */ +#include +#include + +using chip::Protocols::InteractionModel::Status; + +void MatterReportingAttributeChangeCallback(const chip::app::ConcreteAttributePath & aPath) +{ + // NOOP currently, exists to satisfy linker only +} + +Status emAfReadOrWriteAttribute(const EmberAfAttributeSearchRecord * attRecord, const EmberAfAttributeMetadata ** metadata, + uint8_t * buffer, uint16_t readLength, bool write) +{ + if (!write) + { + memset(buffer, 0, readLength); + } + return Status::Success; +} + +Status emAfWriteAttributeExternal(chip::EndpointId endpoint, chip::ClusterId cluster, chip::AttributeId attributeID, + uint8_t * dataPtr, EmberAfAttributeType dataType) +{ + return Status::Success; +} + +Status emberAfWriteAttribute(chip::EndpointId endpoint, chip::ClusterId cluster, chip::AttributeId attributeID, uint8_t * dataPtr, + EmberAfAttributeType dataType) +{ + return emAfWriteAttributeExternal(endpoint, cluster, attributeID, dataPtr, dataType); +} diff --git a/src/controller/tests/data_model/BUILD.gn b/src/controller/tests/data_model/BUILD.gn index db1d9f8273b2ed..9439bd33b83458 100644 --- a/src/controller/tests/data_model/BUILD.gn +++ b/src/controller/tests/data_model/BUILD.gn @@ -41,6 +41,7 @@ chip_test_suite("data_model") { "${chip_root}/src/app", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/tests:helpers", + "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", "${chip_root}/src/controller", "${chip_root}/src/lib/core:string-builder-adapters",