blob: cbe8b8a70df6a9588cff59fd6edb0c81d0bf6022 [file] [log] [blame]
/*
* Copyright (c) 2025 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/boolean-state-server/BooleanStateCluster.h>
#include <pw_unit_test/framework.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/TestEventGenerator.h>
#include <app/server-cluster/testing/TestServerClusterContext.h>
#include <clusters/BooleanState/Attributes.h>
#include <clusters/BooleanState/Metadata.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::BooleanState;
using namespace chip::app::Clusters::BooleanState::Attributes;
using namespace chip::Testing;
namespace {
struct TestBooleanStateCluster : public ::testing::Test
{
static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }
void SetUp() override { ASSERT_EQ(booleanState.Startup(testContext.Get()), CHIP_NO_ERROR); }
void TearDown() override { booleanState.Shutdown(ClusterShutdownType::kClusterShutdown); }
TestBooleanStateCluster() : booleanState(kRootEndpointId) {}
TestServerClusterContext testContext;
BooleanStateCluster booleanState;
};
} // namespace
TEST_F(TestBooleanStateCluster, AttributeTest)
{
ReadOnlyBufferBuilder<DataModel::AttributeEntry> attributes;
ASSERT_EQ(booleanState.Attributes(ConcreteClusterPath(kRootEndpointId, BooleanState::Id), attributes), CHIP_NO_ERROR);
ReadOnlyBufferBuilder<DataModel::AttributeEntry> expected;
AttributeListBuilder listBuilder(expected);
ASSERT_EQ(listBuilder.Append(Span(BooleanState::Attributes::kMandatoryMetadata), {}), CHIP_NO_ERROR);
ASSERT_TRUE(chip::Testing::EqualAttributeSets(attributes.TakeBuffer(), expected.TakeBuffer()));
}
TEST_F(TestBooleanStateCluster, ReadAttributeTest)
{
ClusterTester tester(booleanState);
uint16_t revision{};
ASSERT_EQ(tester.ReadAttribute(Globals::Attributes::ClusterRevision::Id, revision), CHIP_NO_ERROR);
uint32_t features{};
ASSERT_EQ(tester.ReadAttribute(FeatureMap::Id, features), CHIP_NO_ERROR);
bool stateValue{};
ASSERT_EQ(tester.ReadAttribute(StateValue::Id, stateValue), CHIP_NO_ERROR);
}
TEST_F(TestBooleanStateCluster, StateValue)
{
bool stateValue = false;
auto eventNumber = booleanState.SetStateValue(stateValue);
auto stateVal = booleanState.GetStateValue();
EXPECT_EQ(stateVal, stateValue);
EXPECT_FALSE(eventNumber.has_value());
stateValue = true;
eventNumber = booleanState.SetStateValue(stateValue);
stateVal = booleanState.GetStateValue();
EXPECT_EQ(stateVal, stateValue);
EXPECT_TRUE(eventNumber.has_value());
stateValue = true;
eventNumber = booleanState.SetStateValue(stateValue);
stateVal = booleanState.GetStateValue();
EXPECT_EQ(stateVal, stateValue);
EXPECT_FALSE(eventNumber.has_value());
stateValue = false;
eventNumber = booleanState.SetStateValue(stateValue);
stateVal = booleanState.GetStateValue();
EXPECT_EQ(stateVal, stateValue);
EXPECT_TRUE(eventNumber.has_value());
}
TEST_F(TestBooleanStateCluster, EventGeneratedOnStateChange)
{
// Ensure initial value is false, then change to true and expect an event
EXPECT_EQ(booleanState.GetStateValue(), false);
auto eventNumber = booleanState.SetStateValue(true);
auto & logOnlyEvents = testContext.EventsGenerator();
using EventType = chip::app::Clusters::BooleanState::Events::StateChange::Type;
// Lambda to verify the last emitted event metadata and payload
auto verifyLastEvent = [&](bool expectedStateValue) {
auto event = logOnlyEvents.GetNextEvent();
// verify an event was generated
ASSERT_TRUE(event.has_value());
ASSERT_TRUE(eventNumber.has_value());
EXPECT_EQ(eventNumber.value(), event->eventNumber);
EXPECT_EQ(event->eventOptions.mPath,
ConcreteEventPath(kRootEndpointId, EventType::GetClusterId(), EventType::GetEventId()));
chip::app::Clusters::BooleanState::Events::StateChange::DecodableType decodedEvent;
ASSERT_EQ(event->GetEventData(decodedEvent), CHIP_NO_ERROR);
EXPECT_EQ(decodedEvent.stateValue, expectedStateValue);
};
// Verify event with expected true value
verifyLastEvent(true);
// Now, change from true to false and expect an event
eventNumber = booleanState.SetStateValue(false);
// Verify event with expected false value
verifyLastEvent(false);
}
TEST_F(TestBooleanStateCluster, NoEventWhenValueUnchanged)
{
// Ensure initial value is false, then try to set false again and confirm no event occurs
EXPECT_EQ(booleanState.GetStateValue(), false);
// Get initial event count before attempting to set the same value
auto & logOnlyEvents = testContext.EventsGenerator();
// Re-set to the same value (false) and confirm no new event is generated
auto firstEvent = booleanState.SetStateValue(false);
EXPECT_FALSE(firstEvent.has_value());
EXPECT_FALSE(logOnlyEvents.GetNextEvent().has_value());
// Change from false -> true and confirm an event occurs
auto secondEvent = booleanState.SetStateValue(true);
EXPECT_TRUE(secondEvent.has_value());
EXPECT_TRUE(logOnlyEvents.GetNextEvent().has_value());
// Re-set to the same value (true) and confirm no new event is generated
auto thirdEvent = booleanState.SetStateValue(true);
EXPECT_FALSE(thirdEvent.has_value());
EXPECT_FALSE(logOnlyEvents.GetNextEvent().has_value());
}