blob: 29dac7a6d0c6a1f39a45698c3efebfda0378d48f [file] [log] [blame]
/*
* Copyright (c) 2026 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <app/clusters/level-control/tests/TestLevelControlCommon.h>
#include <app/server-cluster/testing/ClusterTester.h>
#include <app/server-cluster/testing/TestServerClusterContext.h>
#include <clusters/LevelControl/Attributes.h>
#include <clusters/LevelControl/Commands.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::LevelControl;
struct TestLevelControlScenes : public LevelControlTestBase
{
};
TEST_F(TestLevelControlScenes, TestSerializeScene)
{
LevelControlCluster cluster{ LevelControlCluster::Config(kTestEndpointId, mockTimer, mockDelegate) };
chip::Testing::ClusterTester tester(cluster);
EXPECT_EQ(cluster.Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR);
EXPECT_TRUE(cluster
.MoveToLevel(55, DataModel::MakeNullable(static_cast<uint16_t>(0)),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff))
.IsSuccess());
uint8_t buffer[128];
MutableByteSpan serializedBytes(buffer);
EXPECT_EQ(cluster.SerializeSave(kTestEndpointId, LevelControl::Id, serializedBytes), CHIP_NO_ERROR);
// Decode to verify
app::DataModel::DecodableList<ScenesManagement::Structs::AttributeValuePairStruct::DecodableType> list;
EXPECT_EQ(cluster.DecodeAttributeValueList(serializedBytes, list), CHIP_NO_ERROR);
auto iter = list.begin();
EXPECT_TRUE(iter.Next());
auto pair = iter.GetValue();
EXPECT_EQ(pair.attributeID, Attributes::CurrentLevel::Id);
EXPECT_EQ(pair.valueUnsigned8.Value(), 55u);
EXPECT_FALSE(iter.Next());
}
TEST_F(TestLevelControlScenes, TestSerializeSceneNullLevel)
{
LevelControlCluster cluster{ LevelControlCluster::Config(kTestEndpointId, mockTimer, mockDelegate) };
chip::Testing::ClusterTester tester(cluster);
EXPECT_EQ(cluster.Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR);
// CurrentLevel defaults to Null
uint8_t buffer[128];
MutableByteSpan serializedBytes(buffer);
EXPECT_EQ(cluster.SerializeSave(kTestEndpointId, LevelControl::Id, serializedBytes), CHIP_NO_ERROR);
// Decode to verify
app::DataModel::DecodableList<ScenesManagement::Structs::AttributeValuePairStruct::DecodableType> list;
EXPECT_EQ(cluster.DecodeAttributeValueList(serializedBytes, list), CHIP_NO_ERROR);
auto iter = list.begin();
// Expect NO items, or at least NO CurrentLevel item.
// If it saves 0, it will have one item with value 0.
bool foundCurrentLevel = false;
while (iter.Next())
{
if (iter.GetValue().attributeID == Attributes::CurrentLevel::Id)
{
foundCurrentLevel = true;
}
}
EXPECT_FALSE(foundCurrentLevel);
}
TEST_F(TestLevelControlScenes, TestApplyScene)
{
LevelControlCluster cluster{ LevelControlCluster::Config(kTestEndpointId, mockTimer, mockDelegate) };
chip::Testing::ClusterTester tester(cluster);
EXPECT_EQ(cluster.Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR);
EXPECT_TRUE(cluster
.MoveToLevel(0, DataModel::MakeNullable(static_cast<uint16_t>(0)),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff))
.IsSuccess());
// Create serialized scene with level 20
uint8_t buffer[128];
MutableByteSpan serializedBytes(buffer);
// Manually create list to simulate saved scene
using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type;
AttributeValuePair pairs[1];
pairs[0].attributeID = Attributes::CurrentLevel::Id;
pairs[0].valueUnsigned8.SetValue(20);
app::DataModel::List<AttributeValuePair> list(pairs);
EXPECT_EQ(cluster.EncodeAttributeValueList(list, serializedBytes), CHIP_NO_ERROR);
// Apply Scene with 1000ms transition
EXPECT_EQ(cluster.ApplyScene(kTestEndpointId, LevelControl::Id, serializedBytes, 1000), CHIP_NO_ERROR);
// Timer should be active (transition from 0 to 20 over 1000ms)
EXPECT_TRUE(mockTimer.IsTimerActive(nullptr));
// Advance to end
while (mockTimer.IsTimerActive(nullptr))
{
AdvanceClock(System::Clock::Milliseconds64(100));
}
DataModel::Nullable<uint8_t> readLevel;
EXPECT_TRUE(tester.ReadAttribute(Attributes::CurrentLevel::Id, readLevel).IsSuccess());
EXPECT_EQ(readLevel.Value(), 20u);
}
TEST_F(TestLevelControlScenes, TestApplySceneImmediate)
{
// Set a default transition time of 10s (100ds) to verify it is IGNORED when ApplyScene(0) is called.
LevelControlCluster cluster{
LevelControlCluster::Config(kTestEndpointId, mockTimer, mockDelegate).WithOnOffTransitionTime(100)
};
chip::Testing::ClusterTester tester(cluster);
EXPECT_EQ(cluster.Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR);
EXPECT_TRUE(cluster
.MoveToLevel(0, DataModel::MakeNullable(static_cast<uint16_t>(0)),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff))
.IsSuccess());
// Scene with level 50
uint8_t buffer[128];
MutableByteSpan serializedBytes(buffer);
using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type;
AttributeValuePair pairs[1];
pairs[0].attributeID = Attributes::CurrentLevel::Id;
pairs[0].valueUnsigned8.SetValue(50);
app::DataModel::List<AttributeValuePair> list(pairs);
EXPECT_EQ(cluster.EncodeAttributeValueList(list, serializedBytes), CHIP_NO_ERROR);
// Apply Scene with 0ms (Immediate)
EXPECT_EQ(cluster.ApplyScene(kTestEndpointId, LevelControl::Id, serializedBytes, 0), CHIP_NO_ERROR);
// Should be immediate -> No Timer
EXPECT_FALSE(mockTimer.IsTimerActive(nullptr));
DataModel::Nullable<uint8_t> readLevel;
EXPECT_TRUE(tester.ReadAttribute(Attributes::CurrentLevel::Id, readLevel).IsSuccess());
EXPECT_EQ(readLevel.Value(), 50u);
}
TEST_F(TestLevelControlScenes, TestApplySceneWhileOff)
{
chip::app::Clusters::OnOffCluster::Context onOffContext{ mockTimer };
chip::app::Clusters::OnOffCluster onOffCluster{ kTestEndpointId, onOffContext };
LevelControlCluster cluster{ LevelControlCluster::Config(kTestEndpointId, mockTimer, mockDelegate).WithOnOff(onOffCluster) };
chip::Testing::ClusterTester tester(cluster);
EXPECT_EQ(cluster.Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR);
EXPECT_EQ(onOffCluster.Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR);
EXPECT_TRUE(cluster
.MoveToLevel(0, DataModel::MakeNullable(static_cast<uint16_t>(0)),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff),
BitMask<LevelControl::OptionsBitmap>(LevelControl::OptionsBitmap::kExecuteIfOff))
.IsSuccess());
EXPECT_EQ(onOffCluster.SetOnOff(false), CHIP_NO_ERROR); // OFF
// Scene with level 50
uint8_t buffer[128];
MutableByteSpan serializedBytes(buffer);
using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type;
AttributeValuePair pairs[1];
pairs[0].attributeID = Attributes::CurrentLevel::Id;
pairs[0].valueUnsigned8.SetValue(50);
app::DataModel::List<AttributeValuePair> list(pairs);
EXPECT_EQ(cluster.EncodeAttributeValueList(list, serializedBytes), CHIP_NO_ERROR);
// Apply Scene with 0ms
// Should succeed and update level even though Off
EXPECT_EQ(cluster.ApplyScene(kTestEndpointId, LevelControl::Id, serializedBytes, 0), CHIP_NO_ERROR);
DataModel::Nullable<uint8_t> readLevel;
EXPECT_TRUE(tester.ReadAttribute(Attributes::CurrentLevel::Id, readLevel).IsSuccess());
EXPECT_EQ(readLevel.Value(), 50u);
}