Skip to content

Commit

Permalink
Add unit test to validate fix
Browse files Browse the repository at this point in the history
  • Loading branch information
mkardous-silabs committed Jan 22, 2024
1 parent 9da9132 commit b91c50f
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 14 deletions.
13 changes: 13 additions & 0 deletions src/app/icd/ICDConfigurationData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "ICDConfigurationData.h"
#include <app/icd/ICDConfig.h>
#include <lib/support/CodeUtils.h>

namespace chip {

Expand All @@ -36,4 +37,16 @@ System::Clock::Milliseconds32 ICDConfigurationData::GetSlowPollingInterval()
return mSlowPollingInterval;
}

CHIP_ERROR ICDConfigurationData::SetDurations(uint32_t activeModeDuration_ms, uint32_t idleModeInterval_s)
{
VerifyOrReturnError(activeModeDuration_ms <= (idleModeInterval_s * 1000), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(idleModeInterval_s <= kMaxIdleModeDuration_s, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(idleModeInterval_s >= kMinIdleModeDuration_s, CHIP_ERROR_INVALID_ARGUMENT);

mIdleModeDuration_s = idleModeInterval_s;
mActiveModeDuration_ms = activeModeDuration_ms;

return CHIP_NO_ERROR;
}

} // namespace chip
34 changes: 28 additions & 6 deletions src/app/icd/ICDConfigurationData.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ namespace chip {
namespace app {
// Forward declaration of ICDManager to allow it to be friend with ICDConfigurationData
class ICDManager;

// Forward declaration of TestICDManager to allow it to be friend with the ICDConfigurationData
// Used in unit tests
class TestICDManager;

} // namespace app

/**
Expand All @@ -47,9 +52,9 @@ class ICDConfigurationData

static ICDConfigurationData & GetInstance() { return instance; };

uint32_t GetIdleModeDurationSec() { return mIdleInterval_s; }
uint32_t GetIdleModeDurationSec() { return mIdleModeDuration_s; }

uint32_t GetActiveModeDurationMs() { return mActiveInterval_ms; }
uint32_t GetActiveModeDurationMs() { return mActiveModeDuration_ms; }

uint16_t GetActiveModeThresholdMs() { return mActiveThreshold_ms; }

Expand Down Expand Up @@ -87,21 +92,38 @@ class ICDConfigurationData
// the ICDManager, the ICDManager is a friend that can access the private setters. If a consummer needs to be notified when a
// value is changed, they can leverage the Observer events the ICDManager generates. See src/app/icd/ICDStateObserver.h
friend class chip::app::ICDManager;
friend class chip::app::TestICDManager;

void SetICDMode(ICDMode mode) { mICDMode = mode; };
void SetICDCounter(uint32_t count) { mICDCounter = count; }
void SetSlowPollingInterval(System::Clock::Milliseconds32 slowPollInterval) { mSlowPollingInterval = slowPollInterval; };
void SetFastPollingInterval(System::Clock::Milliseconds32 fastPollInterval) { mFastPollingInterval = fastPollInterval; };

static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) <= 64800,
/**
* @brief Change the ActiveModeDuration and IdleModeDuration value
* To only change one value, pass the old value for the other one
*
* @param[in] activeModeDuration_ms new ActiveModeDuration value
* @param[in] idleModeDuration_s new IdleModeDuration value
* @return CHIP_ERROR CHIP_ERROR_INVALID_ARGUMENT is returned if idleModeDuration_s is smaller than activeModeDuration_ms
* is returned if idleModeDuration_s is greater than 64800 seconds
* is returned if idleModeDuration_s is smaller than 1 seconds
* CHIP_NO_ERROR is returned if the new intervals were set
*/
CHIP_ERROR SetDurations(uint32_t activeModeDuration_ms, uint32_t idleModeDuration_s);

static constexpr uint32_t kMaxIdleModeDuration_s = 64800;
static constexpr uint32_t kMinIdleModeDuration_s = 1;

static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) <= kMaxIdleModeDuration_s,
"Spec requires the IdleModeDuration to be equal or inferior to 64800s.");
static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) >= 1,
static_assert((CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC) >= kMinIdleModeDuration_s,
"Spec requires the IdleModeDuration to be equal or greater to 1s.");
uint32_t mIdleInterval_s = CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC;
uint32_t mIdleModeDuration_s = CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC;

static_assert((CHIP_CONFIG_ICD_ACTIVE_MODE_DURATION_MS) <= (CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC * kMillisecondsPerSecond),
"Spec requires the IdleModeDuration be equal or greater to the ActiveModeDuration.");
uint32_t mActiveInterval_ms = CHIP_CONFIG_ICD_ACTIVE_MODE_DURATION_MS;
uint32_t mActiveModeDuration_ms = CHIP_CONFIG_ICD_ACTIVE_MODE_DURATION_MS;

uint16_t mActiveThreshold_ms = CHIP_CONFIG_ICD_ACTIVE_MODE_THRESHOLD_MS;

Expand Down
94 changes: 86 additions & 8 deletions src/app/tests/TestICDManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,17 @@ class TestICDManager
// After the init we should be in Idle mode
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
// Idle mode interval expired, ICDManager transitioned to the ActiveMode.
// Idle mode Duration expired, ICDManager transitioned to the ActiveMode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDurationMs() + 1);
// Active mode interval expired, ICDManager transitioned to the IdleMode.
// Active mode Duration expired, ICDManager transitioned to the IdleMode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
// Idle mode interval expired, ICDManager transitioned to the ActiveMode.
// Idle mode Duration expired, ICDManager transitioned to the ActiveMode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == 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 Active interval and validate that the active mode is extended.
// Kick an active Threshold just before the end of the ActiveMode duration and validate that the active mode is extended.
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDurationMs() - 1);
ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeThresholdMs() / 2);
Expand All @@ -161,6 +161,83 @@ class TestICDManager
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
}

/**
* @brief Test verifies that the ICDManager starts its timers correctly based on if it will have any messages to send
* when the IdleModeDuration expires
*/
static void TestICDModeDurationsWith0ActiveModeDuration(nlTestSuite * aSuite, void * aContext)
{
TestContext * ctx = static_cast<TestContext *>(aContext);
typedef ICDListener::ICDManagementEvents ICDMEvent;

// Set FeatureMap - Configures CIP, UAT and LITS to 1
ctx->mICDManager.SetTestFeatureMapValue(0x07);

// Set New durations for test case
uint32_t oldActiveModeDuration_ms = ICDConfigurationData::GetInstance().GetActiveModeDurationMs();
ICDConfigurationData::ICDConfigurationData::GetInstance().SetDurations(
0, ICDConfigurationData::GetInstance().GetIdleModeDurationSec());

// Verify That ICDManager starts in Idle
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);

// Reset IdleModeInterval since it was started before the ActiveModeDuration change
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);

// Force the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is now 0
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeThresholdMs() + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);

// Expire Idle mode duration; ICDManager should remain in IdleMode since it has no message to send
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);

// 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().BroadcastICDManagementEvent(ICDMEvent::kTableUpdated);

// Check ICDManager is now in the LIT operating mode
NL_TEST_ASSERT(aSuite, ICDConfigurationData::GetInstance().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().BroadcastNetworkActivityNotification();
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);

// Return the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is 0
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeThresholdMs() + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);

// Expire IdleModeDuration - Device should be in ActiveMode since it has an ICDM registration
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);

// Remove entry from the fabric - ICDManager won't have any messages to send
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == table.Remove(0));
NL_TEST_ASSERT(aSuite, table.IsEmpty());

// Return the device to return to IdleMode - Increase time by ActiveModeThreshold since ActiveModeDuration is 0
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeThresholdMs() + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);

// Expire Idle mode duration; ICDManager should remain in IdleMode since it has no message to send
AdvanceClockAndRunEventLoop(ctx, SecondsToMilliseconds(ICDConfigurationData::GetInstance().GetIdleModeDurationSec()) + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);

// Reset Old durations
ICDConfigurationData::ICDConfigurationData::GetInstance().SetDurations(
oldActiveModeDuration_ms, ICDConfigurationData::GetInstance().GetIdleModeDurationSec());
}

static void TestKeepActivemodeRequests(nlTestSuite * aSuite, void * aContext)
{
TestContext * ctx = static_cast<TestContext *>(aContext);
Expand All @@ -170,7 +247,7 @@ class TestICDManager
// Setting a requirement will transition the ICD to active mode.
notifier.NotifyActiveRequestNotification(ActiveFlag::kCommissioningWindowOpen);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// Advance time so active mode interval expires.
// Advance time so active mode duration expires.
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDurationMs() + 1);
// Requirement flag still set. We stay in active mode
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
Expand All @@ -183,21 +260,21 @@ class TestICDManager
// Requirement will transition us to active mode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);

// Advance time, but by less than the active mode interval and remove the requirement.
// Advance time, but by less than the active mode duration and remove the requirement.
// We should stay in active mode.
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDurationMs() / 2);
notifier.NotifyActiveRequestWithdrawal(ActiveFlag::kFailSafeArmed);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);

// Advance time again, The activemode interval is completed.
// Advance time again, The activemode duration is completed.
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDurationMs() + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);

// Set two requirements
notifier.NotifyActiveRequestNotification(ActiveFlag::kFailSafeArmed);
notifier.NotifyActiveRequestNotification(ActiveFlag::kExchangeContextOpen);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// advance time so the active mode interval expires.
// advance time so the active mode duration expires.
AdvanceClockAndRunEventLoop(ctx, ICDConfigurationData::GetInstance().GetActiveModeDurationMs() + 1);
// A requirement flag is still set. We stay in active mode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
Expand Down Expand Up @@ -352,6 +429,7 @@ namespace {
static const nlTest sTests[] = {
NL_TEST_DEF("TestICDModeDurations", TestICDManager::TestICDModeDurations),
NL_TEST_DEF("TestOnSubscriptionReport", TestICDManager::TestOnSubscriptionReport),
NL_TEST_DEF("TestICDModeDurationsWith0ActiveModeDuration", TestICDManager::TestICDModeDurationsWith0ActiveModeDuration),
NL_TEST_DEF("TestKeepActivemodeRequests", TestICDManager::TestKeepActivemodeRequests),
NL_TEST_DEF("TestICDMRegisterUnregisterEvents", TestICDManager::TestICDMRegisterUnregisterEvents),
NL_TEST_DEF("TestICDCounter", TestICDManager::TestICDCounter),
Expand Down

0 comments on commit b91c50f

Please sign in to comment.