[ICD] Update and Document ICDManager interface (#33081)
* Update and Document ICDManager interface
* Apply suggestions from code review
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Update ICDManagementCluster test to cover off the Check-In message randomness
* Address comments and update comments
* restyle
---------
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/examples/lit-icd-app/nrfconnect/main/AppTask.cpp b/examples/lit-icd-app/nrfconnect/main/AppTask.cpp
index d7d35bb..336024f 100644
--- a/examples/lit-icd-app/nrfconnect/main/AppTask.cpp
+++ b/examples/lit-icd-app/nrfconnect/main/AppTask.cpp
@@ -296,7 +296,8 @@
void AppTask::IcdUatEventHandler(const AppEvent &)
{
- Server::GetInstance().GetICDManager().UpdateOperationState(ICDManager::OperationalState::ActiveMode);
+ // Temporarily claim network activity, until we implement a "user trigger" reason for ICD wakeups.
+ PlatformMgr().ScheduleWork([](intptr_t) { ICDNotifier::GetInstance().NotifyNetworkActivityNotification(); });
}
void AppTask::FunctionTimerTimeoutCallback(k_timer * timer)
diff --git a/examples/platform/silabs/BaseApplication.cpp b/examples/platform/silabs/BaseApplication.cpp
index a6986db..f02826f 100644
--- a/examples/platform/silabs/BaseApplication.cpp
+++ b/examples/platform/silabs/BaseApplication.cpp
@@ -515,9 +515,7 @@
SILABS_LOG("Network is already provisioned, Ble advertisement not enabled");
#if CHIP_CONFIG_ENABLE_ICD_SERVER
// Temporarily claim network activity, until we implement a "user trigger" reason for ICD wakeups.
- PlatformMgr().LockChipStack();
- ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
- PlatformMgr().UnlockChipStack();
+ PlatformMgr().ScheduleWork([](intptr_t) { ICDNotifier::GetInstance().NotifyNetworkActivityNotification(); });
#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
}
}
diff --git a/src/app/icd/server/ICDManager.cpp b/src/app/icd/server/ICDManager.cpp
index 5f93946..8691a4d 100644
--- a/src/app/icd/server/ICDManager.cpp
+++ b/src/app/icd/server/ICDManager.cpp
@@ -378,12 +378,13 @@
if (ICDConfigurationData::GetInstance().GetICDMode() != tempMode)
{
ICDConfigurationData::GetInstance().SetICDMode(tempMode);
- postObserverEvent(ObserverEventType::ICDModeChange);
// Can't use attribute accessors/Attributes::OperatingMode::Set in unit tests
#if !CONFIG_BUILD_FOR_HOST_UNIT_TEST
Attributes::OperatingMode::Set(kRootEndpointId, static_cast<OperatingModeEnum>(tempMode));
#endif
+
+ postObserverEvent(ObserverEventType::ICDModeChange);
}
// When in SIT mode, the slow poll interval SHOULDN'T be greater than the SIT mode polling threshold, per spec.
@@ -433,6 +434,8 @@
{
ChipLogError(AppServer, "Failed to set Slow Polling Interval: err %" CHIP_ERROR_FORMAT, err.Format());
}
+
+ postObserverEvent(ObserverEventType::EnterIdleMode);
}
else if (state == OperationalState::ActiveMode)
{
@@ -447,7 +450,7 @@
if (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
{
- // A Network Activity triggered the active mode and activeModeDuration is 0.
+ // Network Activity triggered the active mode and activeModeDuration is 0.
// Stay active for at least Active Mode Threshold.
activeModeDuration = ICDConfigurationData::GetInstance().GetActiveModeThreshold();
}
@@ -455,9 +458,14 @@
DeviceLayer::SystemLayer().StartTimer(activeModeDuration, OnActiveModeDone, this);
Milliseconds32 activeModeJitterInterval = Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS);
+ // TODO(#33074): Edge case when we transition to IdleMode with this condition being true
+ // (activeModeDuration == kZero && !mKeepActiveFlags.HasAny())
activeModeJitterInterval =
(activeModeDuration >= activeModeJitterInterval) ? activeModeDuration - activeModeJitterInterval : kZero;
+ // Reset this flag when we enter ActiveMode to avoid having a feedback loop that keeps us indefinitly in
+ // ActiveMode.
+ mTransitionToIdleCalled = false;
DeviceLayer::SystemLayer().StartTimer(activeModeJitterInterval, OnTransitionToIdle, this);
CHIP_ERROR err =
@@ -504,10 +512,6 @@
{
ICDManager * pICDManager = reinterpret_cast<ICDManager *>(appState);
pICDManager->UpdateOperationState(OperationalState::ActiveMode);
-
- // We only reset this flag when idle mode is complete to avoid re-triggering the check when an event brings us back to active,
- // which could cause a loop.
- pICDManager->mTransitionToIdleCalled = false;
}
void ICDManager::OnActiveModeDone(System::Layer * aLayer, void * appState)
@@ -532,10 +536,10 @@
}
/* ICDListener functions. */
+
void ICDManager::OnKeepActiveRequest(KeepActiveFlags request)
{
assertChipStackLockedByCurrentThread();
-
VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
if (request.Has(KeepActiveFlag::kExchangeContextOpen))
@@ -560,7 +564,6 @@
void ICDManager::OnActiveRequestWithdrawal(KeepActiveFlags request)
{
assertChipStackLockedByCurrentThread();
-
VerifyOrReturn(request < KeepActiveFlagsValues::kInvalidFlag);
if (request.Has(KeepActiveFlag::kExchangeContextOpen))
@@ -697,6 +700,10 @@
obs->mObserver->OnEnterActiveMode();
return Loop::Continue;
}
+ case ObserverEventType::EnterIdleMode: {
+ obs->mObserver->OnEnterIdleMode();
+ return Loop::Continue;
+ }
case ObserverEventType::TransitionToIdle: {
obs->mObserver->OnTransitionToIdle();
return Loop::Continue;
diff --git a/src/app/icd/server/ICDManager.h b/src/app/icd/server/ICDManager.h
index 69dfdbf..bf6e439 100644
--- a/src/app/icd/server/ICDManager.h
+++ b/src/app/icd/server/ICDManager.h
@@ -57,7 +57,9 @@
class ICDManager : public ICDListener, public TestEventTriggerHandler
{
public:
- // This structure is used for the creation an ObjectPool of ICDStateObserver pointers
+ /**
+ * @brief This structure is used for the creation an ObjectPool of ICDStateObserver pointers
+ */
struct ObserverPointer
{
ObserverPointer(ICDStateObserver * obs) : mObserver(obs) {}
@@ -71,11 +73,31 @@
ActiveMode,
};
- // This enum class represents to all ICDStateObserver callbacks available from the
- // mStateObserverPool for the ICDManager.
+ /**
+ * @brief This enum class represents to all ICDStateObserver callbacks available from the
+ * mStateObserverPool for the ICDManager.
+ *
+ * EnterActiveMode, TransitionToIdle and EnterIdleMode will always be called as a trio in the same order.
+ * Each event will only be called once per cycle.
+ * EnterActiveMode will always be called first, when the ICD has transitioned to ActiveMode.
+ * TransitionToIdle will always be second. This event will only be called the first time there is
+ * `ICD_ACTIVE_TIME_JITTER_MS` remaining to the ActiveMode timer.
+ * When this event is called, the ICD is still in ActiveMode.
+ * If the ActiveMode timer is increased due to the TransitionToIdle event, the event will not be called a second time in
+ * a given cycle.
+ * OnEnterIdleMode will always the third when the ICD has transitioned to IdleMode.
+ *
+ * The ICDModeChange event can occur independently from the EnterActiveMode, TransitionToIdle and EnterIdleMode.
+ * It will typpically hapen at the ICDManager init when a client is already registered with the ICD before the
+ * OnEnterIdleMode event or when a client send a register command after the OnEnterActiveMode event. Nothing prevents the
+ * ICDModeChange event to happen multiple times per cycle or while the ICD is in IdleMode.
+ *
+ * See src/app/icd/server/ICDStateObserver.h for more information on the APIs each event triggers
+ */
enum class ObserverEventType : uint8_t
{
EnterActiveMode,
+ EnterIdleMode,
TransitionToIdle,
ICDModeChange,
};
@@ -85,22 +107,31 @@
* This type can be used to implement specific verifiers that can be used in the CheckInMessagesWouldBeSent function.
* The goal is to avoid having multiple functions that implement the iterator loop with only the check changing.
*
- * @return true if at least one Check-In message would be sent
- * false No Check-In messages would be sent
+ * @return true: if at least one Check-In message would be sent
+ * false: No Check-In messages would be sent
*/
-
using ShouldCheckInMsgsBeSentFunction = bool(FabricIndex aFabricIndex, NodeId subjectID);
- ICDManager() {}
+ ICDManager() = default;
+ ~ICDManager() = default;
+
void Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeyStore,
- Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * manager);
+ Messaging::ExchangeManager * exchangeManager, SubscriptionsInfoProvider * subInfoProvider);
void Shutdown();
- void UpdateICDMode();
- void UpdateOperationState(OperationalState state);
- void SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state);
- bool IsKeepActive() { return mKeepActiveFlags.HasAny(); }
+
+ /**
+ * @brief SupportsFeature verifies if a given FeatureMap bit is enabled
+ *
+ * @param[in] feature FeatureMap bit to verify
+ *
+ * @return true: if the FeatureMap bit is enabled in the ICDM cluster attribute.
+ * false: if the FeatureMap bit is not enabled in the ICDM cluster attribute.
+ * if we failed to read the FeatureMap attribute.
+ */
bool SupportsFeature(Clusters::IcdManagement::Feature feature);
+
ICDConfigurationData::ICDMode GetICDMode() { return ICDConfigurationData::GetInstance().GetICDMode(); };
+
/**
* @brief Adds the referenced observer in parameters to the mStateObserverPool
* A maximum of CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE observers can be concurrently registered
@@ -111,20 +142,14 @@
/**
* @brief Remove the referenced observer in parameters from the mStateObserverPool
+ * If the observer is not present in the object pool, we do nothing
*/
void ReleaseObserver(ICDStateObserver * observer);
/**
- * @brief Associates the ObserverEventType parameters to the correct
- * ICDStateObservers function and calls it for all observers in the mStateObserverPool
- */
- void postObserverEvent(ObserverEventType event);
- OperationalState GetOperationalState() { return mOperationalState; }
-
- /**
* @brief Ensures that the remaining Active Mode duration is at least the smaller of 30000 milliseconds and stayActiveDuration.
*
- * @param stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode
+ * @param[in] stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode
* @return The duration (in milliseconds) the device will stay in Active Mode
*/
uint32_t StayActiveRequest(uint32_t stayActiveDuration);
@@ -132,15 +157,14 @@
/**
* @brief TestEventTriggerHandler for the ICD feature set
*
- * @param eventTrigger Event trigger to handle.
+ * @param[in] eventTrigger Event trigger to handle.
+ *
* @return CHIP_ERROR CHIP_NO_ERROR - No erros during the processing
* CHIP_ERROR_INVALID_ARGUMENT - eventTrigger isn't a valid value
*/
CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override;
#if CHIP_CONFIG_ENABLE_ICD_CIP
- void SendCheckInMsgs();
-
/**
* @brief Trigger the ICDManager to send Check-In message if necessary
*
@@ -160,47 +184,108 @@
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
void SetTestFeatureMapValue(uint32_t featureMap) { mFeatureMap = featureMap; };
-#if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
bool GetIsBootUpResumeSubscriptionExecuted() { return mIsBootUpResumeSubscriptionExecuted; };
#endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
#endif
// Implementation of ICDListener functions.
// Callers must origin from the chip task context or hold the ChipStack lock.
+
void OnNetworkActivity() override;
void OnKeepActiveRequest(KeepActiveFlags request) override;
void OnActiveRequestWithdrawal(KeepActiveFlags request) override;
void OnICDManagementServerEvent(ICDManagementEvents event) override;
void OnSubscriptionReport() override;
-protected:
+private:
friend class TestICDManager;
+ /**
+ * @brief UpdateICDMode evaluates in which mode the ICD can be in; SIT or LIT mode.
+ * If the current operating mode does not match the evaluated operating mode, function updates the ICDMode and triggers
+ * all necessary operations.
+ * For a SIT ICD, this function does nothing.
+ * For a LIT ICD, the function checks if the ICD has a registration in the ICDMonitoringTable to determine which ICDMode
+ * the ICD must be in.
+ */
+ void UpdateICDMode();
/**
- * @brief Hepler function that extends the Active Mode duration as well as the Active Mode Jitter timer for the transition to
- * iddle mode.
+ * @brief UpdateOperationState updates the OperationState of the ICD to the requested one.
+ * IdleMode -> IdleMode : No actions are necessary, do nothing.
+ * IdleMode -> ActiveMode : Transition the device to ActiveMode, start the ActiveMode timer and trigger all necessary
+ * operations. These operations could be : Send Check-In messages
+ * Send subscription reports
+ * Process user actions
+ * ActiveMode -> ActiveMode : Increase remaining ActiveMode timer to one ActiveModeThreshold.
+ * If ActiveModeThreshold is 0, do nothing.
+ * ActiveMode -> IdleMode : Transition ICD to IdleMode and start the IdleMode timer.
+ *
+ * @param state requested OperationalState for the ICD to transition to
+ */
+ void UpdateOperationState(OperationalState state);
+
+ /**
+ * @brief Set or Remove a keep ActiveMode requirement for the given flag
+ * If state is true and the ICD is in IdleMode, transition the ICD to ActiveMode
+ * If state is false and the ICD is in ActiveMode, check whether we can transition the ICD to IdleMode.
+ * If we can, transition the ICD to IdleMode.
+ *
+ * @param flag KeepActiveFlag to remove or add
+ * @param state true: adding a flag requirement
+ * false: removing a flag requirement
+ */
+ void SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state);
+
+ /**
+ * @brief Associates the ObserverEventType parameters to the correct
+ * ICDStateObservers function and calls it for all observers in the mStateObserverPool
+ */
+ void postObserverEvent(ObserverEventType event);
+
+ /**
+ * @brief Hepler function that extends the ActiveMode timer as well as the Active Mode Jitter timer for the transition to
+ * idle mode event.
*/
void ExtendActiveMode(System::Clock::Milliseconds16 extendDuration);
+ /**
+ * @brief Timer callback function for when the IdleMode timer expires
+ *
+ * @param appState pointer to the ICDManager
+ */
static void OnIdleModeDone(System::Layer * aLayer, void * appState);
+
+ /**
+ * @brief Timer callback function for when the ActiveMode timer expires
+ *
+ * @param appState pointer to the ICDManager
+ */
static void OnActiveModeDone(System::Layer * aLayer, void * appState);
/**
- * @brief Callback function called shortly before the device enters idle mode to allow checks to be made. This is currently only
- * called once to prevent entering in a loop if some events re-trigger this check (for instance if a check for subscription
- * before entering idle mode leads to emiting a report, we will re-enter UpdateOperationState and check again for subscription,
- * etc.)
+ * @brief Timer Callback function called shortly before the device enters idle mode to allow checks to be made.
+ * This is currently only called once to prevent entering in a loop if some events re-trigger this check (for instance if
+ * a check for subscriptions before entering idle mode leads to emiting a report, we will re-enter UpdateOperationState
+ * and check again for subscription, etc.)
+ *
+ * @param appState pointer to the ICDManager
*/
static void OnTransitionToIdle(System::Layer * aLayer, void * appState);
#if CHIP_CONFIG_ENABLE_ICD_CIP
- uint8_t mCheckInRequestCount = 0;
-#endif // CHIP_CONFIG_ENABLE_ICD_CIP
+ /**
+ * @brief Function triggers all necessary Check-In messages to be sent.
+ *
+ * @note For each ICDMonitoring entry, we check if should send a Check-In message with
+ * ShouldCheckInMsgsBeSentAtActiveModeFunction. If we should, we allocate an ICDCheckInSender which tries to send a
+ * Check-In message to the registered client.
+ */
+ void SendCheckInMsgs();
- uint8_t mOpenExchangeContextCount = 0;
-
-private:
-#if CHIP_CONFIG_ENABLE_ICD_CIP
+ /**
+ * @brief See function implementation in .cpp for details on this function.
+ */
bool ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID);
/**
@@ -221,11 +306,15 @@
OperationalState mOperationalState = OperationalState::ActiveMode;
bool mTransitionToIdleCalled = false;
ObjectPool<ObserverPointer, CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE> mStateObserverPool;
+ uint8_t mOpenExchangeContextCount = 0;
#if CHIP_CONFIG_ENABLE_ICD_CIP
+ uint8_t mCheckInRequestCount = 0;
+
#if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
bool mIsBootUpResumeSubscriptionExecuted = false;
#endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+
PersistentStorageDelegate * mStorage = nullptr;
FabricTable * mFabricTable = nullptr;
Messaging::ExchangeManager * mExchangeManager = nullptr;
diff --git a/src/app/icd/server/ICDStateObserver.h b/src/app/icd/server/ICDStateObserver.h
index 996dad0..34fc309 100644
--- a/src/app/icd/server/ICDStateObserver.h
+++ b/src/app/icd/server/ICDStateObserver.h
@@ -27,13 +27,43 @@
namespace chip {
namespace app {
+/**
+ * @brief Public API used by the ICDManager to expose when different events occur.
+ * ICDManager::RegisterObserver can be used to register as an Observer to be notified when these events occur.
+ * These functions are called synchronously.
+ */
class ICDStateObserver
{
public:
virtual ~ICDStateObserver() {}
- virtual void OnEnterActiveMode() = 0;
+
+ /**
+ * @brief API called when the ICD enters ActiveMode. API isn't called if we need to extend the remaining active mode timer
+ * duration. API is called after the ICDManager has finished executing its internal actions.
+ */
+ virtual void OnEnterActiveMode() = 0;
+
+ /**
+ * @brief API called when the ICD enters IdleMode.
+ * API is called after the ICDManager has finished executing its internal actions.
+ */
+ virtual void OnEnterIdleMode() = 0;
+
+ /**
+ * @brief API is called when the ICD is about to enter IdleMode. API is called when there is `ICD_ACTIVE_TIME_JITTER_MS` of time
+ * remaining to the active mode timer.
+ * This API is only called once per transition from ActiveMode to IdleMode.
+ * If OnTransitionToIdle triggers the active mode timer to increase, the next time we are about to enter IdleMode,
+ * this API will not be called.
+ */
virtual void OnTransitionToIdle() = 0;
- virtual void OnICDModeChange() = 0;
+
+ /**
+ * @brief API is called when the ICD changes operating mode. This API is only called if the ICD changes state, not when it
+ * remains in the same state.
+ * API is called after the ICDManager has finished executing its internal actions.
+ */
+ virtual void OnICDModeChange() = 0;
};
} // namespace app
diff --git a/src/app/reporting/ReportSchedulerImpl.h b/src/app/reporting/ReportSchedulerImpl.h
index 38fcc2a..f7889a5 100644
--- a/src/app/reporting/ReportSchedulerImpl.h
+++ b/src/app/reporting/ReportSchedulerImpl.h
@@ -89,6 +89,12 @@
*/
void OnICDModeChange() override{};
+ /**
+ * @brief This implementation does not attempt any synchronization on this ICD event, therefore no action is needed on
+ * ICDEnterIdleMode()
+ */
+ void OnEnterIdleMode() override{};
+
// ReadHandlerObserver
/**
diff --git a/src/app/server/Dnssd.h b/src/app/server/Dnssd.h
index e669f4d..105318c 100644
--- a/src/app/server/Dnssd.h
+++ b/src/app/server/Dnssd.h
@@ -126,12 +126,26 @@
*/
CHIP_ERROR SetEphemeralDiscriminator(Optional<uint16_t> discriminator);
- // ICDStateObserver
- // No action is needed by the DnssdServer on active or idle state entries
- void OnEnterActiveMode() override{};
- void OnTransitionToIdle() override{};
+ /**
+ * @brief When the ICD changes operating mode, the dnssd server needs to restart its DNS-SD advertising to update the TXT keys.
+ */
void OnICDModeChange() override;
+ /**
+ * @brief dnssd server has no action to do on this ICD event. Do nothing.
+ */
+ void OnEnterActiveMode() override{};
+
+ /**
+ * @brief dnssd server has no action to do on this ICD event. Do nothing.
+ */
+ void OnTransitionToIdle() override{};
+
+ /**
+ * @brief dnssd server has no action to do on this ICD event. Do nothing.
+ */
+ void OnEnterIdleMode() override{};
+
private:
/// Overloaded utility method for commissioner and commissionable advertisement
/// This method is used for both commissioner discovery and commissionable node discovery since
diff --git a/src/app/tests/TestICDManager.cpp b/src/app/tests/TestICDManager.cpp
index fd93a14..6e814eb 100644
--- a/src/app/tests/TestICDManager.cpp
+++ b/src/app/tests/TestICDManager.cpp
@@ -77,9 +77,27 @@
class TestICDStateObserver : public app::ICDStateObserver
{
public:
- void OnEnterActiveMode() {}
- void OnTransitionToIdle() {}
- void OnICDModeChange() {}
+ 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
@@ -123,10 +141,10 @@
// Performs setup for each individual test in the test suite
CHIP_ERROR SetUp() override
{
-
ReturnErrorOnFailure(chip::Test::AppContext::SetUp());
- mICDManager.Init(&testStorage, &GetFabricTable(), &mKeystore, &GetExchangeManager(), &mSubInfoProvider);
+ mICDStateObserver.ResetAll();
mICDManager.RegisterObserver(&mICDStateObserver);
+ mICDManager.Init(&testStorage, &GetFabricTable(), &mKeystore, &GetExchangeManager(), &mSubInfoProvider);
return CHIP_NO_ERROR;
}
@@ -196,7 +214,7 @@
/**
* @brief Test verifies that the ICDManager starts its timers correctly based on if it will have any messages to send
- * when the IdleModeDuration expires
+ * when the IdleMode timer expires
*/
static void TestICDModeDurationsWith0ActiveModeDurationWithoutActiveSub(nlTestSuite * aSuite, void * aContext)
{
@@ -254,7 +272,7 @@
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeThreshold() + 1_ms16);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
- // Expire IdleModeDuration - Device should be in ActiveMode since it has an ICDM registration
+ // Expire IdleMode timer - Device should be in ActiveMode since it has an ICDM registration
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetIdleModeDuration() + 1_s);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
@@ -276,7 +294,7 @@
/**
* @brief Test verifies that the ICDManager remains in IdleMode since it will not have any messages to send
- * when the IdleModeDuration expires
+ * when the IdleMode timer expires
*/
static void TestICDModeDurationsWith0ActiveModeDurationWithActiveSub(nlTestSuite * aSuite, void * aContext)
{
@@ -334,7 +352,7 @@
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeThreshold() + 1_ms16);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
- // Expire IdleModeDuration - Device stay in IdleMode since it has an active subscription for the ICDM entry
+ // Expire IdleMode timer - Device stay in IdleMode since it has an active subscription for the ICDM entry
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetIdleModeDuration() + 1_s);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
@@ -414,9 +432,9 @@
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
}
- /*
- * Test that verifies that the ICDManager is the correct operating mode based on entries
- * in the ICDMonitoringTable
+ /**
+ * @brief Test that verifies that the ICDManager is in the correct operating mode based on entries
+ * in the ICDMonitoringTable
*/
static void TestICDMRegisterUnregisterEvents(nlTestSuite * aSuite, void * aContext)
{
@@ -554,7 +572,9 @@
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
}
- /* Test that verifies the logic of the ICDManager when it receives a StayActiveRequest*/
+ /**
+ * @brief Test verifies the logic of the ICDManager when it receives a StayActiveRequest
+ */
static void TestICDMStayActive(nlTestSuite * aSuite, void * aContext)
{
TestContext * ctx = static_cast<TestContext *>(aContext);
@@ -568,7 +588,7 @@
notifier.NotifySubscriptionReport();
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
- // Advance time by the ActiveModeDuration - 1
+ // Advance time just before ActiveMode timer expires
AdvanceClockAndRunEventLoop(ctx, icdConfigData.GetActiveModeDuration() - 1_ms32);
// Confirm ICD manager is in active mode
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
@@ -580,7 +600,7 @@
NL_TEST_ASSERT(aSuite, stayActivePromisedMs == stayActiveRequestedMs);
// Advance time by the duration of the stay stayActiveRequestedMs - 1 ms
- AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(stayActiveRequestedMs) - 1_ms32);
+ AdvanceClockAndRunEventLoop(ctx, Milliseconds32(stayActiveRequestedMs) - 1_ms32);
// Confirm ICD manager is in active mode
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
@@ -601,7 +621,7 @@
NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 30000);
// Advance time by the duration of the max stay active duration - 1 ms
- AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(30000) - 1_ms32);
+ AdvanceClockAndRunEventLoop(ctx, Milliseconds32(30000) - 1_ms32);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// Advance time by 1ms and Confirm ICD manager is in idle mode
@@ -621,7 +641,7 @@
NL_TEST_ASSERT(aSuite, stayActivePromisedMs == 30000);
// Advance time by the duration of the stay active request - 20000 ms
- AdvanceClockAndRunEventLoop(ctx, System::Clock::Milliseconds32(stayActiveRequestedMs) - 20000_ms32);
+ AdvanceClockAndRunEventLoop(ctx, Milliseconds32(stayActiveRequestedMs) - 20000_ms32);
// Confirm ICD manager is in active mode, we should have 20000 seconds left at that point
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
@@ -721,6 +741,329 @@
ctx->mICDManager.HandleEventTrigger(static_cast<uint64_t>(ICDTestEventTriggerEvent::kRemoveActiveModeReq));
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
}
+
+ /**
+ * @brief Test verifies when OnEnterIdleMode is called during normal operations.
+ * Without the ActiveMode timer being extended
+ */
+ static void TestICDStateObserverOnEnterIdleModeActiveModeDuration(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ // Verify that ICDManager starts in IdleMode and calls OnEnterIdleMode
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+ ctx->mICDStateObserver.ResetOnEnterIdleMode();
+
+ // Advance clock just before IdleMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Expire IdleModeInterval
+ AdvanceClockAndRunEventLoop(ctx, 1_s);
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Advance clock Just before ActiveMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDuration() - 1_ms32);
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Expire ActiveMode timer
+ AdvanceClockAndRunEventLoop(ctx, 1_ms32);
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+ }
+
+ /**
+ * @brief Test verifies when OnEnterIdleMode is called with the ActiveMode timer gets extended
+ */
+ static void TestICDStateObserverOnEnterIdleModeActiveModeThreshold(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ // Verify that ICDManager starts in IdleMode and calls OnEnterIdleMode
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+ ctx->mICDStateObserver.ResetOnEnterIdleMode();
+
+ // Advance clock just before the IdleMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Expire IdleMode timer
+ AdvanceClockAndRunEventLoop(ctx, 1_s);
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Advance clock Just before ActiveMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDuration() - 1_ms32);
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Increase ActiveMode timer by one ActiveModeThreshold
+ ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Advance clock Just before ActiveMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeThreshold() - 1_ms32);
+ NL_TEST_ASSERT(aSuite, !ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+
+ // Expire ActiveMode timer
+ AdvanceClockAndRunEventLoop(ctx, 1_ms32);
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnEnterIdleModeCalled);
+ }
+
+ static void TestICDStateObserverOnEnterActiveMode(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ // Verify OnEnterActiveMode wasn't called at Init
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnEnterActiveModeCalled));
+
+ // Advance clock just before IdleMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnEnterActiveModeCalled));
+
+ // Expire IdleMode timer and check wether OnEnterActiveMode was called
+ AdvanceClockAndRunEventLoop(ctx, 1_s);
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnEnterActiveModeCalled);
+ ctx->mICDStateObserver.ResetOnEnterActiveMode();
+
+ // Advance clock just before the ActiveMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDuration() - 1_ms32);
+
+ // Verify OnEnterActiveMde wasn't called
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnEnterActiveModeCalled));
+
+ // Increase ActiveMode timer by one ActiveModeThreshold
+ ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
+
+ // Verify OnEnterActiveMde wasn't called
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnEnterActiveModeCalled));
+
+ // Advance clock just before ActiveMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeThreshold() - 1_ms32);
+
+ // Verify OnEnterActiveMde wasn't called
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnEnterActiveModeCalled));
+
+ // Expire ActiveMode timer
+ AdvanceClockAndRunEventLoop(ctx, 1_ms32);
+
+ // Verify OnEnterActiveMde wasn't called
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnEnterActiveModeCalled));
+
+ // Advance clock just before IdleMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnEnterActiveModeCalled));
+
+ // Expire IdleMode timer and check OnEnterActiveMode was called
+ AdvanceClockAndRunEventLoop(ctx, 1_s);
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnEnterActiveModeCalled);
+ }
+
+ static void TestICDStateObserverOnICDModeChange(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+ typedef ICDListener::ICDManagementEvents ICDMEvent;
+
+ // Since we don't have a registration, we stay in SIT mode. No changes
+ NL_TEST_ASSERT(aSuite, !(ctx->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
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnICDModeChangeCalled));
+
+ // Add an entry to the ICDMonitoringTable
+ ICDMonitoringTable table(ctx->testStorage, kTestFabricIndex1, kMaxTestClients, &(ctx->mKeystore));
+
+ ICDMonitoringEntry entry(&(ctx->mKeystore));
+ entry.checkInNodeID = kClientNodeId11;
+ entry.monitoredSubject = kClientNodeId11;
+ NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == entry.SetKey(ByteSpan(kKeyBuffer1a)));
+ NL_TEST_ASSERT(aSuite, 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
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnICDModeChangeCalled);
+ ctx->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.
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnICDModeChangeCalled));
+
+ // Remove entry from the ICDMonitoringTable
+ NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == table.Remove(0));
+ ICDNotifier::GetInstance().NotifyICDManagementEvent(ICDMEvent::kTableUpdated);
+
+ // Since we don't have a registration anymore. Transition to SIT mode.
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnICDModeChangeCalled);
+ ctx->mICDStateObserver.ResetOnICDModeChange();
+ }
+
+ static void TestICDStateObserverOnICDModeChangeOnInit(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ ICDMonitoringTable table(ctx->testStorage, kTestFabricIndex1, kMaxTestClients, &(ctx->mKeystore));
+
+ // Since we don't have a registration, we stay in SIT mode. No changes
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnICDModeChangeCalled));
+
+ // Add an entry to the ICDMonitoringTable
+ ICDMonitoringEntry entry(&(ctx->mKeystore));
+ entry.checkInNodeID = kClientNodeId11;
+ entry.monitoredSubject = kClientNodeId11;
+ NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == entry.SetKey(ByteSpan(kKeyBuffer1a)));
+ NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == table.Set(0, entry));
+
+ // Shut down and reinit ICDManager - We should go to LIT mode since we have a registration
+ ctx->mICDManager.Shutdown();
+ ctx->mICDManager.RegisterObserver(&(ctx->mICDStateObserver));
+ ctx->mICDManager.Init(&(ctx->testStorage), &(ctx->GetFabricTable()), &(ctx->mKeystore), &(ctx->GetExchangeManager()),
+ &(ctx->mSubInfoProvider));
+
+ // We have a registration, transition to LIT mode
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnICDModeChangeCalled);
+ ctx->mICDStateObserver.ResetOnICDModeChange();
+
+ // Remove entry from the ICDMonitoringTable
+ NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == table.Remove(0));
+ }
+
+ /**
+ * @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is greater than the
+ * ICD_ACTIVE_TIME_JITTER_MS
+ */
+ static void TestICDStateObserverOnTransitionToIdleModeGreaterActiveModeDuration(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ // Set New durations for test case - ActiveModeDuration must be longuer than ICD_ACTIVE_TIME_JITTER_MS
+ Milliseconds32 oldActiveModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
+ ICDConfigurationData::GetInstance().SetModeDurations(
+ MakeOptional<Milliseconds32>(Milliseconds32(200) + Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS)), NullOptional);
+
+ // Advance clock just before IdleMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Expire IdleMode timer
+ AdvanceClockAndRunEventLoop(ctx, 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Advance time just before OnTransitionToIdleMode is called
+ AdvanceClockAndRunEventLoop(
+ ctx, ICDConfigurationData::GetInstance().GetActiveModeDuration() - Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS) - 1_ms32);
+
+ // Check mOnTransitionToIdleCalled has not been called
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Increase ActiveMode timer by one ActiveModeThreshold
+ ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Advance time just before OnTransitionToIdleMode is called
+ AdvanceClockAndRunEventLoop(
+ ctx, ICDConfigurationData::GetInstance().GetActiveModeThreshold() - Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS) - 1_ms32);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Expire OnTransitionToIdleMode
+ AdvanceClockAndRunEventLoop(ctx, 1_ms32);
+ // Check mOnTransitionToIdleCalled has been called
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnTransitionToIdleCalled);
+ ctx->mICDStateObserver.ResetOnTransitionToIdle();
+
+ // Expire ActiveMode timer
+ AdvanceClockAndRunEventLoop(ctx, Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS));
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Reset Old durations
+ ICDConfigurationData::GetInstance().SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
+ }
+
+ /**
+ * @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is equal to the
+ * ICD_ACTIVE_TIME_JITTER_MS.
+ */
+ static void TestICDStateObserverOnTransitionToIdleModeEqualActiveModeDuration(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ // Set New durations for test case - ActiveModeDuration must be equal to ICD_ACTIVE_TIME_JITTER_MS
+ Milliseconds32 oldActiveModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
+ ICDConfigurationData::GetInstance().SetModeDurations(
+ MakeOptional<Milliseconds32>(Milliseconds32(ICD_ACTIVE_TIME_JITTER_MS)), NullOptional);
+
+ // Advance clock just before IdleMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Expire IdleMode timer
+ AdvanceClockAndRunEventLoop(ctx, 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Expire OnTransitionToIdleMode
+ AdvanceClockAndRunEventLoop(ctx, 1_ms32);
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnTransitionToIdleCalled);
+
+ // Reset Old durations
+ ICDConfigurationData::GetInstance().SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
+ }
+
+ /**
+ * @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is 0 and without an ActiveMode req
+ */
+ static void TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWithoutReq(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ // Set New durations for test case - ActiveModeDuration equal 0
+ Milliseconds32 oldActiveModeDuration = ICDConfigurationData::GetInstance().GetActiveModeDuration();
+ ICDConfigurationData::GetInstance().SetModeDurations(MakeOptional<Milliseconds32>(0), NullOptional);
+
+ // Advance clock just before IdleMode timer expires
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration() - 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Expire IdleMode timer
+ AdvanceClockAndRunEventLoop(ctx, 1_s);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Increase time by 1 - Should not trigger OnTransitionToIdle.
+ // Timer length is one ActiveModeThreshold
+ AdvanceClockAndRunEventLoop(ctx, 1_ms32);
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Expire ActiveModeThreshold
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeThreshold());
+ NL_TEST_ASSERT(aSuite, ctx->mICDStateObserver.mOnTransitionToIdleCalled);
+
+ // Reset Old durations
+ ICDConfigurationData::GetInstance().SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
+ }
+
+ /**
+ * @brief Test verifies the OnTransitionToIdleMode event when the ActiveModeDuration is 0 with an ActiveMode req
+ */
+ static void TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWittReq(nlTestSuite * aSuite, void * aContext)
+ {
+ TestContext * ctx = static_cast<TestContext *>(aContext);
+
+ // 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
+ ctx->mICDManager.HandleEventTrigger(static_cast<uint64_t>(ICDTestEventTriggerEvent::kAddActiveModeReq));
+
+ // Expire IdleMode timer
+ AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetIdleModeDuration());
+ NL_TEST_ASSERT(aSuite, !(ctx->mICDStateObserver.mOnTransitionToIdleCalled));
+
+ // Reset Old durations
+ ICDConfigurationData::GetInstance().SetModeDurations(MakeOptional(oldActiveModeDuration), NullOptional);
+ }
};
} // namespace app
@@ -740,6 +1083,22 @@
NL_TEST_DEF("TestICDStayActive", TestICDManager::TestICDMStayActive),
NL_TEST_DEF("TestShouldCheckInMsgsBeSentAtActiveModeFunction", TestICDManager::TestShouldCheckInMsgsBeSentAtActiveModeFunction),
NL_TEST_DEF("TestHandleTestEventTriggerActiveModeReq", TestICDManager::TestHandleTestEventTriggerActiveModeReq),
+ NL_TEST_DEF("TestICDStateObserverOnEnterIdleModeActiveModeDuration",
+ TestICDManager::TestICDStateObserverOnEnterIdleModeActiveModeDuration),
+ NL_TEST_DEF("TestICDStateObserverOnEnterIdleModeActiveModeThreshold",
+ TestICDManager::TestICDStateObserverOnEnterIdleModeActiveModeThreshold),
+ NL_TEST_DEF("TestICDStateObserverOnEnterActiveMode", TestICDManager::TestICDStateObserverOnEnterActiveMode),
+ NL_TEST_DEF("TestICDStateObserverOnICDModeChange", TestICDManager::TestICDStateObserverOnICDModeChange),
+ NL_TEST_DEF("TestICDStateObserverOnICDModeChangeOnInit", TestICDManager::TestICDStateObserverOnICDModeChangeOnInit),
+ NL_TEST_DEF("TestICDStateObserverOnTransitionToIdleModeGreaterActiveModeDuration",
+ TestICDManager::TestICDStateObserverOnTransitionToIdleModeGreaterActiveModeDuration),
+ NL_TEST_DEF("TestICDStateObserverOnTransitionToIdleModeEqualActiveModeDuration",
+ TestICDManager::TestICDStateObserverOnTransitionToIdleModeEqualActiveModeDuration),
+ NL_TEST_DEF("TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWithoutReq",
+ TestICDManager::TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWithoutReq),
+ // TODO(#33074): When the OnTransitionToIdle edge is fixed, we can enable this test
+ // NL_TEST_DEF("TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWittReq",
+ // TestICDManager::TestICDStateObserverOnTransitionToIdleMode0ActiveModeDurationWittReq),
NL_TEST_SENTINEL(),
};
diff --git a/src/app/tests/suites/TestIcdManagementCluster.yaml b/src/app/tests/suites/TestIcdManagementCluster.yaml
index 3d00815..f7f207a 100644
--- a/src/app/tests/suites/TestIcdManagementCluster.yaml
+++ b/src/app/tests/suites/TestIcdManagementCluster.yaml
@@ -87,7 +87,10 @@
command: "readAttribute"
attribute: "ICDCounter"
response:
- value: beforeRebootICDCounter + 101
+ constraints:
+ # It is possible ICD hasn't had time to send a Check-In message after reboot
+ minValue: beforeRebootICDCounter + 100
+ maxValue: beforeRebootICDCounter + 101
- label: "Verify the ICD is operating as a LIT ICD"
command: "readAttribute"
diff --git a/src/messaging/ExchangeContext.cpp b/src/messaging/ExchangeContext.cpp
index 74a861c..3869ab4 100644
--- a/src/messaging/ExchangeContext.cpp
+++ b/src/messaging/ExchangeContext.cpp
@@ -322,6 +322,7 @@
SetAutoRequestAck(session->AllowsMRP());
#if CHIP_CONFIG_ENABLE_ICD_SERVER
+ // TODO(#33075) : Add check for group context to not a req since it serves no purpose
app::ICDNotifier::GetInstance().NotifyActiveRequestNotification(app::ICDListener::KeepActiveFlag::kExchangeContextOpen);
#endif
@@ -341,6 +342,7 @@
VerifyOrDie(mFlags.Has(Flags::kFlagClosed));
#if CHIP_CONFIG_ENABLE_ICD_SERVER
+ // TODO(#33075) : Add check for group context to not a req since it serves no purpose
app::ICDNotifier::GetInstance().NotifyActiveRequestWithdrawal(app::ICDListener::KeepActiveFlag::kExchangeContextOpen);
#endif // CHIP_CONFIG_ENABLE_ICD_SERVER