Separate out `AttributePathExpandIterator::Position` (#36980)
* Copied over the new AttributePathExpandIterator and will incrementally use it (so I can validate tests)
* Rename AttributePathExpandIterator to legacy
* Prepare for using new style iterators ... checking NOT YET enabled though
* Enabled checks ... and unit tests fail, but this now can be debugged
* Fix some of the underlying bugs: read handling logic assumes we are ok to undo
* Unit tests pass now
* Restyle
* Use new iterator in IME
* Update logic to use the new iterator on testRead
* more updates
* Restyle
* Remove the legacy attribute path expand iterator
* Update naming
* Restyle
* Remove extra argument for ReadHandler constructor
* Restyle
* Slight flash improvement
* Fix up includes
* Removed empty line
* added comment on why state is a friend class
* Comment updates
* Restyle, add some comments and add extra checks on validity check only for expansion. This saves a tiny amount of flash (32 bytes)
* Remove an include
* Comment updates, renamed mLastOutputPath to mOutputPath
* Fix one typo
* Re-arrange members of ReadHandler to optimize for memory layout. This saves 8 bytes for struct. We still have a 20-byte padding which I am unsure how to get rid of
* Restyle
* Rename State to Position
* One more rename
* Remove redundant assigment ...we are at a net 0 txt increase now on qpg
* Add more unit tests for non-obvious requirement that wildcard expansion checks path validity, however non-wildcard does not check it
* Update src/app/AttributePathExpandIterator.cpp
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
* Update src/app/AttributePathExpandIterator.h
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
* Update src/app/AttributePathExpandIterator.h
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
* Update src/app/AttributePathExpandIterator.h
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
* Update src/app/ReadHandler.h
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
* Update src/app/ReadHandler.cpp
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
* Update src/app/AttributePathExpandIterator.h
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
* Use different values for the cluster ids for testing
* One more state to position change
* mExpanded is now set during output path returning. Removed 2 more sets to save another tinier amount of .text
* Remove some tests that seem redundant, keep only one
* Update src/app/AttributePathExpandIterator.cpp
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update src/app/AttributePathExpandIterator.cpp
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update src/app/AttributePathExpandIterator.cpp
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update src/app/AttributePathExpandIterator.cpp
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update src/app/InteractionModelEngine.cpp
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update src/app/ReadHandler.h
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update src/app/AttributePathExpandIterator.h
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update src/app/ReadHandler.h
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Use mCompletePosition
* Another rename
* Undo submodule update
* Restyle
* Update comment text to not sound like graph parsing
* Rename method to be more descriptive
* Update peek attribute iterator to rollback and update code logic a bit. Hoping for cleaner code
---------
Co-authored-by: Andrei Litvin <andreilitvin@google.com>
Co-authored-by: Tennessee Carmel-Veilleux <tennessee.carmelveilleux@gmail.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/src/app/AttributePathExpandIterator.cpp b/src/app/AttributePathExpandIterator.cpp
index 419067d..23273c7 100644
--- a/src/app/AttributePathExpandIterator.cpp
+++ b/src/app/AttributePathExpandIterator.cpp
@@ -19,22 +19,86 @@
#include <app/GlobalAttributes.h>
#include <lib/support/CodeUtils.h>
+#include <optional>
+
using namespace chip::app::DataModel;
namespace chip {
namespace app {
-AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * provider,
- SingleLinkedListNode<AttributePathParams> * attributePath) :
- mDataModelProvider(provider),
- mpAttributePath(attributePath), mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
-
+bool AttributePathExpandIterator::AdvanceOutputPath()
{
- mOutputPath.mExpanded = true; // this is reset in 'next' if needed
+ /// Output path invariants
+ /// - kInvalid* constants are used to define "no value available (yet)" and
+ /// iteration loop will fill the first value when such a value is seen (fixed for non-wildcard
+ /// or iteration-based in case of wildcards).
+ /// - Iteration of the output path is done in order: first endpoint, then cluster, then attribute.
+ /// Processing works like:
+ /// - Initial state is kInvalidEndpointId/kInvalidClusterId/kInvalidAttributeId
+ /// - First loop pass fills-in endpointID, followed by clusterID, followed by attributeID
+ /// - Whenever one level is done iterating (there is no "next") the following
+ /// "higher path component" is updated:
+ /// - once a valid path exists, try to advance attributeID
+ /// - if attributeID fails to advance, try to advance clusterID (and restart attributeID)
+ /// - if clusterID fails to advance, try to advance endpointID (and restart clusterID)
+ /// - if endpointID fails to advance, iteration is done
+ while (true)
+ {
+ if (mPosition.mOutputPath.mClusterId != kInvalidClusterId)
+ {
+ std::optional<AttributeId> nextAttribute = NextAttributeId();
+ if (nextAttribute.has_value())
+ {
+ mPosition.mOutputPath.mAttributeId = *nextAttribute;
+ mPosition.mOutputPath.mExpanded = mPosition.mAttributePath->mValue.IsWildcardPath();
+ return true;
+ }
+ }
- // Make the iterator ready to emit the first valid path in the list.
- // TODO: the bool return value here is completely unchecked
- Next();
+ // no valid attribute, try to advance the cluster, see if a suitable one exists
+ if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId)
+ {
+ std::optional<ClusterId> nextCluster = NextClusterId();
+ if (nextCluster.has_value())
+ {
+ // A new cluster ID is to be processed. This sets the cluster ID to the new value and
+ // ALSO resets the attribute ID to "invalid", to trigger an attribute set/expansion from
+ // the beginning.
+ mPosition.mOutputPath.mClusterId = *nextCluster;
+ mPosition.mOutputPath.mAttributeId = kInvalidAttributeId;
+ continue;
+ }
+ }
+
+ // No valid cluster, try advance the endpoint, see if a suitable one exists.
+ std::optional<EndpointId> nextEndpoint = NextEndpointId();
+ if (nextEndpoint.has_value())
+ {
+ // A new endpoint ID is to be processed. This sets the endpoint ID to the new value and
+ // ALSO resets the cluster ID to "invalid", to trigger a cluster set/expansion from
+ // the beginning.
+ mPosition.mOutputPath.mEndpointId = *nextEndpoint;
+ mPosition.mOutputPath.mClusterId = kInvalidClusterId;
+ continue;
+ }
+ return false;
+ }
+}
+
+bool AttributePathExpandIterator::Next(ConcreteAttributePath & path)
+{
+ while (mPosition.mAttributePath != nullptr)
+ {
+ if (AdvanceOutputPath())
+ {
+ path = mPosition.mOutputPath;
+ return true;
+ }
+ mPosition.mAttributePath = mPosition.mAttributePath->mpNext;
+ mPosition.mOutputPath = ConcreteReadAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId);
+ }
+
+ return false;
}
bool AttributePathExpandIterator::IsValidAttributeId(AttributeId attributeId)
@@ -49,40 +113,43 @@
break;
}
- const ConcreteAttributePath attributePath(mOutputPath.mEndpointId, mOutputPath.mClusterId, attributeId);
+ const ConcreteAttributePath attributePath(mPosition.mOutputPath.mEndpointId, mPosition.mOutputPath.mClusterId, attributeId);
return mDataModelProvider->GetAttributeInfo(attributePath).has_value();
}
std::optional<AttributeId> AttributePathExpandIterator::NextAttributeId()
{
- if (mOutputPath.mAttributeId == kInvalidAttributeId)
+ if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId)
{
- if (mpAttributePath->mValue.HasWildcardAttributeId())
+ if (mPosition.mAttributePath->mValue.HasWildcardAttributeId())
{
- AttributeEntry entry = mDataModelProvider->FirstAttribute(mOutputPath);
+ AttributeEntry entry = mDataModelProvider->FirstAttribute(mPosition.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))
+ // At this point, the attributeID is NOT a wildcard (i.e. it is fixed).
+ //
+ // For wildcard expansion, we validate that this is a valid attribute for the given
+ // cluster on the given endpoint. If not a wildcard expansion, return it as-is.
+ if (mPosition.mAttributePath->mValue.IsWildcardPath())
{
- return mpAttributePath->mValue.mAttributeId;
+ if (!IsValidAttributeId(mPosition.mAttributePath->mValue.mAttributeId))
+ {
+ return std::nullopt;
+ }
}
-
- return std::nullopt;
+ return mPosition.mAttributePath->mValue.mAttributeId;
}
- // advance the existing attribute id if it can be advanced
- VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardAttributeId(), std::nullopt);
+ // Advance the existing attribute id if it can be advanced.
+ VerifyOrReturnValue(mPosition.mAttributePath->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)
+ if (GlobalAttributesNotInMetadata[i] != mPosition.mOutputPath.mAttributeId)
{
continue;
}
@@ -93,11 +160,12 @@
return GlobalAttributesNotInMetadata[nextAttributeIndex];
}
- // reached the end of global attributes
+ // Reached the end of global attributes. Since global attributes are
+ // reported last, finishing global attributes means everything completed.
return std::nullopt;
}
- AttributeEntry entry = mDataModelProvider->NextAttribute(mOutputPath);
+ AttributeEntry entry = mDataModelProvider->NextAttribute(mPosition.mOutputPath);
if (entry.IsValid())
{
return entry.path.mAttributeId;
@@ -111,130 +179,55 @@
std::optional<ClusterId> AttributePathExpandIterator::NextClusterId()
{
- if (mOutputPath.mClusterId == kInvalidClusterId)
+ if (mPosition.mOutputPath.mClusterId == kInvalidClusterId)
{
- if (mpAttributePath->mValue.HasWildcardClusterId())
+ if (mPosition.mAttributePath->mValue.HasWildcardClusterId())
{
- ClusterEntry entry = mDataModelProvider->FirstServerCluster(mOutputPath.mEndpointId);
+ ClusterEntry entry = mDataModelProvider->FirstServerCluster(mPosition.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 (!mDataModelProvider->GetServerClusterInfo(clusterPath).has_value())
+ // At this point, the clusterID is NOT a wildcard (i.e. is fixed).
+ //
+ // For wildcard expansion, we validate that this is a valid cluster for the endpoint.
+ // If non-wildcard expansion, we return as-is.
+ if (mPosition.mAttributePath->mValue.IsWildcardPath())
{
- return std::nullopt;
+ const ConcreteClusterPath clusterPath(mPosition.mOutputPath.mEndpointId, mPosition.mAttributePath->mValue.mClusterId);
+ if (!mDataModelProvider->GetServerClusterInfo(clusterPath).has_value())
+ {
+ return std::nullopt;
+ }
}
- return mpAttributePath->mValue.mClusterId;
+ return mPosition.mAttributePath->mValue.mClusterId;
}
- VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
+ VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardClusterId(), std::nullopt);
- ClusterEntry entry = mDataModelProvider->NextServerCluster(mOutputPath);
+ ClusterEntry entry = mDataModelProvider->NextServerCluster(mPosition.mOutputPath);
return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt;
}
std::optional<ClusterId> AttributePathExpandIterator::NextEndpointId()
{
- if (mOutputPath.mEndpointId == kInvalidEndpointId)
+ if (mPosition.mOutputPath.mEndpointId == kInvalidEndpointId)
{
- if (mpAttributePath->mValue.HasWildcardEndpointId())
+ if (mPosition.mAttributePath->mValue.HasWildcardEndpointId())
{
EndpointEntry ep = mDataModelProvider->FirstEndpoint();
return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt;
}
- return mpAttributePath->mValue.mEndpointId;
+ return mPosition.mAttributePath->mValue.mEndpointId;
}
- VerifyOrReturnValue(mpAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
+ // Expand endpoints only if it is a wildcard on the endpoint specifically.
+ VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardEndpointId(), std::nullopt);
- EndpointEntry ep = mDataModelProvider->NextEndpoint(mOutputPath.mEndpointId);
+ EndpointEntry ep = mDataModelProvider->NextEndpoint(mPosition.mOutputPath.mEndpointId);
return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt;
}
-void AttributePathExpandIterator::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 AttributePathExpandIterator::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<AttributeId> 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<ClusterId> 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<EndpointId> nextEndpoint = NextEndpointId();
- if (nextEndpoint.has_value())
- {
- mOutputPath.mEndpointId = *nextEndpoint;
- mOutputPath.mClusterId = kInvalidClusterId; // restarts clusters
- continue;
- }
- return false;
- }
-}
-
-bool AttributePathExpandIterator::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.h b/src/app/AttributePathExpandIterator.h
index 351520f..2916a2e 100644
--- a/src/app/AttributePathExpandIterator.h
+++ b/src/app/AttributePathExpandIterator.h
@@ -20,81 +20,104 @@
#include <app/AttributePathParams.h>
#include <app/ConcreteAttributePath.h>
#include <app/data-model-provider/Provider.h>
+#include <lib/core/DataModelTypes.h>
#include <lib/support/LinkedList.h>
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 remain 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: 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.
- */
+/// Handles attribute path expansions
+/// Usage:
+///
+/// - Start iterating by creating an iteration state
+///
+/// AttributePathExpandIterator::Position position = AttributePathExpandIterator::Position::StartIterating(path);
+///
+/// - Use the iteration state in a for loop:
+///
+/// ConcreteAttributePath path;
+/// for (AttributePathExpandIterator iterator(position); iterator->Next(path);) {
+/// // use `path` here`
+/// }
+///
+/// OR:
+///
+/// ConcreteAttributePath path;
+/// AttributePathExpandIterator iterator(position);
+///
+/// while (iterator.Next(path)) {
+/// // use `path` here`
+/// }
+///
+/// Usage requirements and assumptions:
+///
+/// - An ` AttributePathExpandIterator::Position` can only be used by a single AttributePathExpandIterator at a time.
+///
+/// - `position` is automatically updated by the AttributePathExpandIterator, so
+/// calling `Next` on the iterator will update the position cursor variable.
+///
class AttributePathExpandIterator
{
public:
- AttributePathExpandIterator(DataModel::Provider * provider, SingleLinkedListNode<AttributePathParams> * 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)
+ class Position
{
- aPath = mOutputPath;
- return (mpAttributePath != nullptr);
- }
+ public:
+ // Position is treated as a direct member access by the AttributePathExpandIterator, however it is opaque (except copying)
+ // for external code. We allow friendship here to not have specific get/set for methods (clearer interface and less
+ // likelihood of extra code usage).
+ friend class AttributePathExpandIterator;
- /**
- * 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();
+ /// External callers can only ever start iterating on a new path from the beginning
+ static Position StartIterating(SingleLinkedListNode<AttributePathParams> * path) { return Position(path); }
- /** Start iterating over the given `paths` */
- inline void ResetTo(SingleLinkedListNode<AttributePathParams> * paths)
- {
- *this = AttributePathExpandIterator(mDataModelProvider, paths);
- }
+ /// Copies are allowed
+ Position(const Position &) = default;
+ Position & operator=(const Position &) = default;
+
+ Position() : mAttributePath(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 IterateFromTheStartOfTheCurrentClusterIfAttributeWildcard()
+ {
+ VerifyOrReturn(mAttributePath != nullptr && mAttributePath->mValue.HasWildcardAttributeId());
+ mOutputPath.mAttributeId = kInvalidAttributeId;
+ }
+
+ protected:
+ Position(SingleLinkedListNode<AttributePathParams> * path) :
+ mAttributePath(path), mOutputPath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)
+ {}
+
+ SingleLinkedListNode<AttributePathParams> * mAttributePath;
+ ConcreteAttributePath mOutputPath;
+ };
+
+ AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position) :
+ mDataModelProvider(dataModel), mPosition(position)
+ {}
+
+ // This class may not be copied. A new one should be created when needed and they
+ // should not overlap.
+ AttributePathExpandIterator(const AttributePathExpandIterator &) = delete;
+ AttributePathExpandIterator & operator=(const AttributePathExpandIterator &) = delete;
+
+ /// Get the next path of the expansion (if one exists).
+ ///
+ /// On success, true is returned and `path` is filled with the next path in the
+ /// expansion.
+ /// On iteration completion, false is returned and the content of path IS NOT DEFINED.
+ bool Next(ConcreteAttributePath & path);
private:
DataModel::Provider * mDataModelProvider;
- SingleLinkedListNode<AttributePathParams> * mpAttributePath;
- ConcreteAttributePath mOutputPath;
+ Position & mPosition;
/// Move to the next endpoint/cluster/attribute triplet that is valid given
- /// the current mOutputPath and mpAttributePath
+ /// the current mOutputPath and mpAttributePath.
///
/// returns true if such a next value was found.
bool AdvanceOutputPath();
@@ -125,5 +148,52 @@
bool IsValidAttributeId(AttributeId attributeId);
};
+/// RollbackAttributePathExpandIterator is an AttributePathExpandIterator wrapper that rolls back the Next()
+/// call whenever a new `MarkCompleted()` method is not called.
+///
+/// Example use cases:
+///
+/// - Iterate over all attributes and process one-by-one, however when the iteration fails, resume at
+/// the last failure point:
+///
+/// RollbackAttributePathExpandIterator iterator(....);
+/// ConcreteAttributePath path;
+///
+/// for ( ; iterator.Next(path); iterator.MarkCompleted()) {
+/// if (!CanProcess(path)) {
+/// // iterator state IS PRESERVED so that Next() will return the SAME path on the next call.
+/// return CHIP_ERROR_TRY_AGAIN_LATER;
+/// }
+/// }
+///
+/// - Grab what the next output path would be WITHOUT advancing a state;
+///
+/// {
+/// RollbackAttributePathExpandIterator iterator(...., state);
+/// if (iterator.Next(...)) { ... }
+/// }
+/// // state here is ROLLED BACK (i.e. initializing a new iterator with it will start at the same place as the previous
+/// iteration attempt).
+///
+///
+class RollbackAttributePathExpandIterator
+{
+public:
+ RollbackAttributePathExpandIterator(DataModel::Provider * dataModel, AttributePathExpandIterator::Position & position) :
+ mAttributePathExpandIterator(dataModel, position), mPositionTarget(position), mCompletedPosition(position)
+ {}
+ ~RollbackAttributePathExpandIterator() { mPositionTarget = mCompletedPosition; }
+
+ bool Next(ConcreteAttributePath & path) { return mAttributePathExpandIterator.Next(path); }
+
+ /// Marks the current iteration completed (so peek does not actually roll back)
+ void MarkCompleted() { mCompletedPosition = mPositionTarget; }
+
+private:
+ AttributePathExpandIterator mAttributePathExpandIterator;
+ AttributePathExpandIterator::Position & mPositionTarget;
+ AttributePathExpandIterator::Position mCompletedPosition;
+};
+
} // namespace app
} // namespace chip
diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn
index 1ee02cd..3fb0795 100644
--- a/src/app/BUILD.gn
+++ b/src/app/BUILD.gn
@@ -207,6 +207,7 @@
]
deps = [
+ ":path-expansion",
"${chip_root}/src/app:events",
"${chip_root}/src/app:global-attributes",
]
@@ -406,6 +407,21 @@
]
}
+source_set("path-expansion") {
+ sources = [
+ "AttributePathExpandIterator.cpp",
+ "AttributePathExpandIterator.h",
+ ]
+
+ public_deps = [
+ ":global-attributes",
+ ":paths",
+ "${chip_root}/src/app/data-model-provider",
+ "${chip_root}/src/lib/core:types",
+ "${chip_root}/src/lib/support",
+ ]
+}
+
source_set("attribute-persistence") {
sources = [
"DefaultSafeAttributePersistenceProvider.h",
@@ -429,9 +445,6 @@
output_name = "libCHIPDataModel"
sources = [
- "AttributePathExpandIterator.cpp",
- "AttributePathExpandIterator.h",
- "AttributePathExpandIterator.h",
"ChunkedWriteCallback.cpp",
"ChunkedWriteCallback.h",
"CommandResponseHelper.h",
@@ -464,6 +477,7 @@
":event-reporter",
":global-attributes",
":interaction-model",
+ ":path-expansion",
"${chip_root}/src/app/data-model",
"${chip_root}/src/app/data-model-provider",
"${chip_root}/src/app/icd/server:icd-server-config",
diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp
index 2fafe37..d4afe33 100644
--- a/src/app/InteractionModelEngine.cpp
+++ b/src/app/InteractionModelEngine.cpp
@@ -582,12 +582,14 @@
if (paramsList.mValue.IsWildcardPath())
{
- AttributePathExpandIterator pathIterator(GetDataModelProvider(), ¶msList);
+
+ auto state = AttributePathExpandIterator::Position::StartIterating(¶msList);
+ AttributePathExpandIterator pathIterator(GetDataModelProvider(), state);
ConcreteAttributePath readPath;
// The definition of "valid path" is "path exists and ACL allows access". The "path exists" part is handled by
// AttributePathExpandIterator. So we just need to check the ACL bits.
- for (; pathIterator.Get(readPath); pathIterator.Next())
+ while (pathIterator.Next(readPath))
{
// leave requestPath.entityId optional value unset to indicate wildcard
Access::RequestPath requestPath{ .cluster = readPath.mClusterId,
@@ -846,8 +848,7 @@
// 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, GetDataModelProvider());
+ ReadHandler * handler = mReadHandlers.CreateObject(*this, apExchangeContext, aInteractionType, mReportScheduler);
if (handler == nullptr)
{
ChipLogProgress(InteractionModel, "no resource for %s interaction",
diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp
index 43f9b93..dbde46b 100644
--- a/src/app/ReadHandler.cpp
+++ b/src/app/ReadHandler.cpp
@@ -15,14 +15,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-/**
- * @file
- * This file defines read handler for a CHIP Interaction Data model
- *
- */
-
#include <app/AppConfig.h>
+#include <app/AttributePathExpandIterator.h>
#include <app/InteractionModelEngine.h>
#include <app/MessageDef/EventPathIB.h>
#include <app/MessageDef/StatusResponseMessage.h>
@@ -31,6 +25,7 @@
#include <app/data-model-provider/Provider.h>
#include <app/icd/server/ICDServerConfig.h>
#include <lib/core/TLVUtilities.h>
+#include <lib/support/CodeUtils.h>
#include <messaging/ExchangeContext.h>
#include <app/ReadHandler.h>
@@ -54,9 +49,9 @@
}
ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext,
- InteractionType aInteractionType, Observer * observer, DataModel::Provider * apDataModel) :
- mAttributePathExpandIterator(apDataModel, nullptr),
- mExchangeCtx(*this), mManagementCallback(apCallback)
+ InteractionType aInteractionType, Observer * observer) :
+ mExchangeCtx(*this),
+ mManagementCallback(apCallback)
{
VerifyOrDie(apExchangeContext != nullptr);
@@ -80,8 +75,8 @@
}
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
-ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer, DataModel::Provider * apDataModel) :
- mAttributePathExpandIterator(apDataModel, nullptr), mExchangeCtx(*this), mManagementCallback(apCallback)
+ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer) :
+ mExchangeCtx(*this), mManagementCallback(apCallback)
{
mInteractionType = InteractionType::Subscribe;
mFlags.ClearAll();
@@ -511,8 +506,8 @@
if (CHIP_END_OF_TLV == err)
{
mManagementCallback.GetInteractionModelEngine()->RemoveDuplicateConcreteAttributePath(mpAttributePathList);
- mAttributePathExpandIterator.ResetTo(mpAttributePathList);
- err = CHIP_NO_ERROR;
+ mAttributePathExpandPosition = AttributePathExpandIterator::Position::StartIterating(mpAttributePathList);
+ err = CHIP_NO_ERROR;
}
return err;
}
@@ -854,16 +849,18 @@
void ReadHandler::ResetPathIterator()
{
- mAttributePathExpandIterator.ResetTo(mpAttributePathList);
+ mAttributePathExpandPosition = AttributePathExpandIterator::Position::StartIterating(mpAttributePathList);
mAttributeEncoderState.Reset();
}
-void ReadHandler::AttributePathIsDirty(const AttributePathParams & aAttributeChanged)
+void ReadHandler::AttributePathIsDirty(DataModel::Provider * apDataModel, const AttributePathParams & aAttributeChanged)
{
- ConcreteAttributePath path;
-
mDirtyGeneration = mManagementCallback.GetInteractionModelEngine()->GetReportingEngine().GetDirtySetGeneration();
+ // We want to get the value, but not advance the iterator position.
+ AttributePathExpandIterator::Position tempPosition = mAttributePathExpandPosition;
+ ConcreteAttributePath path;
+
// We won't reset the path iterator for every AttributePathIsDirty call to reduce the number of full data reports.
// The iterator will be reset after finishing each report session.
//
@@ -873,7 +870,7 @@
// TODO (#16699): Currently we can only guarantee the reports generated from a single path in the request are consistent. The
// data might be inconsistent if the user send a request with two paths from the same cluster. We need to clearify the behavior
// or make it consistent.
- if (mAttributePathExpandIterator.Get(path) &&
+ if (AttributePathExpandIterator(apDataModel, tempPosition).Next(path) &&
(aAttributeChanged.HasWildcardEndpointId() || aAttributeChanged.mEndpointId == path.mEndpointId) &&
(aAttributeChanged.HasWildcardClusterId() || aAttributeChanged.mClusterId == path.mClusterId))
{
@@ -883,7 +880,8 @@
// If we're currently in the middle of generating reports for a given cluster and that in turn is marked dirty, let's reset
// our iterator to point back to the beginning of that cluster. This ensures that the receiver will get a coherent view of
// the state of the cluster as present on the server
- mAttributePathExpandIterator.ResetCurrentCluster();
+ mAttributePathExpandPosition.IterateFromTheStartOfTheCurrentClusterIfAttributeWildcard();
+
mAttributeEncoderState.Reset();
}
diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h
index 85976ec..d5b7ad2 100644
--- a/src/app/ReadHandler.h
+++ b/src/app/ReadHandler.h
@@ -212,7 +212,7 @@
*
*/
ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType,
- Observer * observer, DataModel::Provider * apDataModel);
+ Observer * observer);
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
/**
@@ -222,7 +222,7 @@
* The callback passed in has to outlive this handler object.
*
*/
- ReadHandler(ManagementCallback & apCallback, Observer * observer, DataModel::Provider * apDataModel);
+ ReadHandler(ManagementCallback & apCallback, Observer * observer);
#endif
const SingleLinkedListNode<AttributePathParams> * GetAttributePathList() const { return mpAttributePathList; }
@@ -407,12 +407,12 @@
bool IsFabricFiltered() const { return mFlags.Has(ReadHandlerFlags::FabricFiltered); }
CHIP_ERROR OnSubscribeRequest(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload);
void GetSubscriptionId(SubscriptionId & aSubscriptionId) const { aSubscriptionId = mSubscriptionId; }
- AttributePathExpandIterator * GetAttributePathExpandIterator() { return &mAttributePathExpandIterator; }
+ AttributePathExpandIterator::Position & AttributeIterationPosition() { return mAttributePathExpandPosition; }
/// @brief Notifies the read handler that a set of attribute paths has been marked dirty. This will schedule a reporting engine
/// run if the change to the attribute path makes the ReadHandler reportable.
/// @param aAttributeChanged Path to the attribute that was changed.
- void AttributePathIsDirty(const AttributePathParams & aAttributeChanged);
+ void AttributePathIsDirty(DataModel::Provider * apDataModel, const AttributePathParams & aAttributeChanged);
bool IsDirty() const
{
return (mDirtyGeneration > mPreviousReportsBeginGeneration) || mFlags.Has(ReadHandlerFlags::ForceDirty);
@@ -519,7 +519,7 @@
/// @param aFlag Flag to clear
void ClearStateFlag(ReadHandlerFlags aFlag);
- AttributePathExpandIterator mAttributePathExpandIterator;
+ SubscriptionId mSubscriptionId = 0;
// 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.
@@ -561,18 +561,13 @@
// engine, the "oldest" subscription is the subscription with the smallest generation.
uint64_t mTransactionStartGeneration = 0;
- SubscriptionId mSubscriptionId = 0;
- uint16_t mMinIntervalFloorSeconds = 0;
- uint16_t mMaxInterval = 0;
- uint16_t mSubscriberRequestedMaxInterval = 0;
-
EventNumber mEventMin = 0;
// The last schedule event number snapshoted in the beginning when preparing to fill new events to reports
EventNumber mLastScheduledEventNumber = 0;
- // TODO: We should shutdown the transaction when the session expires.
- SessionHolder mSessionHandle;
+ /// Iterator position state for any ongoing path expansion for handling wildcard reads/subscriptions.
+ AttributePathExpandIterator::Position mAttributePathExpandPosition;
Messaging::ExchangeHolder mExchangeCtx;
#if CHIP_CONFIG_UNSAFE_SUBSCRIPTION_EXCHANGE_MANAGER_USE
@@ -587,20 +582,26 @@
ManagementCallback & mManagementCallback;
+ // TODO (#27675): Merge all observers into one and that one will dispatch the callbacks to the right place.
+ Observer * mObserver = nullptr;
+
uint32_t mLastWrittenEventsBytes = 0;
// The detailed encoding state for a single attribute, used by list chunking feature.
// The size of AttributeEncoderState is 2 bytes for now.
AttributeEncodeState mAttributeEncoderState;
+ uint16_t mMinIntervalFloorSeconds = 0;
+ uint16_t mMaxInterval = 0;
+ uint16_t mSubscriberRequestedMaxInterval = 0;
+
// Current Handler state
HandlerState mState = HandlerState::Idle;
PriorityLevel mCurrentPriority = PriorityLevel::Invalid;
BitFlags<ReadHandlerFlags> mFlags;
InteractionType mInteractionType = InteractionType::Read;
- // TODO (#27675): Merge all observers into one and that one will dispatch the callbacks to the right place.
- Observer * mObserver = nullptr;
+ SessionHolder mSessionHandle;
};
} // namespace app
diff --git a/src/app/SubscriptionResumptionSessionEstablisher.cpp b/src/app/SubscriptionResumptionSessionEstablisher.cpp
index f9ea25c..6e01668 100644
--- a/src/app/SubscriptionResumptionSessionEstablisher.cpp
+++ b/src/app/SubscriptionResumptionSessionEstablisher.cpp
@@ -103,8 +103,7 @@
ChipLogProgress(InteractionModel, "no resource for subscription resumption");
return;
}
- ReadHandler * readHandler =
- imEngine->mReadHandlers.CreateObject(*imEngine, imEngine->GetReportScheduler(), imEngine->GetDataModelProvider());
+ ReadHandler * readHandler = imEngine->mReadHandlers.CreateObject(*imEngine, imEngine->GetReportScheduler());
if (readHandler == nullptr)
{
// TODO - Should we keep the subscription here?
diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp
index a9cef5c..ed23d44 100644
--- a/src/app/reporting/Engine.cpp
+++ b/src/app/reporting/Engine.cpp
@@ -19,6 +19,7 @@
#include <access/AccessRestrictionProvider.h>
#include <access/Privilege.h>
#include <app/AppConfig.h>
+#include <app/AttributePathExpandIterator.h>
#include <app/ConcreteEventPath.h>
#include <app/GlobalAttributes.h>
#include <app/InteractionModelEngine.h>
@@ -315,8 +316,9 @@
#endif
// For each path included in the interested path of the read handler...
- for (; apReadHandler->GetAttributePathExpandIterator()->Get(readPath);
- apReadHandler->GetAttributePathExpandIterator()->Next())
+ for (RollbackAttributePathExpandIterator iterator(mpImEngine->GetDataModelProvider(),
+ apReadHandler->AttributeIterationPosition());
+ iterator.Next(readPath); iterator.MarkCompleted())
{
if (!apReadHandler->IsPriming())
{
@@ -428,6 +430,7 @@
// Successfully encoded the attribute, clear the internal state.
apReadHandler->SetAttributeEncodeState(AttributeEncodeState());
}
+
// We just visited all paths interested by this read handler and did not abort in the middle of iteration, there are no more
// chunks for this report.
hasMoreChunks = false;
@@ -1042,8 +1045,9 @@
{
BumpDirtySetGeneration();
- bool intersectsInterestPath = false;
- mpImEngine->mReadHandlers.ForEachActiveObject([&aAttributePath, &intersectsInterestPath](ReadHandler * handler) {
+ bool intersectsInterestPath = false;
+ DataModel::Provider * dataModel = mpImEngine->GetDataModelProvider();
+ mpImEngine->mReadHandlers.ForEachActiveObject([&dataModel, &aAttributePath, &intersectsInterestPath](ReadHandler * handler) {
// We call AttributePathIsDirty for both read interactions and subscribe interactions, since we may send inconsistent
// attribute data between two chunks. AttributePathIsDirty will not schedule a new run for read handlers which are
// waiting for a response to the last message chunk for read interactions.
@@ -1053,7 +1057,7 @@
{
if (object->mValue.Intersects(aAttributePath))
{
- handler->AttributePathIsDirty(aAttributePath);
+ handler->AttributePathIsDirty(dataModel, aAttributePath);
intersectsInterestPath = true;
break;
}
diff --git a/src/app/tests/TestAttributePathExpandIterator.cpp b/src/app/tests/TestAttributePathExpandIterator.cpp
index 913b07e..4a6088a 100644
--- a/src/app/tests/TestAttributePathExpandIterator.cpp
+++ b/src/app/tests/TestAttributePathExpandIterator.cpp
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+#include "pw_unit_test/framework.h"
#include <app-common/zap-generated/ids/Attributes.h>
#include <app/AttributePathExpandIterator.h>
#include <app/ConcreteAttributePath.h>
@@ -106,9 +107,18 @@
size_t index = 0;
- for (app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), &clusInfo); iter.Get(path);
- iter.Next())
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+
+ while (true)
{
+ // re-create the iterator
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ if (!iter.Next(path))
+ {
+ break;
+ }
+
ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
EXPECT_LT(index, ArraySize(paths));
@@ -131,9 +141,16 @@
size_t index = 0;
- for (app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), &clusInfo); iter.Get(path);
- iter.Next())
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+ while (true)
{
+ // re-create the iterator
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ if (!iter.Next(path))
+ {
+ break;
+ }
ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
EXPECT_LT(index, ArraySize(paths));
@@ -159,9 +176,16 @@
size_t index = 0;
- for (app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), &clusInfo); iter.Get(path);
- iter.Next())
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+ while (true)
{
+ // re-create the iterator
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ if (!iter.Next(path))
+ {
+ break;
+ }
ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
EXPECT_LT(index, ArraySize(paths));
@@ -187,9 +211,17 @@
size_t index = 0;
- for (app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), &clusInfo); iter.Get(path);
- iter.Next())
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+
+ while (true)
{
+ // re-create the iterator
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ if (!iter.Next(path))
+ {
+ break;
+ }
ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
EXPECT_LT(index, ArraySize(paths));
@@ -219,9 +251,17 @@
size_t index = 0;
- for (app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), &clusInfo); iter.Get(path);
- iter.Next())
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+
+ while (true)
{
+ // re-create the iterator
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ if (!iter.Next(path))
+ {
+ break;
+ }
ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
EXPECT_LT(index, ArraySize(paths));
@@ -245,9 +285,16 @@
size_t index = 0;
- for (app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), &clusInfo); iter.Get(path);
- iter.Next())
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+ while (true)
{
+ // re-create the iterator
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ if (!iter.Next(path))
+ {
+ break;
+ }
ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
EXPECT_LT(index, ArraySize(paths));
@@ -257,6 +304,69 @@
EXPECT_EQ(index, ArraySize(paths));
}
+TEST(TestAttributePathExpandIterator, TestFixedPathExpansion)
+{
+ // expansion logic requires that:
+ // - paths for wildcard expansion ARE VALIDATED
+ // - path WITHOUT wildcard expansion ARE NOT VALIDATED
+
+ // invalid attribute across all clusters returns empty
+ {
+ SingleLinkedListNode<app::AttributePathParams> clusInfo;
+ clusInfo.mValue.mAttributeId = 122333;
+
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+ ConcreteAttributePath path;
+
+ EXPECT_FALSE(iter.Next(path));
+ }
+
+ // invalid cluster with a valid attribute (featuremap) returns empty
+ {
+ SingleLinkedListNode<app::AttributePathParams> clusInfo;
+ clusInfo.mValue.mClusterId = 122344;
+ clusInfo.mValue.mAttributeId = Clusters::Globals::Attributes::FeatureMap::Id;
+
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+ ConcreteAttributePath path;
+
+ EXPECT_FALSE(iter.Next(path));
+ }
+
+ // invalid cluster with wildcard attribute returns empty
+ {
+ SingleLinkedListNode<app::AttributePathParams> clusInfo;
+ clusInfo.mValue.mClusterId = 122333;
+
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+ ConcreteAttributePath path;
+
+ EXPECT_FALSE(iter.Next(path));
+ }
+
+ // even though all above WERE invalid, if we specify a non-wildcard path it is returned as-is
+ {
+ SingleLinkedListNode<app::AttributePathParams> clusInfo;
+ clusInfo.mValue.mEndpointId = 1;
+ clusInfo.mValue.mClusterId = 122344;
+ clusInfo.mValue.mAttributeId = 122333;
+
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo);
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+ ConcreteAttributePath path;
+
+ EXPECT_TRUE(iter.Next(path));
+ EXPECT_EQ(path.mEndpointId, clusInfo.mValue.mEndpointId);
+ EXPECT_EQ(path.mClusterId, clusInfo.mValue.mClusterId);
+ EXPECT_EQ(path.mAttributeId, clusInfo.mValue.mAttributeId);
+
+ EXPECT_FALSE(iter.Next(path));
+ }
+}
+
TEST(TestAttributePathExpandIterator, TestMultipleClusInfo)
{
@@ -358,18 +468,47 @@
{ kMockEndpoint2, MockClusterId(3), MockAttributeId(3) },
};
- size_t index = 0;
-
- for (app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), &clusInfo1);
- iter.Get(path); iter.Next())
+ // Test that a one-shot iterate through all works
{
- ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
- ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
- EXPECT_LT(index, ArraySize(paths));
- EXPECT_EQ(paths[index], path);
- index++;
+ size_t index = 0;
+
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo1);
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ while (iter.Next(path))
+ {
+ ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
+ ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
+ EXPECT_LT(index, ArraySize(paths));
+ EXPECT_EQ(paths[index], path);
+ index++;
+ }
+ EXPECT_EQ(index, ArraySize(paths));
}
- EXPECT_EQ(index, ArraySize(paths));
+
+ // identical test, however this checks that position re-use works
+ // the same as a one-shot iteration.
+ {
+ size_t index = 0;
+
+ auto position = AttributePathExpandIterator::Position::StartIterating(&clusInfo1);
+ while (true)
+ {
+ // re-create the iterator
+ app::AttributePathExpandIterator iter(CodegenDataModelProviderInstance(nullptr /* delegate */), position);
+
+ if (!iter.Next(path))
+ {
+ break;
+ }
+ ChipLogDetail(AppServer, "Visited Attribute: 0x%04X / " ChipLogFormatMEI " / " ChipLogFormatMEI, path.mEndpointId,
+ ChipLogValueMEI(path.mClusterId), ChipLogValueMEI(path.mAttributeId));
+ EXPECT_LT(index, ArraySize(paths));
+ EXPECT_EQ(paths[index], path);
+ index++;
+ }
+ EXPECT_EQ(index, ArraySize(paths));
+ }
}
} // namespace
diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp
index 87aa3e3..d09f564 100644
--- a/src/app/tests/TestInteractionModelEngine.cpp
+++ b/src/app/tests/TestInteractionModelEngine.cpp
@@ -271,8 +271,7 @@
// Create and setup readHandler 1
ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify that Bob still doesn't have an active subscription
EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
@@ -319,16 +318,14 @@
// Create readHandler 1
engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe,
- reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ reporting::GetDefaultReportScheduler());
// 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(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify that Bob still doesn't have an active subscription
EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
@@ -380,13 +377,11 @@
// Create and setup readHandler 1
ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Create and setup readHandler 2
ReadHandler * readHandler2 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify that Bob still doesn't have an active subscription
EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
@@ -463,23 +458,19 @@
// Create and setup readHandler 1-1
engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx11, ReadHandler::InteractionType::Subscribe,
- reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ reporting::GetDefaultReportScheduler());
// Create and setup readHandler 1-2
ReadHandler * readHandler12 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx12, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx12, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Create and setup readHandler 2-1
engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx21, ReadHandler::InteractionType::Subscribe,
- reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ reporting::GetDefaultReportScheduler());
// Create and setup readHandler 2-2
ReadHandler * readHandler22 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx22, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx22, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify that both Alice and Bob have no active subscriptions
EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId));
@@ -551,8 +542,7 @@
// Create readHandler
ReadHandler * readHandler = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify there are not active subscriptions
EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, valideSubjectId));
@@ -749,8 +739,7 @@
// Create and setup readHandler 1
ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify that fabric 1 still doesn't have an active subscription
EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1));
@@ -766,8 +755,7 @@
// Create and setup readHandler 2
ReadHandler * readHandler2 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Set readHandler2 to active
readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true);
@@ -805,8 +793,7 @@
// Create and setup readHandler 1
ReadHandler * readHandler1 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify that the fabric still doesn't have an active subscription
EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex));
@@ -819,8 +806,7 @@
// Create and setup readHandler 2
ReadHandler * readHandler2 = engine->GetReadHandlerPool().CreateObject(
- nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler());
// Verify that the fabric still has an active subscription
EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex));
diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp
index 21b15d1..b56db3e 100644
--- a/src/app/tests/TestReadInteraction.cpp
+++ b/src/app/tests/TestReadInteraction.cpp
@@ -565,8 +565,7 @@
{
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
- ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
GenerateReportData(reportDatabuf, ReportType::kValid, false /* aSuppressResponse*/);
EXPECT_EQ(readHandler.SendReportData(std::move(reportDatabuf), false), CHIP_ERROR_INCORRECT_STATE);
@@ -624,8 +623,7 @@
uint16_t maxInterval;
// Configure ReadHandler
- ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
writer.Init(std::move(subscribeRequestbuf));
EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR);
@@ -842,8 +840,7 @@
{
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
- ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
GenerateReportData(reportDatabuf, ReportType::kValid, false /* aSuppressResponse*/);
EXPECT_EQ(readHandler.SendReportData(std::move(reportDatabuf), false), CHIP_ERROR_INCORRECT_STATE);
@@ -1592,8 +1589,7 @@
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
{
- ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
writer.Init(std::move(subscribeRequestbuf));
EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR);
@@ -1654,8 +1650,7 @@
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
{
- ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
writer.Init(std::move(subscribeRequestbuf));
EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR);
@@ -1724,8 +1719,7 @@
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
{
- ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
writer.Init(std::move(subscribeRequestbuf));
EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR);
@@ -1794,8 +1788,7 @@
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
{
- ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
writer.Init(std::move(subscribeRequestbuf));
EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR);
@@ -1864,8 +1857,7 @@
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
{
- ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
writer.Init(std::move(subscribeRequestbuf));
EXPECT_EQ(subscribeRequestBuilder.Init(&writer), CHIP_NO_ERROR);
@@ -1932,8 +1924,7 @@
Messaging::ExchangeContext * exchangeCtx = NewExchangeToAlice(nullptr, false);
{
- ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler);
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 03dda96..7047dd6 100644
--- a/src/app/tests/TestReportScheduler.cpp
+++ b/src/app/tests/TestReportScheduler.cpp
@@ -276,8 +276,7 @@
for (size_t i = 0; i < kNumMaxReadHandlers; i++)
{
ReadHandler * readHandler =
- readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
sScheduler.OnSubscriptionEstablished(readHandler);
ASSERT_NE(nullptr, readHandler);
ASSERT_NE(nullptr, sScheduler.FindReadHandlerNode(readHandler));
@@ -341,22 +340,19 @@
// Dirty read handler, will be triggered at min interval
// Test OnReadHandler created
ReadHandler * readHandler1 =
- readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
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,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
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,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler3, &sScheduler, 0, 3));
// Confirms that none of the ReadHandlers are currently reportable
@@ -409,8 +405,8 @@
// Initialize mock timestamp
sTestTimerDelegate.SetMockSystemTimestamp(Milliseconds64(0));
- ReadHandler * readHandler = readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe,
- &sScheduler, CodegenDataModelProviderInstance(nullptr /* delegate */));
+ ReadHandler * readHandler =
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler);
EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler, &sScheduler, 1, 2));
@@ -486,15 +482,13 @@
sTestTimerSynchronizedDelegate.SetMockSystemTimestamp(System::Clock::Milliseconds64(0));
ReadHandler * readHandler1 =
- readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
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,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler2, &syncScheduler, 1, 3));
ReadHandlerNode * node2 = syncScheduler.FindReadHandlerNode(readHandler2);
@@ -622,8 +616,7 @@
sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000));
ReadHandler * readHandler3 =
- readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
EXPECT_EQ(CHIP_NO_ERROR, MockReadHandlerSubscriptionTransaction(readHandler3, &syncScheduler, 2, 3));
ReadHandlerNode * node3 = syncScheduler.FindReadHandlerNode(readHandler3);
@@ -677,8 +670,7 @@
// Now simulate a new readHandler being added with a max forcing a conflict
ReadHandler * readHandler4 =
- readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler,
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler);
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 60852e2..bfeb961 100644
--- a/src/app/tests/TestReportingEngine.cpp
+++ b/src/app/tests/TestReportingEngine.cpp
@@ -209,8 +209,7 @@
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(),
- CodegenDataModelProviderInstance(nullptr /* delegate */));
+ app::reporting::GetDefaultReportScheduler());
readHandler.OnInitialRequest(std::move(readRequestbuf));
EXPECT_EQ(InteractionModelEngine::GetInstance()->GetReportingEngine().BuildAndSendSingleReportData(&readHandler),