diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 338e1d1f6dce8c..6e4c0419e5b68b 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -1584,6 +1584,26 @@ CHIP_ERROR InteractionModelEngine::PushFrontAttributePathList(SingleLinkedListNo return err; } +bool InteractionModelEngine::IsExistingAttributePath(const ConcreteAttributePath & path) +{ +#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE +#if CHIP_CONFIG_USE_EMBER_DATA_MODEL + // Ensure that Provider interface and ember are IDENTICAL in attribute location (i.e. "check" mode) + VerifyOrDie(GetDataModelProvider() + ->GetAttributeInfo(ConcreteAttributePath(path.mEndpointId, path.mClusterId, path.mAttributeId)) + .has_value() == emberAfContainsAttribute(path.mEndpointId, path.mClusterId, path.mAttributeId) + + ); +#endif + + return GetDataModelProvider() + ->GetAttributeInfo(ConcreteAttributePath(path.mEndpointId, path.mClusterId, path.mAttributeId)) + .has_value(); +#else + return emberAfContainsAttribute(path.mEndpointId, path.mClusterId, path.mAttributeId); +#endif +} + void InteractionModelEngine::RemoveDuplicateConcreteAttributePath(SingleLinkedListNode *& aAttributePaths) { SingleLinkedListNode * prev = nullptr; @@ -1592,9 +1612,11 @@ void InteractionModelEngine::RemoveDuplicateConcreteAttributePath(SingleLinkedLi while (path1 != nullptr) { bool duplicate = false; + // skip all wildcard paths and invalid concrete attribute if (path1->mValue.IsWildcardPath() || - !emberAfContainsAttribute(path1->mValue.mEndpointId, path1->mValue.mClusterId, path1->mValue.mAttributeId)) + !IsExistingAttributePath( + ConcreteAttributePath(path1->mValue.mEndpointId, path1->mValue.mClusterId, path1->mValue.mAttributeId))) { prev = path1; path1 = path1->mpNext; diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 8b7bd0743782cd..a51e781dac216e 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -612,6 +612,11 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, void ShutdownMatchingSubscriptions(const Optional & aFabricIndex = NullOptional, const Optional & aPeerNodeId = NullOptional); + /** + * Check if the given attribute path is a valid path in the data model provider. + */ + bool IsExistingAttributePath(const ConcreteAttributePath & path); + static void ResumeSubscriptionsTimerCallback(System::Layer * apSystemLayer, void * apAppState); template diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider.h b/src/app/codegen-data-model-provider/CodegenDataModelProvider.h index 70d5e6f41abdd6..1462ce03c1d5a9 100644 --- a/src/app/codegen-data-model-provider/CodegenDataModelProvider.h +++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider.h @@ -63,11 +63,26 @@ class CodegenDataModelProvider : public chip::app::DataModel::Provider /// Checks if the given command id exists in the given list bool Exists(const CommandId * list, CommandId toCheck); + + void Reset() { mCurrentList = mCurrentHint = nullptr; } }; public: + /// clears out internal caching. Especially useful in unit tests, + /// where path caching does not really apply (the same path may result in different outcomes) + void Reset() + { + mAcceptedCommandsIterator.Reset(); + mGeneratedCommandsIterator.Reset(); + mPreviouslyFoundCluster = std::nullopt; + } + /// Generic model implementations - CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; } + CHIP_ERROR Shutdown() override + { + Reset(); + return CHIP_NO_ERROR; + } DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request, AttributeValueEncoder & encoder) override; diff --git a/src/controller/tests/TestEventChunking.cpp b/src/controller/tests/TestEventChunking.cpp index 60eaf037ada087..6185dff5a94dfa 100644 --- a/src/controller/tests/TestEventChunking.cpp +++ b/src/controller/tests/TestEventChunking.cpp @@ -18,19 +18,19 @@ #include -#include "app-common/zap-generated/ids/Attributes.h" -#include "app-common/zap-generated/ids/Clusters.h" -#include "app/ConcreteAttributePath.h" -#include "protocols/interaction_model/Constants.h" #include +#include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -42,6 +42,7 @@ #include #include #include +#include using namespace chip; using namespace chip::app; @@ -293,6 +294,7 @@ TEST_F(TestEventChunking, TestEventChunking) app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); // Initialize the ember side server logic + CodegenDataModelProviderInstance()->Shutdown(); InitDataModelHandler(); // Register our fake dynamic endpoint. @@ -359,6 +361,7 @@ TEST_F(TestEventChunking, TestMixedEventsAndAttributesChunking) app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); // Initialize the ember side server logic + CodegenDataModelProviderInstance()->Shutdown(); InitDataModelHandler(); // Register our fake dynamic endpoint. @@ -435,6 +438,7 @@ TEST_F(TestEventChunking, TestMixedEventsAndLargeAttributesChunking) app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); // Initialize the ember side server logic + CodegenDataModelProviderInstance()->Shutdown(); InitDataModelHandler(); // Register our fake dynamic endpoint.