#include "gtest/gtest.h" #include "HostRecordQueue.h" #include "TRRQuery.h" #include "TRR.h" using namespace mozilla; using namespace mozilla::net; class MockHostRecord : public nsHostRecord { public: NS_DECL_ISUPPORTS_INHERITED explicit MockHostRecord(const nsHostKey& aKey) : nsHostRecord(aKey) { negative = true; } void ResolveComplete() override {} bool HasUsableResultInternal( const mozilla::TimeStamp& now, nsIDNSService::DNSFlags queryFlags) const override { return true; } private: ~MockHostRecord() = default; }; NS_IMPL_ISUPPORTS_INHERITED(MockHostRecord, nsHostRecord, MockHostRecord) class HostRecordQueueTest : public ::testing::Test { protected: void SetUp() override {} HostRecordQueue queue; Mutex mMutex{"HostRecordQueueTest"}; nsRefPtrHashtable, nsHostRecord> mDB; }; static RefPtr CreateAndInsertMockRecord( nsRefPtrHashtable, nsHostRecord>& aDB, const char* hostName) { nsHostKey key(nsCString(hostName), ""_ns, 0, nsIDNSService::RESOLVE_DEFAULT_FLAGS, 0, false, ""_ns); return aDB.LookupOrInsertWith(key, [&] { return new MockHostRecord(key); }); } TEST_F(HostRecordQueueTest, AddToEvictionQ_BelowMax) { RefPtr rec = CreateAndInsertMockRecord(mDB, "A.com"); MutexAutoLock lock(mMutex); queue.AddToEvictionQ(rec.get(), 100, mDB, lock); ASSERT_EQ(1u, queue.EvictionQSize()); ASSERT_TRUE(rec->isInList()); // Cleanup rec->remove(); } // When the eviction queue is at capacity, adding a new entry evicts the // oldest (head) entry. TEST_F(HostRecordQueueTest, AddToEvictionQ_AtMax_EvictsOldest) { const uint32_t MAX_ENTRIES = 3; RefPtr rec1 = CreateAndInsertMockRecord(mDB, "A.com"); RefPtr rec2 = CreateAndInsertMockRecord(mDB, "B.com"); RefPtr rec3 = CreateAndInsertMockRecord(mDB, "C.com"); MutexAutoLock lock(mMutex); queue.AddToEvictionQ(rec1, MAX_ENTRIES, mDB, lock); queue.AddToEvictionQ(rec2, MAX_ENTRIES, mDB, lock); queue.AddToEvictionQ(rec3, MAX_ENTRIES, mDB, lock); ASSERT_EQ(MAX_ENTRIES, queue.EvictionQSize()); RefPtr rec4 = CreateAndInsertMockRecord(mDB, "New.com"); queue.AddToEvictionQ(rec4, MAX_ENTRIES, mDB, lock); ASSERT_TRUE(rec2->isInList()); ASSERT_TRUE(rec3->isInList()); ASSERT_TRUE(rec4->isInList()); ASSERT_FALSE(rec1->isInList()); rec2->remove(); rec3->remove(); rec4->remove(); } // After adding a new record, the touched entry // remains, and the oldest untouched entry is evicted. TEST_F(HostRecordQueueTest, MoveToEvictionQueueTail) { const uint32_t MAX_ENTRIES = 3; RefPtr rec1 = CreateAndInsertMockRecord(mDB, "A.com"); RefPtr rec2 = CreateAndInsertMockRecord(mDB, "B.com"); RefPtr rec3 = CreateAndInsertMockRecord(mDB, "C.com"); MutexAutoLock lock(mMutex); queue.AddToEvictionQ(rec1, MAX_ENTRIES, mDB, lock); queue.AddToEvictionQ(rec2, MAX_ENTRIES, mDB, lock); queue.AddToEvictionQ(rec3, MAX_ENTRIES, mDB, lock); ASSERT_EQ(MAX_ENTRIES, queue.EvictionQSize()); queue.MoveToEvictionQueueTail(rec1, lock); RefPtr rec4 = CreateAndInsertMockRecord(mDB, "New.com"); queue.AddToEvictionQ(rec4, MAX_ENTRIES, mDB, lock); ASSERT_TRUE(rec1->isInList()); ASSERT_TRUE(rec3->isInList()); ASSERT_TRUE(rec4->isInList()); ASSERT_FALSE(rec2->isInList()); rec1->remove(); rec3->remove(); rec4->remove(); }