// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. #include #include #include #include #include #include #include #include "../nsas_entry.h" #include "../lru_list.h" #include "nsas_test.h" using namespace std; namespace isc { namespace nsas { /// \brief Dropped Functor Class /// /// Functor object is called when an object is dropped from the LRU list. /// To prove that it has run, this function does nothing more than set the /// MS bit on the 16-bit class value. class Dropped : public LruList::Dropped { public: virtual void operator()(TestEntry* entry) const { entry->setClass(RRClass(entry->getClass().getCode() | 0x8000)); } }; /// \brief Text Fixture Class class LruListTest : public ::testing::Test { protected: LruListTest() : entry1_(new TestEntry("alpha", RRClass::IN())), entry2_(new TestEntry("beta", RRClass::CH())), entry3_(new TestEntry("gamma", RRClass::HS())), entry4_(new TestEntry("delta", RRClass::IN())), entry5_(new TestEntry("epsilon", RRClass::HS())), entry6_(new TestEntry("zeta", RRClass::CH())), entry7_(new TestEntry("eta", RRClass::IN())) {} virtual ~LruListTest() {} boost::shared_ptr entry1_; boost::shared_ptr entry2_; boost::shared_ptr entry3_; boost::shared_ptr entry4_; boost::shared_ptr entry5_; boost::shared_ptr entry6_; boost::shared_ptr entry7_; }; // Test of the constructor TEST_F(LruListTest, Constructor) { LruList lru(100); EXPECT_EQ(100, lru.getMaxSize()); EXPECT_EQ(0, lru.size()); } // Test of Get/Set the maximum number of entrys TEST_F(LruListTest, GetSet) { LruList lru(100); EXPECT_EQ(100, lru.getMaxSize()); lru.setMaxSize(42); EXPECT_EQ(42, lru.getMaxSize()); } // Test that adding an entry really does add an entry TEST_F(LruListTest, Add) { LruList lru(100); EXPECT_EQ(0, lru.size()); lru.add(entry1_); EXPECT_EQ(1, lru.size()); lru.add(entry2_); EXPECT_EQ(2, lru.size()); } // Test that removing an entry really does remove it. TEST_F(LruListTest, Remove) { LruList lru(100); EXPECT_EQ(0, lru.size()); EXPECT_FALSE(entry1_->iteratorValid()); lru.add(entry1_); EXPECT_TRUE(entry1_->iteratorValid()); EXPECT_EQ(1, lru.size()); EXPECT_FALSE(entry2_->iteratorValid()); lru.add(entry2_); EXPECT_TRUE(entry2_->iteratorValid()); EXPECT_EQ(2, lru.size()); lru.remove(entry1_); EXPECT_FALSE(entry1_->iteratorValid()); EXPECT_EQ(1, lru.size()); } // Check that adding a new entry to a limited size list does delete the // oldest entry from the list. TEST_F(LruListTest, SizeLimit) { LruList lru(3); EXPECT_EQ(0, lru.size()); // Add first entry and check that the shared pointer's reference count // has increased. There will be two references: one from the "entry1_" // member in the test fixture class, and one from the list. EXPECT_EQ(1, entry1_.use_count()); lru.add(entry1_); EXPECT_EQ(2, entry1_.use_count()); EXPECT_EQ(1, lru.size()); // Same for entry 2. EXPECT_EQ(1, entry2_.use_count()); lru.add(entry2_); EXPECT_EQ(2, entry2_.use_count()); EXPECT_EQ(2, lru.size()); // Same for entry 3. EXPECT_EQ(1, entry3_.use_count()); lru.add(entry3_); EXPECT_EQ(2, entry3_.use_count()); EXPECT_EQ(3, lru.size()); // Adding entry 4 should remove entry 1 from the list. This will // delete the list's shared pointer to the entry and will therefore // drop the reference count back to one (from the "entry1_" member in // the text fixture class). EXPECT_EQ(2, entry1_.use_count()); EXPECT_EQ(1, entry4_.use_count()); lru.add(entry4_); EXPECT_EQ(1, entry1_.use_count()); EXPECT_EQ(2, entry4_.use_count()); EXPECT_EQ(3, lru.size()); // Adding entry 5 should remove entry 2 from the list. EXPECT_EQ(2, entry2_.use_count()); EXPECT_EQ(1, entry5_.use_count()); lru.add(entry5_); EXPECT_EQ(1, entry2_.use_count()); EXPECT_EQ(2, entry5_.use_count()); EXPECT_EQ(3, lru.size()); } // Check that "touching" an entry adds it to the back of the list. TEST_F(LruListTest, Touch) { // Create the list LruList lru(3); EXPECT_EQ(0, lru.size()); lru.add(entry1_); lru.add(entry2_); lru.add(entry3_); // Check the reference counts of the entrys and the list size EXPECT_EQ(2, entry1_.use_count()); EXPECT_EQ(2, entry2_.use_count()); EXPECT_EQ(2, entry3_.use_count()); EXPECT_EQ(1, entry4_.use_count()); EXPECT_EQ(1, entry5_.use_count()); EXPECT_EQ(1, entry6_.use_count()); EXPECT_EQ(1, entry7_.use_count()); EXPECT_EQ(3, lru.size()); // "Touch" the first entry lru.touch(entry1_); // Adding two more entries should not remove the touched entry. lru.add(entry4_); lru.add(entry5_); // Check the status of the entrys and the list. EXPECT_EQ(2, entry1_.use_count()); EXPECT_EQ(1, entry2_.use_count()); EXPECT_EQ(1, entry3_.use_count()); EXPECT_EQ(2, entry4_.use_count()); EXPECT_EQ(2, entry5_.use_count()); EXPECT_EQ(1, entry6_.use_count()); EXPECT_EQ(1, entry7_.use_count()); EXPECT_EQ(3, lru.size()); // Now touch the entry agin to move it to the back of the list. // This checks that the iterator stored in the entry as a result of the // last touch operation is valid. lru.touch(entry1_); // Check this by adding two more entrys and checking reference counts // to see what is stored. lru.add(entry6_); lru.add(entry7_); EXPECT_EQ(2, entry1_.use_count()); EXPECT_EQ(1, entry2_.use_count()); EXPECT_EQ(1, entry3_.use_count()); EXPECT_EQ(1, entry4_.use_count()); EXPECT_EQ(1, entry5_.use_count()); EXPECT_EQ(2, entry6_.use_count()); EXPECT_EQ(2, entry7_.use_count()); EXPECT_EQ(3, lru.size()); } // Dropped functor tests: tests that the function object is called when an // object expires from the list. TEST_F(LruListTest, Dropped) { // Create an object with an expiration handler. LruList lru(3, new Dropped()); // Fill the list lru.add(entry1_); lru.add(entry2_); lru.add(entry3_); EXPECT_EQ(RRClass::IN(), entry1_->getClass()); EXPECT_EQ(RRClass::CH(), entry2_->getClass()); // Add another entry and check that the handler runs. EXPECT_EQ(0, (entry1_->getClass().getCode() & 0x8000)); lru.add(entry4_); EXPECT_NE(0, (entry1_->getClass().getCode() & 0x8000)); EXPECT_EQ(0, (entry2_->getClass().getCode() & 0x8000)); lru.add(entry5_); EXPECT_NE(0, (entry2_->getClass().getCode() & 0x8000)); // Delete an entry and check that the handler does not run. EXPECT_EQ(0, (entry3_->getClass().getCode() & 0x8000)); lru.remove(entry3_); EXPECT_EQ(0, (entry3_->getClass().getCode() & 0x8000)); } // Clear functor tests: tests whether all the elements in // the list are dropped properly and the size of list is // set to 0. TEST_F(LruListTest, Clear) { // Create an object with an expiration handler. LruList lru(3, new Dropped()); // Fill the list lru.add(entry1_); lru.add(entry2_); lru.add(entry3_); EXPECT_EQ(RRClass::IN(), entry1_->getClass()); EXPECT_EQ(RRClass::CH(), entry2_->getClass()); EXPECT_EQ(RRClass::HS(), entry3_->getClass()); EXPECT_EQ(0, (entry1_->getClass().getCode() & 0x8000)); EXPECT_EQ(0, (entry2_->getClass().getCode() & 0x8000)); EXPECT_EQ(0, (entry3_->getClass().getCode() & 0x8000)); // Clear the lru list, and check the drop handler run lru.clear(); EXPECT_NE(0, (entry1_->getClass().getCode() & 0x8000)); EXPECT_NE(0, (entry2_->getClass().getCode() & 0x8000)); EXPECT_NE(0, (entry3_->getClass().getCode() & 0x8000)); EXPECT_EQ(0, lru.size()); } // Miscellaneous tests - pathological conditions TEST_F(LruListTest, Miscellaneous) { // Zero size list should not allow entrys to be added LruList lru_1(0); lru_1.add(entry1_); EXPECT_EQ(0, lru_1.size()); EXPECT_EQ(1, entry1_.use_count()); // Removing an uninserted entry should not affect the list. LruList lru_2(100); lru_2.add(entry1_); lru_2.add(entry2_); lru_2.add(entry3_); EXPECT_EQ(3, lru_2.size()); lru_2.remove(entry4_); EXPECT_EQ(2, entry1_.use_count()); EXPECT_EQ(2, entry2_.use_count()); EXPECT_EQ(2, entry3_.use_count()); EXPECT_EQ(1, entry4_.use_count()); EXPECT_EQ(1, entry5_.use_count()); EXPECT_EQ(3, lru_2.size()); // Touching an uninserted entry should not affect the list. lru_2.touch(entry5_); EXPECT_EQ(2, entry1_.use_count()); EXPECT_EQ(2, entry2_.use_count()); EXPECT_EQ(2, entry3_.use_count()); EXPECT_EQ(1, entry4_.use_count()); EXPECT_EQ(1, entry5_.use_count()); EXPECT_EQ(3, lru_2.size()); } } // namespace nsas } // namespace isc