blob: 4746f868012e4d368d276b601ce5cce8fa7a26a5 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/proxy_resolver/proxy_host_resolver_cache.h"
#include "base/test/task_environment.h"
#include "net/base/ip_address.h"
#include "net/base/network_isolation_key.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace proxy_resolver {
namespace {
class ProxyHostResolverCacheTest : public testing::Test {
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
ProxyHostResolverCache cache_;
};
TEST_F(ProxyHostResolverCacheTest, SimpleNegativeLookup) {
ASSERT_EQ(cache_.GetSizeForTesting(), 0u);
EXPECT_FALSE(cache_.LookupEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
}
TEST_F(ProxyHostResolverCacheTest, SimpleCachedLookup) {
const net::IPAddress kResult(1, 2, 3, 4);
cache_.StoreEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, {kResult});
EXPECT_EQ(cache_.GetSizeForTesting(), 1u);
EXPECT_THAT(cache_.LookupEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false),
testing::Pointee(testing::ElementsAre(kResult)));
}
TEST_F(ProxyHostResolverCacheTest, NoResultWithNonMatchingKeyFields) {
cache_.StoreEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, {net::IPAddress(1, 2, 3, 5)});
ASSERT_EQ(cache_.GetSizeForTesting(), 1u);
// Non-matching hostname
EXPECT_FALSE(cache_.LookupEntry("host1.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
// Non-matching isolation key
EXPECT_FALSE(cache_.LookupEntry("host.test",
net::NetworkIsolationKey::CreateTransient(),
/*is_ex_operation=*/false));
// Non-matching `is_ex_operation`
EXPECT_FALSE(cache_.LookupEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/true));
}
TEST_F(ProxyHostResolverCacheTest, NoResultForExpiredLookup) {
const net::IPAddress kResult(1, 2, 3, 6);
cache_.StoreEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, {kResult});
task_environment_.FastForwardBy(ProxyHostResolverCache::kTtl -
base::Milliseconds(5));
EXPECT_EQ(cache_.GetSizeForTesting(), 1u);
ASSERT_THAT(cache_.LookupEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false),
testing::Pointee(testing::ElementsAre(kResult)));
task_environment_.FastForwardBy(base::Milliseconds(10));
EXPECT_FALSE(cache_.LookupEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
// Expect expired entry to be deleted by lookup attempt.
EXPECT_EQ(cache_.GetSizeForTesting(), 0u);
}
TEST_F(ProxyHostResolverCacheTest, EvictsOldestEntriesWhenFull) {
ProxyHostResolverCache cache(/*max_entries=*/3u);
// Initial entry to be deleted.
cache.StoreEntry("to-be-deleted.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
// Fill to max capacity
task_environment_.FastForwardBy(base::Milliseconds(5));
cache.StoreEntry("other1.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
cache.StoreEntry("other2.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
// Nothing should be evicted yet.
EXPECT_EQ(cache.GetSizeForTesting(), 3u);
EXPECT_TRUE(cache.LookupEntry("to-be-deleted.test",
net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("other1.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("other2.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
// Add another entry and expect eviction of oldest.
cache.StoreEntry("evictor.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
EXPECT_EQ(cache.GetSizeForTesting(), 3u);
EXPECT_FALSE(cache.LookupEntry("to-be-deleted.test",
net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("other1.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("other2.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("evictor.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
}
TEST_F(ProxyHostResolverCacheTest, UpdatesAlreadyExistingEntryWithSameKey) {
cache_.StoreEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
ASSERT_EQ(cache_.GetSizeForTesting(), 1u);
const net::IPAddress kResult(1, 2, 3, 7);
cache_.StoreEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, {kResult});
EXPECT_EQ(cache_.GetSizeForTesting(), 1u);
EXPECT_THAT(cache_.LookupEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false),
testing::Pointee(testing::ElementsAre(kResult)));
}
TEST_F(ProxyHostResolverCacheTest, EntryUpdateRefreshesExpiration) {
ProxyHostResolverCache cache(/*max_entries=*/2u);
// Insert two entries, with "to-be-refreshed.test" as the older one.
cache.StoreEntry("to-be-refreshed.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
task_environment_.FastForwardBy(base::Milliseconds(5));
cache.StoreEntry("to-be-evicted.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
ASSERT_EQ(cache.GetSizeForTesting(), 2u);
// Update "to-be-refreshed.test" to refresh its expiration.
task_environment_.FastForwardBy(base::Milliseconds(5));
cache.StoreEntry("to-be-refreshed.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
ASSERT_EQ(cache.GetSizeForTesting(), 2u);
// Add another entry to force an eviction.
cache.StoreEntry("evictor.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
EXPECT_EQ(cache.GetSizeForTesting(), 2u);
EXPECT_FALSE(cache.LookupEntry("to-be-evicted.test",
net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("to-be-refreshed.test",
net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("evictor.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
}
TEST_F(ProxyHostResolverCacheTest, EntryCanBeEvictedAfterUpdate) {
ProxyHostResolverCache cache(/*max_entries=*/1u);
// Add entry and then update it.
cache.StoreEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
ASSERT_EQ(cache.GetSizeForTesting(), 1u);
task_environment_.FastForwardBy(base::Milliseconds(5));
cache.StoreEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
ASSERT_EQ(cache.GetSizeForTesting(), 1u);
// Add another entry to force an eviction.
task_environment_.FastForwardBy(base::Milliseconds(5));
cache.StoreEntry("evictor.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false, /*results=*/{});
EXPECT_EQ(cache.GetSizeForTesting(), 1u);
EXPECT_FALSE(cache.LookupEntry("host.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
EXPECT_TRUE(cache.LookupEntry("evictor.test", net::NetworkIsolationKey(),
/*is_ex_operation=*/false));
}
} // namespace
} // namespace proxy_resolver