blob: 9690d5214df606aa91139082345c2183ecd68132 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/code_cache/simple_lru_cache.h"
#include "base/feature_list.h"
#include "base/test/scoped_feature_list.h"
#include "content/public/common/content_features.h"
#include "net/base/schemeful_site.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
constexpr auto kEmptyEntrySize = SimpleLruCache::kEmptyEntrySize;
class SimpleLruCacheTest : public testing::TestWithParam<bool> {
public:
SimpleLruCacheTest() {
scoped_feature_list_.InitWithFeatureState(features::kInMemoryCodeCache,
GetParam());
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
std::vector<uint8_t> ToVector(const mojo_base::BigBuffer& buffer) {
return std::vector<uint8_t>(buffer.byte_span().begin(),
buffer.byte_span().end());
}
TEST_P(SimpleLruCacheTest, Empty) {
const std::string kKey = "hello";
SimpleLruCache cache(/*capacity=*/100 * 1024);
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_FALSE(cache.Has(kKey));
}
TEST_P(SimpleLruCacheTest, PutAndGet) {
const std::string kKey1("key1");
const std::string kKey2("key2");
const std::string kKey3("key3");
const std::string kKey4("key4");
// Note that kKey{i} doesn't necessarily correspond to kData{i} or
// kResponseTime{i}.
const std::vector<uint8_t> kData1 = {0x08};
const std::vector<uint8_t> kData2 = {0x02, 0x09};
const std::vector<uint8_t> kData3 = {0x03, 0x0a, 0xf1};
const std::vector<uint8_t> kData4 = {0x04, 0x02, 0x00, 0x30};
const base::Time kResponseTime1 = base::Time::UnixEpoch() + base::Hours(1);
const base::Time kResponseTime2 = base::Time::UnixEpoch() + base::Hours(2);
const base::Time kResponseTime3 = base::Time::UnixEpoch() + base::Hours(3);
const base::Time kResponseTime4 = base::Time::UnixEpoch() + base::Hours(4);
SimpleLruCache cache(/*capacity=*/100 * 1024);
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_FALSE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_FALSE(cache.Has(kKey3));
EXPECT_FALSE(cache.Has(kKey4));
cache.Put(kKey1, kResponseTime1, base::make_span(kData1));
EXPECT_EQ(cache.GetSize(), 1 + 4 + kEmptyEntrySize);
EXPECT_TRUE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_FALSE(cache.Has(kKey3));
EXPECT_FALSE(cache.Has(kKey4));
// Updates the entry.
cache.Put(kKey1, kResponseTime2, base::make_span(kData2));
EXPECT_EQ(cache.GetSize(), 2 + 4 + kEmptyEntrySize);
EXPECT_TRUE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_FALSE(cache.Has(kKey3));
EXPECT_FALSE(cache.Has(kKey4));
cache.Put(kKey2, kResponseTime3, base::make_span(kData3));
EXPECT_EQ(cache.GetSize(), 5 + 8 + 2 * kEmptyEntrySize);
EXPECT_TRUE(cache.Has(kKey1));
EXPECT_TRUE(cache.Has(kKey2));
EXPECT_FALSE(cache.Has(kKey3));
EXPECT_FALSE(cache.Has(kKey4));
// We don't create an entry for `kKey3` intentionally.
cache.Put(kKey4, kResponseTime4, base::make_span(kData4));
EXPECT_EQ(cache.GetSize(), 9 + 12 + 3 * kEmptyEntrySize);
const auto result1 = cache.Get(kKey1);
ASSERT_TRUE(result1.has_value());
EXPECT_EQ(result1->response_time, kResponseTime2);
const auto result2 = cache.Get(kKey2);
ASSERT_TRUE(result2.has_value());
EXPECT_EQ(result2->response_time, kResponseTime3);
const auto result3 = cache.Get(kKey3);
ASSERT_FALSE(result3.has_value());
const auto result4 = cache.Get(kKey4);
ASSERT_TRUE(result4.has_value());
EXPECT_EQ(result4->response_time, kResponseTime4);
if (base::FeatureList::IsEnabled(features::kInMemoryCodeCache)) {
EXPECT_EQ(ToVector(result1->data), kData2);
EXPECT_EQ(ToVector(result2->data), kData3);
EXPECT_EQ(ToVector(result4->data), kData4);
} else {
EXPECT_EQ(result1->data.size(), 0u);
EXPECT_EQ(result2->data.size(), 0u);
EXPECT_EQ(result4->data.size(), 0u);
}
}
TEST_P(SimpleLruCacheTest, PutAndEvict) {
const std::string kKey("key1");
const uint8_t kData1[2] = {0x08, 0x09};
const uint8_t kData2[1] = {0x02};
const uint8_t kData3[2] = {0x03, 0x0a};
const base::Time kResponseTime1 = base::Time::UnixEpoch() + base::Hours(1);
const base::Time kResponseTime2 = base::Time::UnixEpoch() + base::Hours(2);
const base::Time kResponseTime3 = base::Time::UnixEpoch() + base::Hours(3);
SimpleLruCache cache(/*capacity=*/kEmptyEntrySize + kKey.size() + 1);
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_FALSE(cache.Has(kKey));
// This entry is immediately evicted because the size excceeds the capacity.
cache.Put(kKey, kResponseTime1, base::make_span(kData1));
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_FALSE(cache.Has(kKey));
// This entry stays.
cache.Put(kKey, kResponseTime2, base::make_span(kData2));
EXPECT_EQ(cache.GetSize(), 1 + kKey.size() + kEmptyEntrySize);
EXPECT_TRUE(cache.Has(kKey));
// An updated entry can also be evicted.
cache.Put(kKey, kResponseTime3, base::make_span(kData3));
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_FALSE(cache.Has(kKey));
}
TEST_P(SimpleLruCacheTest, LRU) {
const std::string kKey1("key1");
const std::string kKey2("key2");
const std::string kKey3("key3");
const std::string kKey4("key4");
const base::Time kResponseTime = base::Time::UnixEpoch();
SimpleLruCache cache(kEmptyEntrySize * 2 + 8);
cache.Put(kKey1, kResponseTime, base::span<uint8_t>());
cache.Put(kKey2, kResponseTime, base::span<uint8_t>());
cache.Put(kKey3, kResponseTime, base::span<uint8_t>());
cache.Put(kKey4, kResponseTime, base::span<uint8_t>());
// The last two entries are kept.
EXPECT_EQ(cache.GetSize(), 2 * kEmptyEntrySize + 8);
EXPECT_FALSE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_TRUE(cache.Has(kKey3));
EXPECT_TRUE(cache.Has(kKey4));
}
TEST_P(SimpleLruCacheTest, LRUAndGet) {
const std::string kKey1("key1");
const std::string kKey2("key2");
const std::string kKey3("key3");
const std::string kKey4("key4");
const base::Time kResponseTime = base::Time::UnixEpoch();
SimpleLruCache cache(kEmptyEntrySize * 2 + 8);
cache.Put(kKey1, kResponseTime, base::span<uint8_t>());
cache.Put(kKey2, kResponseTime, base::span<uint8_t>());
// This call updates the access time.
EXPECT_TRUE(cache.Has(kKey1));
cache.Put(kKey3, kResponseTime, base::span<uint8_t>());
EXPECT_EQ(cache.GetSize(), 2 * kEmptyEntrySize + 8);
EXPECT_TRUE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_TRUE(cache.Has(kKey3));
EXPECT_FALSE(cache.Has(kKey4));
}
TEST_P(SimpleLruCacheTest, Delete) {
const base::Time kResponseTime = base::Time::UnixEpoch();
const std::string kKey1("key1");
const std::string kKey2("key2");
const std::string kKey3("key3");
const std::string kKey4("key4");
const uint8_t kData1[1] = {0x08};
const uint8_t kData2[2] = {0x02, 0x09};
const uint8_t kData3[3] = {0x03, 0x0a, 0xf1};
const uint8_t kData4[4] = {0x04, 0x02, 0x00, 0x30};
SimpleLruCache cache(/*capacity=*/1024 * 1024);
cache.Put(kKey1, kResponseTime, base::make_span(kData1));
cache.Put(kKey2, kResponseTime, base::make_span(kData2));
cache.Put(kKey3, kResponseTime, base::make_span(kData3));
cache.Put(kKey4, kResponseTime, base::make_span(kData4));
EXPECT_EQ(cache.GetSize(), 4 * kEmptyEntrySize + 16 + 10);
EXPECT_TRUE(cache.Has(kKey1));
EXPECT_TRUE(cache.Has(kKey2));
EXPECT_TRUE(cache.Has(kKey3));
EXPECT_TRUE(cache.Has(kKey4));
cache.Delete(kKey2);
EXPECT_EQ(cache.GetSize(), 3 * kEmptyEntrySize + 12 + 8);
EXPECT_TRUE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_TRUE(cache.Has(kKey3));
EXPECT_TRUE(cache.Has(kKey4));
cache.Delete(kKey2);
EXPECT_EQ(cache.GetSize(), 3 * kEmptyEntrySize + 12 + 8);
EXPECT_TRUE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_TRUE(cache.Has(kKey3));
EXPECT_TRUE(cache.Has(kKey4));
cache.Delete(kKey1);
cache.Delete(kKey2);
cache.Delete(kKey3);
cache.Delete(kKey4);
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_FALSE(cache.Has(kKey1));
EXPECT_FALSE(cache.Has(kKey2));
EXPECT_FALSE(cache.Has(kKey3));
EXPECT_FALSE(cache.Has(kKey4));
}
TEST_P(SimpleLruCacheTest, Clear) {
const base::Time kResponseTime = base::Time::UnixEpoch();
const std::string kKey("key1");
const uint8_t kData[1] = {0x08};
SimpleLruCache cache(/*capacity=*/1024 * 1024);
cache.Put(kKey, kResponseTime, base::make_span(kData));
EXPECT_TRUE(cache.Has(kKey));
EXPECT_GT(cache.GetSize(), 0u);
cache.Clear();
EXPECT_FALSE(cache.Has(kKey));
EXPECT_EQ(cache.GetSize(), 0u);
}
INSTANTIATE_TEST_SUITE_P(SimpleLruCacheTest,
SimpleLruCacheTest,
testing::Bool());
} // namespace
} // namespace content