TestCacheIndex.cpp (5298B)
1 #include "gtest/gtest.h" 2 #include "mozilla/StaticMutex.h" 3 #include "mozilla/TimeStamp.h" 4 #include "nsTArray.h" 5 #include "../cache2/CacheIndex.h" 6 7 namespace mozilla { 8 namespace net { 9 10 using namespace mozilla::net; 11 12 class FrecencyStorageTest : public ::testing::Test { 13 protected: 14 RefPtr<CacheIndexRecordWrapper> MakeRecord(uint32_t frecency) { 15 auto* rec = new CacheIndexRecordWrapper(); 16 rec->Get()->mFrecency = frecency; 17 return RefPtr<CacheIndexRecordWrapper>(rec); 18 } 19 }; 20 21 // Test to ensure that AppendRecord and RemoveRecord work correctly. Also 22 // implicitly tests RecordExistedUnlocked 23 TEST_F(FrecencyStorageTest, AppendRemoveRecordTest) { 24 CacheIndex::FrecencyStorage storage; 25 RefPtr<CacheIndexRecordWrapper> rec1 = MakeRecord(10); 26 RefPtr<CacheIndexRecordWrapper> rec2 = MakeRecord(20); 27 28 mozilla::StaticMutexAutoLock lock(CacheIndex::sLock); 29 30 // sanity check 31 EXPECT_FALSE(storage.RecordExistedUnlocked(rec1)); 32 EXPECT_FALSE(storage.RecordExistedUnlocked(rec2)); 33 EXPECT_EQ(storage.Length(), 0u); 34 35 // Append test 36 storage.AppendRecord(rec1, lock); 37 EXPECT_EQ(storage.Length(), 1u); 38 EXPECT_TRUE(storage.RecordExistedUnlocked(rec1)); 39 40 storage.AppendRecord(rec2, lock); 41 EXPECT_EQ(storage.Length(), 2u); 42 EXPECT_TRUE(storage.RecordExistedUnlocked(rec1)); 43 EXPECT_TRUE(storage.RecordExistedUnlocked(rec2)); 44 45 // Remove test 46 storage.RemoveRecord(rec1, lock); 47 EXPECT_EQ(storage.Length(), 1u); 48 49 storage.RemoveRecord(rec2, lock); 50 EXPECT_EQ(storage.Length(), 0u); 51 52 EXPECT_FALSE(storage.RecordExistedUnlocked(rec1)); 53 EXPECT_FALSE(storage.RecordExistedUnlocked(rec2)); 54 } 55 56 // Test to ensure that ReplaceRecord updates the record correctly. 57 TEST_F(FrecencyStorageTest, ReplaceRecordTest) { 58 RefPtr<CacheIndexRecordWrapper> oldRec = MakeRecord(10); 59 RefPtr<CacheIndexRecordWrapper> newRec = MakeRecord(20); 60 61 CacheIndex::FrecencyStorage storage; 62 mozilla::StaticMutexAutoLock lock(CacheIndex::sLock); 63 storage.AppendRecord(oldRec, lock); 64 EXPECT_TRUE(storage.RecordExistedUnlocked(oldRec)); 65 EXPECT_FALSE(storage.RecordExistedUnlocked(newRec)); 66 67 storage.ReplaceRecord(oldRec, newRec, lock); 68 EXPECT_FALSE(storage.RecordExistedUnlocked(oldRec)); 69 EXPECT_TRUE(storage.RecordExistedUnlocked(newRec)); 70 } 71 72 // Test to ensure that Clear() method empties the storage. 73 TEST_F(FrecencyStorageTest, ClearTest) { 74 RefPtr<CacheIndexRecordWrapper> rec1 = MakeRecord(10); 75 RefPtr<CacheIndexRecordWrapper> rec2 = MakeRecord(20); 76 77 CacheIndex::FrecencyStorage storage; 78 mozilla::StaticMutexAutoLock lock(CacheIndex::sLock); 79 storage.AppendRecord(rec1, lock); 80 storage.AppendRecord(rec2, lock); 81 EXPECT_EQ(storage.Length(), 2u); 82 83 storage.Clear(lock); 84 EXPECT_EQ(storage.Length(), 0u); 85 } 86 87 // Test to ensure that GetSortedSnapshotForEviction returns records in sorted 88 // order based on frecency. 89 TEST_F(FrecencyStorageTest, GetSortedSnapshotForEvictionTest) { 90 auto r1 = MakeRecord(30); 91 auto r2 = MakeRecord(10); 92 auto r3 = MakeRecord(20); 93 94 CacheIndex::FrecencyStorage storage; 95 mozilla::StaticMutexAutoLock lock(CacheIndex::sLock); 96 storage.AppendRecord(r1, lock); 97 storage.AppendRecord(r2, lock); 98 storage.AppendRecord(r3, lock); 99 100 auto snapshot = storage.GetSortedSnapshotForEviction(); 101 ASSERT_EQ(snapshot.Length(), 3u); 102 EXPECT_EQ(snapshot[0]->Get()->mFrecency, 10u); 103 EXPECT_EQ(snapshot[1]->Get()->mFrecency, 20u); 104 EXPECT_EQ(snapshot[2]->Get()->mFrecency, 30u); 105 } 106 107 // Performance test to ensure that AppendRecord and RemoveRecord do not degrade 108 // with large numbers of records. 109 TEST_F(FrecencyStorageTest, PerformanceTest) { 110 constexpr int N = 100'000; 111 CacheIndex::FrecencyStorage storage; 112 113 std::vector<RefPtr<CacheIndexRecordWrapper>> records; 114 records.reserve(N); 115 for (int i = 0; i < N; ++i) { 116 records.push_back(MakeRecord(i)); 117 } 118 mozilla::StaticMutexAutoLock lock(CacheIndex::sLock); 119 120 // Utility function to measure the time taken 121 auto measure = [](const std::function<void()>& func) { 122 auto start = std::chrono::high_resolution_clock::now(); 123 func(); 124 auto end = std::chrono::high_resolution_clock::now(); 125 return std::chrono::duration<double, std::milli>(end - start).count(); 126 }; 127 128 // Measure AppendRecord performance 129 auto append_duration = measure([&] { 130 for (const auto& rec : records) { 131 storage.AppendRecord(rec.get(), lock); 132 } 133 }); 134 EXPECT_LE(append_duration, 200) 135 << "AppendRecord is too slow" << " (" << append_duration << " ms) for " 136 << N << " records"; 137 138 // Measure ContainsRecord 139 auto contains_duration = measure([&] { 140 for (const auto& rec : records) { 141 auto res = storage.RecordExistedUnlocked(rec.get()); 142 EXPECT_TRUE(res); 143 // Avoid any loop optimizations by adding this check 144 if (!res) { 145 break; 146 } 147 } 148 }); 149 EXPECT_LE(contains_duration, 100) 150 << "ContainsRecord is too slow" << " (" << contains_duration 151 << " ms) for " << N << " records"; 152 153 // Measure RemoveRecord performance 154 auto remove_duration = measure([&] { 155 for (const auto& rec : records) { 156 storage.RemoveRecord(rec.get(), lock); 157 } 158 }); 159 EXPECT_LE(remove_duration, 200) 160 << "RemoveRecord is too slow" << " (" << remove_duration << " ms) for " 161 << N << " records"; 162 } 163 164 } // namespace net 165 } // namespace mozilla