blob: cb16f29067d046ca5f382e878417c0730b8c172a [file]
/*
* Copyright (c) 2026 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <pw_unit_test/framework.h>
#include <app/clusters/pressure-measurement-server/PressureMeasurementCluster.h>
#include <app/server-cluster/AttributeListBuilder.h>
#include <app/server-cluster/testing/AttributeTesting.h>
#include <app/server-cluster/testing/ClusterTester.h>
#include <app/server-cluster/testing/TestServerClusterContext.h>
namespace {
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::PressureMeasurement;
using namespace chip::app::Clusters::PressureMeasurement::Attributes;
using namespace chip::Testing;
struct TestPressureMeasurementCluster : public ::testing::Test
{
static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }
TestPressureMeasurementCluster() {}
TestServerClusterContext testContext;
};
void TestMandatoryAttributes(ClusterTester & tester)
{
uint16_t revision{};
ASSERT_EQ(tester.ReadAttribute(ClusterRevision::Id, revision), CHIP_NO_ERROR);
uint32_t featureMap{};
ASSERT_EQ(tester.ReadAttribute(FeatureMap::Id, featureMap), CHIP_NO_ERROR);
DataModel::Nullable<int16_t> measuredValue{};
ASSERT_EQ(tester.ReadAttribute(MeasuredValue::Id, measuredValue), CHIP_NO_ERROR);
DataModel::Nullable<int16_t> minMeasuredValue{};
ASSERT_EQ(tester.ReadAttribute(MinMeasuredValue::Id, minMeasuredValue), CHIP_NO_ERROR);
DataModel::Nullable<int16_t> maxMeasuredValue{};
ASSERT_EQ(tester.ReadAttribute(MaxMeasuredValue::Id, maxMeasuredValue), CHIP_NO_ERROR);
}
} // namespace
TEST_F(TestPressureMeasurementCluster, ReadAttributeTest)
{
{
PressureMeasurementCluster cluster(kRootEndpointId);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
ClusterTester tester(cluster);
TestMandatoryAttributes(tester);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
{
PressureMeasurementCluster::Config config;
config.WithTolerance(0);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
ClusterTester tester(cluster);
TestMandatoryAttributes(tester);
uint16_t tolerance{};
ASSERT_EQ(tester.ReadAttribute(Tolerance::Id, tolerance), CHIP_NO_ERROR);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
}
TEST_F(TestPressureMeasurementCluster, ReadUnsupportedAttribute)
{
PressureMeasurementCluster cluster(kRootEndpointId);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
ClusterTester tester(cluster);
uint16_t dummy{};
EXPECT_NE(tester.ReadAttribute(0xFFF0, dummy), CHIP_NO_ERROR);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
TEST_F(TestPressureMeasurementCluster, ConstructorVariants)
{
// Only min set (max null)
{
PressureMeasurementCluster::Config config;
config.minMeasuredValue.SetNonNull(10);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
EXPECT_EQ(cluster.GetMinMeasuredValue().Value(), 10);
EXPECT_TRUE(cluster.GetMaxMeasuredValue().IsNull());
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
// Only max set (min null)
{
PressureMeasurementCluster::Config config;
config.maxMeasuredValue.SetNonNull(500);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
EXPECT_TRUE(cluster.GetMinMeasuredValue().IsNull());
EXPECT_EQ(cluster.GetMaxMeasuredValue().Value(), 500);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
// With tolerance
{
PressureMeasurementCluster::Config config;
config.minMeasuredValue.SetNonNull(0);
config.maxMeasuredValue.SetNonNull(2048);
config.WithTolerance(2048);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
ClusterTester tester(cluster);
uint16_t tolerance{};
ASSERT_EQ(tester.ReadAttribute(Tolerance::Id, tolerance), CHIP_NO_ERROR);
EXPECT_EQ(tolerance, 2048);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
// Negative min value (pressure can be negative)
{
PressureMeasurementCluster::Config config;
config.minMeasuredValue.SetNonNull(-100);
config.maxMeasuredValue.SetNonNull(500);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
EXPECT_EQ(cluster.GetMinMeasuredValue().Value(), -100);
EXPECT_EQ(cluster.GetMaxMeasuredValue().Value(), 500);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
}
TEST_F(TestPressureMeasurementCluster, MeasuredValue)
{
PressureMeasurementCluster::Config config;
config.minMeasuredValue.SetNonNull(-10);
config.maxMeasuredValue.SetNonNull(10);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
DataModel::Nullable<int16_t> measuredValue{};
// Below min — rejected
measuredValue.SetNonNull(-11);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_IM_GLOBAL_STATUS(ConstraintError));
// At min — accepted
measuredValue.SetNonNull(-10);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_NO_ERROR);
EXPECT_EQ(cluster.GetMeasuredValue().Value(), -10);
// Zero — accepted
measuredValue.SetNonNull(0);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_NO_ERROR);
EXPECT_EQ(cluster.GetMeasuredValue().Value(), 0);
// At max — accepted
measuredValue.SetNonNull(10);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_NO_ERROR);
EXPECT_EQ(cluster.GetMeasuredValue().Value(), 10);
// Above max — rejected
measuredValue.SetNonNull(11);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_IM_GLOBAL_STATUS(ConstraintError));
// Null — always accepted
measuredValue.SetNull();
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_NO_ERROR);
EXPECT_TRUE(cluster.GetMeasuredValue().IsNull());
// -32768 is outside configured range (-10 to 10) — rejected
measuredValue.SetNonNull(-32768);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_IM_GLOBAL_STATUS(ConstraintError));
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
TEST_F(TestPressureMeasurementCluster, MeasuredValueNullRange)
{
// Both min and max null — any valid value accepted
PressureMeasurementCluster cluster(kRootEndpointId);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
DataModel::Nullable<int16_t> measuredValue{};
measuredValue.SetNonNull(-32767);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_NO_ERROR);
measuredValue.SetNonNull(32767);
EXPECT_EQ(cluster.SetMeasuredValue(measuredValue), CHIP_NO_ERROR);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
TEST_F(TestPressureMeasurementCluster, ScaledValueEXT)
{
PressureMeasurementCluster::Config config;
config.minMeasuredValue.SetNonNull(30);
config.maxMeasuredValue.SetNonNull(10000);
config.WithExtendedFeature(DataModel::Nullable<int16_t>(-100), DataModel::Nullable<int16_t>(10000), -1);
config.WithScaledTolerance(10);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
ClusterTester tester(cluster);
// FeatureMap should have EXT bit set
uint32_t featureMap{};
ASSERT_EQ(tester.ReadAttribute(FeatureMap::Id, featureMap), CHIP_NO_ERROR);
EXPECT_EQ(featureMap, 1u);
// Read all scaled attributes
DataModel::Nullable<int16_t> scaledValue{};
ASSERT_EQ(tester.ReadAttribute(ScaledValue::Id, scaledValue), CHIP_NO_ERROR);
DataModel::Nullable<int16_t> minScaledValue{};
ASSERT_EQ(tester.ReadAttribute(MinScaledValue::Id, minScaledValue), CHIP_NO_ERROR);
EXPECT_EQ(minScaledValue.Value(), -100);
DataModel::Nullable<int16_t> maxScaledValue{};
ASSERT_EQ(tester.ReadAttribute(MaxScaledValue::Id, maxScaledValue), CHIP_NO_ERROR);
EXPECT_EQ(maxScaledValue.Value(), 10000);
int8_t scale{};
ASSERT_EQ(tester.ReadAttribute(Scale::Id, scale), CHIP_NO_ERROR);
EXPECT_EQ(scale, -1);
uint16_t scaledTolerance{};
ASSERT_EQ(tester.ReadAttribute(ScaledTolerance::Id, scaledTolerance), CHIP_NO_ERROR);
EXPECT_EQ(scaledTolerance, 10);
// Set scaled value within range
DataModel::Nullable<int16_t> sv;
sv.SetNonNull(5000);
EXPECT_EQ(cluster.SetScaledValue(sv), CHIP_NO_ERROR);
EXPECT_EQ(cluster.GetScaledValue().Value(), 5000);
// Out of range
sv.SetNonNull(10001);
EXPECT_EQ(cluster.SetScaledValue(sv), CHIP_IM_GLOBAL_STATUS(ConstraintError));
// Null — accepted
sv.SetNull();
EXPECT_EQ(cluster.SetScaledValue(sv), CHIP_NO_ERROR);
EXPECT_TRUE(cluster.GetScaledValue().IsNull());
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
TEST_F(TestPressureMeasurementCluster, ScaledToleranceBeforeExtendedFeature)
{
// WithScaledTolerance before WithExtendedFeature — order should not matter
PressureMeasurementCluster::Config config;
config.minMeasuredValue.SetNonNull(30);
config.maxMeasuredValue.SetNonNull(10000);
config.WithScaledTolerance(50);
config.WithExtendedFeature(DataModel::Nullable<int16_t>(-100), DataModel::Nullable<int16_t>(10000), -1);
PressureMeasurementCluster cluster(kRootEndpointId, config);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
ClusterTester tester(cluster);
uint32_t featureMap{};
ASSERT_EQ(tester.ReadAttribute(FeatureMap::Id, featureMap), CHIP_NO_ERROR);
EXPECT_EQ(featureMap, 1u);
uint16_t scaledTolerance{};
ASSERT_EQ(tester.ReadAttribute(ScaledTolerance::Id, scaledTolerance), CHIP_NO_ERROR);
EXPECT_EQ(scaledTolerance, 50);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
TEST_F(TestPressureMeasurementCluster, FeatureMapWithoutEXT)
{
PressureMeasurementCluster cluster(kRootEndpointId);
ASSERT_EQ(cluster.Startup(testContext.Get()), CHIP_NO_ERROR);
ClusterTester tester(cluster);
uint32_t featureMap{};
ASSERT_EQ(tester.ReadAttribute(FeatureMap::Id, featureMap), CHIP_NO_ERROR);
EXPECT_EQ(featureMap, 0u);
// SetScaledValue without EXT feature should fail
DataModel::Nullable<int16_t> sv;
sv.SetNonNull(100);
EXPECT_EQ(cluster.SetScaledValue(sv), CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}