blob: 83d3b1fd9951c1f559d1c8f94758c10aa1e567f2 [file] [log] [blame]
/*
*
* Copyright (c) 2023 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/SubscriptionsInfoProvider.h>
#include <app/TestEventTriggerDelegate.h>
#include <app/icd/server/ICDConfigurationData.h>
#include <app/icd/server/ICDManager.h>
#include <app/icd/server/ICDMonitoringTable.h>
#include <app/icd/server/ICDNotifier.h>
#include <app/icd/server/ICDStateObserver.h>
#include <app/icd/server/tests/ICDConfigurationDataTestAccess.h>
#include <crypto/DefaultSessionKeystore.h>
#include <lib/address_resolve/AddressResolve.h>
#include <lib/core/DataModelTypes.h>
#include <lib/core/NodeId.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <lib/support/TimeUtils.h>
#include <messaging/tests/MessagingContext.h>
#include <system/SystemLayerImpl.h>
using namespace chip;
using namespace chip::Test;
using namespace chip::app;
using namespace chip::AddressResolve;
using namespace chip::System;
using namespace chip::System::Clock;
using namespace chip::System::Clock::Literals;
using TestSessionKeystoreImpl = Crypto::DefaultSessionKeystore;
namespace {
#if CHIP_CONFIG_ENABLE_ICD_CIP
// Test Values
constexpr uint16_t kMaxTestClients = 2;
constexpr FabricIndex kTestFabricIndex1 = 1;
constexpr FabricIndex kTestFabricIndex2 = kMaxValidFabricIndex;
constexpr NodeId kClientNodeId11 = 0x100001;
constexpr NodeId kClientNodeId12 = 0x100002;
constexpr NodeId kClientNodeId21 = 0x200001;
constexpr NodeId kClientNodeId22 = 0x200002;
constexpr uint8_t kKeyBuffer1a[] = {
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};
constexpr uint8_t kKeyBuffer1b[] = {
0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81, 0x71, 0x61, 0x51, 0x14, 0x31, 0x21, 0x11, 0x01
};
constexpr uint8_t kKeyBuffer2a[] = {
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
};
constexpr uint8_t kKeyBuffer2b[] = {
0xf2, 0xe2, 0xd2, 0xc2, 0xb2, 0xa2, 0x92, 0x82, 0x72, 0x62, 0x52, 0x42, 0x32, 0x22, 0x12, 0x02
};
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
// Taken from the ICDManager Implementation
enum class ICDTestEventTriggerEvent : uint64_t
{
kAddActiveModeReq = 0x0046'0000'00000001,
kRemoveActiveModeReq = 0x0046'0000'00000002,
kInvalidateHalfCounterValues = 0x0046'0000'00000003,
kInvalidateAllCounterValues = 0x0046'0000'00000004,
};
class TestICDStateObserver : public app::ICDStateObserver
{
public:
TestICDStateObserver() = default;
~TestICDStateObserver() = default;
void OnEnterActiveMode() { mOnEnterActiveModeCalled = true; }
void OnEnterIdleMode() { mOnEnterIdleModeCalled = true; }
void OnTransitionToIdle() { mOnTransitionToIdleCalled = true; }
void OnICDModeChange() { mOnICDModeChangeCalled = true; }
void ResetOnEnterActiveMode() { mOnEnterActiveModeCalled = false; }
void ResetOnEnterIdleMode() { mOnEnterIdleModeCalled = false; }
void ResetOnTransitionToIdle() { mOnTransitionToIdleCalled = false; }
void ResetOnICDModeChange() { mOnICDModeChangeCalled = false; }
void ResetAll()
{
ResetOnEnterActiveMode();
ResetOnEnterIdleMode();
ResetOnTransitionToIdle();
ResetOnICDModeChange();
}
bool mOnEnterActiveModeCalled = false;
bool mOnEnterIdleModeCalled = false;
bool mOnICDModeChangeCalled = false;
bool mOnTransitionToIdleCalled = false;
};
class TestSubscriptionsInfoProvider : public SubscriptionsInfoProvider
{
public:
TestSubscriptionsInfoProvider() = default;
~TestSubscriptionsInfoProvider(){};
void SetHasActiveSubscription(bool value) { mHasActiveSubscription = value; };
void SetHasPersistedSubscription(bool value) { mHasPersistedSubscription = value; };
bool SubjectHasActiveSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasActiveSubscription; };
bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasPersistedSubscription; };
private:
bool mHasActiveSubscription = false;
bool mHasPersistedSubscription = false;
};
System::Clock::Internal::MockClock * pMockClock = nullptr;
System::Clock::ClockBase * pRealClock = nullptr;
} // namespace
namespace chip {
namespace app {
class TestICDManager : public Test::LoopbackMessagingContext
{
public:
/*
* Advance the test Mock clock time by the amout passed in argument
* and then force the SystemLayer Timer event loop. It will check for any expired timer,
* and invoke their callbacks if there are any.
*
* @param time_ms: Value in milliseconds.
*/
static void AdvanceClockAndRunEventLoop(Milliseconds64 time)
{
pMockClock->AdvanceMonotonic(time);
GetIOContext().DriveIO();
}
// Performs shared setup for all tests in the test suite
static void SetUpTestSuite()
{
if (pMockClock == nullptr)
{
pMockClock = new Clock::Internal::MockClock();
ASSERT_NE(pMockClock, nullptr);
}
LoopbackMessagingContext::SetUpTestSuite();
VerifyOrReturn(!HasFailure());
ASSERT_EQ(chip::DeviceLayer::PlatformMgr().InitChipStack(), CHIP_NO_ERROR);
DeviceLayer::SetSystemLayerForTesting(&GetSystemLayer());
pRealClock = &SystemClock();
Clock::Internal::SetSystemClockForTesting(pMockClock);
}
// Performs shared teardown for all tests in the test suite
static void TearDownTestSuite()
{
Clock::Internal::SetSystemClockForTesting(pRealClock);
DeviceLayer::SetSystemLayerForTesting(nullptr);
DeviceLayer::PlatformMgr().Shutdown();
LoopbackMessagingContext::TearDownTestSuite();
if (pMockClock != nullptr)
{
delete pMockClock;
pMockClock = nullptr;
}
pRealClock = nullptr;
}
// Performs setup for each individual test in the test suite
void SetUp() override
{
LoopbackMessagingContext::SetUp();
VerifyOrReturn(!HasFailure());
mICDStateObserver.ResetAll();
mICDManager.RegisterObserver(&mICDStateObserver);
mICDManager.Init(&testStorage, &GetFabricTable(), &mKeystore, &GetExchangeManager(), &mSubInfoProvider);
}
// Performs teardown for each individual test in the test suite
void TearDown() override
{
mICDManager.Shutdown();
LoopbackMessagingContext::TearDown();
}
TestSessionKeystoreImpl mKeystore;
ICDManager mICDManager;
TestSubscriptionsInfoProvider mSubInfoProvider;
TestPersistentStorageDelegate testStorage;
TestICDStateObserver mICDStateObserver;
};
TEST_F(TestICDManager, TestICDModeDurations)
{
// After the init we should be in Idle mode
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() + 1_s);
// Idle mode Duration expired, ICDManager transitioned to the ActiveMode.
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() + 1_ms32);
// Active mode Duration expired, ICDManager transitioned to the IdleMode.
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() + 1_s);
// Idle mode Duration expired, ICDManager transitioned to the ActiveMode.
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Events updating the Operation to Active mode can extend the current active mode time by 1 Active mode threshold.
// Kick an active Threshold just before the end of the ActiveMode duration and validate that the active mode is extended.
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() - 1_ms32);
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeThreshold() / 2);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeThreshold());
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
}
#if CHIP_CONFIG_ENABLE_ICD_CIP
/**
* @brief Test verifies that the ICDManager starts its timers correctly based on if it will have any messages to send
* when the IdleMode timer expires
*/
TEST_F(TestICDManager, TestICDModeDurationsWith0ActiveModeDurationWithoutActiveSub)
{
typedef ICDListener::ICDManagementEvents ICDMEvent;
ICDConfigurationData & icdConfigData = ICDConfigurationData::GetInstance();
ICDConfigurationDataTestAccess privateIcdConfigData(&icdConfigData);
// Set FeatureMap - Configures CIP, UAT and LITS to 1
mICDManager.SetTestFeatureMapValue(0x07);
// Set that there are no matching subscriptions
mSubInfoProvider.SetHasActiveSubscription(false);
mSubInfoProvider.SetHasPersistedSubscription(false);
// Set New durations for test case
Milliseconds32 oldActiveModeDuration = icdConfigData.GetActiveModeDuration();
privateIcdConfigData.SetModeDurations(MakeOptional<Milliseconds32>(0), NullOptional);
// Verify That ICDManager starts in Idle
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Reset IdleModeInterval since it was started before the ActiveModeDuration change
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Force the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is now 0
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeThreshold() + 1_ms16);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Expire Idle mode duration; ICDManager should remain in IdleMode since it has no message to send
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Add an entry to the ICDMonitoringTable
ICDMonitoringTable table(testStorage, kTestFabricIndex1, kMaxTestClients, &(mKeystore));
ICDMonitoringEntry entry(&(mKeystore));
entry.checkInNodeID = kClientNodeId11;
entry.monitoredSubject = kClientNodeId11;
EXPECT_EQ(CHIP_NO_ERROR, entry.SetKey(ByteSpan(kKeyBuffer1a)));
EXPECT_EQ(CHIP_NO_ERROR, table.Set(0, entry));
// Trigger register event after first entry was added
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is now in the LIT operating mode
EXPECT_EQ(icdConfigData.GetICDMode(), ICDConfigurationData::ICDMode::LIT);
// Kick an ActiveModeThreshold since a Registration can only happen from an incoming message that would transition the ICD
// to ActiveMode
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Return the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is 0
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeThreshold() + 1_ms16);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Expire IdleMode timer - Device should be in ActiveMode since it has an ICDM registration
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Remove entry from the fabric - ICDManager won't have any messages to send
EXPECT_EQ(CHIP_NO_ERROR, table.Remove(0));
EXPECT_TRUE(table.IsEmpty());
// Return the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is 0
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeThreshold() + 1_ms16);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Expire Idle mode duration; ICDManager should remain in IdleMode since it has no message to send
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Reset Old durations
privateIcdConfigData.SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
}
/**
* @brief Test verifies that the ICDManager remains in IdleMode since it will not have any messages to send
* when the IdleMode timer expires
*/
TEST_F(TestICDManager, TestICDModeDurationsWith0ActiveModeDurationWithActiveSub)
{
typedef ICDListener::ICDManagementEvents ICDMEvent;
ICDConfigurationData & icdConfigData = ICDConfigurationData::GetInstance();
ICDConfigurationDataTestAccess privateIcdConfigData(&icdConfigData);
// Set FeatureMap - Configures CIP, UAT and LITS to 1
mICDManager.SetTestFeatureMapValue(0x07);
// Set that there are not matching subscriptions
mSubInfoProvider.SetHasActiveSubscription(true);
mSubInfoProvider.SetHasPersistedSubscription(true);
// Set New durations for test case
Milliseconds32 oldActiveModeDuration = icdConfigData.GetActiveModeDuration();
privateIcdConfigData.SetModeDurations(MakeOptional<Milliseconds32>(0), NullOptional);
// Verify That ICDManager starts in Idle
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Reset IdleModeInterval since it was started before the ActiveModeDuration change
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Force the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is now 0
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeThreshold() + 1_ms16);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Expire Idle mode duration; ICDManager should remain in IdleMode since it has no message to send
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Add an entry to the ICDMonitoringTable
ICDMonitoringTable table(testStorage, kTestFabricIndex1, kMaxTestClients, &(mKeystore));
ICDMonitoringEntry entry(&(mKeystore));
entry.checkInNodeID = kClientNodeId11;
entry.monitoredSubject = kClientNodeId11;
EXPECT_EQ(CHIP_NO_ERROR, entry.SetKey(ByteSpan(kKeyBuffer1a)));
EXPECT_EQ(CHIP_NO_ERROR, table.Set(0, entry));
// Trigger register event after first entry was added
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is now in the LIT operating mode
EXPECT_EQ(icdConfigData.GetICDMode(), ICDConfigurationData::ICDMode::LIT);
// Kick an ActiveModeThreshold since a Registration can only happen from an incoming message that would transition the ICD
// to ActiveMode
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Return the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is 0
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeThreshold() + 1_ms16);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Expire IdleMode timer - Device stay in IdleMode since it has an active subscription for the ICDM entry
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Remove entry from the fabric
EXPECT_EQ(CHIP_NO_ERROR, table.Remove(0));
EXPECT_TRUE(table.IsEmpty());
// Trigger unregister event after last entry was removed
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is now in the LIT operating mode
EXPECT_EQ(icdConfigData.GetICDMode(), ICDConfigurationData::ICDMode::SIT);
// Kick an ActiveModeThreshold since a Unregistration can only happen from an incoming message that would transition the ICD
// to ActiveMode
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Return the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is 0
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeThreshold() + 1_ms16);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Expire Idle mode duration; ICDManager should remain in IdleMode since it has no message to send
AdvanceClockAndRunEventLoop(icdConfigData.GetIdleModeDuration() + 1_s);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Reset Old durations
privateIcdConfigData.SetModeDurations(MakeOptional<Milliseconds32>(oldActiveModeDuration), NullOptional);
}
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
TEST_F(TestICDManager, TestKeepActivemodeRequests)
{
typedef ICDListener::KeepActiveFlag ActiveFlag;
ICDNotifier notifier = ICDNotifier::GetInstance();
// Setting a requirement will transition the ICD to active mode.
notifier.NotifyActiveRequestNotification(ActiveFlag::kCommissioningWindowOpen);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time so active mode duration expires.
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() + 1_ms32);
// Requirement flag still set. We stay in active mode
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Remove requirement. we should directly transition to idle mode.
notifier.NotifyActiveRequestWithdrawal(ActiveFlag::kCommissioningWindowOpen);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
notifier.NotifyActiveRequestNotification(ActiveFlag::kFailSafeArmed);
// Requirement will transition us to active mode.
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time, but by less than the active mode duration and remove the requirement.
// We should stay in active mode.
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() / 2);
notifier.NotifyActiveRequestWithdrawal(ActiveFlag::kFailSafeArmed);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time again, The activemode duration is completed.
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() + 1_ms32);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Set two requirements
notifier.NotifyActiveRequestNotification(ActiveFlag::kFailSafeArmed);
notifier.NotifyActiveRequestNotification(ActiveFlag::kExchangeContextOpen);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// advance time so the active mode duration expires.
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() + 1_ms32);
// A requirement flag is still set. We stay in active mode.
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// remove 1 requirement. Active mode is maintained
notifier.NotifyActiveRequestWithdrawal(ActiveFlag::kFailSafeArmed);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// remove the last requirement
notifier.NotifyActiveRequestWithdrawal(ActiveFlag::kExchangeContextOpen);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
}
#if CHIP_CONFIG_ENABLE_ICD_CIP
/**
* @brief Test that verifies that the ICDManager is in the correct operating mode based on entries
* in the ICDMonitoringTable
*/
TEST_F(TestICDManager, TestICDMRegisterUnregisterEvents)
{
typedef ICDListener::ICDManagementEvents ICDMEvent;
ICDNotifier notifier = ICDNotifier::GetInstance();
// Set FeatureMap
// Configures CIP, UAT and LITS to 1
mICDManager.SetTestFeatureMapValue(0x07);
// Check ICDManager starts in SIT mode if no entries are present
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::SIT);
// Trigger a "fake" register, ICDManager shoudl remain in SIT mode
notifier.NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager stayed in SIT mode
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::SIT);
// Create tables with different fabrics
ICDMonitoringTable table1(testStorage, kTestFabricIndex1, kMaxTestClients, &(mKeystore));
ICDMonitoringTable table2(testStorage, kTestFabricIndex2, kMaxTestClients, &(mKeystore));
// Add first entry to the first fabric
ICDMonitoringEntry entry1(&(mKeystore));
entry1.checkInNodeID = kClientNodeId11;
entry1.monitoredSubject = kClientNodeId12;
EXPECT_EQ(CHIP_NO_ERROR, entry1.SetKey(ByteSpan(kKeyBuffer1a)));
EXPECT_EQ(CHIP_NO_ERROR, table1.Set(0, entry1));
// Trigger register event after first entry was added
notifier.NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is now in the LIT operating mode
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::LIT);
// Add second entry to the first fabric
ICDMonitoringEntry entry2(&(mKeystore));
entry2.checkInNodeID = kClientNodeId12;
entry2.monitoredSubject = kClientNodeId11;
EXPECT_EQ(CHIP_NO_ERROR, entry2.SetKey(ByteSpan(kKeyBuffer1b)));
EXPECT_EQ(CHIP_NO_ERROR, table1.Set(1, entry2));
// Trigger register event after first entry was added
notifier.NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is now in the LIT operating mode
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::LIT);
// Add first entry to the first fabric
ICDMonitoringEntry entry3(&(mKeystore));
entry3.checkInNodeID = kClientNodeId21;
entry3.monitoredSubject = kClientNodeId22;
EXPECT_EQ(CHIP_NO_ERROR, entry3.SetKey(ByteSpan(kKeyBuffer2a)));
EXPECT_EQ(CHIP_NO_ERROR, table2.Set(0, entry3));
// Trigger register event after first entry was added
notifier.NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is now in the LIT operating mode
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::LIT);
// Add second entry to the first fabric
ICDMonitoringEntry entry4(&(mKeystore));
entry4.checkInNodeID = kClientNodeId22;
entry4.monitoredSubject = kClientNodeId21;
EXPECT_EQ(CHIP_NO_ERROR, entry4.SetKey(ByteSpan(kKeyBuffer2b)));
EXPECT_EQ(CHIP_NO_ERROR, table2.Set(1, entry4));
// Clear a fabric
EXPECT_EQ(CHIP_NO_ERROR, table2.RemoveAll());
// Trigger register event after fabric was cleared
notifier.NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is still in the LIT operating mode
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::LIT);
// Remove single entry from remaining fabric
EXPECT_EQ(CHIP_NO_ERROR, table1.Remove(1));
// Trigger register event after fabric was cleared
notifier.NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is still in the LIT operating mode
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::LIT);
// Remove last entry from remaining fabric
EXPECT_EQ(CHIP_NO_ERROR, table1.Remove(0));
EXPECT_TRUE(table1.IsEmpty());
EXPECT_TRUE(table2.IsEmpty());
// Trigger register event after fabric was cleared
notifier.NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Check ICDManager is still in the LIT operating mode
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDMode(), ICDConfigurationData::ICDMode::SIT);
}
TEST_F(TestICDManager, TestICDCounter)
{
uint32_t counter = ICDConfigurationData::GetInstance().GetICDCounter().GetValue();
// Shut down and reinit ICDManager to increment counter
mICDManager.Shutdown();
mICDManager.Init(&(testStorage), &GetFabricTable(), &(mKeystore), &GetExchangeManager(), &(mSubInfoProvider));
mICDManager.RegisterObserver(&(mICDStateObserver));
EXPECT_EQ(counter + ICDConfigurationData::kICDCounterPersistenceIncrement,
ICDConfigurationData::GetInstance().GetICDCounter().GetValue());
}
TEST_F(TestICDManager, TestOnSubscriptionReport)
{
ICDNotifier notifier = ICDNotifier::GetInstance();
// After the init we should be in Idle mode
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Trigger a subscription report
notifier.NotifySubscriptionReport();
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Trigger another subscription report - active time should not be increased
notifier.NotifySubscriptionReport();
// Advance time so active mode interval expires.
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() + 1_ms32);
// After the init we should be in Idle mode
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
}
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
/**
* @brief Test verifies the logic of the ICDManager when it receives a StayActiveRequest
*/
TEST_F(TestICDManager, TestICDMStayActive)
{
ICDNotifier notifier = ICDNotifier::GetInstance();
ICDConfigurationData & icdConfigData = ICDConfigurationData::GetInstance();
// Verify That ICDManager starts in Idle
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Trigger a subscription report. Put the ICD manager into active mode.
notifier.NotifySubscriptionReport();
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time just before ActiveMode timer expires
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeDuration() - 1_ms32);
// Confirm ICD manager is in active mode
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
uint32_t stayActiveRequestedMs = 20000;
// Send a stay active request for 20 seconds
uint32_t stayActivePromisedMs = mICDManager.StayActiveRequest(stayActiveRequestedMs);
// confirm the promised time is the same as the requested time
EXPECT_EQ(stayActivePromisedMs, stayActiveRequestedMs);
// Advance time by the duration of the stay stayActiveRequestedMs - 1 ms
AdvanceClockAndRunEventLoop(Milliseconds32(stayActiveRequestedMs) - 1_ms32);
// Confirm ICD manager is in active mode
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time by 1ms and Confirm ICD manager is in idle mode
AdvanceClockAndRunEventLoop(1_ms32);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Trigger a subscription report Put the ICD manager into active mode
notifier.NotifySubscriptionReport();
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time by the duration of the stay active request - 1 ms
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeDuration() - 1_ms32);
stayActiveRequestedMs = 35000;
// Send a stay active request for 35 seconds, which is higher than the maximum stay active duration (30 seconds)
stayActivePromisedMs = mICDManager.StayActiveRequest(stayActiveRequestedMs);
// confirm the promised time is the maximum stay active duration (30 seconds)
EXPECT_EQ(stayActivePromisedMs, 30000UL);
// Advance time by the duration of the max stay active duration - 1 ms
AdvanceClockAndRunEventLoop(Milliseconds32(30000) - 1_ms32);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time by 1ms and Confirm ICD manager is in idle mode
AdvanceClockAndRunEventLoop(1_ms32);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Trigger a subscription report Put the ICD manager into active mode
notifier.NotifySubscriptionReport();
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance time by the duration of the stay active request - 1 ms
AdvanceClockAndRunEventLoop(icdConfigData.GetActiveModeDuration() - 1_ms32);
stayActiveRequestedMs = 30000;
// Send a stay active request for 30 seconds
stayActivePromisedMs = mICDManager.StayActiveRequest(stayActiveRequestedMs);
// confirm the promised time is the same as the requested time
EXPECT_EQ(stayActivePromisedMs, 30000UL);
// Advance time by the duration of the stay active request - 20000 ms
AdvanceClockAndRunEventLoop(Milliseconds32(stayActiveRequestedMs) - 20000_ms32);
// Confirm ICD manager is in active mode, we should have 20000 seconds left at that point
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
stayActiveRequestedMs = 10000;
stayActivePromisedMs = mICDManager.StayActiveRequest(stayActiveRequestedMs);
// confirm the promised time is 20000 since the device is already planing to stay active longer than the requested time
EXPECT_EQ(stayActivePromisedMs, 20000UL);
}
#if CHIP_CONFIG_ENABLE_ICD_CIP
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
TEST_F(TestICDManager, TestShouldCheckInMsgsBeSentAtActiveModeFunction)
{
// Test 1 - Has no ActiveSubscription & no persisted subscription
mSubInfoProvider.SetHasActiveSubscription(false);
mSubInfoProvider.SetHasPersistedSubscription(false);
EXPECT_TRUE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
// Test 2 - Has no active subscription & a persisted subscription
mSubInfoProvider.SetHasActiveSubscription(false);
mSubInfoProvider.SetHasPersistedSubscription(true);
EXPECT_FALSE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
// Test 3 - Has an active subscription & a persisted subscription
mSubInfoProvider.SetHasActiveSubscription(true);
mSubInfoProvider.SetHasPersistedSubscription(true);
EXPECT_FALSE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
}
#else
TEST_F(TestICDManager, TestShouldCheckInMsgsBeSentAtActiveModeFunction)
{
// Test 1 - Has no active subscription and no persisted subscription at boot up
mSubInfoProvider.SetHasActiveSubscription(false);
mSubInfoProvider.SetHasPersistedSubscription(false);
EXPECT_TRUE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
// Test 2 - Has no active subscription and a persisted subscription at boot up
mSubInfoProvider.SetHasActiveSubscription(false);
mSubInfoProvider.SetHasPersistedSubscription(true);
EXPECT_FALSE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
mICDManager.SetBootUpResumeSubscriptionExecuted();
// Test 3 - Has an active subscription and a persisted subscription during normal operations
mSubInfoProvider.SetHasActiveSubscription(true);
mSubInfoProvider.SetHasPersistedSubscription(true);
EXPECT_FALSE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
// Test 4 - Has no active subscription and a persisted subscription during normal operations
mSubInfoProvider.SetHasActiveSubscription(false);
mSubInfoProvider.SetHasPersistedSubscription(true);
EXPECT_TRUE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
}
#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
#else
TEST_F(TestICDManager, TestShouldCheckInMsgsBeSentAtActiveModeFunction)
{
// Test 1 - Has an active subscription
mSubInfoProvider.SetHasActiveSubscription(true);
EXPECT_FALSE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
// Test 2 - Has no active subscription
mSubInfoProvider.SetHasActiveSubscription(false);
EXPECT_TRUE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
// Test 3 - Make sure that the persisted subscription has no impact
mSubInfoProvider.SetHasPersistedSubscription(true);
EXPECT_TRUE(mICDManager.ShouldCheckInMsgsBeSentAtActiveModeFunction(kTestFabricIndex1, kClientNodeId11));
}
#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
TEST_F(TestICDManager, TestHandleTestEventTriggerActiveModeReq)
{
// Verify That ICDManager starts in Idle
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
// Add ActiveMode req for the Test event trigger event
mICDManager.HandleEventTrigger(static_cast<uint64_t>(ICDTestEventTriggerEvent::kAddActiveModeReq));
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Advance clock by the ActiveModeDuration and check that the device is still in ActiveMode
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() + 1_ms32);
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::ActiveMode);
// Remove req and device should go to IdleMode
mICDManager.HandleEventTrigger(static_cast<uint64_t>(ICDTestEventTriggerEvent::kRemoveActiveModeReq));
EXPECT_EQ(mICDManager.GetOperaionalState(), ICDManager::OperationalState::IdleMode);
}
#if CHIP_CONFIG_ENABLE_ICD_CIP
TEST_F(TestICDManager, TestHandleTestEventTriggerInvalidateHalfCounterValues)
{
constexpr uint32_t startValue = 1;
constexpr uint32_t expectedValue = 2147483648;
// Set starting value
uint32_t currentValue = ICDConfigurationData::GetInstance().GetICDCounter().GetValue();
uint32_t delta = startValue - currentValue;
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDCounter().AdvanceBy(delta), CHIP_NO_ERROR);
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDCounter().GetValue(), startValue);
// Trigger ICD kInvalidateHalfCounterValues event
mICDManager.HandleEventTrigger(static_cast<uint64_t>(ICDTestEventTriggerEvent::kInvalidateHalfCounterValues));
// Validate counter has the expected value
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDCounter().GetValue(), expectedValue);
}
TEST_F(TestICDManager, TestHandleTestEventTriggerInvalidateAllCounterValues)
{
constexpr uint32_t startValue = 105;
constexpr uint32_t expectedValue = 104;
// Set starting value
uint32_t currentValue = ICDConfigurationData::GetInstance().GetICDCounter().GetValue();
uint32_t delta = startValue - currentValue;
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDCounter().AdvanceBy(delta), CHIP_NO_ERROR);
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDCounter().GetValue(), startValue);
// Trigger ICD kInvalidateAllCounterValues event
mICDManager.HandleEventTrigger(static_cast<uint64_t>(ICDTestEventTriggerEvent::kInvalidateAllCounterValues));
// Validate counter has the expected value
EXPECT_EQ(ICDConfigurationData::GetInstance().GetICDCounter().GetValue(), expectedValue);
}
#endif // CHIP_CONFIG_ENABLE_ICD_CIP
/**
* @brief Test verifies when OnEnterIdleMode is called during normal operations.
* Without the ActiveMode timer being extended
*/
TEST_F(TestICDManager, TestICDStateObserverOnEnterIdleModeActiveModeDuration)
{
// Verify that ICDManager starts in IdleMode and calls OnEnterIdleMode
EXPECT_TRUE(mICDStateObserver.mOnEnterIdleModeCalled);
mICDStateObserver.ResetOnEnterIdleMode();
// Advance clock just before IdleMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Expire IdleModeInterval
AdvanceClockAndRunEventLoop(1_s);
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Advance clock Just before ActiveMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() - 1_ms32);
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Expire ActiveMode timer
AdvanceClockAndRunEventLoop(1_ms32);
EXPECT_TRUE(mICDStateObserver.mOnEnterIdleModeCalled);
}
/**
* @brief Test verifies when OnEnterIdleMode is called with the ActiveMode timer gets extended
*/
TEST_F(TestICDManager, TestICDStateObserverOnEnterIdleModeActiveModeThreshold)
{
// Verify that ICDManager starts in IdleMode and calls OnEnterIdleMode
EXPECT_TRUE(mICDStateObserver.mOnEnterIdleModeCalled);
mICDStateObserver.ResetOnEnterIdleMode();
// Advance clock just before the IdleMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Expire IdleMode timer
AdvanceClockAndRunEventLoop(1_s);
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Advance clock Just before ActiveMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() - 1_ms32);
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Increase ActiveMode timer by one ActiveModeThreshold
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Advance clock Just before ActiveMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeThreshold() - 1_ms32);
EXPECT_FALSE(mICDStateObserver.mOnEnterIdleModeCalled);
// Expire ActiveMode timer
AdvanceClockAndRunEventLoop(1_ms32);
EXPECT_TRUE(mICDStateObserver.mOnEnterIdleModeCalled);
}
TEST_F(TestICDManager, TestICDStateObserverOnEnterActiveMode)
{
// Verify OnEnterActiveMode wasn't called at Init
EXPECT_FALSE(mICDStateObserver.mOnEnterActiveModeCalled);
// Advance clock just before IdleMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
EXPECT_FALSE(mICDStateObserver.mOnEnterActiveModeCalled);
// Expire IdleMode timer and check wether OnEnterActiveMode was called
AdvanceClockAndRunEventLoop(1_s);
EXPECT_TRUE(mICDStateObserver.mOnEnterActiveModeCalled);
mICDStateObserver.ResetOnEnterActiveMode();
// Advance clock just before the ActiveMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() - 1_ms32);
// Verify OnEnterActiveMde wasn't called
EXPECT_FALSE(mICDStateObserver.mOnEnterActiveModeCalled);
// Increase ActiveMode timer by one ActiveModeThreshold
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
// Verify OnEnterActiveMde wasn't called
EXPECT_FALSE(mICDStateObserver.mOnEnterActiveModeCalled);
// Advance clock just before ActiveMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeThreshold() - 1_ms32);
// Verify OnEnterActiveMde wasn't called
EXPECT_FALSE(mICDStateObserver.mOnEnterActiveModeCalled);
// Expire ActiveMode timer
AdvanceClockAndRunEventLoop(1_ms32);
// Verify OnEnterActiveMde wasn't called
EXPECT_FALSE(mICDStateObserver.mOnEnterActiveModeCalled);
// Advance clock just before IdleMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
EXPECT_FALSE(mICDStateObserver.mOnEnterActiveModeCalled);
// Expire IdleMode timer and check OnEnterActiveMode was called
AdvanceClockAndRunEventLoop(1_s);
EXPECT_TRUE(mICDStateObserver.mOnEnterActiveModeCalled);
}
#if CHIP_CONFIG_ENABLE_ICD_CIP
TEST_F(TestICDManager, TestICDStateObserverOnICDModeChange)
{
typedef ICDListener::ICDManagementEvents ICDMEvent;
// Set FeatureMap - Configures CIP, UAT and LITS to 1
mICDManager.SetTestFeatureMapValue(0x07);
// Since we don't have a registration, we stay in SIT mode. No changes
EXPECT_FALSE(mICDStateObserver.mOnICDModeChangeCalled);
// Trigger register event to force ICDManager to re-evaluate OperatingMode
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Since we don't have a registration, we stay in SIT mode. No changes
EXPECT_FALSE(mICDStateObserver.mOnICDModeChangeCalled);
// Add an entry to the ICDMonitoringTable
ICDMonitoringTable table(testStorage, kTestFabricIndex1, kMaxTestClients, &(mKeystore));
ICDMonitoringEntry entry(&(mKeystore));
entry.checkInNodeID = kClientNodeId11;
entry.monitoredSubject = kClientNodeId11;
EXPECT_EQ(CHIP_NO_ERROR, entry.SetKey(ByteSpan(kKeyBuffer1a)));
EXPECT_EQ(CHIP_NO_ERROR, table.Set(0, entry));
// Trigger register event after first entry was added
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// We have a registration. Transition to LIT mode
EXPECT_TRUE(mICDStateObserver.mOnICDModeChangeCalled);
mICDStateObserver.ResetOnICDModeChange();
// Trigger register event to force ICDManager to re-evaluate OperatingMode
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// We have a registration. We stay in LIT mode. No changes.
EXPECT_FALSE(mICDStateObserver.mOnICDModeChangeCalled);
// Remove entry from the ICDMonitoringTable
EXPECT_EQ(CHIP_NO_ERROR, table.Remove(0));
ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
// Since we don't have a registration anymore. Transition to SIT mode.
EXPECT_TRUE(mICDStateObserver.mOnICDModeChangeCalled);
mICDStateObserver.ResetOnICDModeChange();
}
TEST_F(TestICDManager, TestICDStateObserverOnICDModeChangeOnInit)
{
// Set FeatureMap - Configures CIP, UAT and LITS to 1
mICDManager.SetTestFeatureMapValue(0x07);
ICDMonitoringTable table(testStorage, kTestFabricIndex1, kMaxTestClients, &(mKeystore));
// Since we don't have a registration, we stay in SIT mode. No changes
EXPECT_FALSE(mICDStateObserver.mOnICDModeChangeCalled);
// Add an entry to the ICDMonitoringTable
ICDMonitoringEntry entry(&(mKeystore));
entry.checkInNodeID = kClientNodeId11;
entry.monitoredSubject = kClientNodeId11;
EXPECT_EQ(CHIP_NO_ERROR, entry.SetKey(ByteSpan(kKeyBuffer1a)));
EXPECT_EQ(CHIP_NO_ERROR, table.Set(0, entry));
// Shut down and reinit ICDManager - We should go to LIT mode since we have a registration
mICDManager.Shutdown();
mICDManager.RegisterObserver(&(mICDStateObserver));
mICDManager.Init(&testStorage, &GetFabricTable(), &mKeystore, &GetExchangeManager(), &mSubInfoProvider);
// We have a registration, transition to LIT mode
EXPECT_TRUE(mICDStateObserver.mOnICDModeChangeCalled);
mICDStateObserver.ResetOnICDModeChange();
// Remove entry from the ICDMonitoringTable
EXPECT_EQ(CHIP_NO_ERROR, table.Remove(0));
}
#endif
/**
* @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is greater than the
* ICD_ACTIVE_TIME_JITTER_MS
*/
TEST_F(TestICDManager, TestICDStateObserverOnTransitionToIdleModeGreaterActiveModeDuration)
{
ICDConfigurationDataTestAccess privateIcdConfigData(&ICDConfigurationData::GetInstance());
// Set New durations for test case - ActiveModeDuration must be longuer than ICD_ACTIVE_TIME_JITTER_MS
Milliseconds32 oldActiveModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
privateIcdConfigData.SetModeDurations(
MakeOptional<Milliseconds32>(Milliseconds32(200) + Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS)), NullOptional);
// Advance clock just before IdleMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Expire IdleMode timer
AdvanceClockAndRunEventLoop(1_s);
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Advance time just before OnTransitionToIdleMode is called
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeDuration() -
Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS) - 1_ms32);
// Check mOnTransitionToIdleCalled has not been called
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Increase ActiveMode timer by one ActiveModeThreshold
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Advance time just before OnTransitionToIdleMode is called
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeThreshold() -
Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS) - 1_ms32);
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Expire OnTransitionToIdleMode
AdvanceClockAndRunEventLoop(1_ms32);
// Check mOnTransitionToIdleCalled has been called
EXPECT_TRUE(mICDStateObserver.mOnTransitionToIdleCalled);
mICDStateObserver.ResetOnTransitionToIdle();
// Expire ActiveMode timer
AdvanceClockAndRunEventLoop(Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS));
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Reset Old durations
privateIcdConfigData.SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
}
/**
* @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is equal to the
* ICD_ACTIVE_TIME_JITTER_MS.
*/
TEST_F(TestICDManager, TestICDStateObserverOnTransitionToIdleModeEqualActiveModeDuration)
{
ICDConfigurationDataTestAccess privateIcdConfigData(&ICDConfigurationData::GetInstance());
// Set New durations for test case - ActiveModeDuration must be equal to ICD_ACTIVE_TIME_JITTER_MS
Milliseconds32 oldActiveModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
privateIcdConfigData.SetModeDurations(MakeOptional<Milliseconds32>(Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS)), NullOptional);
// Advance clock just before IdleMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Expire IdleMode timer
AdvanceClockAndRunEventLoop(1_s);
// In this scenario, The ICD state machine kicked a OnTransitionToIdle timer with a duration of 0 seconds.
// The freeRTOS systemlayer timer calls a 0s timer's callback instantly while on posix it take and 1 addition event loop.
// Thefore, the expect result diverges here based on the systemlayer implementation. Skip this check.
// https://github.com/project-chip/connectedhomeip/issues/33441
// EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Expire OnTransitionToIdleMode
AdvanceClockAndRunEventLoop(1_ms32);
// All systems should have called the OnTransitionToIdle callback by now.
EXPECT_TRUE(mICDStateObserver.mOnTransitionToIdleCalled);
// Reset Old durations
privateIcdConfigData.SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
}
/**
* @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is 0 and without an ActiveMode req
*/
TEST_F(TestICDManager, TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWithoutReq)
{
ICDConfigurationDataTestAccess privateIcdConfigData(&ICDConfigurationData::GetInstance());
// Set New durations for test case - ActiveModeDuration equal 0
Milliseconds32 oldActiveModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
privateIcdConfigData.SetModeDurations(MakeOptional<Milliseconds32>(0), NullOptional);
// Advance clock just before IdleMode timer expires
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Expire IdleMode timer
AdvanceClockAndRunEventLoop(1_s);
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Increase time by 1 - Should not trigger OnTransitionToIdle.
// Timer length is one ActiveModeThreshold
AdvanceClockAndRunEventLoop(1_ms32);
EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// Expire ActiveModeThreshold
AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetActiveModeThreshold());
EXPECT_TRUE(mICDStateObserver.mOnTransitionToIdleCalled);
// Reset Old durations
privateIcdConfigData.SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
}
/**
* @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is 0 with an ActiveMode req
* TODO(#33074): When the OnTransitionToIdle edge is fixed, we can enable this test
*/
// TEST_F(TestICDManager, TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWithReq)
// {
// // Set New durations for test case - ActiveModeDuration equal 0
// Milliseconds32 oldActiveModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
// ICDConfigurationData::GetInstance().SetModeDurations(MakeOptional<Milliseconds32>(0), NullOptional);
// // Add ActiveMode req for the Test event trigger event
// mICDManager.HandleEventTrigger(static_cast<uint64_t>(ICDTestEventTriggerEvent::kAddActiveModeReq));
// // Expire IdleMode timer
// AdvanceClockAndRunEventLoop(ICDConfigurationData::GetInstance().GetIdleModeDuration());
// EXPECT_FALSE(mICDStateObserver.mOnTransitionToIdleCalled);
// // Reset Old durations
// ICDConfigurationData::GetInstance().SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
// }
} // namespace app
} // namespace chip