#include "net/disk_cache/sql/sql_backend_impl.h"
#include <cstdint>
#include <variant>
#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_file_util.h"
#include "base/test/test_future.h"
#include "components/performance_manager/scenario_api/performance_scenario_test_support.h"
#include "net/base/features.h"
#include "net/base/io_buffer.h"
#include "net/base/test_completion_callback.h"
#include "net/disk_cache/disk_cache_test_util.h"
#include "net/disk_cache/sql/sql_backend_constants.h"
#include "net/disk_cache/sql/sql_entry_impl.h"
#include "net/test/gtest_util.h"
#include "sql/database.h"
#include "sql/statement.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using net::test::IsError;
using net::test::IsOk;
using performance_scenarios::InputScenario;
using performance_scenarios::LoadingScenario;
using performance_scenarios::PerformanceScenarioTestHelper;
using performance_scenarios::ScenarioScope;
namespace disk_cache {
namespace {
using testing::ElementsAre;
using testing::Pair;
using FakeIndexFileError = SqlBackendImpl::FakeIndexFileError;
inline constexpr int64_t kDefaultMaxBytes = 10 * 1024 * 1024;
Entry* CreateEntryAndWriteData(SqlBackendImpl* backend,
const std::string& key,
const std::string& data) {
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry(key, net::HIGHEST, cb_create.callback()));
CHECK_EQ(create_result.net_error(), net::OK);
auto* entry = create_result.ReleaseEntry();
auto buffer = base::MakeRefCounted<net::StringIOBuffer>(data);
net::TestCompletionCallback cb_write;
EXPECT_EQ(
cb_write.GetResult(entry->WriteData(1, 0, buffer.get(), buffer->size(),
cb_write.callback(), false)),
static_cast<int>(buffer->size()));
return entry;
}
void ReadAndVerifyData(Entry* entry, std::string_view expected_data) {
auto read_buffer =
base::MakeRefCounted<net::IOBufferWithSize>(expected_data.size() + 1);
net::TestCompletionCallback cb_read;
int rv_read = entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
cb_read.callback());
EXPECT_EQ(cb_read.GetResult(rv_read), static_cast<int>(expected_data.size()));
EXPECT_EQ(std::string_view(read_buffer->data(), expected_data.size()),
expected_data);
}
size_t GetShardCount() {
return std::max(std::min(net::features::kSqlDiskCacheShardCount.Get(), 255),
1);
}
std::string GetExpectedFakeIndexContents() {
return base::StrCat(
{kSqlBackendFakeIndexPrefix, base::NumberToString(GetShardCount())});
}
class SqlBackendImplTest : public testing::Test {
public:
SqlBackendImplTest() = default;
~SqlBackendImplTest() override = default;
void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
protected:
std::unique_ptr<SqlBackendImpl> CreateBackend() {
return std::make_unique<SqlBackendImpl>(
temp_dir_.GetPath(), kDefaultMaxBytes, net::CacheType::DISK_CACHE);
}
std::unique_ptr<SqlBackendImpl> CreateBackendAndInit(
int64_t max_bytes = kDefaultMaxBytes) {
auto backend = std::make_unique<SqlBackendImpl>(
temp_dir_.GetPath(), max_bytes, net::CacheType::DISK_CACHE);
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
CHECK_EQ(future.Get(), net::OK);
return backend;
}
void WaitUntilInitialized(
SqlBackendImpl& backend,
const scoped_refptr<SqlBackendImpl::ResIdOrErrorHolder>&
res_id_or_error) {
CHECK(res_id_or_error);
while (!res_id_or_error->data.has_value()) {
FlushQueue(backend);
}
}
void FlushQueue(SqlBackendImpl& backend) {
net::TestCompletionCallback flush_cb;
backend.FlushQueueForTest(flush_cb.callback());
EXPECT_THAT(flush_cb.WaitForResult(), IsOk());
}
void FlushQueueInTaskRunners(
const std::vector<scoped_refptr<base::SequencedTaskRunner>>&
task_runners) {
for (auto& runner : task_runners) {
base::RunLoop run_loop;
runner->PostTask(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
}
}
bool LoadInMemoryIndex(SqlBackendImpl& backend) {
auto* store = backend.GetSqlStoreForTest();
base::test::TestFuture<SqlPersistentStore::Error> future;
auto ret = store->MaybeLoadInMemoryIndex(future.GetCallback());
if (ret) {
CHECK_EQ(future.Get(), SqlPersistentStore::Error::kOk);
return true;
}
return false;
}
int64_t GetSizeOfAllEntries(SqlBackendImpl& backend) {
return backend.GetSqlStoreForTest()->GetSizeOfAllEntries();
}
int64_t OpenDatabaseAndGetBlobsCount(SqlPersistentStore::ShardId shard_id,
SqlPersistentStore::ResId res_id) {
auto db = std::make_unique<sql::Database>(
sql::DatabaseOptions()
.set_exclusive_locking(true)
#if BUILDFLAG(IS_WIN)
.set_exclusive_database_file_lock(true)
#endif
.set_preload(true)
.set_wal_mode(true),
sql::Database::Tag("HttpCacheDiskCache"));
CHECK(db->Open(temp_dir_.GetPath().AppendASCII(
base::StrCat({kSqlBackendDatabaseFileNamePrefix,
base::NumberToString(shard_id.value())}))));
sql::Statement s(
db->GetUniqueStatement("SELECT COUNT(*) FROM blobs where res_id = ?"));
s.BindInt64(0, res_id.value());
CHECK(s.Step());
return s.ColumnInt64(0);
}
void RunDelayedPostInitializationTasksTest();
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::ScopedTempDir temp_dir_;
};
TEST_F(SqlBackendImplTest, InitWithNoFakeIndexFile) {
const std::string expected_contents = GetExpectedFakeIndexContents();
base::HistogramTester histogram_tester;
auto backend = CreateBackend();
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::OK);
histogram_tester.ExpectUniqueSample("Net.SqlDiskCache.FakeIndexFileError",
FakeIndexFileError::kOkNew, 1);
base::FilePath file_path =
temp_dir_.GetPath().Append(kSqlBackendFakeIndexFileName);
const std::optional<int64_t> file_size = base::GetFileSize(file_path);
ASSERT_TRUE(file_size.has_value());
EXPECT_EQ(*file_size, expected_contents.size());
std::vector<uint8_t> contents(expected_contents.size());
ASSERT_TRUE(base::ReadFile(file_path, contents));
EXPECT_EQ(contents, base::as_byte_span(expected_contents));
}
TEST_F(SqlBackendImplTest, InitWithFakeIndexFile) {
const std::string expected_contents = GetExpectedFakeIndexContents();
base::HistogramTester histogram_tester;
base::FilePath file_path =
temp_dir_.GetPath().Append(kSqlBackendFakeIndexFileName);
ASSERT_TRUE(
base::WriteFile(file_path, base::as_byte_span(expected_contents)));
auto backend = CreateBackend();
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::OK);
histogram_tester.ExpectUniqueSample("Net.SqlDiskCache.FakeIndexFileError",
FakeIndexFileError::kOkExisting, 1);
}
TEST_F(SqlBackendImplTest, InitWithCorruptedFakeIndexFile) {
std::string corrupted_contents = GetExpectedFakeIndexContents();
base::span<uint8_t> corrupted_contents_span =
base::as_writable_bytes(base::span(corrupted_contents));
corrupted_contents_span.subspan(corrupted_contents_span.size() - 1)
.copy_from({'X'});
base::HistogramTester histogram_tester;
base::FilePath file_path =
temp_dir_.GetPath().Append(kSqlBackendFakeIndexFileName);
ASSERT_TRUE(base::WriteFile(file_path, corrupted_contents_span));
auto backend = CreateBackend();
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::ERR_FAILED);
histogram_tester.ExpectUniqueSample("Net.SqlDiskCache.FakeIndexFileError",
FakeIndexFileError::kWrongMagicNumber, 1);
}
TEST_F(SqlBackendImplTest, InitWithWrongSizeFakeIndexFile) {
base::HistogramTester histogram_tester;
base::FilePath file_path =
temp_dir_.GetPath().Append(kSqlBackendFakeIndexFileName);
const int32_t kWrongMagicNumber = 0xDEADBEEF;
ASSERT_TRUE(
base::WriteFile(file_path, base::byte_span_from_ref(kWrongMagicNumber)));
auto backend = CreateBackend();
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::ERR_FAILED);
histogram_tester.ExpectUniqueSample("Net.SqlDiskCache.FakeIndexFileError",
FakeIndexFileError::kWrongFileSize, 1);
}
TEST_F(SqlBackendImplTest, InitWithOpenFileFailed) {
const std::string expected_contents = GetExpectedFakeIndexContents();
base::HistogramTester histogram_tester;
base::FilePath file_path =
temp_dir_.GetPath().Append(kSqlBackendFakeIndexFileName);
ASSERT_TRUE(
base::WriteFile(file_path, base::as_byte_span(expected_contents)));
base::FilePermissionRestorer permission_restorer(file_path);
ASSERT_TRUE(base::MakeFileUnreadable(file_path));
auto backend = CreateBackend();
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::ERR_FAILED);
histogram_tester.ExpectUniqueSample("Net.SqlDiskCache.FakeIndexFileError",
FakeIndexFileError::kOpenFileFailed, 1);
}
TEST_F(SqlBackendImplTest, InitWithCreateFileFailed) {
base::HistogramTester histogram_tester;
base::FilePermissionRestorer permission_restorer(temp_dir_.GetPath());
ASSERT_TRUE(base::MakeFileUnwritable(temp_dir_.GetPath()));
auto backend = CreateBackend();
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::ERR_FAILED);
histogram_tester.ExpectUniqueSample("Net.SqlDiskCache.FakeIndexFileError",
FakeIndexFileError::kCreateFileFailed, 1);
}
TEST_F(SqlBackendImplTest, InitWithFailedToCreateDirectory) {
base::HistogramTester histogram_tester;
base::FilePath cache_dir =
temp_dir_.GetPath().Append(FILE_PATH_LITERAL("cache"));
ASSERT_TRUE(base::WriteFile(cache_dir, ""));
auto backend = std::make_unique<SqlBackendImpl>(cache_dir, kDefaultMaxBytes,
net::CacheType::DISK_CACHE);
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::ERR_FAILED);
histogram_tester.ExpectUniqueSample(
"Net.SqlDiskCache.FakeIndexFileError",
FakeIndexFileError::kFailedToCreateDirectory, 1);
}
TEST_F(SqlBackendImplTest, MaxFileSizeSmallMax) {
const int64_t kMaxBytes = 10 * 1024 * 1024;
auto backend = CreateBackendAndInit(kMaxBytes);
EXPECT_EQ(backend->MaxFileSize(), kSqlBackendMinFileSizeLimit);
}
TEST_F(SqlBackendImplTest, MaxFileSizeCalculation) {
const int64_t kLargeMaxBytes = 100 * 1024 * 1024;
auto backend = CreateBackendAndInit(kLargeMaxBytes);
EXPECT_EQ(backend->MaxFileSize(),
kLargeMaxBytes / kSqlBackendMaxFileRatioDenominator);
}
TEST_F(SqlBackendImplTest, GetStats) {
auto backend = CreateBackendAndInit();
base::StringPairs stats;
backend->GetStats(&stats);
EXPECT_THAT(stats, ElementsAre(Pair("Cache type", "SQL Cache")));
}
TEST_F(SqlBackendImplTest, IteratorParallelEntryDoom) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
auto* entry1 = create_result.ReleaseEntry();
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb;
EntryResult result_iter = iter->OpenNextEntry(cb.callback());
entry1->Doom();
result_iter = cb.GetResult(std::move(result_iter));
ASSERT_THAT(result_iter.net_error(), IsOk());
auto* entry2 = result_iter.ReleaseEntry();
EXPECT_EQ(entry1, entry2);
EXPECT_TRUE((static_cast<SqlEntryImpl*>(entry1))->doomed());
entry1->Close();
entry2->Close();
}
TEST_F(SqlBackendImplTest, IteratorParallelEntryDoomAndClose) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
auto* entry = create_result.ReleaseEntry();
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb;
EntryResult result_iter = iter->OpenNextEntry(cb.callback());
entry->Doom();
EXPECT_FALSE((static_cast<SqlEntryImpl*>(entry))->doomed());
entry->Close();
result_iter = cb.GetResult(std::move(result_iter));
ASSERT_THAT(result_iter.net_error(), IsOk());
entry = result_iter.ReleaseEntry();
EXPECT_TRUE((static_cast<SqlEntryImpl*>(entry))->doomed());
entry->Close();
}
TEST_F(SqlBackendImplTest, IteratorParallelEntryDoomOpenNext) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create1;
disk_cache::EntryResult create_result1 = cb_create1.GetResult(
backend->CreateEntry("key1", net::HIGHEST, cb_create1.callback()));
ASSERT_THAT(create_result1.net_error(), IsOk());
create_result1.ReleaseEntry()->Close();
TestEntryResultCompletionCallback cb_create2;
disk_cache::EntryResult create_result2 = cb_create2.GetResult(
backend->CreateEntry("key2", net::HIGHEST, cb_create2.callback()));
ASSERT_THAT(create_result2.net_error(), IsOk());
auto* entry = create_result2.ReleaseEntry();
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb_iter;
entry->Doom();
entry->Close();
EntryResult result =
cb_iter.GetResult(iter->OpenNextEntry(cb_iter.callback()));
ASSERT_THAT(result.net_error(), IsOk());
entry = result.ReleaseEntry();
EXPECT_EQ(entry->GetKey(), "key1");
entry->Close();
EntryResult result2 =
cb_iter.GetResult(iter->OpenNextEntry(cb_iter.callback()));
ASSERT_THAT(result2.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest, IteratorParallelDoom) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
auto* entry1 = create_result.ReleaseEntry();
const std::string kData = "some data";
auto buffer = base::MakeRefCounted<net::StringIOBuffer>(kData);
net::TestCompletionCallback cb_write;
int rv_write = entry1->WriteData(1, 0, buffer.get(), buffer->size(),
cb_write.callback(), false);
EXPECT_EQ(cb_write.GetResult(rv_write), static_cast<int>(buffer->size()));
entry1->Close();
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb;
EntryResult result_iter = iter->OpenNextEntry(cb.callback());
net::TestCompletionCallback cb_doom;
int rv_doom = backend->DoomEntry("key", net::HIGHEST, cb_doom.callback());
EXPECT_EQ(net::OK, cb_doom.GetResult(rv_doom));
result_iter = cb.GetResult(std::move(result_iter));
ASSERT_THAT(result_iter.net_error(), IsOk());
auto* entry = result_iter.ReleaseEntry();
EXPECT_TRUE(static_cast<SqlEntryImpl*>(entry)->doomed());
auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(kData.size());
net::TestCompletionCallback cb_read;
int rv_read = entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
cb_read.callback());
EXPECT_EQ(cb_read.GetResult(rv_read), static_cast<int>(kData.size()));
EXPECT_EQ(std::string_view(read_buffer->data(), kData.size()), kData);
entry->Close();
}
TEST_F(SqlBackendImplTest, IteratorParallelDoomAll) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
auto* entry1 = create_result.ReleaseEntry();
const std::string kData = "some data";
auto buffer = base::MakeRefCounted<net::StringIOBuffer>(kData);
net::TestCompletionCallback cb_write;
int rv_write = entry1->WriteData(1, 0, buffer.get(), buffer->size(),
cb_write.callback(), false);
EXPECT_EQ(cb_write.GetResult(rv_write), static_cast<int>(buffer->size()));
entry1->Close();
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb;
EntryResult result_iter = iter->OpenNextEntry(cb.callback());
net::TestCompletionCallback cb_doom;
int rv_doom = backend->DoomAllEntries(cb_doom.callback());
EXPECT_EQ(net::OK, cb_doom.GetResult(rv_doom));
result_iter = cb.GetResult(std::move(result_iter));
ASSERT_THAT(result_iter.net_error(), IsOk());
auto* entry = result_iter.ReleaseEntry();
EXPECT_TRUE(static_cast<SqlEntryImpl*>(entry)->doomed());
auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(kData.size());
net::TestCompletionCallback cb_read;
int rv_read = entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
cb_read.callback());
EXPECT_EQ(cb_read.GetResult(rv_read), static_cast<int>(kData.size()));
EXPECT_EQ(std::string_view(read_buffer->data(), kData.size()), kData);
entry->Close();
}
TEST_F(SqlBackendImplTest, IteratorParallelWriteDataAndClose) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
auto* entry = create_result.ReleaseEntry();
task_environment_.AdvanceClock(base::Minutes(1));
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb;
EntryResult result_iter = iter->OpenNextEntry(cb.callback());
const base::Time kWriteTime = base::Time::Now();
const std::string kHeadData = "header_data";
auto buffer = base::MakeRefCounted<net::StringIOBuffer>(kHeadData);
net::TestCompletionCallback cb_write;
int rv_write = entry->WriteData(0, 0, buffer.get(), buffer->size(),
cb_write.callback(), false);
entry->Close();
EXPECT_EQ(cb_write.GetResult(rv_write), buffer->size());
result_iter = cb.GetResult(std::move(result_iter));
ASSERT_THAT(result_iter.net_error(), IsOk());
entry = result_iter.ReleaseEntry();
EXPECT_THAT(entry->GetLastUsed(), kWriteTime);
auto read_buffer =
base::MakeRefCounted<net::IOBufferWithSize>(kHeadData.size() * 2);
net::TestCompletionCallback cb_read;
int rv_read = entry->ReadData(0, 0, read_buffer.get(), read_buffer->size(),
cb_read.callback());
EXPECT_EQ(cb_read.GetResult(rv_read), kHeadData.size());
entry->Close();
}
TEST_F(SqlBackendImplTest, IteratorParallelWriteBodyDataAndClose) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
auto* entry = create_result.ReleaseEntry();
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb;
EntryResult result_iter = iter->OpenNextEntry(cb.callback());
const std::string kBodyData = "body_data";
auto buffer = base::MakeRefCounted<net::StringIOBuffer>(kBodyData);
net::TestCompletionCallback cb_write;
int rv_write = entry->WriteData(1, 0, buffer.get(), buffer->size(),
cb_write.callback(), false);
entry->Close();
EXPECT_EQ(cb_write.GetResult(rv_write), static_cast<int>(buffer->size()));
result_iter = cb.GetResult(std::move(result_iter));
ASSERT_THAT(result_iter.net_error(), IsOk());
entry = result_iter.ReleaseEntry();
EXPECT_EQ(entry->GetDataSize(1), static_cast<int32_t>(kBodyData.size()));
auto read_buffer =
base::MakeRefCounted<net::IOBufferWithSize>(kBodyData.size() * 2);
net::TestCompletionCallback cb_read;
int rv_read = entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
cb_read.callback());
EXPECT_EQ(cb_read.GetResult(rv_read), static_cast<int>(kBodyData.size()));
EXPECT_EQ(std::string_view(read_buffer->data(), kBodyData.size()), kBodyData);
entry->Close();
}
TEST_F(SqlBackendImplTest, IteratorParallelReadDataAndClose) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
auto* entry = create_result.ReleaseEntry();
task_environment_.AdvanceClock(base::Minutes(1));
auto iter = backend->CreateIterator();
TestEntryResultCompletionCallback cb;
EntryResult result_iter = iter->OpenNextEntry(cb.callback());
const base::Time kReadTime = base::Time::Now();
auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1);
net::TestCompletionCallback cb_read;
int rv_read = entry->ReadData(0, 0, read_buffer.get(), read_buffer->size(),
cb_read.callback());
EXPECT_EQ(cb_read.GetResult(rv_read), 0);
entry->Close();
result_iter = cb.GetResult(std::move(result_iter));
ASSERT_THAT(result_iter.net_error(), IsOk());
entry = result_iter.ReleaseEntry();
EXPECT_THAT(entry->GetLastUsed(), kReadTime);
entry->Close();
}
TEST_F(SqlBackendImplTest, IteratorAndOpenEntryParallelRace) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
create_result.ReleaseEntry()->Close();
base::test::TestFuture<EntryResult> future_iter;
base::test::TestFuture<EntryResult> future_open;
auto iter = backend->CreateIterator();
ASSERT_EQ(iter->OpenNextEntry(future_iter.GetCallback()).net_error(),
net::ERR_IO_PENDING);
ASSERT_EQ(backend->OpenEntry("key", net::HIGHEST, future_open.GetCallback())
.net_error(),
net::ERR_IO_PENDING);
EntryResult iter_res = future_iter.Take();
EntryResult open_res = future_open.Take();
ASSERT_THAT(iter_res.net_error(), IsOk());
ASSERT_THAT(open_res.net_error(), IsOk());
auto* entry1 = iter_res.ReleaseEntry();
auto* entry2 = open_res.ReleaseEntry();
EXPECT_EQ(entry1, entry2);
entry1->Close();
entry2->Close();
}
TEST_F(SqlBackendImplTest, IteratorAndOpenEntryAndDoomParallelRace) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
create_result.ReleaseEntry()->Close();
base::test::TestFuture<EntryResult> future_iter;
base::test::TestFuture<EntryResult> future_open;
base::test::TestFuture<int> future_doom;
auto iter = backend->CreateIterator();
ASSERT_EQ(iter->OpenNextEntry(future_iter.GetCallback()).net_error(),
net::ERR_IO_PENDING);
ASSERT_EQ(backend->OpenEntry("key", net::HIGHEST, future_open.GetCallback())
.net_error(),
net::ERR_IO_PENDING);
ASSERT_EQ(backend->DoomEntry("key", net::HIGHEST, future_doom.GetCallback()),
net::ERR_IO_PENDING);
EntryResult iter_res = future_iter.Take();
ASSERT_THAT(iter_res.net_error(), IsOk());
auto* entry1 = iter_res.ReleaseEntry();
EntryResult open_res = future_open.Take();
ASSERT_THAT(open_res.net_error(), IsOk());
auto* entry2 = open_res.ReleaseEntry();
EXPECT_EQ(entry1, entry2);
EXPECT_EQ(future_doom.Take(), net::OK);
EXPECT_TRUE(static_cast<SqlEntryImpl*>(entry1)->doomed());
entry1->Close();
entry2->Close();
}
TEST_F(SqlBackendImplTest, OpenEntryRacesWithIteratorAndDoom) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
create_result.ReleaseEntry()->Close();
auto iter = backend->CreateIterator();
ASSERT_EQ(iter->OpenNextEntry(
base::BindLambdaForTesting([&](EntryResult entry_result) {
auto* entry = entry_result.ReleaseEntry();
entry->Doom();
entry->Close();
}))
.net_error(),
net::ERR_IO_PENDING);
base::test::TestFuture<EntryResult> open_future;
ASSERT_EQ(backend->OpenEntry("key", net::HIGHEST, open_future.GetCallback())
.net_error(),
net::ERR_IO_PENDING);
EntryResult open_result = open_future.Take();
ASSERT_THAT(open_result.net_error(), IsOk());
auto* entry = open_result.ReleaseEntry();
EXPECT_TRUE(static_cast<SqlEntryImpl*>(entry)->doomed());
entry->Close();
}
TEST_F(SqlBackendImplTest, OpenOrCreateEntryEntryRacesWithIteratorAndDoom) {
auto backend = CreateBackendAndInit();
base::Time first_entry_creation_time = base::Time::Now();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
EXPECT_EQ(entry->GetLastUsed(), first_entry_creation_time);
entry->Close();
task_environment_.AdvanceClock(base::Minutes(1));
auto iter = backend->CreateIterator();
ASSERT_EQ(iter->OpenNextEntry(
base::BindLambdaForTesting([&](EntryResult entry_result) {
auto* entry = entry_result.ReleaseEntry();
EXPECT_EQ(entry->GetLastUsed(),
first_entry_creation_time);
entry->Doom();
entry->Close();
}))
.net_error(),
net::ERR_IO_PENDING);
base::test::TestFuture<EntryResult> open_or_create_future;
ASSERT_EQ(backend
->OpenOrCreateEntry("key", net::HIGHEST,
open_or_create_future.GetCallback())
.net_error(),
net::ERR_IO_PENDING);
EntryResult open_or_create_result = open_or_create_future.Take();
ASSERT_THAT(open_or_create_result.net_error(), IsOk());
entry = open_or_create_result.ReleaseEntry();
EXPECT_EQ(entry->GetLastUsed(), first_entry_creation_time);
EXPECT_TRUE(static_cast<SqlEntryImpl*>(entry)->doomed());
entry->Close();
}
TEST_F(SqlBackendImplTest, OpenEntryRacesWithIteratorAndWriteData) {
auto backend = CreateBackendAndInit();
base::Time first_entry_creation_time = base::Time::Now();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
EXPECT_EQ(entry->GetLastUsed(), first_entry_creation_time);
entry->Close();
task_environment_.AdvanceClock(base::Minutes(1));
const std::string kHeadData = "header_data";
auto iter = backend->CreateIterator();
ASSERT_EQ(iter->OpenNextEntry(
base::BindLambdaForTesting([&](EntryResult entry_result) {
auto* entry = entry_result.ReleaseEntry();
EXPECT_EQ(entry->GetLastUsed(),
first_entry_creation_time);
auto buffer =
base::MakeRefCounted<net::StringIOBuffer>(kHeadData);
entry->WriteData(0, 0, buffer.get(), buffer->size(),
base::DoNothing(), false);
entry->Close();
}))
.net_error(),
net::ERR_IO_PENDING);
base::test::TestFuture<EntryResult> open_future;
ASSERT_EQ(backend->OpenEntry("key", net::HIGHEST, open_future.GetCallback())
.net_error(),
net::ERR_IO_PENDING);
EntryResult open_result = open_future.Take();
ASSERT_THAT(open_result.net_error(), IsOk());
entry = open_result.ReleaseEntry();
EXPECT_EQ(entry->GetLastUsed(), first_entry_creation_time + base::Minutes(1));
auto buffer =
base::MakeRefCounted<net::IOBufferWithSize>(kHeadData.size() * 2);
ASSERT_EQ(
entry->ReadData(0, 0, buffer.get(), buffer->size(), base::DoNothing()),
kHeadData.size());
EXPECT_EQ(buffer->first(kHeadData.size()), base::as_byte_span(kHeadData));
entry->Close();
}
TEST_F(SqlBackendImplTest, OnExternalCacheHitRacesWithOpen) {
auto backend = CreateBackendAndInit();
const std::string kKey = "my-key";
TestEntryResultCompletionCallback create_cb;
disk_cache::EntryResult create_result = create_cb.GetResult(
backend->CreateEntry(kKey, net::HIGHEST, create_cb.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* created_entry = create_result.ReleaseEntry();
base::Time create_time = created_entry->GetLastUsed();
created_entry->Close();
task_environment_.AdvanceClock(base::Minutes(1));
base::test::TestFuture<EntryResult> open_future;
ASSERT_EQ(backend->OpenEntry(kKey, net::HIGHEST, open_future.GetCallback())
.net_error(),
net::ERR_IO_PENDING);
base::Time hit_time = base::Time::Now();
EXPECT_NE(create_time, hit_time);
backend->OnExternalCacheHit(kKey);
EntryResult open_result = open_future.Take();
ASSERT_THAT(open_result.net_error(), IsOk());
auto* entry = open_result.ReleaseEntry();
EXPECT_EQ(entry->GetLastUsed(), hit_time);
entry->Close();
}
TEST_F(SqlBackendImplTest, DoomEntryNonExistent) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
const std::string kNonExistentKey = "non-existent-key";
net::TestCompletionCallback cb_doom;
int rv_doom =
backend->DoomEntry(kNonExistentKey, net::HIGHEST,
base::BindOnce([](int rv) { NOTREACHED(); }));
EXPECT_EQ(net::OK, rv_doom);
}
TEST_F(SqlBackendImplTest, MultipleDoomsOnSameEntry) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
entry->Doom();
entry->Doom();
FlushQueue(*backend);
EXPECT_TRUE(static_cast<SqlEntryImpl*>(entry)->doomed());
entry->Close();
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry("key", net::HIGHEST, cb_open.callback()));
EXPECT_THAT(open_result.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest, RecursiveOpenNextEntry) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result1 = cb_create.GetResult(
backend->CreateEntry("key1", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result1.net_error(), IsOk());
create_result1.ReleaseEntry()->Close();
disk_cache::EntryResult create_result2 = cb_create.GetResult(
backend->CreateEntry("key2", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result2.net_error(), IsOk());
create_result2.ReleaseEntry()->Close();
auto iter = backend->CreateIterator();
base::RunLoop run_loop;
Entry* entry3 = nullptr;
bool key_1_found = false;
bool key_2_found = false;
ASSERT_THAT(
iter->OpenNextEntry(base::BindLambdaForTesting([&](EntryResult result1) {
ASSERT_THAT(result1.net_error(), IsOk());
auto* entry1_itr = result1.ReleaseEntry();
key_1_found = entry1_itr->GetKey() == "key1";
key_2_found = entry1_itr->GetKey() == "key2";
EXPECT_TRUE(key_1_found || key_2_found);
entry1_itr->Close();
ASSERT_THAT(
iter->OpenNextEntry(
base::BindLambdaForTesting([&](EntryResult result2) {
ASSERT_THAT(result2.net_error(), IsOk());
CHECK(entry3);
auto* entry2_itr = result2.ReleaseEntry();
if (entry2_itr->GetKey() == "key3") {
EXPECT_EQ(entry2_itr, entry3);
} else {
if (key_1_found) {
EXPECT_EQ(entry2_itr->GetKey(), "key2");
} else {
EXPECT_EQ(entry2_itr->GetKey(), "key1");
}
}
entry2_itr->Close();
entry3->Close();
run_loop.Quit();
}))
.net_error(),
IsError(net::ERR_IO_PENDING));
}))
.net_error(),
IsError(net::ERR_IO_PENDING));
ASSERT_THAT(
backend
->CreateEntry("key3", net::HIGHEST,
base::BindLambdaForTesting([&](EntryResult result3) {
ASSERT_THAT(result3.net_error(), IsOk());
entry3 = result3.ReleaseEntry();
}))
.net_error(),
IsError(net::ERR_IO_PENDING));
run_loop.Run();
}
TEST_F(SqlBackendImplTest, RecursiveOpenNextEntryWithActiveEntry) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result1 = cb_create.GetResult(
backend->CreateEntry("key1", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result1.net_error(), IsOk());
create_result1.ReleaseEntry()->Close();
disk_cache::EntryResult create_result2 = cb_create.GetResult(
backend->CreateEntry("key2", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result2.net_error(), IsOk());
auto* entry2_active = create_result2.ReleaseEntry();
auto iter = backend->CreateIterator();
base::RunLoop run_loop;
Entry* entry3 = nullptr;
bool key_1_found = false;
bool key_2_found = false;
ASSERT_THAT(
iter->OpenNextEntry(base::BindLambdaForTesting([&](EntryResult result1) {
ASSERT_THAT(result1.net_error(), IsOk());
auto* entry1_iter = result1.ReleaseEntry();
key_1_found = entry1_iter->GetKey() == "key1";
key_2_found = entry1_iter->GetKey() == "key2";
EXPECT_TRUE(key_1_found || key_2_found);
if (key_2_found) {
EXPECT_EQ(entry1_iter, entry2_active);
}
entry1_iter->Close();
ASSERT_THAT(
iter->OpenNextEntry(
base::BindLambdaForTesting([&](EntryResult result2) {
ASSERT_THAT(result2.net_error(), IsOk());
CHECK(entry3);
auto* entry2_itr = result2.ReleaseEntry();
if (entry2_itr->GetKey() == "key3") {
EXPECT_EQ(entry2_itr, entry3);
} else {
if (key_1_found) {
EXPECT_EQ(entry2_itr->GetKey(), "key2");
} else {
EXPECT_EQ(entry2_itr->GetKey(), "key1");
}
}
entry2_itr->Close();
entry3->Close();
run_loop.Quit();
}))
.net_error(),
IsError(net::ERR_IO_PENDING));
}))
.net_error(),
IsError(net::ERR_IO_PENDING));
ASSERT_THAT(
backend
->CreateEntry("key3", net::HIGHEST,
base::BindLambdaForTesting([&](EntryResult result3) {
ASSERT_THAT(result3.net_error(), IsOk());
entry3 = result3.ReleaseEntry();
}))
.net_error(),
IsError(net::ERR_IO_PENDING));
run_loop.Run();
entry2_active->Close();
}
TEST_F(SqlBackendImplTest, AbortPendingReadData) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback create_cb;
disk_cache::EntryResult create_result = create_cb.GetResult(
backend->CreateEntry("key", net::HIGHEST, create_cb.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
const std::string kBodyData = "body_data";
auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>(kBodyData);
net::TestCompletionCallback write_cb;
int write_rv =
entry->WriteData(1, 0, write_buffer.get(), write_buffer->size(),
write_cb.callback(), false);
ASSERT_EQ(write_cb.GetResult(write_rv),
static_cast<int>(write_buffer->size()));
auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(10);
base::test::TestFuture<int> read_future;
int rv = entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
read_future.GetCallback());
ASSERT_THAT(rv, IsError(net::ERR_IO_PENDING));
backend.reset();
EXPECT_EQ(read_future.Get(), net::ERR_ABORTED);
entry->Close();
}
TEST_F(SqlBackendImplTest, AbortPendingWriteData) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback create_cb;
disk_cache::EntryResult create_result = create_cb.GetResult(
backend->CreateEntry("key", net::HIGHEST, create_cb.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
net::TestCompletionCallback flush_cb;
backend->FlushQueueForTest(flush_cb.callback());
EXPECT_THAT(flush_cb.WaitForResult(), IsOk());
auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>("data");
base::test::TestFuture<int> write_future;
int rv = entry->WriteData(1, 0, write_buffer.get(), write_buffer->size(),
write_future.GetCallback(), false);
ASSERT_THAT(rv, write_buffer->size());
auto task_runners = backend->GetBackgroundTaskRunnersForTest();
backend.reset();
auto res_id_or_error = static_cast<SqlEntryImpl*>(entry)->res_id_or_error();
while (!(res_id_or_error->data.has_value() &&
std::holds_alternative<SqlPersistentStore::Error>(
*res_id_or_error->data))) {
FlushQueueInTaskRunners(task_runners);
}
EXPECT_EQ(std::get<SqlPersistentStore::Error>(*res_id_or_error->data),
SqlPersistentStore::Error::kAborted);
entry->Close();
}
TEST_F(SqlBackendImplTest, AbortPendingGetAvailableRange) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback create_cb;
disk_cache::EntryResult create_result = create_cb.GetResult(
backend->CreateEntry("key", net::HIGHEST, create_cb.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
base::test::TestFuture<const RangeResult&> range_future;
RangeResult result =
entry->GetAvailableRange(0, 100, range_future.GetCallback());
ASSERT_THAT(result.net_error, IsError(net::ERR_IO_PENDING));
backend.reset();
const RangeResult& aborted_result = range_future.Get();
EXPECT_THAT(aborted_result.net_error, IsError(net::ERR_ABORTED));
entry->Close();
}
TEST_F(SqlBackendImplTest, DoomedEntriesCleanup) {
auto backend = CreateBackendAndInit();
auto task_runners = backend->GetBackgroundTaskRunnersForTest();
const std::string kKey1 = "key1";
const std::string kKey2 = "key2";
const std::string kKey3 = "key3";
const std::string kData = "some data";
auto* entry1 = CreateEntryAndWriteData(backend.get(), kKey1, kData);
auto* entry2 = CreateEntryAndWriteData(backend.get(), kKey2, kData);
auto* entry3 = CreateEntryAndWriteData(backend.get(), kKey3, kData);
WaitUntilInitialized(*backend,
static_cast<SqlEntryImpl*>(entry3)->res_id_or_error());
auto res_id = std::get<SqlPersistentStore::ResId>(
static_cast<SqlEntryImpl*>(entry3)->res_id_or_error()->data.value());
entry1->Close();
entry2->Close();
entry3->Close();
backend.reset();
FlushQueueInTaskRunners(task_runners);
{
auto store = std::make_unique<SqlPersistentStore>(
temp_dir_.GetPath(), kDefaultMaxBytes, net::CacheType::DISK_CACHE,
task_runners);
base::test::TestFuture<disk_cache::SqlPersistentStore::Error> future_init;
store->Initialize(future_init.GetCallback());
ASSERT_EQ(future_init.Get(), disk_cache::SqlPersistentStore::Error::kOk);
base::test::TestFuture<SqlPersistentStore::Error> future_doom;
store->DoomEntry(CacheEntryKey(kKey3), res_id, future_doom.GetCallback());
EXPECT_EQ(future_doom.Get(), SqlPersistentStore::Error::kOk);
store.reset();
}
FlushQueueInTaskRunners(task_runners);
backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
TestEntryResultCompletionCallback cb_open1;
disk_cache::EntryResult open_result1 = cb_open1.GetResult(
backend->OpenEntry(kKey1, net::HIGHEST, cb_open1.callback()));
entry1 = open_result1.ReleaseEntry();
entry1->Doom();
TestEntryResultCompletionCallback cb_open2;
disk_cache::EntryResult open_result2 = cb_open2.GetResult(
backend->OpenEntry(kKey2, net::HIGHEST, cb_open2.callback()));
entry2 = open_result2.ReleaseEntry();
entry2->Doom();
base::HistogramTester histogram_tester;
backend->OnBrowserIdle();
net::TestCompletionCallback flush_cb;
backend->FlushQueueForTest(flush_cb.callback());
EXPECT_THAT(flush_cb.WaitForResult(), IsOk());
histogram_tester.ExpectUniqueSample(
"Net.SqlDiskCache.DeleteDoomedEntriesCount", 1, 1);
ReadAndVerifyData(entry1, kData);
ReadAndVerifyData(entry2, kData);
entry1->Close();
entry2->Close();
}
TEST_F(SqlBackendImplTest, SpeculativeCreateEntry) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
const std::string kKey = "my-key";
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result =
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback());
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto* sql_entry = static_cast<SqlEntryImpl*>(entry);
EXPECT_FALSE(sql_entry->res_id_or_error()->data.has_value());
WaitUntilInitialized(*backend, sql_entry->res_id_or_error());
ASSERT_TRUE(sql_entry->res_id_or_error()->data.has_value());
EXPECT_TRUE(std::holds_alternative<SqlPersistentStore::ResId>(
sql_entry->res_id_or_error()->data.value()));
entry->Doom();
EXPECT_TRUE(sql_entry->doomed());
entry->Close();
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, cb_open.callback()));
EXPECT_THAT(open_result.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest, SpeculativeCreateEntrySyncClose) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
const std::string kKey = "my-key";
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result =
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback());
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
ASSERT_TRUE(entry);
entry->Close();
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, cb_open.callback()));
ASSERT_THAT(open_result.net_error(), IsOk());
entry = open_result.ReleaseEntry();
ASSERT_TRUE(entry);
entry->Close();
}
TEST_F(SqlBackendImplTest, SpeculativeCreateEntrySyncDoom) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
const std::string kKey = "my-key";
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result =
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback());
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
ASSERT_TRUE(entry);
entry->Doom();
entry->Close();
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, cb_open.callback()));
EXPECT_THAT(open_result.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest, SpeculativeCreateEntrySyncWrite) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
const std::string kKey = "my-key";
const std::string kData = "some data";
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result =
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback());
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
ASSERT_TRUE(entry);
net::TestCompletionCallback write_cb;
auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>(kData);
EXPECT_EQ(write_cb.GetResult(entry->WriteData(1, 0, write_buffer.get(),
write_buffer->size(),
write_cb.callback(), false)),
static_cast<int>(write_buffer->size()));
entry->Close();
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, cb_open.callback()));
ASSERT_THAT(open_result.net_error(), IsOk());
entry = open_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(10);
base::test::TestFuture<int> read_future;
int rv = entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
read_future.GetCallback());
ASSERT_THAT(rv, IsError(net::ERR_IO_PENDING));
ASSERT_EQ(read_future.Get(), write_buffer->size());
EXPECT_EQ(std::string_view(read_buffer->data(), kData.size()), kData);
entry->Close();
}
TEST_F(SqlBackendImplTest, SpeculativeCreateEntryWithDbFailure) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
const std::string kKey = "my-key";
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result =
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback());
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto* sql_entry = static_cast<SqlEntryImpl*>(entry);
EXPECT_FALSE(sql_entry->res_id_or_error()->data.has_value());
WaitUntilInitialized(*backend, sql_entry->res_id_or_error());
ASSERT_TRUE(sql_entry->res_id_or_error()->data.has_value());
ASSERT_TRUE(std::holds_alternative<SqlPersistentStore::Error>(
sql_entry->res_id_or_error()->data.value()));
EXPECT_EQ(std::get<SqlPersistentStore::Error>(
sql_entry->res_id_or_error()->data.value()),
SqlPersistentStore::Error::kFailedForTesting);
entry->Doom();
EXPECT_TRUE(sql_entry->doomed());
entry->Close();
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, cb_open.callback()));
EXPECT_THAT(open_result.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest,
SpeculativeCreateEntryDbFailureOperationsBeforeErrorSet) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
TestEntryResultCompletionCallback cb;
disk_cache::EntryResult entry_result =
backend->CreateEntry("key", net::HIGHEST, cb.callback());
ASSERT_THAT(entry_result.net_error(), IsOk());
auto* entry = entry_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>("data");
EXPECT_EQ(
entry->WriteData(1, 0, write_buffer.get(), write_buffer->size(),
base::BindOnce([](int rv) { NOTREACHED(); }), false),
write_buffer->size());
net::TestCompletionCallback read_cb;
auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(10);
EXPECT_EQ(entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
read_cb.callback()),
net::ERR_IO_PENDING);
base::test::TestFuture<const RangeResult&> range_future;
EXPECT_EQ(
entry->GetAvailableRange(0, 10, range_future.GetCallback()).net_error,
net::ERR_IO_PENDING);
EXPECT_THAT(read_cb.WaitForResult(), IsError(net::ERR_FAILED));
EXPECT_THAT(range_future.Get().net_error, IsError(net::ERR_FAILED));
entry->Close();
}
TEST_F(SqlBackendImplTest,
SpeculativeCreateEntryDbFailureOperationsAfterErrorSet) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
TestEntryResultCompletionCallback cb;
disk_cache::EntryResult entry_result =
backend->CreateEntry("key", net::HIGHEST, cb.callback());
ASSERT_THAT(entry_result.net_error(), IsOk());
auto* entry = entry_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto* sql_entry = static_cast<SqlEntryImpl*>(entry);
WaitUntilInitialized(*backend, sql_entry->res_id_or_error());
auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>("data");
EXPECT_EQ(entry->WriteData(0, 0, write_buffer.get(), write_buffer->size(),
base::DoNothing(), false),
static_cast<int>(write_buffer->size()));
net::TestCompletionCallback write_cb;
EXPECT_EQ(
entry->WriteData(1, 0, write_buffer.get(), write_buffer->size(),
base::BindOnce([](int rv) { NOTREACHED(); }), false),
net::ERR_FAILED);
auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(10);
EXPECT_EQ(entry->ReadData(1, 0, read_buffer.get(), read_buffer->size(),
base::BindOnce([](int) { NOTREACHED(); })),
net::ERR_FAILED);
EXPECT_EQ(
entry
->GetAvailableRange(
0, 10, base::BindOnce([](const RangeResult&) { NOTREACHED(); }))
.net_error,
net::ERR_FAILED);
EXPECT_EQ(backend->DoomEntry("key", net::HIGHEST,
base::BindOnce([](int) { NOTREACHED(); })),
net::ERR_FAILED);
entry->Close();
EXPECT_EQ(backend->GetSizeOfInFlightEntryModificationsMapForTesting(), 0u);
}
TEST_F(SqlBackendImplTest, SpeculativeCreateEntryDbFailureDoom) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
TestEntryResultCompletionCallback cb;
disk_cache::EntryResult entry_result =
backend->CreateEntry("key", net::HIGHEST, cb.callback());
ASSERT_THAT(entry_result.net_error(), IsOk());
auto* entry = entry_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto* sql_entry = static_cast<SqlEntryImpl*>(entry);
WaitUntilInitialized(*backend, sql_entry->res_id_or_error());
entry->Doom();
EXPECT_TRUE(static_cast<SqlEntryImpl*>(entry)->doomed());
entry->Close();
entry = nullptr;
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry("key", net::HIGHEST, cb_open.callback()));
EXPECT_THAT(open_result.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest, OptimisticWriteBufferSize) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
net::features::kDiskCacheBackendExperiment,
{{net::features::kSqlDiskCacheOptimisticWriteBufferSize.name, "100"}});
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
TestEntryResultCompletionCallback cb;
disk_cache::EntryResult entry_result =
backend->CreateEntry("key", net::HIGHEST, cb.callback());
ASSERT_THAT(entry_result.net_error(), IsOk());
auto* entry = entry_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto write_buffer1 = base::MakeRefCounted<net::StringIOBuffer>("data1");
EXPECT_EQ(entry->WriteData(1, 0, write_buffer1.get(), write_buffer1->size(),
base::DoNothing(), false),
static_cast<int>(write_buffer1->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size());
auto write_buffer2 =
base::MakeRefCounted<net::StringIOBuffer>(std::string(100, 'a'));
net::TestCompletionCallback write_cb;
EXPECT_EQ(entry->WriteData(1, write_buffer1->size(), write_buffer2.get(),
write_buffer2->size(), write_cb.callback(), false),
net::ERR_IO_PENDING);
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size());
EXPECT_EQ(write_cb.WaitForResult(), static_cast<int>(write_buffer2->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(), 0);
entry->Close();
}
TEST_F(SqlBackendImplTest, OptimisticWriteBufferLifecycle) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
net::features::kDiskCacheBackendExperiment,
{{net::features::kSqlDiskCacheOptimisticWriteBufferSize.name, "100"}});
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
TestEntryResultCompletionCallback cb;
disk_cache::EntryResult entry_result =
backend->CreateEntry("key", net::HIGHEST, cb.callback());
ASSERT_THAT(entry_result.net_error(), IsOk());
auto* entry = entry_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto write_buffer1 =
base::MakeRefCounted<net::StringIOBuffer>(std::string(50, 'a'));
EXPECT_EQ(entry->WriteData(1, 0, write_buffer1.get(), write_buffer1->size(),
base::DoNothing(), false),
static_cast<int>(write_buffer1->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size());
auto write_buffer2 =
base::MakeRefCounted<net::StringIOBuffer>(std::string(50, 'b'));
EXPECT_EQ(entry->WriteData(1, 50, write_buffer2.get(), write_buffer2->size(),
base::DoNothing(), false),
static_cast<int>(write_buffer2->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size() + write_buffer2->size());
auto write_buffer3 = base::MakeRefCounted<net::StringIOBuffer>("c");
net::TestCompletionCallback write_cb3;
EXPECT_EQ(entry->WriteData(1, 100, write_buffer3.get(), write_buffer3->size(),
write_cb3.callback(), false),
net::ERR_IO_PENDING);
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size() + write_buffer2->size());
net::TestCompletionCallback flush_cb1;
backend->FlushQueueForTest(flush_cb1.callback());
EXPECT_THAT(flush_cb1.WaitForResult(), IsOk());
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(), 0);
EXPECT_EQ(write_cb3.WaitForResult(), static_cast<int>(write_buffer3->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(), 0);
auto write_buffer4 =
base::MakeRefCounted<net::StringIOBuffer>(std::string(50, 'd'));
EXPECT_EQ(entry->WriteData(1, 101, write_buffer4.get(), write_buffer4->size(),
base::DoNothing(), false),
static_cast<int>(write_buffer4->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer4->size());
entry->Close();
net::TestCompletionCallback flush_cb2;
backend->FlushQueueForTest(flush_cb2.callback());
EXPECT_THAT(flush_cb2.WaitForResult(), IsOk());
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(), 0);
}
TEST_F(SqlBackendImplTest, OptimisticWriteFailure) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
net::features::kDiskCacheBackendExperiment,
{{net::features::kSqlDiskCacheOptimisticWriteBufferSize.name, "100"}});
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
const std::string kKey = "my-key";
const std::string kInitialData = "initial data";
auto* entry = CreateEntryAndWriteData(backend.get(), kKey, kInitialData);
entry->Close();
TestEntryResultCompletionCallback open_cb;
disk_cache::EntryResult open_result = open_cb.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, open_cb.callback()));
ASSERT_THAT(open_result.net_error(), IsOk());
entry = open_result.ReleaseEntry();
auto* sql_entry = static_cast<SqlEntryImpl*>(entry);
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>("new data");
EXPECT_EQ(entry->WriteData(1, kInitialData.size(), write_buffer.get(),
write_buffer->size(), base::DoNothing(), false),
static_cast<int>(write_buffer->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer->size());
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(false);
net::TestCompletionCallback flush_cb1;
backend->FlushQueueForTest(flush_cb1.callback());
EXPECT_THAT(flush_cb1.WaitForResult(), IsOk());
ASSERT_TRUE(sql_entry->res_id_or_error()->data.has_value());
ASSERT_TRUE(std::holds_alternative<SqlPersistentStore::Error>(
sql_entry->res_id_or_error()->data.value()));
EXPECT_EQ(std::get<SqlPersistentStore::Error>(
sql_entry->res_id_or_error()->data.value()),
SqlPersistentStore::Error::kFailedForTesting);
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(), 0);
EXPECT_EQ(entry->WriteData(1, 0, write_buffer.get(), write_buffer->size(),
base::DoNothing(), false),
net::ERR_FAILED);
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(), 0);
entry->Close();
TestEntryResultCompletionCallback open_cb2;
open_result = open_cb2.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, open_cb2.callback()));
ASSERT_THAT(open_result.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest, OptimisticWriteAfterSpeculativeCreateEntry) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
const std::string kKey = "my-key";
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result =
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback());
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
ASSERT_TRUE(entry);
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(false);
auto write_buffer1 =
base::MakeRefCounted<net::StringIOBuffer>(std::string(50, 'a'));
EXPECT_EQ(entry->WriteData(1, 0, write_buffer1.get(), write_buffer1->size(),
base::DoNothing(), false),
static_cast<int>(write_buffer1->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size());
net::TestCompletionCallback flush_cb;
backend->FlushQueueForTest(flush_cb.callback());
EXPECT_THAT(flush_cb.WaitForResult(), IsOk());
auto* sql_entry = static_cast<SqlEntryImpl*>(entry);
ASSERT_TRUE(sql_entry->res_id_or_error()->data.has_value());
ASSERT_TRUE(std::holds_alternative<SqlPersistentStore::Error>(
sql_entry->res_id_or_error()->data.value()));
EXPECT_EQ(std::get<SqlPersistentStore::Error>(
sql_entry->res_id_or_error()->data.value()),
SqlPersistentStore::Error::kFailedForTesting);
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(), 0);
entry->Close();
}
TEST_F(SqlBackendImplTest,
SpeculativeCreateEntryDbFailureAndNonOptimisticWrite) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
net::features::kDiskCacheBackendExperiment,
{{net::features::kSqlDiskCacheOptimisticWriteBufferSize.name, "100"}});
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
disk_cache::EntryResult entry_result1 = backend->CreateEntry(
"key1", net::HIGHEST, base::BindOnce([](EntryResult) { NOTREACHED(); }));
ASSERT_THAT(entry_result1.net_error(), IsOk());
auto* entry1 = entry_result1.ReleaseEntry();
ASSERT_TRUE(entry1);
net::TestCompletionCallback flush_cb;
backend->FlushQueueForTest(flush_cb.callback());
EXPECT_THAT(flush_cb.WaitForResult(), IsOk());
auto* sql_entry1 = static_cast<SqlEntryImpl*>(entry1);
ASSERT_TRUE(sql_entry1->res_id_or_error()->data.has_value());
ASSERT_TRUE(std::holds_alternative<SqlPersistentStore::ResId>(
sql_entry1->res_id_or_error()->data.value()));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
disk_cache::EntryResult entry_result2 = backend->CreateEntry(
"key2", net::HIGHEST, base::BindOnce([](EntryResult) { NOTREACHED(); }));
ASSERT_THAT(entry_result2.net_error(), IsOk());
auto* entry2 = entry_result2.ReleaseEntry();
ASSERT_TRUE(entry2);
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(false);
auto write_buffer1 =
base::MakeRefCounted<net::StringIOBuffer>(std::string(100, 'a'));
EXPECT_EQ(entry1->WriteData(1, 0, write_buffer1.get(), write_buffer1->size(),
base::BindOnce([](int) { NOTREACHED(); }), false),
static_cast<int>(write_buffer1->size()));
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size());
net::TestCompletionCallback write_cb;
auto write_buffer2 =
base::MakeRefCounted<net::StringIOBuffer>(std::string(50, 'b'));
EXPECT_EQ(entry2->WriteData(1, 0, write_buffer2.get(), write_buffer2->size(),
write_cb.callback(), false),
net::ERR_IO_PENDING);
EXPECT_EQ(backend->GetOptimisticWriteBufferTotalSizeForTesting(),
write_buffer1->size());
EXPECT_EQ(write_cb.GetResult(net::ERR_IO_PENDING), net::ERR_FAILED);
entry1->Close();
entry2->Close();
}
TEST_F(SqlBackendImplTest, IdleTimeEviction) {
const int64_t kMaxBytes = 10000;
const int64_t kIdleTimeHighWatermark =
kMaxBytes * kSqlBackendIdleTimeEvictionHighWaterMarkPermille /
1000;
auto buffer =
base::MakeRefCounted<net::StringIOBuffer>(std::string(1000, 'x'));
auto backend = CreateBackendAndInit(kMaxBytes);
int i = 0;
while (GetSizeOfAllEntries(*backend) <= kIdleTimeHighWatermark) {
TestEntryResultCompletionCallback cb;
EntryResult result = cb.GetResult(backend->CreateEntry(
base::StringPrintf("key%d", i++), net::HIGHEST, cb.callback()));
ASSERT_THAT(result.net_error(), IsOk());
auto* entry = result.ReleaseEntry();
net::TestCompletionCallback write_cb;
EXPECT_EQ(
write_cb.GetResult(entry->WriteData(1, 0, buffer.get(), buffer->size(),
write_cb.callback(), false)),
buffer->size());
entry->Close();
FlushQueue(*backend);
}
auto test_helper = PerformanceScenarioTestHelper::Create();
test_helper->SetLoadingScenario(ScenarioScope::kGlobal,
LoadingScenario::kNoPageLoading);
test_helper->SetInputScenario(ScenarioScope::kGlobal,
InputScenario::kNoInput);
backend->OnBrowserIdle();
FlushQueue(*backend);
FlushQueue(*backend);
const int64_t kLowWatermark =
kMaxBytes * kSqlBackendEvictionLowWaterMarkPermille / 1000;
EXPECT_LE(GetSizeOfAllEntries(*backend), kLowWatermark);
}
void SqlBackendImplTest::RunDelayedPostInitializationTasksTest() {
auto backend = CreateBackendAndInit();
auto* sql_store = backend->GetSqlStoreForTest();
auto task_runners = backend->GetBackgroundTaskRunnersForTest();
const auto kKey1 = CacheEntryKey("key1");
const auto kKey2 = CacheEntryKey("key2");
const std::string kData = "some data";
const auto shard_id1 = sql_store->GetShardIdForHash(kKey1.hash());
const auto shard_id2 = sql_store->GetShardIdForHash(kKey2.hash());
auto* entry1 = CreateEntryAndWriteData(backend.get(), kKey1.string(), kData);
auto* entry2 = CreateEntryAndWriteData(backend.get(), kKey2.string(), kData);
WaitUntilInitialized(*backend,
static_cast<SqlEntryImpl*>(entry1)->res_id_or_error());
WaitUntilInitialized(*backend,
static_cast<SqlEntryImpl*>(entry2)->res_id_or_error());
auto res_id1 = std::get<SqlPersistentStore::ResId>(
static_cast<SqlEntryImpl*>(entry1)->res_id_or_error()->data.value());
auto res_id2 = std::get<SqlPersistentStore::ResId>(
static_cast<SqlEntryImpl*>(entry2)->res_id_or_error()->data.value());
entry1->Close();
entry2->Close();
backend.reset();
FlushQueueInTaskRunners(task_runners);
{
auto store = std::make_unique<SqlPersistentStore>(
temp_dir_.GetPath(), kDefaultMaxBytes, net::CacheType::DISK_CACHE,
task_runners);
base::test::TestFuture<disk_cache::SqlPersistentStore::Error> future_init;
store->Initialize(future_init.GetCallback());
ASSERT_EQ(future_init.Get(), disk_cache::SqlPersistentStore::Error::kOk);
base::test::TestFuture<SqlPersistentStore::Error> future_doom;
store->DoomEntry(kKey1, res_id1, future_doom.GetCallback());
EXPECT_EQ(future_doom.Get(), SqlPersistentStore::Error::kOk);
store.reset();
FlushQueueInTaskRunners(task_runners);
}
EXPECT_EQ(OpenDatabaseAndGetBlobsCount(shard_id1, res_id1), 1);
EXPECT_EQ(OpenDatabaseAndGetBlobsCount(shard_id2, res_id2), 1);
backend = CreateBackend();
sql_store = backend->GetSqlStoreForTest();
base::test::TestFuture<int> future;
backend->Init(future.GetCallback());
ASSERT_EQ(future.Get(), net::OK);
if (net::features::kSqlDiskCacheLoadIndexOnInit.Get()) {
EXPECT_EQ(sql_store->GetIndexStateForHash(kKey1.hash()),
SqlPersistentStore::IndexState::kHashNotFound);
EXPECT_EQ(sql_store->GetIndexStateForHash(kKey2.hash()),
SqlPersistentStore::IndexState::kHashFound);
} else {
EXPECT_EQ(sql_store->GetIndexStateForHash(kKey1.hash()),
SqlPersistentStore::IndexState::kNotReady);
EXPECT_EQ(sql_store->GetIndexStateForHash(kKey2.hash()),
SqlPersistentStore::IndexState::kNotReady);
}
task_environment_.FastForwardBy(kSqlBackendPostInitializationTasksDelay);
FlushQueue(*backend);
EXPECT_EQ(sql_store->GetIndexStateForHash(kKey1.hash()),
SqlPersistentStore::IndexState::kHashNotFound);
EXPECT_EQ(sql_store->GetIndexStateForHash(kKey2.hash()),
SqlPersistentStore::IndexState::kHashFound);
task_runners = backend->GetBackgroundTaskRunnersForTest();
backend.reset();
FlushQueueInTaskRunners(task_runners);
EXPECT_EQ(OpenDatabaseAndGetBlobsCount(shard_id1, res_id1), 0);
EXPECT_EQ(OpenDatabaseAndGetBlobsCount(shard_id2, res_id2), 1);
}
TEST_F(SqlBackendImplTest, DelayedPostInitializationTasks) {
RunDelayedPostInitializationTasksTest();
}
TEST_F(SqlBackendImplTest,
DelayedPostInitializationTasksWithLoadIndexOnInitFeature) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeaturesAndParameters(
{{net::features::kDiskCacheBackendExperiment,
{{net::features::kDiskCacheBackendParam.name, "sql"},
{net::features::kSqlDiskCacheLoadIndexOnInit.name, "true"}}}},
{});
RunDelayedPostInitializationTasksTest();
}
TEST_F(SqlBackendImplTest, DestructionWithPendingOperationOnEntry) {
auto backend = CreateBackendAndInit();
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result = cb_create.GetResult(
backend->CreateEntry("key", net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
backend->CalculateSizeOfAllEntries(base::DoNothing());
entry->Doom();
entry->Close();
entry = nullptr;
auto task_runners = backend->GetBackgroundTaskRunnersForTest();
backend.reset();
FlushQueueInTaskRunners(task_runners);
}
TEST_F(SqlBackendImplTest, DoomEntryWithInMemoryIndex) {
auto backend = CreateBackendAndInit();
const std::string kKey = "my-key";
const CacheEntryKey kEntryKey(kKey);
TestEntryResultCompletionCallback create_cb;
disk_cache::EntryResult create_result = create_cb.GetResult(
backend->CreateEntry(kKey, net::HIGHEST, create_cb.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
create_result.ReleaseEntry()->Close();
ASSERT_TRUE(LoadInMemoryIndex(*backend));
EXPECT_EQ(
backend->GetSqlStoreForTest()->GetIndexStateForHash(kEntryKey.hash()),
SqlPersistentStore::IndexState::kHashFound);
net::TestCompletionCallback cb_doom;
int rv_doom = backend->DoomEntry(kKey, net::HIGHEST, cb_doom.callback());
EXPECT_EQ(
backend->GetSqlStoreForTest()->GetIndexStateForHash(kEntryKey.hash()),
SqlPersistentStore::IndexState::kHashNotFound);
EXPECT_THAT(cb_doom.GetResult(rv_doom), IsOk());
TestEntryResultCompletionCallback cb_open;
disk_cache::EntryResult open_result = cb_open.GetResult(
backend->OpenEntry(kKey, net::HIGHEST, cb_open.callback()));
EXPECT_THAT(open_result.net_error(), IsError(net::ERR_FAILED));
}
TEST_F(SqlBackendImplTest, SetDataHintsAndDoomAndWriteOptimistically) {
auto backend = CreateBackendAndInit();
const std::string kKey = "my-key";
const uint8_t kUnusableHint = 1;
TestEntryResultCompletionCallback cb_create;
EntryResult create_result = cb_create.GetResult(
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback()));
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
entry->SetEntryInMemoryData(kUnusableHint);
entry->Close();
backend->OnBrowserIdle();
FlushQueue(*backend);
EXPECT_EQ(backend->GetEntryInMemoryData(kKey), kUnusableHint);
base::test::TestFuture<int> doom_future;
int doom_rv =
backend->DoomEntry(kKey, net::HIGHEST, doom_future.GetCallback());
EXPECT_EQ(doom_rv, net::ERR_IO_PENDING);
TestEntryResultCompletionCallback cb_open_or_create;
EntryResult open_or_create_result = backend->OpenOrCreateEntry(
kKey, net::HIGHEST, cb_open_or_create.callback());
ASSERT_THAT(open_or_create_result.net_error(), IsOk());
EXPECT_FALSE(open_or_create_result.opened());
open_or_create_result.ReleaseEntry()->Close();
EXPECT_EQ(doom_future.Get(), net::OK);
}
TEST_F(SqlBackendImplTest, SetEntryDataHintsWithSpeculativeCreateEntryFailure) {
auto backend = CreateBackendAndInit();
EXPECT_TRUE(LoadInMemoryIndex(*backend));
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(true);
const std::string kKey = "my-key";
TestEntryResultCompletionCallback cb_create;
disk_cache::EntryResult create_result =
backend->CreateEntry(kKey, net::HIGHEST, cb_create.callback());
ASSERT_THAT(create_result.net_error(), IsOk());
auto* entry = create_result.ReleaseEntry();
ASSERT_TRUE(entry);
auto* sql_entry = static_cast<SqlEntryImpl*>(entry);
WaitUntilInitialized(*backend, sql_entry->res_id_or_error());
backend->GetSqlStoreForTest()->SetSimulateDbFailureForTesting(false);
const uint8_t kUnusableHint = 1;
entry->SetEntryInMemoryData(kUnusableHint);
entry->Close();
FlushQueue(*backend);
EXPECT_EQ(backend->GetEntryInMemoryData(kKey), 0);
}
}
}