blob: 76c8cd345cb0c4026bca79f5d8c07a893b03b6bc [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 <app/EventManagement.h>
#include <app/icd/ICDManagementServer.h>
#include <app/icd/ICDManager.h>
#include <app/icd/ICDNotifier.h>
#include <app/icd/ICDStateObserver.h>
#include <app/tests/AppTestContext.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <lib/support/TimeUtils.h>
#include <lib/support/UnitTestContext.h>
#include <lib/support/UnitTestRegistration.h>
#include <nlunit-test.h>
#include <system/SystemLayerImpl.h>
using namespace chip;
using namespace chip::app;
using namespace chip::System;
namespace {
class TestICDStateObserver : public app::ICDStateObserver
{
public:
void OnEnterActiveMode() {}
void OnTransitionToIdle() {}
};
TestICDStateObserver mICDStateObserver;
static Clock::Internal::MockClock gMockClock;
static Clock::ClockBase * gRealClock;
class TestContext : public Test::AppContext
{
public:
static int Initialize(void * context)
{
if (AppContext::Initialize(context) != SUCCESS)
return FAILURE;
auto * ctx = static_cast<TestContext *>(context);
DeviceLayer::SetSystemLayerForTesting(&ctx->GetSystemLayer());
gRealClock = &SystemClock();
Clock::Internal::SetSystemClockForTesting(&gMockClock);
if (ctx->mEventCounter.Init(0) != CHIP_NO_ERROR)
{
return FAILURE;
}
ctx->mICDManager.Init(&ctx->testStorage, &ctx->GetFabricTable(), &mICDStateObserver);
return SUCCESS;
}
static int Finalize(void * context)
{
auto * ctx = static_cast<TestContext *>(context);
ctx->mICDManager.Shutdown();
app::EventManagement::DestroyEventManagement();
System::Clock::Internal::SetSystemClockForTesting(gRealClock);
DeviceLayer::SetSystemLayerForTesting(nullptr);
if (AppContext::Finalize(context) != SUCCESS)
return FAILURE;
return SUCCESS;
}
app::ICDManager mICDManager;
private:
TestPersistentStorageDelegate testStorage;
MonotonicallyIncreasingCounter<EventNumber> mEventCounter;
};
} // namespace
namespace chip {
namespace app {
class TestICDManager
{
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(TestContext * ctx, uint32_t time_ms)
{
gMockClock.AdvanceMonotonic(System::Clock::Timeout(time_ms));
ctx->GetIOContext().DriveIO();
}
static void TestICDModeIntervals(nlTestSuite * aSuite, void * aContext)
{
TestContext * ctx = static_cast<TestContext *>(aContext);
// After the init we should be in active mode
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeIntervalMs() + 1);
// Active mode interval expired, ICDManager transitioned to the IdleMode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
AdvanceClockAndRunEventLoop(ctx, secondsToMilliseconds(ICDManagementServer::GetInstance().GetIdleModeIntervalSec()) + 1);
// Idle mode interval 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.
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeIntervalMs() - 1);
ICDNotifier::GetInstance().BroadcastNetworkActivityNotification();
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeThresholdMs() / 2);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeThresholdMs());
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
}
static void TestKeepActivemodeRequests(nlTestSuite * aSuite, void * aContext)
{
TestContext * ctx = static_cast<TestContext *>(aContext);
typedef ICDListener::KeepActiveFlags ActiveFlag;
ICDNotifier notifier = ICDNotifier::GetInstance();
// Setting a requirement will transition the ICD to active mode.
notifier.BroadcastActiveRequestNotification(ActiveFlag::kCommissioningWindowOpen);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// Advance time so active mode interval expires.
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeIntervalMs() + 1);
// Requirement flag still set. We stay in active mode
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// Remove requirement. we should directly transition to idle mode.
notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kCommissioningWindowOpen);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
notifier.BroadcastActiveRequestNotification(ActiveFlag::kFailSafeArmed);
// 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.
// We should stay in active mode.
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeIntervalMs() / 2);
notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kFailSafeArmed);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// Advance time again, The activemode interval is completed.
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeIntervalMs() + 1);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
// Set two requirements
notifier.BroadcastActiveRequestNotification(ActiveFlag::kFailSafeArmed);
notifier.BroadcastActiveRequestNotification(ActiveFlag::kExchangeContextOpen);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// advance time so the active mode interval expires.
AdvanceClockAndRunEventLoop(ctx, ICDManagementServer::GetInstance().GetActiveModeIntervalMs() + 1);
// A requirement flag is still set. We stay in active mode.
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// remove 1 requirement. Active mode is maintained
notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kFailSafeArmed);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::ActiveMode);
// remove the last requirement
notifier.BroadcastActiveRequestWithdrawal(ActiveFlag::kExchangeContextOpen);
NL_TEST_ASSERT(aSuite, ctx->mICDManager.mOperationalState == ICDManager::OperationalState::IdleMode);
}
};
} // namespace app
} // namespace chip
namespace {
/**
* Test Suite. It lists all the test functions.
*/
// clang-format off
static const nlTest sTests[] =
{
NL_TEST_DEF("TestICDModeIntervals", TestICDManager::TestICDModeIntervals),
NL_TEST_DEF("TestKeepActivemodeRequests", TestICDManager::TestKeepActivemodeRequests),
NL_TEST_SENTINEL()
};
// clang-format on
// clang-format off
nlTestSuite cmSuite =
{
"TestICDManager",
&sTests[0],
TestContext::Initialize,
TestContext::Finalize
};
// clang-format on
} // namespace
int TestSuiteICDManager()
{
return ExecuteTestsWithContext<TestContext>(&cmSuite);
}
CHIP_REGISTER_TEST_SUITE(TestSuiteICDManager)