| /* |
| * |
| * 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/ConcreteClusterPath.h> |
| #include <app/clusters/identify-server/identify-server.h> |
| #include <app/server-cluster/testing/ClusterTester.h> |
| #include <data-model-providers/codegen/CodegenDataModelProvider.h> |
| #include <gtest/gtest.h> |
| #include <lib/support/TimerDelegateMock.h> |
| |
| using namespace chip; |
| using namespace chip::app; |
| using namespace chip::app::Clusters; |
| using namespace chip::app::Clusters::Identify; |
| using namespace chip::app::Clusters::Identify::Attributes; |
| |
| // Forward declaration of the callback to emulate ember initialization |
| void MatterIdentifyClusterInitCallback(chip::EndpointId endpointId); |
| // Stub for DataModelHandler init function |
| void InitDataModelHandler() {} |
| |
| namespace { |
| |
| bool onIdentifyStartCalled = false; |
| bool onIdentifyStopCalled = false; |
| bool onEffectIdentifierCalled = false; |
| |
| void onIdentifyStart(struct Identify * identify) |
| { |
| onIdentifyStartCalled = true; |
| } |
| |
| void onIdentifyStop(struct Identify * identify) |
| { |
| onIdentifyStopCalled = true; |
| } |
| |
| void onEffectIdentifier(struct Identify * identify) |
| { |
| onEffectIdentifierCalled = true; |
| identify->mCurrentEffectIdentifier = identify->mCluster.Cluster().GetEffectIdentifier(); |
| } |
| |
| struct TestIdentifyClusterBackwardsCompatibility : public ::testing::Test |
| { |
| static void SetUpTestSuite() { ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); } |
| static void TearDownTestSuite() { Platform::MemoryShutdown(); } |
| |
| TimerDelegateMock mMockTimerDelegate; |
| }; |
| |
| TEST_F(TestIdentifyClusterBackwardsCompatibility, TestLegacyInstantiattion) |
| { |
| struct Identify identify(1, nullptr, nullptr, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone); |
| } |
| |
| TEST_F(TestIdentifyClusterBackwardsCompatibility, TestLegacyCallbacks) |
| { |
| onIdentifyStartCalled = false; |
| onIdentifyStopCalled = false; |
| onEffectIdentifierCalled = false; |
| |
| // Old style struct |
| struct Identify identify(1, onIdentifyStart, onIdentifyStop, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone, |
| onEffectIdentifier, EffectIdentifierEnum::kBlink, EffectVariantEnum::kDefault, &mMockTimerDelegate); |
| chip::Testing::ClusterTester tester(identify.mCluster.Cluster()); |
| EXPECT_EQ(identify.mCluster.Cluster().Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR); |
| |
| // Test onIdentifyStart callback by writing to IdentifyTime. |
| EXPECT_TRUE(tester.WriteAttribute(IdentifyTime::Id, 10u).IsSuccess()); |
| EXPECT_TRUE(onIdentifyStartCalled); |
| |
| // Test onIdentifyStop callback by writing to IdentifyTime. |
| EXPECT_TRUE(tester.WriteAttribute(IdentifyTime::Id, 0u).IsSuccess()); |
| EXPECT_TRUE(onIdentifyStopCalled); |
| |
| // Test onEffectIdentifier callback by invoking TriggerEffect command. |
| Commands::TriggerEffect::Type data; |
| data.effectIdentifier = EffectIdentifierEnum::kBlink; |
| data.effectVariant = EffectVariantEnum::kDefault; |
| auto result = tester.Invoke(Commands::TriggerEffect::Id, data); |
| EXPECT_TRUE(result.IsSuccess()); |
| EXPECT_TRUE(onEffectIdentifierCalled); |
| } |
| |
| TEST_F(TestIdentifyClusterBackwardsCompatibility, TestCurrentEffectIdentifierUpdate) |
| { |
| onEffectIdentifierCalled = false; |
| struct Identify identify(1, nullptr, nullptr, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone, onEffectIdentifier, |
| EffectIdentifierEnum::kStopEffect, EffectVariantEnum::kDefault, &mMockTimerDelegate); |
| chip::Testing::ClusterTester tester(identify.mCluster.Cluster()); |
| EXPECT_EQ(identify.mCluster.Cluster().Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR); |
| |
| // Check that the effect identifier is the default one |
| EXPECT_EQ(identify.mCurrentEffectIdentifier, EffectIdentifierEnum::kStopEffect); |
| |
| // Invoke the TriggerEffect command with kBlink |
| Commands::TriggerEffect::Type dataBlink; |
| dataBlink.effectIdentifier = EffectIdentifierEnum::kBlink; |
| dataBlink.effectVariant = EffectVariantEnum::kDefault; |
| auto resultBlink = tester.Invoke(Commands::TriggerEffect::Id, dataBlink); |
| EXPECT_TRUE(resultBlink.IsSuccess()); |
| EXPECT_EQ(identify.mCurrentEffectIdentifier, EffectIdentifierEnum::kBlink); |
| |
| // Invoke the TriggerEffect command with kFinishEffect |
| Commands::TriggerEffect::Type dataFinish; |
| dataFinish.effectIdentifier = EffectIdentifierEnum::kFinishEffect; |
| dataFinish.effectVariant = EffectVariantEnum::kDefault; |
| auto resultFinish = tester.Invoke(Commands::TriggerEffect::Id, dataFinish); |
| EXPECT_TRUE(resultFinish.IsSuccess()); |
| EXPECT_EQ(identify.mCurrentEffectIdentifier, EffectIdentifierEnum::kFinishEffect); |
| } |
| |
| TEST_F(TestIdentifyClusterBackwardsCompatibility, TestIdentifyTypeInitialization) |
| { |
| // Test with kNone |
| struct Identify identifyNone(1, nullptr, nullptr, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone); |
| EXPECT_EQ(identifyNone.mIdentifyType, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone); |
| |
| // Test with kLightOutput |
| struct Identify identifyLightOutput(1, nullptr, nullptr, chip::app::Clusters::Identify::IdentifyTypeEnum::kLightOutput); |
| EXPECT_EQ(identifyLightOutput.mIdentifyType, chip::app::Clusters::Identify::IdentifyTypeEnum::kLightOutput); |
| |
| // Test with kAudibleBeep |
| struct Identify identifyAudibleBeep(1, nullptr, nullptr, chip::app::Clusters::Identify::IdentifyTypeEnum::kAudibleBeep); |
| EXPECT_EQ(identifyAudibleBeep.mIdentifyType, chip::app::Clusters::Identify::IdentifyTypeEnum::kAudibleBeep); |
| } |
| |
| TEST_F(TestIdentifyClusterBackwardsCompatibility, TestMActive) |
| { |
| struct Identify identify(1, nullptr, nullptr, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone, nullptr, |
| EffectIdentifierEnum::kBlink, EffectVariantEnum::kDefault, &mMockTimerDelegate); |
| chip::Testing::ClusterTester tester(identify.mCluster.Cluster()); |
| EXPECT_EQ(identify.mCluster.Cluster().Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR); |
| |
| // Test that mActive is false initially. |
| EXPECT_FALSE(identify.mActive); |
| |
| // Test that mActive is true after writing a non-zero value to IdentifyTime. |
| EXPECT_TRUE(tester.WriteAttribute(IdentifyTime::Id, 10u).IsSuccess()); |
| EXPECT_TRUE(identify.mActive); |
| |
| // Test that mActive is false after writing 0 to IdentifyTime. |
| EXPECT_TRUE(tester.WriteAttribute(IdentifyTime::Id, 0u).IsSuccess()); |
| EXPECT_FALSE(identify.mActive); |
| } |
| |
| TEST_F(TestIdentifyClusterBackwardsCompatibility, TestLateLegacyInstantiation) |
| { |
| EndpointId endpointId = 1; |
| |
| // Call MatterIdentifyClusterInitCallback(enpointId) to emulate ember initialization |
| MatterIdentifyClusterInitCallback(endpointId); |
| |
| // Instantiate legacy Identify late (after the callback/ember init) |
| struct Identify identify(endpointId, nullptr, nullptr, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone); |
| |
| // Check the cluster is properly registered in the CodegenDMP Registry |
| EXPECT_TRUE(CodegenDataModelProvider::Instance().Registry().Get(ConcreteClusterPath(endpointId, Clusters::Identify::Id)) != |
| nullptr); |
| } |
| |
| TEST_F(TestIdentifyClusterBackwardsCompatibility, StopIdentifyingTest) |
| { |
| onIdentifyStopCalled = false; |
| |
| // Old style struct |
| struct Identify identify(1, onIdentifyStart, onIdentifyStop, chip::app::Clusters::Identify::IdentifyTypeEnum::kNone, |
| onEffectIdentifier, EffectIdentifierEnum::kBlink, EffectVariantEnum::kDefault, &mMockTimerDelegate); |
| chip::Testing::ClusterTester tester(identify.mCluster.Cluster()); |
| EXPECT_EQ(identify.mCluster.Cluster().Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR); |
| |
| // Start identifying. |
| EXPECT_TRUE(tester.WriteAttribute(IdentifyTime::Id, 10u).IsSuccess()); |
| EXPECT_TRUE(identify.mActive); |
| |
| // Find the cluster on the endpoint and call StopIdentifying |
| IdentifyCluster * cluster = FindIdentifyClusterOnEndpoint(1); |
| ASSERT_NE(cluster, nullptr); |
| cluster->StopIdentifying(); |
| |
| // Verify identifying stopped |
| EXPECT_FALSE(identify.mActive); |
| |
| // Verify stop callback was called |
| EXPECT_TRUE(onIdentifyStopCalled); |
| } |
| |
| } // namespace |