blob: 23480ad0651536130fad2fc99aa21895779ae490 [file] [edit]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/disk_cache/sql/sql_persistent_store_in_memory_index.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace disk_cache {
namespace {
const CacheEntryKeyHash kHash1(1);
const SqlPersistentStoreResId kResId1(1);
const CacheEntryKeyHash kHash2(2);
const SqlPersistentStoreResId kResId2(2);
} // namespace
TEST(SqlPersistentStoreInMemoryIndexTest, Insert) {
SqlPersistentStoreInMemoryIndex index;
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_TRUE(index.Contains(kHash1));
}
TEST(SqlPersistentStoreInMemoryIndexTest, InsertDuplicateResId) {
SqlPersistentStoreInMemoryIndex index;
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_FALSE(index.Insert(kHash2, kResId1));
EXPECT_TRUE(index.Contains(kHash1));
}
TEST(SqlPersistentStoreInMemoryIndexTest, InsertSameHash) {
SqlPersistentStoreInMemoryIndex index;
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_TRUE(index.Insert(kHash1, kResId2));
EXPECT_TRUE(index.Contains(kHash1));
}
TEST(SqlPersistentStoreInMemoryIndexTest, RemoveWithHashAndResId) {
SqlPersistentStoreInMemoryIndex index;
index.Insert(kHash1, kResId1);
EXPECT_TRUE(index.Remove(kHash1, kResId1));
EXPECT_FALSE(index.Contains(kHash1));
}
TEST(SqlPersistentStoreInMemoryIndexTest, RemoveWithResId) {
SqlPersistentStoreInMemoryIndex index;
index.Insert(kHash1, kResId1);
EXPECT_TRUE(index.Remove(kResId1));
EXPECT_FALSE(index.Contains(kHash1));
}
TEST(SqlPersistentStoreInMemoryIndexTest, RemoveNonExistent) {
SqlPersistentStoreInMemoryIndex index;
index.Insert(kHash1, kResId1);
EXPECT_FALSE(index.Remove(kResId2));
EXPECT_FALSE(index.Remove(kHash2, kResId2));
EXPECT_FALSE(index.Remove(kHash2, kResId1));
EXPECT_FALSE(index.Remove(kHash1, kResId2));
EXPECT_TRUE(index.Contains(kHash1));
}
TEST(SqlPersistentStoreInMemoryIndexTest, Clear) {
SqlPersistentStoreInMemoryIndex index;
index.Insert(kHash1, kResId1);
index.Insert(kHash2, kResId2);
index.Clear();
EXPECT_FALSE(index.Contains(kHash1));
EXPECT_FALSE(index.Contains(kHash2));
}
TEST(SqlPersistentStoreInMemoryIndexTest, MultipleEntries) {
SqlPersistentStoreInMemoryIndex index;
index.Insert(kHash1, kResId1);
index.Insert(kHash2, kResId2);
EXPECT_TRUE(index.Contains(kHash1));
EXPECT_TRUE(index.Contains(kHash2));
EXPECT_TRUE(index.Remove(kResId1));
EXPECT_FALSE(index.Contains(kHash1));
EXPECT_TRUE(index.Contains(kHash2));
EXPECT_TRUE(index.Remove(kHash2, kResId2));
EXPECT_FALSE(index.Contains(kHash2));
}
TEST(SqlPersistentStoreInMemoryIndexTest, BehavesCorrectlyWithBothMaps) {
const CacheEntryKeyHash kHashLarge(3);
const SqlPersistentStoreResId kResIdLarge(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
SqlPersistentStoreInMemoryIndex index;
// Add to the 32-bit map.
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_EQ(1u, index.size());
EXPECT_TRUE(index.Contains(kHash1));
// Add to the 64-bit map.
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge));
EXPECT_EQ(2u, index.size());
EXPECT_TRUE(index.Contains(kHashLarge));
// Check that both entries are present.
EXPECT_TRUE(index.Contains(kHash1));
// Remove the entry from the 32-bit map.
EXPECT_TRUE(index.Remove(kResId1));
EXPECT_EQ(1u, index.size());
EXPECT_FALSE(index.Contains(kHash1));
// Trying to remove the already removed entry should fail.
EXPECT_FALSE(index.Remove(kResId1));
EXPECT_FALSE(index.Remove(kHash1, kResId1));
// It should be possible to re-insert and remove the same entry.
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_TRUE(index.Remove(kHash1, kResId1));
// Remove the entry from the 64-bit map.
EXPECT_TRUE(index.Remove(kResIdLarge));
EXPECT_EQ(0u, index.size());
// It should be possible to re-insert and remove the same entry.
EXPECT_FALSE(index.Remove(kResIdLarge));
EXPECT_FALSE(index.Remove(kHashLarge, kResIdLarge));
// Add entries again to ensure it still works.
EXPECT_TRUE(index.Insert(kHash2, kResId2));
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge));
EXPECT_EQ(2u, index.size());
// Remove an entry from the 64-bit map.
EXPECT_TRUE(index.Remove(kHashLarge, kResIdLarge));
// Clear both maps.
index.Clear();
EXPECT_EQ(0u, index.size());
EXPECT_FALSE(index.Contains(kHash2));
EXPECT_FALSE(index.Contains(kHashLarge));
}
TEST(SqlPersistentStoreInMemoryIndexTest, MoveOperations) {
// Test move constructor.
SqlPersistentStoreInMemoryIndex index1;
index1.Insert(kHash1, kResId1);
index1.Insert(kHash2, kResId2);
SqlPersistentStoreInMemoryIndex index2(std::move(index1));
EXPECT_TRUE(index2.Contains(kHash1));
EXPECT_TRUE(index2.Contains(kHash2));
EXPECT_EQ(2u, index2.size());
// Test move assignment.
SqlPersistentStoreInMemoryIndex index3;
index3 = std::move(index2);
EXPECT_TRUE(index3.Contains(kHash1));
EXPECT_TRUE(index3.Contains(kHash2));
EXPECT_EQ(2u, index3.size());
}
TEST(SqlPersistentStoreInMemoryIndexTest, MoveOperationsWithResId64) {
const CacheEntryKeyHash kHashLarge(3);
const SqlPersistentStoreResId kResIdLarge(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
// Test move constructor with ResId64.
SqlPersistentStoreInMemoryIndex index1;
index1.Insert(kHash1, kResId1);
index1.Insert(kHashLarge, kResIdLarge);
SqlPersistentStoreInMemoryIndex index2(std::move(index1));
EXPECT_TRUE(index2.Contains(kHash1));
EXPECT_TRUE(index2.Contains(kHashLarge));
EXPECT_EQ(2u, index2.size());
// Test move assignment with ResId64.
SqlPersistentStoreInMemoryIndex index3;
index3 = std::move(index2);
EXPECT_TRUE(index3.Contains(kHash1));
EXPECT_TRUE(index3.Contains(kHashLarge));
EXPECT_EQ(2u, index3.size());
}
TEST(SqlPersistentStoreInMemoryIndexTest, TryGetSingleResIdNoEntry) {
SqlPersistentStoreInMemoryIndex index;
EXPECT_EQ(index.TryGetSingleResId(kHash1), std::nullopt);
}
TEST(SqlPersistentStoreInMemoryIndexTest, TryGetSingleResIdOneEntrySmall) {
SqlPersistentStoreInMemoryIndex index;
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_THAT(index.TryGetSingleResId(kHash1), kResId1);
}
TEST(SqlPersistentStoreInMemoryIndexTest, TryGetSingleResIdOneEntryLarge) {
SqlPersistentStoreInMemoryIndex index;
const CacheEntryKeyHash kHashLarge(3);
const SqlPersistentStoreResId kResIdLarge(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge));
EXPECT_THAT(index.TryGetSingleResId(kHashLarge), kResIdLarge);
}
TEST(SqlPersistentStoreInMemoryIndexTest,
TryGetSingleResIdCollisionSmallSmall) {
SqlPersistentStoreInMemoryIndex index;
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_TRUE(index.Insert(kHash1, kResId2));
// Should fail because there are multiple entries for the same hash.
EXPECT_EQ(index.TryGetSingleResId(kHash1), std::nullopt);
// After removing one, it should be unique again.
EXPECT_TRUE(index.Remove(kResId2));
EXPECT_THAT(index.TryGetSingleResId(kHash1), kResId1);
}
TEST(SqlPersistentStoreInMemoryIndexTest,
TryGetSingleResIdCollisionLargeLarge) {
SqlPersistentStoreInMemoryIndex index;
const CacheEntryKeyHash kHashLarge(3);
const SqlPersistentStoreResId kResIdLarge1(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
const SqlPersistentStoreResId kResIdLarge2(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 2);
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge1));
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge2));
// Should fail because there are multiple entries for the same hash.
EXPECT_EQ(index.TryGetSingleResId(kHashLarge), std::nullopt);
EXPECT_TRUE(index.Remove(kResIdLarge2));
EXPECT_THAT(index.TryGetSingleResId(kHashLarge), kResIdLarge1);
}
TEST(SqlPersistentStoreInMemoryIndexTest,
TryGetSingleResIdCollisionSmallLarge) {
SqlPersistentStoreInMemoryIndex index;
const CacheEntryKeyHash kHashCollision(10);
const SqlPersistentStoreResId kResIdSmall(100);
const SqlPersistentStoreResId kResIdLarge(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
EXPECT_TRUE(index.Insert(kHashCollision, kResIdSmall));
EXPECT_TRUE(index.Insert(kHashCollision, kResIdLarge));
// Should fail because there are multiple entries for the same hash.
EXPECT_EQ(index.TryGetSingleResId(kHashCollision), std::nullopt);
EXPECT_TRUE(index.Remove(kResIdLarge));
EXPECT_THAT(index.TryGetSingleResId(kHashCollision), kResIdSmall);
}
TEST(SqlPersistentStoreInMemoryIndexTest, EntryDataHintsOneEntrySmall) {
SqlPersistentStoreInMemoryIndex index;
const MemoryEntryDataHints kHints(1);
EXPECT_TRUE(index.Insert(kHash1, kResId1));
index.SetEntryDataHints(kResId1, kHints);
EXPECT_THAT(index.GetEntryDataHints(kHash1), kHints);
}
TEST(SqlPersistentStoreInMemoryIndexTest, EntryDataHintsOneEntryLarge) {
SqlPersistentStoreInMemoryIndex index;
const CacheEntryKeyHash kHashLarge(3);
const SqlPersistentStoreResId kResIdLarge(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
const MemoryEntryDataHints kHints(5);
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge));
index.SetEntryDataHints(kResIdLarge, kHints);
EXPECT_THAT(index.GetEntryDataHints(kHashLarge), kHints);
}
TEST(SqlPersistentStoreInMemoryIndexTest, EntryDataHintsCollisionSmallSmall) {
SqlPersistentStoreInMemoryIndex index;
const MemoryEntryDataHints kHints1(1);
EXPECT_TRUE(index.Insert(kHash1, kResId1));
index.SetEntryDataHints(kResId1, kHints1);
EXPECT_TRUE(index.Insert(kHash1, kResId2));
// Even if we don't set hints for the second one, ambiguous hash lookups
// should fail.
EXPECT_EQ(index.GetEntryDataHints(kHash1), std::nullopt);
EXPECT_TRUE(index.Remove(kResId2));
EXPECT_THAT(index.GetEntryDataHints(kHash1), kHints1);
}
TEST(SqlPersistentStoreInMemoryIndexTest, EntryDataHintsCollisionLargeLarge) {
SqlPersistentStoreInMemoryIndex index;
const CacheEntryKeyHash kHashLarge(3);
const SqlPersistentStoreResId kResIdLarge1(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
const SqlPersistentStoreResId kResIdLarge2(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 2);
const MemoryEntryDataHints kHints(5);
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge1));
index.SetEntryDataHints(kResIdLarge1, kHints);
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge2));
EXPECT_EQ(index.GetEntryDataHints(kHashLarge), std::nullopt);
EXPECT_TRUE(index.Remove(kResIdLarge2));
EXPECT_THAT(index.GetEntryDataHints(kHashLarge), kHints);
}
TEST(SqlPersistentStoreInMemoryIndexTest, EntryDataHintsCollisionSmallLarge) {
SqlPersistentStoreInMemoryIndex index;
const CacheEntryKeyHash kHashCollision(10);
const SqlPersistentStoreResId kResIdSmall(100);
const SqlPersistentStoreResId kResIdLarge(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
const MemoryEntryDataHints kHints(1);
EXPECT_TRUE(index.Insert(kHashCollision, kResIdSmall));
index.SetEntryDataHints(kResIdSmall, kHints);
EXPECT_TRUE(index.Insert(kHashCollision, kResIdLarge));
EXPECT_EQ(index.GetEntryDataHints(kHashCollision), std::nullopt);
EXPECT_TRUE(index.Remove(kResIdLarge));
EXPECT_THAT(index.GetEntryDataHints(kHashCollision), kHints);
}
TEST(SqlPersistentStoreInMemoryIndexTest, EntryDataHintsNoHint) {
SqlPersistentStoreInMemoryIndex index;
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_EQ(index.GetEntryDataHints(kHash1), std::nullopt);
}
TEST(SqlPersistentStoreInMemoryIndexTest, GetResIdsWithHints) {
SqlPersistentStoreInMemoryIndex index;
const CacheEntryKeyHash kHashLarge(3);
const SqlPersistentStoreResId kResIdLarge(
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 1);
const CacheEntryKeyHash kHashBoth(4);
const SqlPersistentStoreResId kResIdBoth(4);
EXPECT_TRUE(index.Insert(kHash1, kResId1));
EXPECT_TRUE(index.Insert(kHash2, kResId2));
EXPECT_TRUE(index.Insert(kHashLarge, kResIdLarge));
EXPECT_TRUE(index.Insert(kHashBoth, kResIdBoth));
const MemoryEntryDataHints kHint1(1 << 0);
const MemoryEntryDataHints kHint2(1 << 1);
// Set hints.
index.SetEntryDataHints(kResId1, kHint1);
index.SetEntryDataHints(kResId2, kHint2);
index.SetEntryDataHints(kResIdLarge, kHint1);
index.SetEntryDataHints(
kResIdBoth, MemoryEntryDataHints(kHint1.value() | kHint2.value()));
// Entries with kHint1 (at least).
EXPECT_THAT(index.GetResIdsWithHints(kHint1),
testing::UnorderedElementsAre(kResId1, kResIdLarge, kResIdBoth));
// Entries with kHint2 (at least).
EXPECT_THAT(index.GetResIdsWithHints(kHint2),
testing::UnorderedElementsAre(kResId2, kResIdBoth));
// Entries that have BOTH hints.
EXPECT_THAT(index.GetResIdsWithHints(
MemoryEntryDataHints(kHint1.value() | kHint2.value())),
testing::UnorderedElementsAre(kResIdBoth));
}
} // namespace disk_cache