summaryrefslogtreecommitdiffstats
path: root/net/disk_cache/backend_unittest.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 22:42:52 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 22:42:52 +0000
commit586acc5fe142f498261f52c66862fa417c3d52d2 (patch)
treec98b3417a883f2477029c8cd5888f4078681e24e /net/disk_cache/backend_unittest.cc
parenta814a8d55429605fe6d7045045cd25b6bf624580 (diff)
downloadchromium_src-586acc5fe142f498261f52c66862fa417c3d52d2.zip
chromium_src-586acc5fe142f498261f52c66862fa417c3d52d2.tar.gz
chromium_src-586acc5fe142f498261f52c66862fa417c3d52d2.tar.bz2
Add net to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/disk_cache/backend_unittest.cc')
-rw-r--r--net/disk_cache/backend_unittest.cc944
1 files changed, 944 insertions, 0 deletions
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
new file mode 100644
index 0000000..0514758
--- /dev/null
+++ b/net/disk_cache/backend_unittest.cc
@@ -0,0 +1,944 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/disk_cache_test_base.h"
+#include "net/disk_cache/disk_cache_test_util.h"
+#include "net/disk_cache/mapped_file.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Copies a set of cache files from the data folder to the test folder.
+bool CopyTestCache(const std::wstring& name) {
+ std::wstring path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ file_util::AppendToPath(&path, L"net");
+ file_util::AppendToPath(&path, L"data");
+ file_util::AppendToPath(&path, L"cache_tests");
+ file_util::AppendToPath(&path, name);
+
+ std::wstring dest = GetCachePath();
+ if (!DeleteCache(dest.c_str()))
+ return false;
+ return file_util::CopyDirectory(path, dest, false);
+}
+
+// Verifies that we can recover a transaction (insert or remove on the rankings
+// list) that is interrupted.
+int TestTransaction(const std::wstring& name, int num_entries, bool load) {
+ if (!CopyTestCache(name))
+ return 1;
+ std::wstring path = GetCachePath();
+ scoped_ptr<disk_cache::Backend> cache;
+
+ if (!load) {
+ cache.reset(disk_cache::CreateCacheBackend(path, false, 0));
+ } else {
+ disk_cache::BackendImpl* cache2 = new disk_cache::BackendImpl(path, 0xf);
+ if (!cache2 || !cache2->SetMaxSize(0x100000) || !cache2->Init())
+ return 2;
+ cache.reset(cache2);
+ }
+ if (!cache.get())
+ return 2;
+
+ if (num_entries + 1 != cache->GetEntryCount())
+ return 3;
+
+ std::string key("the first key");
+ disk_cache::Entry* entry1;
+ if (cache->OpenEntry(key, &entry1))
+ return 4;
+
+ int actual = cache->GetEntryCount();
+ if (num_entries != actual) {
+ if (!load)
+ return 5;
+ // If there is a heavy load, inserting an entry will make another entry
+ // dirty (on the hash bucket) so two entries are removed.
+ if (actual != num_entries - 1)
+ return 5;
+ }
+
+ cache.reset();
+
+ if (!CheckCacheIntegrity(path))
+ return 6;
+
+ return 0;
+}
+
+} // namespace
+
+// Tests that can run with different types of caches.
+class DiskCacheBackendTest : public DiskCacheTestBase {
+ protected:
+ void BackendBasics();
+ void BackendSetSize();
+ void BackendLoad();
+ void BackendKeying();
+ void BackendEnumerations();
+ void BackendDoomRecent();
+ void BackendDoomBetween();
+ void BackendDoomAll();
+};
+
+void DiskCacheBackendTest::BackendBasics() {
+ disk_cache::Entry *entry1 = NULL, *entry2 = NULL;
+ EXPECT_FALSE(cache_->OpenEntry("the first key", &entry1));
+ ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
+ ASSERT_TRUE(NULL != entry1);
+ entry1->Close();
+ entry1 = NULL;
+
+ ASSERT_TRUE(cache_->OpenEntry("the first key", &entry1));
+ ASSERT_TRUE(NULL != entry1);
+ entry1->Close();
+ entry1 = NULL;
+
+ EXPECT_FALSE(cache_->CreateEntry("the first key", &entry1));
+ ASSERT_TRUE(cache_->OpenEntry("the first key", &entry1));
+ EXPECT_FALSE(cache_->OpenEntry("some other key", &entry2));
+ ASSERT_TRUE(cache_->CreateEntry("some other key", &entry2));
+ ASSERT_TRUE(NULL != entry1);
+ ASSERT_TRUE(NULL != entry2);
+ EXPECT_EQ(2, cache_->GetEntryCount());
+
+ disk_cache::Entry* entry3 = NULL;
+ ASSERT_TRUE(cache_->OpenEntry("some other key", &entry3));
+ ASSERT_TRUE(NULL != entry3);
+ EXPECT_TRUE(entry2 == entry3);
+ EXPECT_EQ(2, cache_->GetEntryCount());
+
+ EXPECT_TRUE(cache_->DoomEntry("some other key"));
+ EXPECT_EQ(1, cache_->GetEntryCount());
+ entry1->Close();
+ entry2->Close();
+ entry3->Close();
+
+ EXPECT_TRUE(cache_->DoomEntry("the first key"));
+ EXPECT_EQ(0, cache_->GetEntryCount());
+
+ ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
+ ASSERT_TRUE(cache_->CreateEntry("some other key", &entry2));
+ entry1->Doom();
+ entry1->Close();
+ EXPECT_TRUE(cache_->DoomEntry("some other key"));
+ EXPECT_EQ(0, cache_->GetEntryCount());
+ entry2->Close();
+}
+
+TEST_F(DiskCacheBackendTest, Basics) {
+ InitCache();
+ BackendBasics();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyBasics) {
+ SetMemoryOnlyMode();
+ InitCache();
+ BackendBasics();
+}
+
+void DiskCacheBackendTest::BackendKeying() {
+ const char* kName1 = "the first key";
+ const char* kName2 = "the first Key";
+ disk_cache::Entry *entry1, *entry2;
+ ASSERT_TRUE(cache_->CreateEntry(kName1, &entry1));
+
+ ASSERT_TRUE(cache_->CreateEntry(kName2, &entry2));
+ EXPECT_TRUE(entry1 != entry2) << "Case sensitive";
+ entry2->Close();
+
+ char buffer[30];
+ EXPECT_EQ(0, strcpy_s(buffer, kName1));
+ ASSERT_TRUE(cache_->OpenEntry(buffer, &entry2));
+ EXPECT_TRUE(entry1 == entry2);
+ entry2->Close();
+
+ EXPECT_EQ(0, strcpy_s(buffer + 1, sizeof(buffer) - 1 , kName1));
+ ASSERT_TRUE(cache_->OpenEntry(buffer + 1, &entry2));
+ EXPECT_TRUE(entry1 == entry2);
+ entry2->Close();
+
+ EXPECT_EQ(0, strcpy_s(buffer + 3, sizeof(buffer) - 3, kName1));
+ ASSERT_TRUE(cache_->OpenEntry(buffer + 3, &entry2));
+ EXPECT_TRUE(entry1 == entry2);
+ entry2->Close();
+
+ // Now verify long keys.
+ char buffer2[20000];
+ memset(buffer2, 's', sizeof(buffer2));
+ buffer2[1023] = '\0';
+ ASSERT_TRUE(cache_->CreateEntry(buffer2, &entry2)) << "key on block file";
+ entry2->Close();
+
+ buffer2[1023] = 'g';
+ buffer2[19999] = '\0';
+ ASSERT_TRUE(cache_->CreateEntry(buffer2, &entry2)) << "key on external file";
+ entry2->Close();
+ entry1->Close();
+}
+
+TEST_F(DiskCacheBackendTest, Keying) {
+ InitCache();
+ BackendKeying();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyKeying) {
+ SetMemoryOnlyMode();
+ InitCache();
+ BackendKeying();
+}
+
+void DiskCacheBackendTest::BackendSetSize() {
+ SetDirectMode();
+ const int cache_size = 0x10000; // 64 kB
+ SetMaxSize(cache_size);
+ InitCache();
+
+ std::string first("some key");
+ std::string second("something else");
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache_->CreateEntry(first, &entry));
+
+ char buffer[cache_size] = {0};
+ EXPECT_EQ(cache_size / 10, entry->WriteData(0, 0, buffer, cache_size / 10,
+ NULL, false)) << "normal file";
+
+ EXPECT_EQ(net::ERR_FAILED, entry->WriteData(1, 0, buffer, cache_size / 5,
+ NULL, false)) << "file size above the limit";
+
+ // By doubling the total size, we make this file cacheable.
+ SetMaxSize(cache_size * 2);
+ EXPECT_EQ(cache_size / 5, entry->WriteData(1, 0, buffer, cache_size / 5,
+ NULL, false));
+
+ // Let's fill up the cache!.
+ SetMaxSize(cache_size * 10);
+ EXPECT_EQ(cache_size * 3 / 4, entry->WriteData(0, 0, buffer,
+ cache_size * 3 / 4, NULL, false));
+ entry->Close();
+
+ SetMaxSize(cache_size);
+
+ // Verify that the cache is 95% full.
+ ASSERT_TRUE(cache_->OpenEntry(first, &entry));
+ EXPECT_EQ(cache_size * 3 / 4, entry->GetDataSize(0));
+ EXPECT_EQ(cache_size / 5, entry->GetDataSize(1));
+ entry->Close();
+
+ ASSERT_TRUE(cache_->CreateEntry(second, &entry));
+ EXPECT_EQ(cache_size / 10, entry->WriteData(0, 0, buffer, cache_size / 10,
+ NULL, false)) << "trim the cache";
+ entry->Close();
+
+ EXPECT_FALSE(cache_->OpenEntry(first, &entry));
+ ASSERT_TRUE(cache_->OpenEntry(second, &entry));
+ EXPECT_EQ(cache_size / 10, entry->GetDataSize(0));
+ entry->Close();
+}
+
+TEST_F(DiskCacheBackendTest, SetSize) {
+ BackendSetSize();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlySetSize) {
+ SetMemoryOnlyMode();
+ BackendSetSize();
+}
+
+void DiskCacheBackendTest::BackendLoad() {
+ int seed = static_cast<int>(Time::Now().ToInternalValue());
+ srand(seed);
+
+ disk_cache::Entry* entries[100];
+ for (int i = 0; i < 100; i++) {
+ std::string key = GenerateKey(true);
+ ASSERT_TRUE(cache_->CreateEntry(key, &entries[i]));
+ }
+ EXPECT_EQ(100, cache_->GetEntryCount());
+
+ for (int i = 0; i < 100; i++) {
+ int source1 = rand() % 100;
+ int source2 = rand() % 100;
+ disk_cache::Entry* temp = entries[source1];
+ entries[source1] = entries[source2];
+ entries[source2] = temp;
+ }
+
+ for (int i = 0; i < 100; i++) {
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache_->OpenEntry(entries[i]->GetKey(), &entry));
+ EXPECT_TRUE(entry == entries[i]);
+ entry->Close();
+ entries[i]->Doom();
+ entries[i]->Close();
+ }
+ EXPECT_EQ(0, cache_->GetEntryCount());
+}
+
+TEST_F(DiskCacheBackendTest, Load) {
+ // Work with a tiny index table (16 entries)
+ SetMask(0xf);
+ SetMaxSize(0x100000);
+ InitCache();
+ BackendLoad();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyLoad) {
+ // Work with a tiny index table (16 entries)
+ SetMaxSize(0x100000);
+ SetMemoryOnlyMode();
+ InitCache();
+ BackendLoad();
+}
+
+// Before looking for invalid entries, let's check a valid entry.
+TEST_F(DiskCacheBackendTest, ValidEntry) {
+ SetDirectMode();
+ InitCache();
+
+ std::string key("Some key");
+ disk_cache::Entry* entry1;
+ ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+
+ char data[] = "And the data to save";
+ EXPECT_EQ(sizeof(data), entry1->WriteData(0, 0, data, sizeof(data), NULL,
+ false));
+ entry1->Close();
+ SimulateCrash();
+
+ ASSERT_TRUE(cache_->OpenEntry(key, &entry1));
+
+ char buffer[40];
+ memset(buffer, 0, sizeof(buffer));
+ EXPECT_EQ(sizeof(data), entry1->ReadData(0, 0, buffer, sizeof(data), NULL));
+ entry1->Close();
+ EXPECT_STREQ(data, buffer);
+}
+
+// The same logic of the previous test (ValidEntry), but this time force the
+// entry to be invalid, simulating a crash in the middle.
+// We'll be leaking memory from this test.
+TEST_F(DiskCacheBackendTest, InvalidEntry) {
+ // Use the implementation directly... we need to simulate a crash.
+ SetDirectMode();
+ InitCache();
+
+ std::string key("Some key");
+ disk_cache::Entry* entry1;
+ ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+
+ char data[] = "And the data to save";
+ EXPECT_EQ(sizeof(data), entry1->WriteData(0, 0, data, sizeof(data), NULL,
+ false));
+ SimulateCrash();
+
+ EXPECT_FALSE(cache_->OpenEntry(key, &entry1));
+ EXPECT_EQ(0, cache_->GetEntryCount());
+}
+
+// Almost the same test, but this time crash the cache after reading an entry.
+// We'll be leaking memory from this test.
+TEST_F(DiskCacheBackendTest, InvalidEntryRead) {
+ // Use the implementation directly... we need to simulate a crash.
+ SetDirectMode();
+ InitCache();
+
+ std::string key("Some key");
+ disk_cache::Entry* entry1;
+ ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+
+ char data[] = "And the data to save";
+ EXPECT_EQ(sizeof(data), entry1->WriteData(0, 0, data, sizeof(data), NULL,
+ false));
+ entry1->Close();
+ ASSERT_TRUE(cache_->OpenEntry(key, &entry1));
+ EXPECT_EQ(sizeof(data), entry1->ReadData(0, 0, data, sizeof(data), NULL));
+
+ SimulateCrash();
+
+ EXPECT_FALSE(cache_->OpenEntry(key, &entry1));
+ EXPECT_EQ(0, cache_->GetEntryCount());
+}
+
+// We'll be leaking memory from this test.
+TEST_F(DiskCacheBackendTest, InvalidEntryWithLoad) {
+ // Work with a tiny index table (16 entries)
+ SetMask(0xf);
+ SetMaxSize(0x100000);
+ InitCache();
+
+ int seed = static_cast<int>(Time::Now().ToInternalValue());
+ srand(seed);
+
+ const int kNumEntries = 100;
+ disk_cache::Entry* entries[kNumEntries];
+ for (int i = 0; i < kNumEntries; i++) {
+ std::string key = GenerateKey(true);
+ ASSERT_TRUE(cache_->CreateEntry(key, &entries[i]));
+ }
+ EXPECT_EQ(kNumEntries, cache_->GetEntryCount());
+
+ for (int i = 0; i < kNumEntries; i++) {
+ int source1 = rand() % kNumEntries;
+ int source2 = rand() % kNumEntries;
+ disk_cache::Entry* temp = entries[source1];
+ entries[source1] = entries[source2];
+ entries[source2] = temp;
+ }
+
+ std::string keys[kNumEntries];
+ for (int i = 0; i < kNumEntries; i++) {
+ keys[i] = entries[i]->GetKey();
+ if (i < kNumEntries / 2)
+ entries[i]->Close();
+ }
+
+ SimulateCrash();
+
+ for (int i = kNumEntries / 2; i < kNumEntries; i++) {
+ disk_cache::Entry* entry;
+ EXPECT_FALSE(cache_->OpenEntry(keys[i], &entry));
+ }
+
+ for (int i = 0; i < kNumEntries / 2; i++) {
+ disk_cache::Entry* entry;
+ EXPECT_TRUE(cache_->OpenEntry(keys[i], &entry));
+ entry->Close();
+ }
+
+ EXPECT_EQ(kNumEntries / 2, cache_->GetEntryCount());
+}
+
+// We'll be leaking memory from this test.
+TEST_F(DiskCacheBackendTest, TrimInvalidEntry) {
+ // Use the implementation directly... we need to simulate a crash.
+ SetDirectMode();
+
+ const int cache_size = 0x4000; // 16 kB
+ SetMaxSize(cache_size * 10);
+ InitCache();
+
+ std::string first("some key");
+ std::string second("something else");
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache_->CreateEntry(first, &entry));
+
+ char buffer[cache_size] = {0};
+ EXPECT_EQ(cache_size * 19 / 20, entry->WriteData(0, 0, buffer,
+ cache_size * 19 / 20, NULL, false));
+
+ // Simulate a crash.
+ SimulateCrash();
+
+ ASSERT_TRUE(cache_->CreateEntry(second, &entry));
+ EXPECT_EQ(cache_size / 10, entry->WriteData(0, 0, buffer, cache_size / 10,
+ NULL, false)) << "trim the cache";
+ entry->Close();
+
+ EXPECT_FALSE(cache_->OpenEntry(first, &entry));
+ EXPECT_EQ(1, cache_->GetEntryCount());
+}
+
+void DiskCacheBackendTest::BackendEnumerations() {
+ Time initial = Time::Now();
+ int seed = static_cast<int>(initial.ToInternalValue());
+ srand(seed);
+
+ const int kNumEntries = 100;
+ for (int i = 0; i < kNumEntries; i++) {
+ std::string key = GenerateKey(true);
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+ entry->Close();
+ }
+ EXPECT_EQ(kNumEntries, cache_->GetEntryCount());
+ Time final = Time::Now();
+
+ disk_cache::Entry* entry;
+ void* iter = NULL;
+ int count = 0;
+ Time last_modified[kNumEntries];
+ Time last_used[kNumEntries];
+ while (cache_->OpenNextEntry(&iter, &entry)) {
+ ASSERT_TRUE(NULL != entry);
+ if (count < kNumEntries) {
+ last_modified[count] = entry->GetLastModified();
+ last_used[count] = entry->GetLastUsed();
+ }
+
+ EXPECT_TRUE(initial <= last_modified[count]);
+ EXPECT_TRUE(final >= last_modified[count]);
+ entry->Close();
+ count++;
+ };
+ EXPECT_EQ(kNumEntries, count);
+
+ iter = NULL;
+ count = 0;
+ // The previous enumeration should not have changed the timestamps.
+ while (cache_->OpenNextEntry(&iter, &entry)) {
+ ASSERT_TRUE(NULL != entry);
+ if (count < kNumEntries) {
+ EXPECT_TRUE(last_modified[count] == entry->GetLastModified());
+ EXPECT_TRUE(last_used[count] == entry->GetLastUsed());
+ }
+ entry->Close();
+ count++;
+ };
+ EXPECT_EQ(kNumEntries, count);
+}
+
+TEST_F(DiskCacheBackendTest, Enumerations) {
+ InitCache();
+ BackendEnumerations();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyEnumerations) {
+ SetMemoryOnlyMode();
+ InitCache();
+ BackendEnumerations();
+}
+
+// Verify handling of invalid entries while doing enumerations.
+// We'll be leaking memory from this test.
+TEST_F(DiskCacheBackendTest, InvalidEntryEnumeration) {
+ // Use the implementation directly... we need to simulate a crash.
+ SetDirectMode();
+ InitCache();
+
+ std::string key("Some key");
+ disk_cache::Entry *entry, *entry1, *entry2;
+ ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+
+ char data[] = "And the data to save";
+ EXPECT_EQ(sizeof(data), entry1->WriteData(0, 0, data, sizeof(data), NULL,
+ false));
+ entry1->Close();
+ ASSERT_TRUE(cache_->OpenEntry(key, &entry1));
+ EXPECT_EQ(sizeof(data), entry1->ReadData(0, 0, data, sizeof(data), NULL));
+
+ std::string key2("Another key");
+ ASSERT_TRUE(cache_->CreateEntry(key2, &entry2));
+ entry2->Close();
+ ASSERT_EQ(2, cache_->GetEntryCount());
+
+ SimulateCrash();
+
+ void* iter = NULL;
+ int count = 0;
+ while (cache_->OpenNextEntry(&iter, &entry)) {
+ ASSERT_TRUE(NULL != entry);
+ EXPECT_EQ(key2, entry->GetKey());
+ entry->Close();
+ count++;
+ };
+ EXPECT_EQ(1, count);
+ EXPECT_EQ(1, cache_->GetEntryCount());
+}
+
+// Tests that if for some reason entries are modified close to existing cache
+// iterators, we don't generate fatal errors or reset the cache.
+TEST_F(DiskCacheBackendTest, FixEnumerators) {
+ InitCache();
+
+ int seed = static_cast<int>(Time::Now().ToInternalValue());
+ srand(seed);
+
+ const int kNumEntries = 10;
+ for (int i = 0; i < kNumEntries; i++) {
+ std::string key = GenerateKey(true);
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+ entry->Close();
+ }
+ EXPECT_EQ(kNumEntries, cache_->GetEntryCount());
+
+ disk_cache::Entry *entry1, *entry2;
+ void* iter1 = NULL;
+ void* iter2 = NULL;
+ ASSERT_TRUE(cache_->OpenNextEntry(&iter1, &entry1));
+ ASSERT_TRUE(NULL != entry1);
+ entry1->Close();
+ entry1 = NULL;
+
+ // Let's go to the middle of the list.
+ for (int i = 0; i < kNumEntries / 2; i++) {
+ if (entry1)
+ entry1->Close();
+ ASSERT_TRUE(cache_->OpenNextEntry(&iter1, &entry1));
+ ASSERT_TRUE(NULL != entry1);
+
+ ASSERT_TRUE(cache_->OpenNextEntry(&iter2, &entry2));
+ ASSERT_TRUE(NULL != entry2);
+ entry2->Close();
+ }
+
+ // Messing up with entry1 will modify entry2->next.
+ entry1->Doom();
+ ASSERT_TRUE(cache_->OpenNextEntry(&iter2, &entry2));
+ ASSERT_TRUE(NULL != entry2);
+
+ // The link entry2->entry1 should be broken.
+ EXPECT_NE(entry2->GetKey(), entry1->GetKey());
+ entry1->Close();
+ entry2->Close();
+
+ // And the second iterator should keep working.
+ ASSERT_TRUE(cache_->OpenNextEntry(&iter2, &entry2));
+ ASSERT_TRUE(NULL != entry2);
+ entry2->Close();
+
+ cache_->EndEnumeration(&iter1);
+ cache_->EndEnumeration(&iter2);
+}
+
+void DiskCacheBackendTest::BackendDoomRecent() {
+ Time initial = Time::Now();
+
+ disk_cache::Entry *entry;
+ ASSERT_TRUE(cache_->CreateEntry("first", &entry));
+ entry->Close();
+ ASSERT_TRUE(cache_->CreateEntry("second", &entry));
+ entry->Close();
+
+ Sleep(20);
+ Time middle = Time::Now();
+
+ ASSERT_TRUE(cache_->CreateEntry("third", &entry));
+ entry->Close();
+ ASSERT_TRUE(cache_->CreateEntry("fourth", &entry));
+ entry->Close();
+
+ Sleep(20);
+ Time final = Time::Now();
+
+ ASSERT_EQ(4, cache_->GetEntryCount());
+ EXPECT_TRUE(cache_->DoomEntriesSince(final));
+ ASSERT_EQ(4, cache_->GetEntryCount());
+
+ EXPECT_TRUE(cache_->DoomEntriesSince(middle));
+ ASSERT_EQ(2, cache_->GetEntryCount());
+
+ ASSERT_TRUE(cache_->OpenEntry("second", &entry));
+ entry->Close();
+}
+
+void DiskCacheBackendTest::BackendDoomBetween() {
+ Time initial = Time::Now();
+
+ disk_cache::Entry *entry;
+ ASSERT_TRUE(cache_->CreateEntry("first", &entry));
+ entry->Close();
+
+ Sleep(20);
+ Time middle_start = Time::Now();
+
+ ASSERT_TRUE(cache_->CreateEntry("second", &entry));
+ entry->Close();
+ ASSERT_TRUE(cache_->CreateEntry("third", &entry));
+ entry->Close();
+
+ Sleep(20);
+ Time middle_end = Time::Now();
+
+ ASSERT_TRUE(cache_->CreateEntry("fourth", &entry));
+ entry->Close();
+
+ Sleep(20);
+ Time final = Time::Now();
+
+ ASSERT_EQ(4, cache_->GetEntryCount());
+ EXPECT_TRUE(cache_->DoomEntriesBetween(middle_start, middle_end));
+ ASSERT_EQ(2, cache_->GetEntryCount());
+
+ ASSERT_TRUE(cache_->OpenEntry("fourth", &entry));
+ entry->Close();
+
+ EXPECT_TRUE(cache_->DoomEntriesBetween(middle_start, final));
+ ASSERT_EQ(1, cache_->GetEntryCount());
+
+ ASSERT_TRUE(cache_->OpenEntry("first", &entry));
+ entry->Close();
+}
+
+TEST_F(DiskCacheBackendTest, DoomRecent) {
+ InitCache();
+ BackendDoomRecent();
+}
+
+TEST_F(DiskCacheBackendTest, DoomBetween) {
+ InitCache();
+ BackendDoomBetween();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyDoomRecent) {
+ SetMemoryOnlyMode();
+ InitCache();
+ BackendDoomRecent();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyDoomBetween) {
+ SetMemoryOnlyMode();
+ InitCache();
+ BackendDoomBetween();
+}
+
+TEST(DiskCacheTest, Backend_RecoverInsert) {
+ // Tests with an empty cache.
+ EXPECT_EQ(0, TestTransaction(L"insert_empty1", 0, false));
+ EXPECT_EQ(0, TestTransaction(L"insert_empty2", 0, false));
+ EXPECT_EQ(0, TestTransaction(L"insert_empty3", 0, false));
+
+ // Tests with one entry on the cache.
+ EXPECT_EQ(0, TestTransaction(L"insert_one1", 1, false));
+ EXPECT_EQ(0, TestTransaction(L"insert_one2", 1, false));
+ EXPECT_EQ(0, TestTransaction(L"insert_one3", 1, false));
+
+ // Tests with one hundred entries on the cache, tiny index.
+ EXPECT_EQ(0, TestTransaction(L"insert_load1", 100, true));
+ EXPECT_EQ(0, TestTransaction(L"insert_load2", 100, true));
+}
+
+TEST(DiskCacheTest, Backend_RecoverRemove) {
+ // Removing the only element.
+ EXPECT_EQ(0, TestTransaction(L"remove_one1", 0, false));
+ EXPECT_EQ(0, TestTransaction(L"remove_one2", 0, false));
+ EXPECT_EQ(0, TestTransaction(L"remove_one3", 0, false));
+
+ // Removing the head.
+ EXPECT_EQ(0, TestTransaction(L"remove_head1", 1, false));
+ EXPECT_EQ(0, TestTransaction(L"remove_head2", 1, false));
+ EXPECT_EQ(0, TestTransaction(L"remove_head3", 1, false));
+
+ // Removing the tail.
+ EXPECT_EQ(0, TestTransaction(L"remove_tail1", 1, false));
+ EXPECT_EQ(0, TestTransaction(L"remove_tail2", 1, false));
+ EXPECT_EQ(0, TestTransaction(L"remove_tail3", 1, false));
+
+ // Removing with one hundred entries on the cache, tiny index.
+ EXPECT_EQ(0, TestTransaction(L"remove_load1", 100, true));
+ EXPECT_EQ(0, TestTransaction(L"remove_load2", 100, true));
+ EXPECT_EQ(0, TestTransaction(L"remove_load3", 100, true));
+
+#ifdef NDEBUG
+ // This case cannot be reverted, so it will assert on debug builds.
+ EXPECT_EQ(0, TestTransaction(L"remove_one4", 0, false));
+ EXPECT_EQ(0, TestTransaction(L"remove_head4", 1, false));
+#endif
+}
+
+// Tests dealing with cache files that cannot be recovered.
+TEST(DiskCacheTest, Backend_DeleteOld) {
+ ASSERT_TRUE(CopyTestCache(L"wrong_version"));
+ std::wstring path = GetCachePath();
+ scoped_ptr<disk_cache::Backend> cache;
+ cache.reset(disk_cache::CreateCacheBackend(path, true, 0));
+
+ MessageLoopHelper helper;
+
+ ASSERT_TRUE(NULL != cache.get());
+ ASSERT_EQ(0, cache->GetEntryCount());
+
+ // Wait for a callback that never comes... about 2 secs :). The message loop
+ // has to run to allow destruction of the cleaner thread.
+ helper.WaitUntilCacheIoFinished(1);
+}
+
+// We want to be able to deal with messed up entries on disk.
+TEST(DiskCacheTest, Backend_InvalidEntry) {
+ ASSERT_TRUE(CopyTestCache(L"bad_entry"));
+ std::wstring path = GetCachePath();
+ disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0);
+ ASSERT_TRUE(NULL != cache);
+
+ disk_cache::Entry *entry1, *entry2;
+ ASSERT_TRUE(cache->OpenEntry("the first key", &entry1));
+ EXPECT_FALSE(cache->OpenEntry("some other key", &entry2));
+ entry1->Close();
+
+ // CheckCacheIntegrity will fail at this point.
+ delete cache;
+}
+
+// We want to be able to deal with messed up entries on disk.
+TEST(DiskCacheTest, Backend_InvalidRankings) {
+ ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+ std::wstring path = GetCachePath();
+ disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0);
+ ASSERT_TRUE(NULL != cache);
+
+ disk_cache::Entry *entry1, *entry2;
+ EXPECT_FALSE(cache->OpenEntry("the first key", &entry1));
+ ASSERT_TRUE(cache->OpenEntry("some other key", &entry2));
+ entry2->Close();
+
+ // CheckCacheIntegrity will fail at this point.
+ delete cache;
+}
+
+// If the LRU is corrupt, we delete the cache.
+TEST(DiskCacheTest, Backend_InvalidRankings2) {
+ ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+ std::wstring path = GetCachePath();
+ disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0);
+ ASSERT_TRUE(NULL != cache);
+
+ disk_cache::Entry* entry;
+ void* iter = NULL;
+ ASSERT_TRUE(cache->OpenNextEntry(&iter, &entry));
+ entry->Close();
+ EXPECT_EQ(2, cache->GetEntryCount());
+
+ EXPECT_FALSE(cache->OpenNextEntry(&iter, &entry));
+ EXPECT_EQ(0, cache->GetEntryCount());
+
+ delete cache;
+ EXPECT_TRUE(CheckCacheIntegrity(path));
+}
+
+// If the LRU is corrupt and we have open entries, we disable the cache.
+TEST(DiskCacheTest, Backend_Disable) {
+ ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+ std::wstring path = GetCachePath();
+ disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0);
+ ASSERT_TRUE(NULL != cache);
+
+ disk_cache::Entry *entry1, *entry2;
+ void* iter = NULL;
+ ASSERT_TRUE(cache->OpenNextEntry(&iter, &entry1));
+
+ EXPECT_FALSE(cache->OpenNextEntry(&iter, &entry2));
+ EXPECT_EQ(2, cache->GetEntryCount());
+ EXPECT_FALSE(cache->CreateEntry("Something new", &entry2));
+
+ entry1->Close();
+
+ EXPECT_EQ(0, cache->GetEntryCount());
+
+ delete cache;
+ EXPECT_TRUE(CheckCacheIntegrity(path));
+}
+
+// This is another type of corruption on the LRU; disable the cache.
+TEST(DiskCacheTest, Backend_Disable2) {
+ ASSERT_TRUE(CopyTestCache(L"list_loop"));
+ std::wstring path = GetCachePath();
+ disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0);
+ ASSERT_TRUE(NULL != cache);
+
+ EXPECT_EQ(8, cache->GetEntryCount());
+
+ disk_cache::Entry* entry;
+ void* iter = NULL;
+ int count = 0;
+ while (cache->OpenNextEntry(&iter, &entry)) {
+ ASSERT_TRUE(NULL != entry);
+ entry->Close();
+ count++;
+ ASSERT_LT(count, 9);
+ };
+
+ EXPECT_EQ(0, cache->GetEntryCount());
+
+ delete cache;
+ EXPECT_TRUE(CheckCacheIntegrity(path));
+}
+
+TEST(DiskCacheTest, Backend_UsageStats) {
+ MessageLoopHelper helper;
+
+ std::wstring path = GetCachePath();
+ ASSERT_TRUE(DeleteCache(path.c_str()));
+ scoped_ptr<disk_cache::BackendImpl> cache;
+ cache.reset(new disk_cache::BackendImpl(path));
+ ASSERT_TRUE(NULL != cache.get());
+ cache->SetUnitTestMode();
+ ASSERT_TRUE(cache->Init());
+
+ // Wait for a callback that never comes... about 2 secs :). The message loop
+ // has to run to allow invocation of the usage timer.
+ helper.WaitUntilCacheIoFinished(1);
+}
+
+void DiskCacheBackendTest::BackendDoomAll() {
+ Time initial = Time::Now();
+
+ disk_cache::Entry *entry1, *entry2;
+ ASSERT_TRUE(cache_->CreateEntry("first", &entry1));
+ ASSERT_TRUE(cache_->CreateEntry("second", &entry2));
+ entry1->Close();
+ entry2->Close();
+
+ ASSERT_TRUE(cache_->CreateEntry("third", &entry1));
+ ASSERT_TRUE(cache_->CreateEntry("fourth", &entry2));
+
+ ASSERT_EQ(4, cache_->GetEntryCount());
+ EXPECT_TRUE(cache_->DoomAllEntries());
+ ASSERT_EQ(0, cache_->GetEntryCount());
+
+ disk_cache::Entry *entry3, *entry4;
+ ASSERT_TRUE(cache_->CreateEntry("third", &entry3));
+ ASSERT_TRUE(cache_->CreateEntry("fourth", &entry4));
+
+ EXPECT_TRUE(cache_->DoomAllEntries());
+ ASSERT_EQ(0, cache_->GetEntryCount());
+
+ entry1->Close();
+ entry2->Close();
+ entry3->Doom(); // The entry should be already doomed, but this must work.
+ entry3->Close();
+ entry4->Close();
+
+ // Now try with all references released.
+ ASSERT_TRUE(cache_->CreateEntry("third", &entry1));
+ ASSERT_TRUE(cache_->CreateEntry("fourth", &entry2));
+ entry1->Close();
+ entry2->Close();
+
+ ASSERT_EQ(2, cache_->GetEntryCount());
+ EXPECT_TRUE(cache_->DoomAllEntries());
+ ASSERT_EQ(0, cache_->GetEntryCount());
+}
+
+TEST_F(DiskCacheBackendTest, DoomAll) {
+ InitCache();
+ BackendDoomAll();
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyDoomAll) {
+ SetMemoryOnlyMode();
+ InitCache();
+ BackendDoomAll();
+}