Skip to content

Commit 86c4366

Browse files
lpbeliveauCopilotrestyled-commits
authored
[Energy] EEM Accuracy Range Buffer Fix (project-chip#43398)
* Added a ReadOnlyBuffer for the accuracy ranges in the EEM cluster. * Update src/app/clusters/electrical-energy-measurement-server/tests/TestElectricalEnergyMeasurementClusterBackwardsCompatibility.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Modified logic to avoid taking actions on empty lists * Restyled by whitespace --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Restyled.io <commits@restyled.io>
1 parent 8310821 commit 86c4366

5 files changed

Lines changed: 173 additions & 26 deletions

File tree

src/app/clusters/electrical-energy-measurement-server/CodegenIntegration.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@
3131
#include <lib/support/logging/CHIPLogging.h>
3232
#include <zap-generated/gen_config.h>
3333

34-
namespace {
3534
using namespace chip;
3635
using namespace chip::app;
3736
using namespace chip::app::Clusters;
3837
using namespace chip::app::Clusters::ElectricalEnergyMeasurement;
38+
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Attributes;
39+
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs;
3940

41+
namespace {
4042
SingleLinkedListNode<ElectricalEnergyMeasurementCluster *> * EEMFirstInstance = nullptr;
4143

4244
inline void RegisterLegacyEEM(SingleLinkedListNode<ElectricalEnergyMeasurementCluster *> * inst)
@@ -70,27 +72,23 @@ inline void UnregisterLegacyEEM(SingleLinkedListNode<ElectricalEnergyMeasurement
7072
}
7173
}
7274

75+
// Default empty accuracy used at construction time; real values are set later via SetMeasurementAccuracy.
76+
const MeasurementAccuracyStruct::Type kDefaultAccuracy = {};
7377
} // namespace
7478

7579
namespace chip {
7680
namespace app {
7781
namespace Clusters {
7882
namespace ElectricalEnergyMeasurement {
7983

80-
using namespace chip;
81-
using namespace chip::app;
82-
using namespace chip::app::Clusters;
83-
using namespace chip::app::Clusters::ElectricalEnergyMeasurement;
84-
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Attributes;
85-
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs;
86-
8784
ElectricalEnergyMeasurementAttrAccess::ElectricalEnergyMeasurementAttrAccess(BitMask<Feature> aFeature,
8885
BitMask<OptionalAttributes> aOptionalAttrs,
8986
EndpointId endpointId) :
9087
mCluster(ElectricalEnergyMeasurementCluster::Config{
9188
.endpointId = endpointId,
9289
.featureFlags = aFeature,
9390
.optionalAttributes = aOptionalAttrs,
91+
.accuracyStruct = kDefaultAccuracy,
9492
})
9593
{
9694
mClusterListNode.mValue = &mCluster.Cluster();

src/app/clusters/electrical-energy-measurement-server/ElectricalEnergyMeasurementCluster.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,29 @@ CHIP_ERROR ElectricalEnergyMeasurementCluster::GetCumulativeEnergyImported(Optio
101101
return CHIP_NO_ERROR;
102102
}
103103

104+
CHIP_ERROR ElectricalEnergyMeasurementCluster::SetMeasurementAccuracy(const MeasurementAccuracyStruct & value)
105+
{
106+
if (ValueChanged(mMeasurementData.measurementAccuracy, value))
107+
{
108+
mMeasurementData.measurementAccuracy.measurementType = value.measurementType;
109+
mMeasurementData.measurementAccuracy.measured = value.measured;
110+
mMeasurementData.measurementAccuracy.minMeasuredValue = value.minMeasuredValue;
111+
mMeasurementData.measurementAccuracy.maxMeasuredValue = value.maxMeasuredValue;
112+
NotifyAttributeChanged(Accuracy::Id);
113+
}
114+
115+
// Always update ranges if they are present since ValueChanged intentionally skips comparing them
116+
if (!value.accuracyRanges.empty())
117+
{
118+
ReadOnlyBufferBuilder<MeasurementAccuracyRangeStruct::Type> rangesBuilder;
119+
ReturnErrorOnFailure(rangesBuilder.AppendElements(value.accuracyRanges));
120+
mAccuracyRangesStorage = rangesBuilder.TakeBuffer();
121+
mMeasurementData.measurementAccuracy.accuracyRanges = mAccuracyRangesStorage;
122+
}
123+
124+
return CHIP_NO_ERROR;
125+
}
126+
104127
CHIP_ERROR ElectricalEnergyMeasurementCluster::GetCumulativeEnergyExported(Optional<EnergyMeasurementStruct> & outValue) const
105128
{
106129
if (!mFeatureFlags.HasAll(ElectricalEnergyMeasurement::Feature::kCumulativeEnergy,
@@ -151,16 +174,6 @@ CHIP_ERROR ElectricalEnergyMeasurementCluster::GetCumulativeEnergyReset(Optional
151174
return CHIP_NO_ERROR;
152175
}
153176

154-
CHIP_ERROR ElectricalEnergyMeasurementCluster::SetMeasurementAccuracy(const MeasurementAccuracyStruct & value)
155-
{
156-
if (ValueChanged(mMeasurementData.measurementAccuracy, value))
157-
{
158-
mMeasurementData.measurementAccuracy = value;
159-
NotifyAttributeChanged(Accuracy::Id);
160-
}
161-
return CHIP_NO_ERROR;
162-
}
163-
164177
CHIP_ERROR ElectricalEnergyMeasurementCluster::SetCumulativeEnergyImported(const Optional<EnergyMeasurementStruct> & value)
165178
{
166179
if (!mFeatureFlags.HasAll(ElectricalEnergyMeasurement::Feature::kCumulativeEnergy,

src/app/clusters/electrical-energy-measurement-server/ElectricalEnergyMeasurementCluster.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <clusters/ElectricalEnergyMeasurement/ClusterId.h>
2424
#include <clusters/ElectricalEnergyMeasurement/Structs.h>
2525
#include <lib/core/Optional.h>
26+
#include <lib/support/ReadOnlyBuffer.h>
2627

2728
namespace chip {
2829
namespace app {
@@ -66,8 +67,12 @@ class ElectricalEnergyMeasurementCluster : public DefaultServerCluster
6667
EndpointId endpointId;
6768
BitMask<ElectricalEnergyMeasurement::Feature> featureFlags;
6869
BitMask<ElectricalEnergyMeasurement::OptionalAttributes> optionalAttributes;
70+
const ElectricalEnergyMeasurement::Structs::MeasurementAccuracyStruct::Type & accuracyStruct;
6971
};
7072

73+
/// @brief Constructor for ElectricalEnergyMeasurementCluster.
74+
/// @param config The configuration for the cluster.
75+
/// @note The accuracyStruct must outlive the cluster to avoid dangling pointers.
7176
ElectricalEnergyMeasurementCluster(const Config & config) :
7277
DefaultServerCluster({ config.endpointId, ElectricalEnergyMeasurement::Id }), mFeatureFlags(config.featureFlags),
7378
mEnabledOptionalAttributes([&]() {
@@ -86,7 +91,19 @@ class ElectricalEnergyMeasurementCluster : public DefaultServerCluster
8691
config.featureFlags.Has(ElectricalEnergyMeasurement::Feature::kCumulativeEnergy));
8792
return attrs;
8893
}())
89-
{}
94+
{
95+
mMeasurementData.measurementAccuracy.measurementType = config.accuracyStruct.measurementType;
96+
mMeasurementData.measurementAccuracy.measured = config.accuracyStruct.measured;
97+
mMeasurementData.measurementAccuracy.minMeasuredValue = config.accuracyStruct.minMeasuredValue;
98+
mMeasurementData.measurementAccuracy.maxMeasuredValue = config.accuracyStruct.maxMeasuredValue;
99+
100+
// ReferenceExisting: caller's accuracyRanges data must outlive the cluster
101+
using RangeType = ElectricalEnergyMeasurement::Structs::MeasurementAccuracyRangeStruct::Type;
102+
ReadOnlyBufferBuilder<RangeType> rangesBuilder;
103+
VerifyOrDie(rangesBuilder.ReferenceExisting(config.accuracyStruct.accuracyRanges) == CHIP_NO_ERROR);
104+
mAccuracyRangesStorage = rangesBuilder.TakeBuffer();
105+
mMeasurementData.measurementAccuracy.accuracyRanges = mAccuracyRangesStorage;
106+
}
90107

91108
const OptionalAttributesSet & OptionalAttributes() const { return mEnabledOptionalAttributes; }
92109
const BitFlags<ElectricalEnergyMeasurement::Feature> & Features() const { return mFeatureFlags; }
@@ -102,7 +119,10 @@ class ElectricalEnergyMeasurementCluster : public DefaultServerCluster
102119
CHIP_ERROR GetPeriodicEnergyExported(Optional<EnergyMeasurementStruct> & outValue) const;
103120
CHIP_ERROR GetCumulativeEnergyReset(Optional<CumulativeEnergyResetStruct> & outValue) const;
104121

105-
// Setters - update values and notify data model
122+
/// @brief Sets the measurement accuracy.
123+
/// @param value The new measurement accuracy value.
124+
/// @return CHIP_ERROR_NO_MEMORY if the deep copy of accuracyRanges fails to allocate.
125+
/// @note Use the constructor with Config::accuracyStruct for zero-copy reference to long-lived (e.g. flash) data.
106126
CHIP_ERROR SetMeasurementAccuracy(const MeasurementAccuracyStruct & value);
107127
CHIP_ERROR SetCumulativeEnergyReset(const Optional<CumulativeEnergyResetStruct> & value);
108128

@@ -127,6 +147,9 @@ class ElectricalEnergyMeasurementCluster : public DefaultServerCluster
127147
const BitFlags<ElectricalEnergyMeasurement::Feature> mFeatureFlags;
128148
const OptionalAttributesSet mEnabledOptionalAttributes;
129149
ElectricalEnergyMeasurement::MeasurementData mMeasurementData;
150+
151+
// Owns the accuracyRanges backing store; either references long-lived data (ReferenceExisting) or an allocated copy.
152+
ReadOnlyBuffer<ElectricalEnergyMeasurement::Structs::MeasurementAccuracyRangeStruct::Type> mAccuracyRangesStorage;
130153
};
131154

132155
} // namespace Clusters

src/app/clusters/electrical-energy-measurement-server/tests/TestElectricalEnergyMeasurementCluster.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ namespace {
3939

4040
constexpr EndpointId kTestEndpointId = 1;
4141

42+
const Structs::MeasurementAccuracyRangeStruct::Type kTestAccuracyRanges[] = {
43+
{ .rangeMin = 0,
44+
.rangeMax = 1'000'000'000'000'000, // 1 million Mwh
45+
.percentMax = MakeOptional(static_cast<chip::Percent100ths>(500)),
46+
.percentMin = MakeOptional(static_cast<chip::Percent100ths>(50)) }
47+
};
48+
49+
const Structs::MeasurementAccuracyStruct::Type kTestAccuracy = {
50+
.measurementType = MeasurementTypeEnum::kElectricalEnergy,
51+
.measured = true,
52+
.minMeasuredValue = 0,
53+
.maxMeasuredValue = 1'000'000'000'000'000,
54+
.accuracyRanges = DataModel::List<const Structs::MeasurementAccuracyRangeStruct::Type>(kTestAccuracyRanges)
55+
};
56+
4257
struct TestElectricalEnergyMeasurementCluster : public ::testing::Test
4358
{
4459
static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
@@ -59,6 +74,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, AttributeListTest)
5974
.endpointId = kTestEndpointId,
6075
.featureFlags = noFeatures,
6176
.optionalAttributes = static_cast<ElectricalEnergyMeasurement::OptionalAttributes>(0),
77+
.accuracyStruct = kTestAccuracy,
6278
});
6379

6480
EXPECT_EQ(cluster.Startup(context.Get()), CHIP_NO_ERROR);
@@ -78,6 +94,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, AttributeListTest)
7894
.endpointId = kTestEndpointId,
7995
.featureFlags = allFeatures,
8096
.optionalAttributes = ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset,
97+
.accuracyStruct = kTestAccuracy,
8198
});
8299

83100
EXPECT_EQ(cluster.Startup(context.Get()), CHIP_NO_ERROR);
@@ -105,6 +122,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, AttributeListTest)
105122
.endpointId = kTestEndpointId,
106123
.featureFlags = noFeatures,
107124
.optionalAttributes = ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset,
125+
.accuracyStruct = kTestAccuracy,
108126
});
109127

110128
EXPECT_EQ(cluster.Startup(context.Get()), CHIP_NO_ERROR);
@@ -129,6 +147,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, GettersSettersWithFeatureValidati
129147
.endpointId = kTestEndpointId,
130148
.featureFlags = allFeatures,
131149
.optionalAttributes = ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset,
150+
.accuracyStruct = kTestAccuracy,
132151
});
133152

134153
EXPECT_EQ(cluster.Startup(context.Get()), CHIP_NO_ERROR);
@@ -178,6 +197,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, GettersSettersWithFeatureValidati
178197
.endpointId = kTestEndpointId,
179198
.featureFlags = noFeatures,
180199
.optionalAttributes = static_cast<ElectricalEnergyMeasurement::OptionalAttributes>(0),
200+
.accuracyStruct = kTestAccuracy,
181201
});
182202

183203
EXPECT_EQ(cluster.Startup(context.Get()), CHIP_NO_ERROR);
@@ -209,6 +229,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, FeatureAttributeTest)
209229
.endpointId = kTestEndpointId,
210230
.featureFlags = allFeatures,
211231
.optionalAttributes = ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset,
232+
.accuracyStruct = kTestAccuracy,
212233
});
213234

214235
EXPECT_EQ(cluster.Startup(context.Get()), CHIP_NO_ERROR);
@@ -261,6 +282,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, ReadAttributeWithClusterTesterTes
261282
.endpointId = kTestEndpointId,
262283
.featureFlags = allFeatures,
263284
.optionalAttributes = ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset,
285+
.accuracyStruct = kTestAccuracy,
264286
});
265287

266288
EXPECT_EQ(cluster.Startup(context.Get()), CHIP_NO_ERROR);
@@ -316,6 +338,7 @@ TEST_F(TestElectricalEnergyMeasurementCluster, SnapshotsSetValuesAndGenerateEven
316338
.endpointId = kTestEndpointId,
317339
.featureFlags = allFeatures,
318340
.optionalAttributes = ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset,
341+
.accuracyStruct = kTestAccuracy,
319342
});
320343

321344
EXPECT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);

src/app/clusters/electrical-energy-measurement-server/tests/TestElectricalEnergyMeasurementClusterBackwardsCompatibility.cpp

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ static chip::app::CircularEventBuffer gCircularEventBuffer[3];
4141

4242
constexpr EndpointId kTestEndpointId = 1;
4343

44+
const Structs::MeasurementAccuracyRangeStruct::Type kTestAccuracyRanges[] = {
45+
{ .rangeMin = 0,
46+
.rangeMax = 1'000'000'000'000'000, // 1 million Mwh
47+
.percentMax = MakeOptional(static_cast<chip::Percent100ths>(500)),
48+
.percentMin = MakeOptional(static_cast<chip::Percent100ths>(50)) }
49+
};
50+
51+
const Structs::MeasurementAccuracyStruct::Type kTestAccuracy = {
52+
.measurementType = MeasurementTypeEnum::kElectricalEnergy,
53+
.measured = true,
54+
.minMeasuredValue = 0,
55+
.maxMeasuredValue = 1'000'000'000'000'000,
56+
.accuracyRanges = DataModel::List<const Structs::MeasurementAccuracyRangeStruct::Type>(kTestAccuracyRanges)
57+
};
58+
4459
struct TestElectricalEnergyMeasurementClusterBackwardsCompatibility : public ::testing::Test
4560
{
4661
static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
@@ -95,7 +110,7 @@ TEST_F(TestElectricalEnergyMeasurementClusterBackwardsCompatibility, TestAttrAcc
95110
EXPECT_FALSE(minimalAttrAccess.HasFeature(Feature::kExportedEnergy));
96111
EXPECT_FALSE(minimalAttrAccess.HasFeature(Feature::kCumulativeEnergy));
97112
EXPECT_FALSE(minimalAttrAccess.HasFeature(Feature::kPeriodicEnergy));
98-
minimalAttrAccess.Shutdown(ClusterShutdownType::kClusterShutdown);
113+
minimalAttrAccess.Shutdown();
99114

100115
// Test optional attribute checking methods
101116
EXPECT_TRUE(attrAccess.SupportsOptAttr(OptionalAttributes::kOptionalAttributeCumulativeEnergyReset));
@@ -104,10 +119,10 @@ TEST_F(TestElectricalEnergyMeasurementClusterBackwardsCompatibility, TestAttrAcc
104119
ElectricalEnergyMeasurementAttrAccess noOptAttrAccess(features, noOptionalAttrs, kTestEndpointId + 2);
105120
EXPECT_EQ(noOptAttrAccess.Init(), CHIP_NO_ERROR);
106121
EXPECT_FALSE(noOptAttrAccess.SupportsOptAttr(OptionalAttributes::kOptionalAttributeCumulativeEnergyReset));
107-
noOptAttrAccess.Shutdown(ClusterShutdownType::kClusterShutdown);
122+
noOptAttrAccess.Shutdown();
108123

109124
// Test shutdown
110-
attrAccess.Shutdown(ClusterShutdownType::kClusterShutdown);
125+
attrAccess.Shutdown();
111126

112127
// Verify cluster is unregistered from the registry
113128
EXPECT_EQ(FindElectricalEnergyMeasurementClusterOnEndpoint(kTestEndpointId), nullptr);
@@ -130,22 +145,68 @@ TEST_F(TestElectricalEnergyMeasurementClusterBackwardsCompatibility, TestCodegen
130145
// Initialize the cluster with test context for event logging
131146
EXPECT_EQ(cluster->Startup(mContext.Get()), CHIP_NO_ERROR);
132147

133-
// Test SetMeasurementAccuracy
148+
// Test SetMeasurementAccuracy and verify accuracyRanges survives source data destruction
149+
{
150+
Structs::MeasurementAccuracyRangeStruct::Type testMeasurementAccuracyRanges[] = {
151+
{ .rangeMin = 0,
152+
.rangeMax = 1'000'000'000'000'000, // 1 million Mwh
153+
.percentMax = MakeOptional(static_cast<chip::Percent100ths>(500)),
154+
.percentMin = MakeOptional(static_cast<chip::Percent100ths>(50)) }
155+
};
156+
157+
Structs::MeasurementAccuracyStruct::Type accuracy;
158+
accuracy.measurementType = MeasurementTypeEnum::kApparentEnergy;
159+
accuracy.measured = true;
160+
accuracy.minMeasuredValue = 0;
161+
accuracy.maxMeasuredValue = 1000000;
162+
accuracy.accuracyRanges = DataModel::List<const Structs::MeasurementAccuracyRangeStruct::Type>(
163+
testMeasurementAccuracyRanges, MATTER_ARRAY_SIZE(testMeasurementAccuracyRanges));
164+
165+
EXPECT_EQ(SetMeasurementAccuracy(kTestEndpointId, accuracy), CHIP_NO_ERROR);
166+
167+
// Overwrite source data; if the cluster only holds a Span, reads will return these values instead
168+
testMeasurementAccuracyRanges[0].rangeMin = 999;
169+
testMeasurementAccuracyRanges[0].rangeMax = 999;
170+
testMeasurementAccuracyRanges[0].percentMax = MakeOptional(static_cast<chip::Percent100ths>(999));
171+
testMeasurementAccuracyRanges[0].percentMin = MakeOptional(static_cast<chip::Percent100ths>(999));
172+
}
173+
174+
// Verify that the MeasurementAccuracyStruct is preserved past the scope of the previous test
175+
Structs::MeasurementAccuracyStruct::Type readAccuracy;
176+
cluster->GetMeasurementAccuracy(readAccuracy);
177+
EXPECT_EQ(readAccuracy.measurementType, MeasurementTypeEnum::kApparentEnergy);
178+
EXPECT_TRUE(readAccuracy.measured);
179+
EXPECT_EQ(readAccuracy.minMeasuredValue, 0);
180+
EXPECT_EQ(readAccuracy.maxMeasuredValue, 1000000);
181+
EXPECT_EQ(readAccuracy.accuracyRanges.size(), 1u);
182+
EXPECT_EQ(readAccuracy.accuracyRanges[0].rangeMin, 0);
183+
EXPECT_EQ(readAccuracy.accuracyRanges[0].rangeMax, 1'000'000'000'000'000);
184+
EXPECT_EQ(readAccuracy.accuracyRanges[0].percentMax.Value(), 500);
185+
EXPECT_EQ(readAccuracy.accuracyRanges[0].percentMin.Value(), 50);
186+
187+
// Test SetMeasurementAccuracy and verify empty accuracyRanges doesn't modify the existing accuracyRanges
134188
{
135189
Structs::MeasurementAccuracyStruct::Type accuracy;
136190
accuracy.measurementType = MeasurementTypeEnum::kApparentEnergy;
137191
accuracy.measured = true;
138192
accuracy.minMeasuredValue = 0;
139193
accuracy.maxMeasuredValue = 1000000;
140-
accuracy.accuracyRanges = DataModel::List<Structs::MeasurementAccuracyRangeStruct::Type>();
141194

142195
EXPECT_EQ(SetMeasurementAccuracy(kTestEndpointId, accuracy), CHIP_NO_ERROR);
143196

144197
// Verify the value was set
145198
Structs::MeasurementAccuracyStruct::Type readAccuracy;
146199
cluster->GetMeasurementAccuracy(readAccuracy);
200+
// Verify that the MeasurementAccuracyStruct is not erased by the empty accuracyRanges
147201
EXPECT_EQ(readAccuracy.measurementType, MeasurementTypeEnum::kApparentEnergy);
148202
EXPECT_TRUE(readAccuracy.measured);
203+
EXPECT_EQ(readAccuracy.minMeasuredValue, 0);
204+
EXPECT_EQ(readAccuracy.maxMeasuredValue, 1000000);
205+
EXPECT_EQ(readAccuracy.accuracyRanges.size(), 1u);
206+
EXPECT_EQ(readAccuracy.accuracyRanges[0].rangeMin, 0);
207+
EXPECT_EQ(readAccuracy.accuracyRanges[0].rangeMax, 1'000'000'000'000'000);
208+
EXPECT_EQ(readAccuracy.accuracyRanges[0].percentMax.Value(), 500);
209+
EXPECT_EQ(readAccuracy.accuracyRanges[0].percentMin.Value(), 50);
149210
}
150211

151212
// Test SetCumulativeReset
@@ -272,7 +333,36 @@ TEST_F(TestElectricalEnergyMeasurementClusterBackwardsCompatibility, TestCodegen
272333
}
273334

274335
// Cleanup
275-
attrAccess.Shutdown(ClusterShutdownType::kClusterShutdown);
336+
attrAccess.Shutdown();
337+
}
338+
339+
TEST_F(TestElectricalEnergyMeasurementClusterBackwardsCompatibility, TestConstructorWithAccuracy)
340+
{
341+
BitMask<Feature> features(Feature::kImportedEnergy, Feature::kCumulativeEnergy);
342+
BitMask<OptionalAttributes> noOptionalAttrs;
343+
344+
ElectricalEnergyMeasurementCluster cluster(ElectricalEnergyMeasurementCluster::Config{
345+
.endpointId = kTestEndpointId + 10,
346+
.featureFlags = features,
347+
.optionalAttributes = noOptionalAttrs,
348+
.accuracyStruct = kTestAccuracy,
349+
});
350+
351+
Structs::MeasurementAccuracyStruct::Type readAccuracy;
352+
cluster.GetMeasurementAccuracy(readAccuracy);
353+
354+
EXPECT_EQ(readAccuracy.measurementType, MeasurementTypeEnum::kElectricalEnergy);
355+
EXPECT_TRUE(readAccuracy.measured);
356+
EXPECT_EQ(readAccuracy.minMeasuredValue, 0);
357+
EXPECT_EQ(readAccuracy.maxMeasuredValue, 1'000'000'000'000'000);
358+
ASSERT_EQ(readAccuracy.accuracyRanges.size(), 1u);
359+
EXPECT_EQ(readAccuracy.accuracyRanges[0].rangeMin, 0);
360+
EXPECT_EQ(readAccuracy.accuracyRanges[0].rangeMax, 1'000'000'000'000'000);
361+
EXPECT_EQ(readAccuracy.accuracyRanges[0].percentMax.Value(), 500);
362+
EXPECT_EQ(readAccuracy.accuracyRanges[0].percentMin.Value(), 50);
363+
364+
// Verify zero-copy: the ranges Span should point directly at the static data
365+
EXPECT_EQ(readAccuracy.accuracyRanges.data(), kTestAccuracyRanges);
276366
}
277367

278368
} // namespace

0 commit comments

Comments
 (0)