summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc')
-rw-r--r--src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc433
1 files changed, 433 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc b/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc
new file mode 100644
index 0000000..b7e74d8
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc
@@ -0,0 +1,433 @@
+// Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <dhcpsrv/cfg_expiration.h>
+#include <dhcpsrv/timer_mgr.h>
+#include <exceptions/exceptions.h>
+#include <testutils/test_to_element.h>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <functional>
+#include <stdint.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Type definition of the @c CfgExpiration modified function.
+typedef std::function<void(CfgExpiration*, const int64_t)> ModifierFun;
+/// @brief Type definition of the @c CfgExpiration accessor function
+/// returning uint16_t value.
+typedef std::function<uint16_t(CfgExpiration*)> AccessorFunUint16;
+/// @brief Type definition of the @c CfgExpiration accessor function
+/// returning uint32_t value.
+typedef std::function<uint32_t(CfgExpiration*)> AccessorFunUint32;
+
+/// @brief Tests the accessor and modifier function for a particular
+/// configuration parameter held in @c CfgExpiration.
+///
+/// This is a simple test which tries to set the given parameter to
+/// different values:
+/// - value greater than maximum allowed for this parameter - expects
+/// the exception to be thrown,
+/// - value lower than 0 - expects the exception to be thrown,
+/// - value equal to the maximum allowed,
+/// - value equal to maximum allowed minus 1.
+///
+/// @param limit Maximum allowed value for the parameter.
+/// @param modifier Pointer to the modifier function to be tested.
+/// @param accessor Pointer to the accessor function to be tested.
+/// @tparam ReturnType Type of the value returned by the accessor,
+/// i.e. uint16_t or uint32_t.
+template<typename ReturnType>
+void
+testAccessModify(const int64_t limit, const ModifierFun& modifier,
+ const std::function<ReturnType(CfgExpiration*)>& accessor) {
+ CfgExpiration cfg;
+
+ // Setting the value to maximum allowed + 1 should result in
+ // an exception.
+ ASSERT_THROW(modifier(&cfg, limit + 1), OutOfRange);
+
+ // Setting to the negative value should result in an exception.
+ ASSERT_THROW(modifier(&cfg, -1), OutOfRange);
+
+ // Setting the value to the maximum allowed should pass.
+ ASSERT_NO_THROW(modifier(&cfg, limit));
+ EXPECT_EQ(limit, accessor(&cfg));
+
+ // Setting the value to the maximum allowed - 1 should pass.
+ ASSERT_NO_THROW(modifier(&cfg, limit - 1));
+ EXPECT_EQ(limit - 1, accessor(&cfg));
+
+ // Setting the value to 0 should pass.
+ ASSERT_NO_THROW(modifier(&cfg, 0));
+ EXPECT_EQ(0, accessor(&cfg));
+}
+
+/// @brief Tests that modifier and the accessor returning uint16_t value
+/// work as expected.
+///
+/// @param limit Maximum allowed value for the parameter.
+/// @param modifier Pointer to the modifier function to be tested.
+/// @param accessor Pointer to the accessor function to be tested.
+void
+testAccessModifyUint16(const int64_t limit, const ModifierFun& modifier,
+ const AccessorFunUint16& accessor) {
+ testAccessModify<uint16_t>(limit, modifier, accessor);
+}
+
+/// @brief Tests that modifier and the accessor returning uint32_t value
+/// work as expected.
+///
+/// @param limit Maximum allowed value for the parameter.
+/// @param modifier Pointer to the modifier function to be tested.
+/// @param accessor Pointer to the accessor function to be tested.
+void
+testAccessModifyUint32(const int64_t limit, const ModifierFun& modifier,
+ const AccessorFunUint32& accessor) {
+ testAccessModify<uint32_t>(limit, modifier, accessor);
+}
+
+/// Test the default values of CfgExpiration object.
+TEST(CfgExpirationTest, defaults) {
+ CfgExpiration cfg;
+ EXPECT_EQ(CfgExpiration::DEFAULT_RECLAIM_TIMER_WAIT_TIME,
+ cfg.getReclaimTimerWaitTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_FLUSH_RECLAIMED_TIMER_WAIT_TIME,
+ cfg.getFlushReclaimedTimerWaitTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_HOLD_RECLAIMED_TIME,
+ cfg.getHoldReclaimedTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_LEASES,
+ cfg.getMaxReclaimLeases());
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_TIME,
+ cfg.getMaxReclaimTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_UNWARNED_RECLAIM_CYCLES,
+ cfg.getUnwarnedReclaimCycles());
+}
+
+/// @brief Tests that unparse returns an expected value
+TEST(CfgExpirationTest, unparse) {
+ CfgExpiration cfg;
+ std::string defaults = "{\n"
+ "\"reclaim-timer-wait-time\": 10,\n"
+ "\"flush-reclaimed-timer-wait-time\": 25,\n"
+ "\"hold-reclaimed-time\": 3600,\n"
+ "\"max-reclaim-leases\": 100,\n"
+ "\"max-reclaim-time\": 250,\n"
+ "\"unwarned-reclaim-cycles\": 5 }";
+ isc::test::runToElementTest<CfgExpiration>(defaults, cfg);
+}
+
+// Test the {get,set}ReclaimTimerWaitTime.
+TEST(CfgExpirationTest, getReclaimTimerWaitTime) {
+ testAccessModify<uint16_t>(CfgExpiration::LIMIT_RECLAIM_TIMER_WAIT_TIME,
+ &CfgExpiration::setReclaimTimerWaitTime,
+ &CfgExpiration::getReclaimTimerWaitTime);
+}
+
+// Test the {get,set}FlushReclaimedTimerWaitTime.
+TEST(CfgExpirationTest, getFlushReclaimedTimerWaitTime) {
+ testAccessModifyUint16(CfgExpiration::LIMIT_FLUSH_RECLAIMED_TIMER_WAIT_TIME,
+ &CfgExpiration::setFlushReclaimedTimerWaitTime,
+ &CfgExpiration::getFlushReclaimedTimerWaitTime);
+}
+
+// Test the {get,set}HoldReclaimedTime.
+TEST(CfgExpirationTest, getHoldReclaimedTime) {
+ testAccessModifyUint32(CfgExpiration::LIMIT_HOLD_RECLAIMED_TIME,
+ &CfgExpiration::setHoldReclaimedTime,
+ &CfgExpiration::getHoldReclaimedTime);
+}
+
+// Test the {get,set}MaxReclaimLeases.
+TEST(CfgExpirationTest, getMaxReclaimLeases) {
+ testAccessModifyUint32(CfgExpiration::LIMIT_MAX_RECLAIM_LEASES,
+ &CfgExpiration::setMaxReclaimLeases,
+ &CfgExpiration::getMaxReclaimLeases);
+}
+
+// Test the {get,set}MaxReclaimTime.
+TEST(CfgExpirationTest, getMaxReclaimTime) {
+ testAccessModifyUint16(CfgExpiration::LIMIT_MAX_RECLAIM_TIME,
+ &CfgExpiration::setMaxReclaimTime,
+ &CfgExpiration::getMaxReclaimTime);
+}
+
+// Test the {get,set}UnwarnedReclaimCycles.
+TEST(CfgExpirationTest, getUnwarnedReclaimCycles) {
+ testAccessModifyUint16(CfgExpiration::LIMIT_UNWARNED_RECLAIM_CYCLES,
+ &CfgExpiration::setUnwarnedReclaimCycles,
+ &CfgExpiration::getUnwarnedReclaimCycles);
+}
+
+/// @brief Implements test routines for leases reclamation.
+///
+/// This class implements two routines called by the @c CfgExpiration object
+/// instead of the typical routines for leases' reclamation in the
+/// @c AllocEngine. These methods do not perform the actual reclamation,
+/// but instead they record the number of calls to them and the parameters
+/// with which they were executed. This allows for checking if the
+/// @c CfgExpiration object calls the leases reclamation routine with the
+/// appropriate parameters.
+class LeaseReclamationStub {
+public:
+
+ /// @brief Collection of parameters with which the @c reclaimExpiredLeases
+ /// method is called.
+ ///
+ /// Examination of these values allows for assessment if the @c CfgExpiration
+ /// calls the routine with the appropriate values.
+ struct RecordedParams {
+ /// @brief Maximum number of leases to be processed.
+ size_t max_leases;
+
+ /// @brief Timeout for processing leases in milliseconds.
+ uint16_t timeout;
+
+ /// @brief Boolean flag which indicates if the leases should be removed
+ /// when reclaimed.
+ bool remove_lease;
+
+ /// @brief Maximum number of reclamation attempts after which all leases
+ /// should be reclaimed.
+ uint16_t max_unwarned_cycles;
+
+ /// @brief Constructor
+ ///
+ /// Sets all numeric values to 0xFFFF and the boolean values to false.
+ RecordedParams()
+ : max_leases(0xFFFF), timeout(0xFFFF), remove_lease(false),
+ max_unwarned_cycles(0xFFFF) {
+ }
+ };
+
+ /// @brief Constructor.
+ ///
+ /// Resets recorded parameters and obtains the instance of the @c TimerMgr.
+ LeaseReclamationStub()
+ : reclaim_calls_count_(0), delete_calls_count_(0), reclaim_params_(),
+ secs_param_(0), timer_mgr_(TimerMgr::instance()) {
+ }
+
+ /// @brief Stub implementation of the leases' reclamation routine.
+ ///
+ /// @param max_leases Maximum number of leases to be processed.
+ /// @param timeout Timeout for processing leases in milliseconds.
+ /// @remove_lease Boolean flag which indicates if the leases should be
+ /// removed when it is reclaimed.
+ /// @param Maximum number of reclamation attempts after which all leases
+ /// should be reclaimed.
+ void
+ reclaimExpiredLeases(const size_t max_leases, const uint16_t timeout,
+ const bool remove_lease,
+ const uint16_t max_unwarned_cycles) {
+ // Increase calls counter for this method.
+ ++reclaim_calls_count_;
+ // Record all parameters with which this method has been called.
+ reclaim_params_.max_leases = max_leases;
+ reclaim_params_.timeout = timeout;
+ reclaim_params_.remove_lease = remove_lease;
+ reclaim_params_.max_unwarned_cycles = max_unwarned_cycles;
+
+ // Leases' reclamation routine is responsible for re-scheduling
+ // the timer.
+ timer_mgr_->setup(CfgExpiration::RECLAIM_EXPIRED_TIMER_NAME);
+ }
+
+ /// @brief Stub implementation of the routine which flushes
+ /// expired-reclaimed leases.
+ ///
+ /// @param secs Specifies the minimum amount of time, expressed in
+ /// seconds, that must elapse before the expired-reclaimed lease is
+ /// deleted from the database.
+ void
+ deleteReclaimedLeases(const uint32_t secs) {
+ // Increase calls counter for this method.
+ ++delete_calls_count_;
+ // Record the value of the parameter.
+ secs_param_ = secs;
+
+ // Routine which flushes the reclaimed leases is responsible for
+ // re-scheduling the timer.
+ timer_mgr_->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME);
+ }
+
+ /// @brief Counter holding the number of calls to @c reclaimExpiredLeases.
+ long reclaim_calls_count_;
+
+ /// @brief Counter holding the number of calls to @c deleteReclaimedLeases.
+ long delete_calls_count_;
+
+ /// @brief Structure holding values of parameters with which the
+ /// @c reclaimExpiredLeases was called.
+ ///
+ /// These values are overridden on subsequent calls to this method.
+ RecordedParams reclaim_params_;
+
+ /// @brief Value of the parameter with which the @c deleteReclaimedLeases
+ /// was called.
+ uint32_t secs_param_;
+
+private:
+
+ /// @brief Pointer to the @c TimerMgr.
+ TimerMgrPtr timer_mgr_;
+
+};
+
+/// @brief Pointer to the @c LeaseReclamationStub.
+typedef boost::shared_ptr<LeaseReclamationStub> LeaseReclamationStubPtr;
+
+/// @brief Test fixture class for the @c CfgExpiration.
+class CfgExpirationTimersTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates instance of the test fixture class. Besides initialization
+ /// of the class members, it also stops the @c TimerMgr worker thread
+ /// and removes any registered timers.
+ CfgExpirationTimersTest()
+ : io_service_(new IOService()),
+ timer_mgr_(TimerMgr::instance()),
+ stub_(new LeaseReclamationStub()),
+ cfg_(true) {
+ cleanupTimerMgr();
+ }
+
+ /// @brief Destructor.
+ ///
+ /// It stops the @c TimerMgr worker thread and removes any registered
+ /// timers.
+ virtual ~CfgExpirationTimersTest() {
+ cleanupTimerMgr();
+ }
+
+ /// @brief Stop @c TimerMgr worker thread and remove the timers.
+ void cleanupTimerMgr() const {
+ timer_mgr_->unregisterTimers();
+ timer_mgr_->setIOService(io_service_);
+ }
+
+ /// @brief Runs IOService and stops after a specified time.
+ ///
+ /// @param timeout_ms Amount of time after which the method returns.
+ void runTimersWithTimeout(const long timeout_ms) {
+ IntervalTimer timer(*io_service_);
+ timer.setup([this]() {
+ io_service_->stop();
+ }, timeout_ms, IntervalTimer::ONE_SHOT);
+
+ io_service_->run();
+ io_service_->get_io_service().reset();
+ }
+
+ /// @brief Setup timers according to the configuration and run them
+ /// for the specified amount of time.
+ ///
+ /// @param timeout_ms Timeout in milliseconds.
+ void setupAndRun(const long timeout_ms) {
+ cfg_.setupTimers(&LeaseReclamationStub::reclaimExpiredLeases,
+ &LeaseReclamationStub::deleteReclaimedLeases,
+ stub_.get());
+ // Run timers.
+ ASSERT_NO_THROW({
+ runTimersWithTimeout(timeout_ms);
+ });
+ }
+
+ /// @brief Pointer to the IO service used by the tests.
+ IOServicePtr io_service_;
+
+ /// @brief Pointer to the @c TimerMgr.
+ TimerMgrPtr timer_mgr_;
+
+ /// @brief Pointer to the @c LeaseReclamationStub instance.
+ LeaseReclamationStubPtr stub_;
+
+ /// @brief Instance of the @c CfgExpiration class used by the tests.
+ CfgExpiration cfg_;
+};
+
+// Test that the reclamation routines are called with the appropriate parameters.
+TEST_F(CfgExpirationTimersTest, reclamationParameters) {
+ // Set this value to true, to make sure that the timer callback would
+ // modify this value to false.
+ stub_->reclaim_params_.remove_lease = true;
+
+ // Set parameters to some non-default values.
+ cfg_.setMaxReclaimLeases(1000);
+ cfg_.setMaxReclaimTime(1500);
+ cfg_.setHoldReclaimedTime(1800);
+ cfg_.setUnwarnedReclaimCycles(13);
+
+ // Run timers for 500ms.
+ ASSERT_NO_FATAL_FAILURE(setupAndRun(500));
+
+ // Make sure we had more than one call to the reclamation routine.
+ ASSERT_GT(stub_->reclaim_calls_count_, 1);
+ // Make sure it was called with appropriate arguments.
+ EXPECT_EQ(1000, stub_->reclaim_params_.max_leases);
+ EXPECT_EQ(1500, stub_->reclaim_params_.timeout);
+ EXPECT_FALSE(stub_->reclaim_params_.remove_lease);
+ EXPECT_EQ(13, stub_->reclaim_params_.max_unwarned_cycles);
+
+ // Make sure we had more than one call to the routine which flushes
+ // expired reclaimed leases.
+ ASSERT_GT(stub_->delete_calls_count_, 1);
+ // Make sure that the argument was correct.
+ EXPECT_EQ(1800, stub_->secs_param_);
+}
+
+// This test verifies that if the value of "flush-reclaimed-timer-wait-time"
+// configuration parameter is set to 0, the lease reclamation routine would
+// delete reclaimed leases from a lease database.
+TEST_F(CfgExpirationTimersTest, noLeaseAffinity) {
+ // Set the timer for flushing leases to 0. This effectively disables
+ // the timer.
+ cfg_.setFlushReclaimedTimerWaitTime(0);
+
+ // Run the lease reclamation timer for a while.
+ ASSERT_NO_FATAL_FAILURE(setupAndRun(500));
+
+ // Make sure that the lease reclamation routine has been executed a
+ // couple of times.
+ ASSERT_GT(stub_->reclaim_calls_count_, 1);
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_LEASES,
+ stub_->reclaim_params_.max_leases);
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_TIME,
+ stub_->reclaim_params_.timeout);
+ // When the "flush" timer is disabled, the lease reclamation routine is
+ // responsible for removal of reclaimed leases. This is controlled using
+ // the "remove_lease" parameter which should be set to true in this case.
+ EXPECT_TRUE(stub_->reclaim_params_.remove_lease);
+
+ // The routine flushing reclaimed leases should not be run at all.
+ EXPECT_EQ(0, stub_->delete_calls_count_);
+}
+
+// This test verifies that lease reclamation may be disabled.
+TEST_F(CfgExpirationTimersTest, noLeaseReclamation) {
+ // Disable both timers.
+ cfg_.setReclaimTimerWaitTime(0);
+ cfg_.setFlushReclaimedTimerWaitTime(0);
+
+ // Wait for 500ms.
+ ASSERT_NO_FATAL_FAILURE(setupAndRun(500));
+
+ // Make sure that neither leases' reclamation routine nor the routine
+ // flushing expired-reclaimed leases was run.
+ EXPECT_EQ(0, stub_->reclaim_calls_count_);
+ EXPECT_EQ(0, stub_->delete_calls_count_);
+}
+
+} // end of anonymous namespace