diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 22:42:52 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 22:42:52 +0000 |
commit | 586acc5fe142f498261f52c66862fa417c3d52d2 (patch) | |
tree | c98b3417a883f2477029c8cd5888f4078681e24e /net/disk_cache/backend_unittest.cc | |
parent | a814a8d55429605fe6d7045045cd25b6bf624580 (diff) | |
download | chromium_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.cc | 944 |
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(); +} |