| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/basictypes.h" |
| #include "base/file_util.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
| #include "base/threading/platform_thread.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/disk_cache/backend_impl.h" |
| #include "net/disk_cache/cache_util.h" |
| #include "net/disk_cache/disk_cache_test_base.h" |
| #include "net/disk_cache/disk_cache_test_util.h" |
| #include "net/disk_cache/histogram_macros.h" |
| #include "net/disk_cache/mapped_file.h" |
| #include "net/disk_cache/mem_backend_impl.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #if defined(OS_WIN) |
| #include "base/win/scoped_handle.h" |
| #endif |
| |
| using base::Time; |
| |
| // Tests that can run with different types of caches. |
| class DiskCacheBackendTest : public DiskCacheTestWithCache { |
| protected: |
| void BackendBasics(); |
| void BackendKeying(); |
| void BackendSetSize(); |
| void BackendLoad(); |
| void BackendValidEntry(); |
| void BackendInvalidEntry(); |
| void BackendInvalidEntryRead(); |
| void BackendInvalidEntryWithLoad(); |
| void BackendTrimInvalidEntry(); |
| void BackendTrimInvalidEntry2(); |
| void BackendEnumerations(); |
| void BackendEnumerations2(); |
| void BackendInvalidEntryEnumeration(); |
| void BackendFixEnumerators(); |
| void BackendDoomRecent(); |
| void BackendDoomBetween(); |
| void BackendTransaction(const std::string& name, int num_entries, bool load); |
| void BackendRecoverInsert(); |
| void BackendRecoverRemove(); |
| void BackendRecoverWithEviction(); |
| void BackendInvalidEntry2(); |
| void BackendInvalidEntry3(); |
| void BackendNotMarkedButDirty(const std::string& name); |
| void BackendDoomAll(); |
| void BackendDoomAll2(); |
| void BackendInvalidRankings(); |
| void BackendInvalidRankings2(); |
| void BackendDisable(); |
| void BackendDisable2(); |
| void BackendDisable3(); |
| void BackendDisable4(); |
| }; |
| |
| void DiskCacheBackendTest::BackendBasics() { |
| InitCache(); |
| disk_cache::Entry *entry1 = NULL, *entry2 = NULL; |
| EXPECT_NE(net::OK, OpenEntry("the first key", &entry1)); |
| ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1)); |
| ASSERT_TRUE(NULL != entry1); |
| entry1->Close(); |
| entry1 = NULL; |
| |
| ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1)); |
| ASSERT_TRUE(NULL != entry1); |
| entry1->Close(); |
| entry1 = NULL; |
| |
| EXPECT_NE(net::OK, CreateEntry("the first key", &entry1)); |
| ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1)); |
| EXPECT_NE(net::OK, OpenEntry("some other key", &entry2)); |
| ASSERT_EQ(net::OK, CreateEntry("some other key", &entry2)); |
| ASSERT_TRUE(NULL != entry1); |
| ASSERT_TRUE(NULL != entry2); |
| EXPECT_EQ(2, cache_->GetEntryCount()); |
| |
| disk_cache::Entry* entry3 = NULL; |
| ASSERT_EQ(net::OK, OpenEntry("some other key", &entry3)); |
| ASSERT_TRUE(NULL != entry3); |
| EXPECT_TRUE(entry2 == entry3); |
| EXPECT_EQ(2, cache_->GetEntryCount()); |
| |
| EXPECT_EQ(net::OK, DoomEntry("some other key")); |
| EXPECT_EQ(1, cache_->GetEntryCount()); |
| entry1->Close(); |
| entry2->Close(); |
| entry3->Close(); |
| |
| EXPECT_EQ(net::OK, DoomEntry("the first key")); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| |
| ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1)); |
| ASSERT_EQ(net::OK, CreateEntry("some other key", &entry2)); |
| entry1->Doom(); |
| entry1->Close(); |
| EXPECT_EQ(net::OK, DoomEntry("some other key")); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| entry2->Close(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, Basics) { |
| BackendBasics(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionBasics) { |
| SetNewEviction(); |
| BackendBasics(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyBasics) { |
| SetMemoryOnlyMode(); |
| BackendBasics(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, AppCacheBasics) { |
| SetCacheType(net::APP_CACHE); |
| BackendBasics(); |
| } |
| |
| void DiskCacheBackendTest::BackendKeying() { |
| InitCache(); |
| const char* kName1 = "the first key"; |
| const char* kName2 = "the first Key"; |
| disk_cache::Entry *entry1, *entry2; |
| ASSERT_EQ(net::OK, CreateEntry(kName1, &entry1)); |
| |
| ASSERT_EQ(net::OK, CreateEntry(kName2, &entry2)); |
| EXPECT_TRUE(entry1 != entry2) << "Case sensitive"; |
| entry2->Close(); |
| |
| char buffer[30]; |
| base::strlcpy(buffer, kName1, arraysize(buffer)); |
| ASSERT_EQ(net::OK, OpenEntry(buffer, &entry2)); |
| EXPECT_TRUE(entry1 == entry2); |
| entry2->Close(); |
| |
| base::strlcpy(buffer + 1, kName1, arraysize(buffer) - 1); |
| ASSERT_EQ(net::OK, OpenEntry(buffer + 1, &entry2)); |
| EXPECT_TRUE(entry1 == entry2); |
| entry2->Close(); |
| |
| base::strlcpy(buffer + 3, kName1, arraysize(buffer) - 3); |
| ASSERT_EQ(net::OK, 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_EQ(net::OK, CreateEntry(buffer2, &entry2)) << "key on block file"; |
| entry2->Close(); |
| |
| buffer2[1023] = 'g'; |
| buffer2[19999] = '\0'; |
| ASSERT_EQ(net::OK, CreateEntry(buffer2, &entry2)) << "key on external file"; |
| entry2->Close(); |
| entry1->Close(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, Keying) { |
| BackendKeying(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionKeying) { |
| SetNewEviction(); |
| BackendKeying(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyKeying) { |
| SetMemoryOnlyMode(); |
| BackendKeying(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, AppCacheKeying) { |
| SetCacheType(net::APP_CACHE); |
| BackendKeying(); |
| } |
| |
| TEST_F(DiskCacheTest, CreateBackend) { |
| TestCompletionCallback cb; |
| |
| { |
| FilePath path = GetCacheFilePath(); |
| ASSERT_TRUE(DeleteCache(path)); |
| base::Thread cache_thread("CacheThread"); |
| ASSERT_TRUE(cache_thread.StartWithOptions( |
| base::Thread::Options(MessageLoop::TYPE_IO, 0))); |
| |
| // Test the private factory methods. |
| disk_cache::Backend* cache = NULL; |
| int rv = disk_cache::BackendImpl::CreateBackend( |
| path, false, 0, net::DISK_CACHE, disk_cache::kNoRandom, |
| cache_thread.message_loop_proxy(), NULL, &cache, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| ASSERT_TRUE(cache); |
| delete cache; |
| |
| cache = disk_cache::MemBackendImpl::CreateBackend(0, NULL); |
| ASSERT_TRUE(cache); |
| delete cache; |
| cache = NULL; |
| |
| // Now test the public API. |
| rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, path, 0, false, |
| cache_thread.message_loop_proxy(), |
| NULL, &cache, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| ASSERT_TRUE(cache); |
| delete cache; |
| cache = NULL; |
| |
| rv = disk_cache::CreateCacheBackend(net::MEMORY_CACHE, FilePath(), 0, false, |
| NULL, NULL, &cache, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| ASSERT_TRUE(cache); |
| delete cache; |
| } |
| |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, ExternalFiles) { |
| InitCache(); |
| // First, let's create a file on the folder. |
| FilePath filename = GetCacheFilePath().AppendASCII("f_000001"); |
| |
| const int kSize = 50; |
| scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); |
| CacheTestFillBuffer(buffer1->data(), kSize, false); |
| ASSERT_EQ(kSize, file_util::WriteFile(filename, buffer1->data(), kSize)); |
| |
| // Now let's create a file with the cache. |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry("key", &entry)); |
| ASSERT_EQ(0, WriteData(entry, 0, 20000, buffer1, 0, false)); |
| entry->Close(); |
| |
| // And verify that the first file is still there. |
| scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize)); |
| ASSERT_EQ(kSize, file_util::ReadFile(filename, buffer2->data(), kSize)); |
| EXPECT_EQ(0, memcmp(buffer1->data(), buffer2->data(), kSize)); |
| } |
| |
| // Tests that we deal with file-level pending operations at destruction time. |
| TEST_F(DiskCacheTest, ShutdownWithPendingIO) { |
| TestCompletionCallback cb; |
| |
| { |
| FilePath path = GetCacheFilePath(); |
| ASSERT_TRUE(DeleteCache(path)); |
| base::Thread cache_thread("CacheThread"); |
| ASSERT_TRUE(cache_thread.StartWithOptions( |
| base::Thread::Options(MessageLoop::TYPE_IO, 0))); |
| |
| disk_cache::Backend* cache; |
| int rv = disk_cache::BackendImpl::CreateBackend( |
| path, false, 0, net::DISK_CACHE, disk_cache::kNoRandom, |
| base::MessageLoopProxy::CreateForCurrentThread(), NULL, |
| &cache, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| |
| disk_cache::EntryImpl* entry; |
| rv = cache->CreateEntry("some key", |
| reinterpret_cast<disk_cache::Entry**>(&entry), &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| |
| const int kSize = 25000; |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); |
| CacheTestFillBuffer(buffer->data(), kSize, false); |
| |
| for (int i = 0; i < 10 * 1024 * 1024; i += 64 * 1024) { |
| // We are using the current thread as the cache thread because we want to |
| // be able to call directly this method to make sure that the OS (instead |
| // of us switching thread) is returning IO pending. |
| rv = entry->WriteDataImpl(0, i, buffer, kSize, &cb, false); |
| if (rv == net::ERR_IO_PENDING) |
| break; |
| EXPECT_EQ(kSize, rv); |
| } |
| |
| // Don't call Close() to avoid going through the queue or we'll deadlock |
| // waiting for the operation to finish. |
| entry->Release(); |
| |
| // The cache destructor will see one pending operation here. |
| delete cache; |
| |
| if (rv == net::ERR_IO_PENDING) { |
| EXPECT_TRUE(cb.have_result()); |
| } |
| } |
| |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| // Tests that we deal with background-thread pending operations. |
| TEST_F(DiskCacheTest, ShutdownWithPendingIO2) { |
| TestCompletionCallback cb; |
| |
| { |
| FilePath path = GetCacheFilePath(); |
| ASSERT_TRUE(DeleteCache(path)); |
| base::Thread cache_thread("CacheThread"); |
| ASSERT_TRUE(cache_thread.StartWithOptions( |
| base::Thread::Options(MessageLoop::TYPE_IO, 0))); |
| |
| disk_cache::Backend* cache; |
| int rv = disk_cache::BackendImpl::CreateBackend( |
| path, false, 0, net::DISK_CACHE, disk_cache::kNoRandom, |
| cache_thread.message_loop_proxy(), NULL, &cache, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| |
| disk_cache::Entry* entry; |
| rv = cache->CreateEntry("some key", &entry, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| |
| const int kSize = 25000; |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); |
| CacheTestFillBuffer(buffer->data(), kSize, false); |
| |
| rv = entry->WriteData(0, 0, buffer, kSize, &cb, false); |
| EXPECT_EQ(net::ERR_IO_PENDING, rv); |
| |
| entry->Close(); |
| |
| // The cache destructor will see two pending operations here. |
| delete cache; |
| } |
| |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_F(DiskCacheTest, TruncatedIndex) { |
| FilePath path = GetCacheFilePath(); |
| ASSERT_TRUE(DeleteCache(path)); |
| FilePath index = path.AppendASCII("index"); |
| ASSERT_EQ(5, file_util::WriteFile(index, "hello", 5)); |
| |
| base::Thread cache_thread("CacheThread"); |
| ASSERT_TRUE(cache_thread.StartWithOptions( |
| base::Thread::Options(MessageLoop::TYPE_IO, 0))); |
| TestCompletionCallback cb; |
| |
| disk_cache::Backend* backend = NULL; |
| int rv = disk_cache::BackendImpl::CreateBackend( |
| path, false, 0, net::DISK_CACHE, disk_cache::kNone, |
| cache_thread.message_loop_proxy(), NULL, &backend, &cb); |
| ASSERT_NE(net::OK, cb.GetResult(rv)); |
| |
| ASSERT_TRUE(backend == NULL); |
| delete backend; |
| } |
| |
| 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_EQ(net::OK, CreateEntry(first, &entry)); |
| |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(cache_size)); |
| memset(buffer->data(), 0, cache_size); |
| EXPECT_EQ(cache_size / 10, WriteData(entry, 0, 0, buffer, cache_size / 10, |
| false)) << "normal file"; |
| |
| EXPECT_EQ(net::ERR_FAILED, WriteData(entry, 1, 0, buffer, cache_size / 5, |
| 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, WriteData(entry, 1, 0, buffer, cache_size / 5, |
| false)); |
| |
| // Let's fill up the cache!. |
| SetMaxSize(cache_size * 10); |
| EXPECT_EQ(cache_size * 3 / 4, WriteData(entry, 0, 0, buffer, |
| cache_size * 3 / 4, false)); |
| entry->Close(); |
| FlushQueueForTest(); |
| |
| SetMaxSize(cache_size); |
| |
| // The cache is 95% full. |
| |
| ASSERT_EQ(net::OK, CreateEntry(second, &entry)); |
| EXPECT_EQ(cache_size / 10, WriteData(entry, 0, 0, buffer, cache_size / 10, |
| false)); |
| |
| disk_cache::Entry* entry2; |
| ASSERT_EQ(net::OK, CreateEntry("an extra key", &entry2)); |
| EXPECT_EQ(cache_size / 10, WriteData(entry2, 0, 0, buffer, cache_size / 10, |
| false)); |
| entry2->Close(); // This will trigger the cache trim. |
| |
| EXPECT_NE(net::OK, OpenEntry(first, &entry2)); |
| |
| FlushQueueForTest(); // Make sure that we are done trimming the cache. |
| FlushQueueForTest(); // We may have posted two tasks to evict stuff. |
| |
| entry->Close(); |
| ASSERT_EQ(net::OK, OpenEntry(second, &entry)); |
| EXPECT_EQ(cache_size / 10, entry->GetDataSize(0)); |
| entry->Close(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, SetSize) { |
| BackendSetSize(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionSetSize) { |
| SetNewEviction(); |
| BackendSetSize(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlySetSize) { |
| SetMemoryOnlyMode(); |
| BackendSetSize(); |
| } |
| |
| void DiskCacheBackendTest::BackendLoad() { |
| InitCache(); |
| 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_EQ(net::OK, 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_EQ(net::OK, OpenEntry(entries[i]->GetKey(), &entry)); |
| EXPECT_TRUE(entry == entries[i]); |
| entry->Close(); |
| entries[i]->Doom(); |
| entries[i]->Close(); |
| } |
| FlushQueueForTest(); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, Load) { |
| // Work with a tiny index table (16 entries) |
| SetMask(0xf); |
| SetMaxSize(0x100000); |
| BackendLoad(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionLoad) { |
| SetNewEviction(); |
| // Work with a tiny index table (16 entries) |
| SetMask(0xf); |
| SetMaxSize(0x100000); |
| BackendLoad(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyLoad) { |
| // Work with a tiny index table (16 entries) |
| SetMaxSize(0x100000); |
| SetMemoryOnlyMode(); |
| BackendLoad(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, AppCacheLoad) { |
| SetCacheType(net::APP_CACHE); |
| // Work with a tiny index table (16 entries) |
| SetMask(0xf); |
| SetMaxSize(0x100000); |
| BackendLoad(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionTrim) { |
| SetNewEviction(); |
| SetDirectMode(); |
| InitCache(); |
| |
| disk_cache::Entry* entry; |
| for (int i = 0; i < 100; i++) { |
| std::string name(StringPrintf("Key %d", i)); |
| ASSERT_EQ(net::OK, CreateEntry(name, &entry)); |
| entry->Close(); |
| if (i < 90) { |
| // Entries 0 to 89 are in list 1; 90 to 99 are in list 0. |
| ASSERT_EQ(net::OK, OpenEntry(name, &entry)); |
| entry->Close(); |
| } |
| } |
| |
| // The first eviction must come from list 1 (10% limit), the second must come |
| // from list 0. |
| TrimForTest(false); |
| EXPECT_NE(net::OK, OpenEntry("Key 0", &entry)); |
| TrimForTest(false); |
| EXPECT_NE(net::OK, OpenEntry("Key 90", &entry)); |
| |
| // Double check that we still have the list tails. |
| ASSERT_EQ(net::OK, OpenEntry("Key 1", &entry)); |
| entry->Close(); |
| ASSERT_EQ(net::OK, OpenEntry("Key 91", &entry)); |
| entry->Close(); |
| } |
| |
| // Before looking for invalid entries, let's check a valid entry. |
| void DiskCacheBackendTest::BackendValidEntry() { |
| SetDirectMode(); |
| InitCache(); |
| |
| std::string key("Some key"); |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry(key, &entry)); |
| |
| const int kSize = 50; |
| scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); |
| memset(buffer1->data(), 0, kSize); |
| base::strlcpy(buffer1->data(), "And the data to save", kSize); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer1, kSize, false)); |
| entry->Close(); |
| SimulateCrash(); |
| |
| ASSERT_EQ(net::OK, OpenEntry(key, &entry)); |
| |
| scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize)); |
| memset(buffer2->data(), 0, kSize); |
| EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer2, kSize)); |
| entry->Close(); |
| EXPECT_STREQ(buffer1->data(), buffer2->data()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, ValidEntry) { |
| BackendValidEntry(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionValidEntry) { |
| SetNewEviction(); |
| BackendValidEntry(); |
| } |
| |
| // 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. |
| void DiskCacheBackendTest::BackendInvalidEntry() { |
| // Use the implementation directly... we need to simulate a crash. |
| SetDirectMode(); |
| InitCache(); |
| |
| std::string key("Some key"); |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry(key, &entry)); |
| |
| const int kSize = 50; |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); |
| memset(buffer->data(), 0, kSize); |
| base::strlcpy(buffer->data(), "And the data to save", kSize); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer, kSize, false)); |
| SimulateCrash(); |
| |
| EXPECT_NE(net::OK, OpenEntry(key, &entry)); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| } |
| |
| // This and the other intentionally leaky tests below are excluded from |
| // purify and valgrind runs by naming them in the files |
| // net/data/purify/net_unittests.exe.gtest.txt and |
| // net/data/valgrind/net_unittests.gtest.txt |
| // The scripts tools/{purify,valgrind}/chrome_tests.sh |
| // read those files and pass the appropriate --gtest_filter to net_unittests. |
| TEST_F(DiskCacheBackendTest, InvalidEntry) { |
| BackendInvalidEntry(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidEntry) { |
| SetNewEviction(); |
| BackendInvalidEntry(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, AppCacheInvalidEntry) { |
| SetCacheType(net::APP_CACHE); |
| BackendInvalidEntry(); |
| } |
| |
| // Almost the same test, but this time crash the cache after reading an entry. |
| // We'll be leaking memory from this test. |
| void DiskCacheBackendTest::BackendInvalidEntryRead() { |
| // Use the implementation directly... we need to simulate a crash. |
| SetDirectMode(); |
| InitCache(); |
| |
| std::string key("Some key"); |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry(key, &entry)); |
| |
| const int kSize = 50; |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); |
| memset(buffer->data(), 0, kSize); |
| base::strlcpy(buffer->data(), "And the data to save", kSize); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer, kSize, false)); |
| entry->Close(); |
| ASSERT_EQ(net::OK, OpenEntry(key, &entry)); |
| EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer, kSize)); |
| |
| SimulateCrash(); |
| |
| if (type_ == net::APP_CACHE) { |
| // Reading an entry and crashing should not make it dirty. |
| ASSERT_EQ(net::OK, OpenEntry(key, &entry)); |
| EXPECT_EQ(1, cache_->GetEntryCount()); |
| entry->Close(); |
| } else { |
| EXPECT_NE(net::OK, OpenEntry(key, &entry)); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| } |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, InvalidEntryRead) { |
| BackendInvalidEntryRead(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidEntryRead) { |
| SetNewEviction(); |
| BackendInvalidEntryRead(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, AppCacheInvalidEntryRead) { |
| SetCacheType(net::APP_CACHE); |
| BackendInvalidEntryRead(); |
| } |
| |
| // We'll be leaking memory from this test. |
| void DiskCacheBackendTest::BackendInvalidEntryWithLoad() { |
| // 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_EQ(net::OK, 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_NE(net::OK, OpenEntry(keys[i], &entry)); |
| } |
| |
| for (int i = 0; i < kNumEntries / 2; i++) { |
| disk_cache::Entry* entry; |
| EXPECT_EQ(net::OK, OpenEntry(keys[i], &entry)); |
| entry->Close(); |
| } |
| |
| EXPECT_EQ(kNumEntries / 2, cache_->GetEntryCount()); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, InvalidEntryWithLoad) { |
| BackendInvalidEntryWithLoad(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidEntryWithLoad) { |
| SetNewEviction(); |
| BackendInvalidEntryWithLoad(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, AppCacheInvalidEntryWithLoad) { |
| SetCacheType(net::APP_CACHE); |
| BackendInvalidEntryWithLoad(); |
| } |
| |
| // We'll be leaking memory from this test. |
| void DiskCacheBackendTest::BackendTrimInvalidEntry() { |
| // Use the implementation directly... we need to simulate a crash. |
| SetDirectMode(); |
| |
| const int kSize = 0x3000; // 12 kB |
| SetMaxSize(kSize * 10); |
| InitCache(); |
| |
| std::string first("some key"); |
| std::string second("something else"); |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry(first, &entry)); |
| |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); |
| memset(buffer->data(), 0, kSize); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer, kSize, false)); |
| |
| // Simulate a crash. |
| SimulateCrash(); |
| |
| ASSERT_EQ(net::OK, CreateEntry(second, &entry)); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer, kSize, false)); |
| |
| EXPECT_EQ(2, cache_->GetEntryCount()); |
| SetMaxSize(kSize); |
| entry->Close(); // Trim the cache. |
| FlushQueueForTest(); |
| |
| // If we evicted the entry in less than 20mS, we have one entry in the cache; |
| // if it took more than that, we posted a task and we'll delete the second |
| // entry too. |
| MessageLoop::current()->RunAllPending(); |
| |
| // This may be not thread-safe in general, but for now it's OK so add some |
| // ThreadSanitizer annotations to ignore data races on cache_. |
| // See http://crbug.com/55970 |
| ANNOTATE_IGNORE_READS_BEGIN(); |
| EXPECT_GE(1, cache_->GetEntryCount()); |
| ANNOTATE_IGNORE_READS_END(); |
| |
| EXPECT_NE(net::OK, OpenEntry(first, &entry)); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, TrimInvalidEntry) { |
| BackendTrimInvalidEntry(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, NewEvictionTrimInvalidEntry) { |
| SetNewEviction(); |
| BackendTrimInvalidEntry(); |
| } |
| |
| // We'll be leaking memory from this test. |
| void DiskCacheBackendTest::BackendTrimInvalidEntry2() { |
| // Use the implementation directly... we need to simulate a crash. |
| SetDirectMode(); |
| SetMask(0xf); // 16-entry table. |
| |
| const int kSize = 0x3000; // 12 kB |
| SetMaxSize(kSize * 40); |
| InitCache(); |
| |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); |
| memset(buffer->data(), 0, kSize); |
| disk_cache::Entry* entry; |
| |
| // Writing 32 entries to this cache chains most of them. |
| for (int i = 0; i < 32; i++) { |
| std::string key(base::StringPrintf("some key %d", i)); |
| ASSERT_EQ(net::OK, CreateEntry(key, &entry)); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer, kSize, false)); |
| entry->Close(); |
| ASSERT_EQ(net::OK, OpenEntry(key, &entry)); |
| // Note that we are not closing the entries. |
| } |
| |
| // Simulate a crash. |
| SimulateCrash(); |
| |
| ASSERT_EQ(net::OK, CreateEntry("Something else", &entry)); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer, kSize, false)); |
| |
| EXPECT_EQ(33, cache_->GetEntryCount()); |
| SetMaxSize(kSize); |
| |
| // For the new eviction code, all corrupt entries are on the second list so |
| // they are not going away that easy. |
| if (new_eviction_) { |
| EXPECT_EQ(net::OK, DoomAllEntries()); |
| } |
| |
| entry->Close(); // Trim the cache. |
| FlushQueueForTest(); |
| |
| // We may abort the eviction before cleaning up everything. |
| MessageLoop::current()->RunAllPending(); |
| EXPECT_GE(30, cache_->GetEntryCount()); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, TrimInvalidEntry2) { |
| BackendTrimInvalidEntry2(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, NewEvictionTrimInvalidEntry2) { |
| SetNewEviction(); |
| BackendTrimInvalidEntry2(); |
| } |
| |
| void DiskCacheBackendTest::BackendEnumerations() { |
| InitCache(); |
| 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_EQ(net::OK, 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 (OpenNextEntry(&iter, &entry) == net::OK) { |
| 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 (OpenNextEntry(&iter, &entry) == net::OK) { |
| 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) { |
| BackendEnumerations(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionEnumerations) { |
| SetNewEviction(); |
| BackendEnumerations(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyEnumerations) { |
| SetMemoryOnlyMode(); |
| BackendEnumerations(); |
| } |
| |
| // Flaky, http://crbug.com/74387. |
| TEST_F(DiskCacheBackendTest, FLAKY_AppCacheEnumerations) { |
| SetCacheType(net::APP_CACHE); |
| BackendEnumerations(); |
| } |
| |
| // Verifies enumerations while entries are open. |
| void DiskCacheBackendTest::BackendEnumerations2() { |
| InitCache(); |
| const std::string first("first"); |
| const std::string second("second"); |
| disk_cache::Entry *entry1, *entry2; |
| ASSERT_EQ(net::OK, CreateEntry(first, &entry1)); |
| entry1->Close(); |
| ASSERT_EQ(net::OK, CreateEntry(second, &entry2)); |
| entry2->Close(); |
| |
| // Make sure that the timestamp is not the same. |
| base::PlatformThread::Sleep(20); |
| ASSERT_EQ(net::OK, OpenEntry(second, &entry1)); |
| void* iter = NULL; |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry2)); |
| EXPECT_EQ(entry2->GetKey(), second); |
| |
| // Two entries and the iterator pointing at "first". |
| entry1->Close(); |
| entry2->Close(); |
| |
| // The iterator should still be valid, so we should not crash. |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry2)); |
| EXPECT_EQ(entry2->GetKey(), first); |
| entry2->Close(); |
| cache_->EndEnumeration(&iter); |
| |
| // Modify the oldest entry and get the newest element. |
| ASSERT_EQ(net::OK, OpenEntry(first, &entry1)); |
| EXPECT_EQ(0, WriteData(entry1, 0, 200, NULL, 0, false)); |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry2)); |
| if (type_ == net::APP_CACHE) { |
| // The list is not updated. |
| EXPECT_EQ(entry2->GetKey(), second); |
| } else { |
| EXPECT_EQ(entry2->GetKey(), first); |
| } |
| |
| entry1->Close(); |
| entry2->Close(); |
| cache_->EndEnumeration(&iter); |
| } |
| |
| TEST_F(DiskCacheBackendTest, Enumerations2) { |
| BackendEnumerations2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionEnumerations2) { |
| SetNewEviction(); |
| BackendEnumerations2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyEnumerations2) { |
| SetMemoryOnlyMode(); |
| BackendEnumerations2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, AppCacheEnumerations2) { |
| SetCacheType(net::APP_CACHE); |
| BackendEnumerations2(); |
| } |
| |
| // Verify handling of invalid entries while doing enumerations. |
| // We'll be leaking memory from this test. |
| void DiskCacheBackendTest::BackendInvalidEntryEnumeration() { |
| // Use the implementation directly... we need to simulate a crash. |
| SetDirectMode(); |
| InitCache(); |
| |
| std::string key("Some key"); |
| disk_cache::Entry *entry, *entry1, *entry2; |
| ASSERT_EQ(net::OK, CreateEntry(key, &entry1)); |
| |
| const int kSize = 50; |
| scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize)); |
| memset(buffer1->data(), 0, kSize); |
| base::strlcpy(buffer1->data(), "And the data to save", kSize); |
| EXPECT_EQ(kSize, WriteData(entry1, 0, 0, buffer1, kSize, false)); |
| entry1->Close(); |
| ASSERT_EQ(net::OK, OpenEntry(key, &entry1)); |
| EXPECT_EQ(kSize, ReadData(entry1, 0, 0, buffer1, kSize)); |
| |
| std::string key2("Another key"); |
| ASSERT_EQ(net::OK, CreateEntry(key2, &entry2)); |
| entry2->Close(); |
| ASSERT_EQ(2, cache_->GetEntryCount()); |
| |
| SimulateCrash(); |
| |
| void* iter = NULL; |
| int count = 0; |
| while (OpenNextEntry(&iter, &entry) == net::OK) { |
| ASSERT_TRUE(NULL != entry); |
| EXPECT_EQ(key2, entry->GetKey()); |
| entry->Close(); |
| count++; |
| }; |
| EXPECT_EQ(1, count); |
| EXPECT_EQ(1, cache_->GetEntryCount()); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, InvalidEntryEnumeration) { |
| BackendInvalidEntryEnumeration(); |
| } |
| |
| // We'll be leaking memory from this test. |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidEntryEnumeration) { |
| SetNewEviction(); |
| BackendInvalidEntryEnumeration(); |
| } |
| |
| // Tests that if for some reason entries are modified close to existing cache |
| // iterators, we don't generate fatal errors or reset the cache. |
| void DiskCacheBackendTest::BackendFixEnumerators() { |
| 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_EQ(net::OK, CreateEntry(key, &entry)); |
| entry->Close(); |
| } |
| EXPECT_EQ(kNumEntries, cache_->GetEntryCount()); |
| |
| disk_cache::Entry *entry1, *entry2; |
| void* iter1 = NULL; |
| void* iter2 = NULL; |
| ASSERT_EQ(net::OK, 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_EQ(net::OK, OpenNextEntry(&iter1, &entry1)); |
| ASSERT_TRUE(NULL != entry1); |
| |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter2, &entry2)); |
| ASSERT_TRUE(NULL != entry2); |
| entry2->Close(); |
| } |
| |
| // Messing up with entry1 will modify entry2->next. |
| entry1->Doom(); |
| ASSERT_EQ(net::OK, 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_EQ(net::OK, OpenNextEntry(&iter2, &entry2)); |
| ASSERT_TRUE(NULL != entry2); |
| entry2->Close(); |
| |
| cache_->EndEnumeration(&iter1); |
| cache_->EndEnumeration(&iter2); |
| } |
| |
| TEST_F(DiskCacheBackendTest, FixEnumerators) { |
| BackendFixEnumerators(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionFixEnumerators) { |
| SetNewEviction(); |
| BackendFixEnumerators(); |
| } |
| |
| void DiskCacheBackendTest::BackendDoomRecent() { |
| InitCache(); |
| Time initial = Time::Now(); |
| |
| disk_cache::Entry *entry; |
| ASSERT_EQ(net::OK, CreateEntry("first", &entry)); |
| entry->Close(); |
| ASSERT_EQ(net::OK, CreateEntry("second", &entry)); |
| entry->Close(); |
| |
| base::PlatformThread::Sleep(20); |
| Time middle = Time::Now(); |
| |
| ASSERT_EQ(net::OK, CreateEntry("third", &entry)); |
| entry->Close(); |
| ASSERT_EQ(net::OK, CreateEntry("fourth", &entry)); |
| entry->Close(); |
| |
| base::PlatformThread::Sleep(20); |
| Time final = Time::Now(); |
| |
| ASSERT_EQ(4, cache_->GetEntryCount()); |
| EXPECT_EQ(net::OK, DoomEntriesSince(final)); |
| ASSERT_EQ(4, cache_->GetEntryCount()); |
| |
| EXPECT_EQ(net::OK, DoomEntriesSince(middle)); |
| ASSERT_EQ(2, cache_->GetEntryCount()); |
| |
| ASSERT_EQ(net::OK, OpenEntry("second", &entry)); |
| entry->Close(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DoomRecent) { |
| BackendDoomRecent(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDoomRecent) { |
| SetNewEviction(); |
| BackendDoomRecent(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyDoomRecent) { |
| SetMemoryOnlyMode(); |
| BackendDoomRecent(); |
| } |
| |
| void DiskCacheBackendTest::BackendDoomBetween() { |
| InitCache(); |
| Time initial = Time::Now(); |
| |
| disk_cache::Entry *entry; |
| ASSERT_EQ(net::OK, CreateEntry("first", &entry)); |
| entry->Close(); |
| |
| base::PlatformThread::Sleep(20); |
| Time middle_start = Time::Now(); |
| |
| ASSERT_EQ(net::OK, CreateEntry("second", &entry)); |
| entry->Close(); |
| ASSERT_EQ(net::OK, CreateEntry("third", &entry)); |
| entry->Close(); |
| |
| base::PlatformThread::Sleep(20); |
| Time middle_end = Time::Now(); |
| |
| ASSERT_EQ(net::OK, CreateEntry("fourth", &entry)); |
| entry->Close(); |
| ASSERT_EQ(net::OK, OpenEntry("fourth", &entry)); |
| entry->Close(); |
| |
| base::PlatformThread::Sleep(20); |
| Time final = Time::Now(); |
| |
| ASSERT_EQ(4, cache_->GetEntryCount()); |
| EXPECT_EQ(net::OK, DoomEntriesBetween(middle_start, middle_end)); |
| ASSERT_EQ(2, cache_->GetEntryCount()); |
| |
| ASSERT_EQ(net::OK, OpenEntry("fourth", &entry)); |
| entry->Close(); |
| |
| EXPECT_EQ(net::OK, DoomEntriesBetween(middle_start, final)); |
| ASSERT_EQ(1, cache_->GetEntryCount()); |
| |
| ASSERT_EQ(net::OK, OpenEntry("first", &entry)); |
| entry->Close(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DoomBetween) { |
| BackendDoomBetween(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDoomBetween) { |
| SetNewEviction(); |
| BackendDoomBetween(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyDoomBetween) { |
| SetMemoryOnlyMode(); |
| BackendDoomBetween(); |
| } |
| |
| void DiskCacheBackendTest::BackendTransaction(const std::string& name, |
| int num_entries, bool load) { |
| success_ = false; |
| ASSERT_TRUE(CopyTestCache(name)); |
| DisableFirstCleanup(); |
| |
| if (load) { |
| SetMask(0xf); |
| SetMaxSize(0x100000); |
| } else { |
| // Clear the settings from the previous run. |
| SetMask(0); |
| SetMaxSize(0); |
| } |
| |
| InitCache(); |
| ASSERT_EQ(num_entries + 1, cache_->GetEntryCount()); |
| |
| std::string key("the first key"); |
| disk_cache::Entry* entry1; |
| ASSERT_NE(net::OK, OpenEntry(key, &entry1)); |
| |
| int actual = cache_->GetEntryCount(); |
| if (num_entries != actual) { |
| ASSERT_TRUE(load); |
| // If there is a heavy load, inserting an entry will make another entry |
| // dirty (on the hash bucket) so two entries are removed. |
| ASSERT_EQ(num_entries - 1, actual); |
| } |
| |
| delete cache_; |
| cache_ = NULL; |
| cache_impl_ = NULL; |
| |
| ASSERT_TRUE(CheckCacheIntegrity(GetCacheFilePath(), new_eviction_)); |
| success_ = true; |
| } |
| |
| void DiskCacheBackendTest::BackendRecoverInsert() { |
| // Tests with an empty cache. |
| BackendTransaction("insert_empty1", 0, false); |
| ASSERT_TRUE(success_) << "insert_empty1"; |
| BackendTransaction("insert_empty2", 0, false); |
| ASSERT_TRUE(success_) << "insert_empty2"; |
| BackendTransaction("insert_empty3", 0, false); |
| ASSERT_TRUE(success_) << "insert_empty3"; |
| |
| // Tests with one entry on the cache. |
| BackendTransaction("insert_one1", 1, false); |
| ASSERT_TRUE(success_) << "insert_one1"; |
| BackendTransaction("insert_one2", 1, false); |
| ASSERT_TRUE(success_) << "insert_one2"; |
| BackendTransaction("insert_one3", 1, false); |
| ASSERT_TRUE(success_) << "insert_one3"; |
| |
| // Tests with one hundred entries on the cache, tiny index. |
| BackendTransaction("insert_load1", 100, true); |
| ASSERT_TRUE(success_) << "insert_load1"; |
| BackendTransaction("insert_load2", 100, true); |
| ASSERT_TRUE(success_) << "insert_load2"; |
| } |
| |
| TEST_F(DiskCacheBackendTest, RecoverInsert) { |
| BackendRecoverInsert(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionRecoverInsert) { |
| SetNewEviction(); |
| BackendRecoverInsert(); |
| } |
| |
| void DiskCacheBackendTest::BackendRecoverRemove() { |
| // Removing the only element. |
| BackendTransaction("remove_one1", 0, false); |
| ASSERT_TRUE(success_) << "remove_one1"; |
| BackendTransaction("remove_one2", 0, false); |
| ASSERT_TRUE(success_) << "remove_one2"; |
| BackendTransaction("remove_one3", 0, false); |
| ASSERT_TRUE(success_) << "remove_one3"; |
| |
| // Removing the head. |
| BackendTransaction("remove_head1", 1, false); |
| ASSERT_TRUE(success_) << "remove_head1"; |
| BackendTransaction("remove_head2", 1, false); |
| ASSERT_TRUE(success_) << "remove_head2"; |
| BackendTransaction("remove_head3", 1, false); |
| ASSERT_TRUE(success_) << "remove_head3"; |
| |
| // Removing the tail. |
| BackendTransaction("remove_tail1", 1, false); |
| ASSERT_TRUE(success_) << "remove_tail1"; |
| BackendTransaction("remove_tail2", 1, false); |
| ASSERT_TRUE(success_) << "remove_tail2"; |
| BackendTransaction("remove_tail3", 1, false); |
| ASSERT_TRUE(success_) << "remove_tail3"; |
| |
| // Removing with one hundred entries on the cache, tiny index. |
| BackendTransaction("remove_load1", 100, true); |
| ASSERT_TRUE(success_) << "remove_load1"; |
| BackendTransaction("remove_load2", 100, true); |
| ASSERT_TRUE(success_) << "remove_load2"; |
| BackendTransaction("remove_load3", 100, true); |
| ASSERT_TRUE(success_) << "remove_load3"; |
| |
| // This case cannot be reverted. |
| BackendTransaction("remove_one4", 0, false); |
| ASSERT_TRUE(success_) << "remove_one4"; |
| BackendTransaction("remove_head4", 1, false); |
| ASSERT_TRUE(success_) << "remove_head4"; |
| } |
| |
| TEST_F(DiskCacheBackendTest, RecoverRemove) { |
| BackendRecoverRemove(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionRecoverRemove) { |
| SetNewEviction(); |
| BackendRecoverRemove(); |
| } |
| |
| void DiskCacheBackendTest::BackendRecoverWithEviction() { |
| success_ = false; |
| ASSERT_TRUE(CopyTestCache("insert_load1")); |
| DisableFirstCleanup(); |
| |
| SetMask(0xf); |
| SetMaxSize(0x1000); |
| |
| // We should not crash here. |
| InitCache(); |
| DisableIntegrityCheck(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, RecoverWithEviction) { |
| BackendRecoverWithEviction(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionRecoverWithEviction) { |
| SetNewEviction(); |
| BackendRecoverWithEviction(); |
| } |
| |
| // Tests dealing with cache files that cannot be recovered. |
| TEST_F(DiskCacheTest, DeleteOld) { |
| ASSERT_TRUE(CopyTestCache("wrong_version")); |
| FilePath path = GetCacheFilePath(); |
| base::Thread cache_thread("CacheThread"); |
| ASSERT_TRUE(cache_thread.StartWithOptions( |
| base::Thread::Options(MessageLoop::TYPE_IO, 0))); |
| TestCompletionCallback cb; |
| |
| disk_cache::Backend* cache; |
| int rv = disk_cache::BackendImpl::CreateBackend( |
| path, true, 0, net::DISK_CACHE, disk_cache::kNoRandom, |
| cache_thread.message_loop_proxy(), NULL, &cache, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| |
| MessageLoopHelper helper; |
| |
| ASSERT_TRUE(NULL != cache); |
| ASSERT_EQ(0, cache->GetEntryCount()); |
| |
| delete cache; |
| } |
| |
| // We want to be able to deal with messed up entries on disk. |
| void DiskCacheBackendTest::BackendInvalidEntry2() { |
| ASSERT_TRUE(CopyTestCache("bad_entry")); |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| disk_cache::Entry *entry1, *entry2; |
| ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1)); |
| EXPECT_NE(net::OK, OpenEntry("some other key", &entry2)); |
| entry1->Close(); |
| |
| // CheckCacheIntegrity will fail at this point. |
| DisableIntegrityCheck(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, InvalidEntry2) { |
| BackendInvalidEntry2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidEntry2) { |
| SetNewEviction(); |
| BackendInvalidEntry2(); |
| } |
| |
| // Tests that we don't crash or hang when enumerating this cache. |
| void DiskCacheBackendTest::BackendInvalidEntry3() { |
| SetMask(0x1); // 2-entry table. |
| SetMaxSize(0x3000); // 12 kB. |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| disk_cache::Entry* entry; |
| void* iter = NULL; |
| while (OpenNextEntry(&iter, &entry) == net::OK) { |
| entry->Close(); |
| } |
| } |
| |
| TEST_F(DiskCacheBackendTest, InvalidEntry3) { |
| ASSERT_TRUE(CopyTestCache("dirty_entry3")); |
| BackendInvalidEntry3(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidEntry3) { |
| ASSERT_TRUE(CopyTestCache("dirty_entry4")); |
| SetNewEviction(); |
| BackendInvalidEntry3(); |
| DisableIntegrityCheck(); |
| } |
| |
| // Test that we handle a dirty entry on the LRU list, already replaced with |
| // the same key, and with hash collisions. |
| TEST_F(DiskCacheBackendTest, InvalidEntry4) { |
| ASSERT_TRUE(CopyTestCache("dirty_entry3")); |
| SetMask(0x1); // 2-entry table. |
| SetMaxSize(0x3000); // 12 kB. |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| TrimForTest(false); |
| } |
| |
| // Test that we handle a dirty entry on the deleted list, already replaced with |
| // the same key, and with hash collisions. |
| TEST_F(DiskCacheBackendTest, InvalidEntry5) { |
| ASSERT_TRUE(CopyTestCache("dirty_entry4")); |
| SetNewEviction(); |
| SetMask(0x1); // 2-entry table. |
| SetMaxSize(0x3000); // 12 kB. |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| TrimDeletedListForTest(false); |
| } |
| |
| TEST_F(DiskCacheBackendTest, InvalidEntry6) { |
| ASSERT_TRUE(CopyTestCache("dirty_entry5")); |
| SetMask(0x1); // 2-entry table. |
| SetMaxSize(0x3000); // 12 kB. |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| // There is a dirty entry (but marked as clean) at the end, pointing to a |
| // deleted entry through the hash collision list. We should not re-insert the |
| // deleted entry into the index table. |
| |
| TrimForTest(false); |
| // The cache should be clean (as detected by CheckCacheIntegrity). |
| } |
| |
| // Tests that we don't hang when there is a loop on the hash collision list. |
| // The test cache could be a result of bug 69135. |
| TEST_F(DiskCacheBackendTest, BadNextEntry1) { |
| ASSERT_TRUE(CopyTestCache("list_loop2")); |
| SetMask(0x1); // 2-entry table. |
| SetMaxSize(0x3000); // 12 kB. |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| // The second entry points at itselft, and the first entry is not accessible |
| // though the index, but it is at the head of the LRU. |
| |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry("The first key", &entry)); |
| entry->Close(); |
| |
| TrimForTest(false); |
| TrimForTest(false); |
| ASSERT_EQ(net::OK, OpenEntry("The first key", &entry)); |
| entry->Close(); |
| EXPECT_EQ(1, cache_->GetEntryCount()); |
| } |
| |
| // Tests that we don't hang when there is a loop on the hash collision list. |
| // The test cache could be a result of bug 69135. |
| TEST_F(DiskCacheBackendTest, BadNextEntry2) { |
| ASSERT_TRUE(CopyTestCache("list_loop3")); |
| SetMask(0x1); // 2-entry table. |
| SetMaxSize(0x3000); // 12 kB. |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| // There is a wide loop of 5 entries. |
| |
| disk_cache::Entry* entry; |
| ASSERT_NE(net::OK, OpenEntry("Not present key", &entry)); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidEntry6) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings3")); |
| DisableFirstCleanup(); |
| SetNewEviction(); |
| InitCache(); |
| |
| // The second entry is dirty, but removing it should not corrupt the list. |
| disk_cache::Entry* entry; |
| ASSERT_NE(net::OK, OpenEntry("the second key", &entry)); |
| ASSERT_EQ(net::OK, OpenEntry("the first key", &entry)); |
| |
| // This should not delete the cache. |
| entry->Doom(); |
| FlushQueueForTest(); |
| entry->Close(); |
| |
| ASSERT_EQ(net::OK, OpenEntry("some other key", &entry)); |
| entry->Close(); |
| } |
| |
| // We want to be able to deal with abnormal dirty entries. |
| void DiskCacheBackendTest::BackendNotMarkedButDirty(const std::string& name) { |
| ASSERT_TRUE(CopyTestCache(name)); |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| disk_cache::Entry *entry1, *entry2; |
| ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1)); |
| EXPECT_NE(net::OK, OpenEntry("some other key", &entry2)); |
| entry1->Close(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NotMarkedButDirty) { |
| BackendNotMarkedButDirty("dirty_entry"); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionNotMarkedButDirty) { |
| SetNewEviction(); |
| BackendNotMarkedButDirty("dirty_entry"); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NotMarkedButDirty2) { |
| BackendNotMarkedButDirty("dirty_entry2"); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionNotMarkedButDirty2) { |
| SetNewEviction(); |
| BackendNotMarkedButDirty("dirty_entry2"); |
| } |
| |
| // We want to be able to deal with messed up entries on disk. |
| void DiskCacheBackendTest::BackendInvalidRankings2() { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| FilePath path = GetCacheFilePath(); |
| DisableFirstCleanup(); |
| InitCache(); |
| |
| disk_cache::Entry *entry1, *entry2; |
| EXPECT_NE(net::OK, OpenEntry("the first key", &entry1)); |
| ASSERT_EQ(net::OK, OpenEntry("some other key", &entry2)); |
| entry2->Close(); |
| |
| // CheckCacheIntegrity will fail at this point. |
| DisableIntegrityCheck(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, InvalidRankings2) { |
| BackendInvalidRankings2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidRankings2) { |
| SetNewEviction(); |
| BackendInvalidRankings2(); |
| } |
| |
| // If the LRU is corrupt, we delete the cache. |
| void DiskCacheBackendTest::BackendInvalidRankings() { |
| disk_cache::Entry* entry; |
| void* iter = NULL; |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry)); |
| entry->Close(); |
| EXPECT_EQ(2, cache_->GetEntryCount()); |
| |
| EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry)); |
| FlushQueueForTest(); // Allow the restart to finish. |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, InvalidRankingsSuccess) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| InitCache(); |
| BackendInvalidRankings(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidRankingsSuccess) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| SetNewEviction(); |
| InitCache(); |
| BackendInvalidRankings(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, InvalidRankingsFailure) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| InitCache(); |
| SetTestMode(); // Fail cache reinitialization. |
| BackendInvalidRankings(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionInvalidRankingsFailure) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| SetNewEviction(); |
| InitCache(); |
| SetTestMode(); // Fail cache reinitialization. |
| BackendInvalidRankings(); |
| } |
| |
| // If the LRU is corrupt and we have open entries, we disable the cache. |
| void DiskCacheBackendTest::BackendDisable() { |
| disk_cache::Entry *entry1, *entry2; |
| void* iter = NULL; |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry1)); |
| |
| EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry2)); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| EXPECT_NE(net::OK, CreateEntry("Something new", &entry2)); |
| |
| entry1->Close(); |
| FlushQueueForTest(); // Flushing the Close posts a task to restart the cache. |
| FlushQueueForTest(); // This one actually allows that task to complete. |
| |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DisableSuccess) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| InitCache(); |
| BackendDisable(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| SetNewEviction(); |
| InitCache(); |
| BackendDisable(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DisableFailure) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| InitCache(); |
| SetTestMode(); // Fail cache reinitialization. |
| BackendDisable(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDisableFailure) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| SetNewEviction(); |
| InitCache(); |
| SetTestMode(); // Fail cache reinitialization. |
| BackendDisable(); |
| } |
| |
| // This is another type of corruption on the LRU; disable the cache. |
| void DiskCacheBackendTest::BackendDisable2() { |
| EXPECT_EQ(8, cache_->GetEntryCount()); |
| |
| disk_cache::Entry* entry; |
| void* iter = NULL; |
| int count = 0; |
| while (OpenNextEntry(&iter, &entry) == net::OK) { |
| ASSERT_TRUE(NULL != entry); |
| entry->Close(); |
| count++; |
| ASSERT_LT(count, 9); |
| }; |
| |
| FlushQueueForTest(); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DisableSuccess2) { |
| ASSERT_TRUE(CopyTestCache("list_loop")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| InitCache(); |
| BackendDisable2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess2) { |
| ASSERT_TRUE(CopyTestCache("list_loop")); |
| DisableFirstCleanup(); |
| SetNewEviction(); |
| SetDirectMode(); |
| InitCache(); |
| BackendDisable2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DisableFailure2) { |
| ASSERT_TRUE(CopyTestCache("list_loop")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| InitCache(); |
| SetTestMode(); // Fail cache reinitialization. |
| BackendDisable2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDisableFailure2) { |
| ASSERT_TRUE(CopyTestCache("list_loop")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| SetNewEviction(); |
| InitCache(); |
| SetTestMode(); // Fail cache reinitialization. |
| BackendDisable2(); |
| } |
| |
| // If the index size changes when we disable the cache, we should not crash. |
| void DiskCacheBackendTest::BackendDisable3() { |
| disk_cache::Entry *entry1, *entry2; |
| void* iter = NULL; |
| EXPECT_EQ(2, cache_->GetEntryCount()); |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry1)); |
| entry1->Close(); |
| |
| EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry2)); |
| FlushQueueForTest(); |
| |
| ASSERT_EQ(net::OK, CreateEntry("Something new", &entry2)); |
| entry2->Close(); |
| |
| EXPECT_EQ(1, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DisableSuccess3) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings2")); |
| DisableFirstCleanup(); |
| SetMaxSize(20 * 1024 * 1024); |
| InitCache(); |
| BackendDisable3(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess3) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings2")); |
| DisableFirstCleanup(); |
| SetMaxSize(20 * 1024 * 1024); |
| SetNewEviction(); |
| InitCache(); |
| BackendDisable3(); |
| } |
| |
| // If we disable the cache, already open entries should work as far as possible. |
| void DiskCacheBackendTest::BackendDisable4() { |
| disk_cache::Entry *entry1, *entry2, *entry3, *entry4; |
| void* iter = NULL; |
| ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry1)); |
| |
| char key2[2000]; |
| char key3[20000]; |
| CacheTestFillBuffer(key2, sizeof(key2), true); |
| CacheTestFillBuffer(key3, sizeof(key3), true); |
| key2[sizeof(key2) - 1] = '\0'; |
| key3[sizeof(key3) - 1] = '\0'; |
| ASSERT_EQ(net::OK, CreateEntry(key2, &entry2)); |
| ASSERT_EQ(net::OK, CreateEntry(key3, &entry3)); |
| |
| const int kBufSize = 20000; |
| scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufSize)); |
| memset(buf->data(), 0, kBufSize); |
| EXPECT_EQ(100, WriteData(entry2, 0, 0, buf, 100, false)); |
| EXPECT_EQ(kBufSize, WriteData(entry3, 0, 0, buf, kBufSize, false)); |
| |
| // This line should disable the cache but not delete it. |
| EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry4)); |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| |
| EXPECT_NE(net::OK, CreateEntry("cache is disabled", &entry4)); |
| |
| EXPECT_EQ(100, ReadData(entry2, 0, 0, buf, 100)); |
| EXPECT_EQ(100, WriteData(entry2, 0, 0, buf, 100, false)); |
| EXPECT_EQ(100, WriteData(entry2, 1, 0, buf, 100, false)); |
| |
| EXPECT_EQ(kBufSize, ReadData(entry3, 0, 0, buf, kBufSize)); |
| EXPECT_EQ(kBufSize, WriteData(entry3, 0, 0, buf, kBufSize, false)); |
| EXPECT_EQ(kBufSize, WriteData(entry3, 1, 0, buf, kBufSize, false)); |
| |
| std::string key = entry2->GetKey(); |
| EXPECT_EQ(sizeof(key2) - 1, key.size()); |
| key = entry3->GetKey(); |
| EXPECT_EQ(sizeof(key3) - 1, key.size()); |
| |
| entry1->Close(); |
| entry2->Close(); |
| entry3->Close(); |
| FlushQueueForTest(); // Flushing the Close posts a task to restart the cache. |
| FlushQueueForTest(); // This one actually allows that task to complete. |
| |
| EXPECT_EQ(0, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DisableSuccess4) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| InitCache(); |
| BackendDisable4(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess4) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings")); |
| DisableFirstCleanup(); |
| SetDirectMode(); |
| SetNewEviction(); |
| InitCache(); |
| BackendDisable4(); |
| } |
| |
| TEST_F(DiskCacheTest, Backend_UsageStats) { |
| MessageLoopHelper helper; |
| |
| FilePath path = GetCacheFilePath(); |
| ASSERT_TRUE(DeleteCache(path)); |
| scoped_ptr<disk_cache::BackendImpl> cache; |
| cache.reset(new disk_cache::BackendImpl( |
| path, base::MessageLoopProxy::CreateForCurrentThread(), |
| NULL)); |
| ASSERT_TRUE(NULL != cache.get()); |
| cache->SetUnitTestMode(); |
| ASSERT_EQ(net::OK, cache->SyncInit()); |
| |
| // 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() { |
| InitCache(); |
| Time initial = Time::Now(); |
| |
| disk_cache::Entry *entry1, *entry2; |
| ASSERT_EQ(net::OK, CreateEntry("first", &entry1)); |
| ASSERT_EQ(net::OK, CreateEntry("second", &entry2)); |
| entry1->Close(); |
| entry2->Close(); |
| |
| ASSERT_EQ(net::OK, CreateEntry("third", &entry1)); |
| ASSERT_EQ(net::OK, CreateEntry("fourth", &entry2)); |
| |
| ASSERT_EQ(4, cache_->GetEntryCount()); |
| EXPECT_EQ(net::OK, DoomAllEntries()); |
| ASSERT_EQ(0, cache_->GetEntryCount()); |
| |
| // We should stop posting tasks at some point (if we post any). |
| MessageLoop::current()->RunAllPending(); |
| |
| disk_cache::Entry *entry3, *entry4; |
| ASSERT_EQ(net::OK, CreateEntry("third", &entry3)); |
| ASSERT_EQ(net::OK, CreateEntry("fourth", &entry4)); |
| |
| EXPECT_EQ(net::OK, 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_EQ(net::OK, CreateEntry("third", &entry1)); |
| ASSERT_EQ(net::OK, CreateEntry("fourth", &entry2)); |
| entry1->Close(); |
| entry2->Close(); |
| |
| ASSERT_EQ(2, cache_->GetEntryCount()); |
| EXPECT_EQ(net::OK, DoomAllEntries()); |
| ASSERT_EQ(0, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DoomAll) { |
| BackendDoomAll(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDoomAll) { |
| SetNewEviction(); |
| BackendDoomAll(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, MemoryOnlyDoomAll) { |
| SetMemoryOnlyMode(); |
| BackendDoomAll(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, AppCacheOnlyDoomAll) { |
| SetCacheType(net::APP_CACHE); |
| BackendDoomAll(); |
| } |
| |
| // If the index size changes when we doom the cache, we should not crash. |
| void DiskCacheBackendTest::BackendDoomAll2() { |
| EXPECT_EQ(2, cache_->GetEntryCount()); |
| EXPECT_EQ(net::OK, DoomAllEntries()); |
| |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry("Something new", &entry)); |
| entry->Close(); |
| |
| EXPECT_EQ(1, cache_->GetEntryCount()); |
| } |
| |
| TEST_F(DiskCacheBackendTest, DoomAll2) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings2")); |
| DisableFirstCleanup(); |
| SetMaxSize(20 * 1024 * 1024); |
| InitCache(); |
| BackendDoomAll2(); |
| } |
| |
| TEST_F(DiskCacheBackendTest, NewEvictionDoomAll2) { |
| ASSERT_TRUE(CopyTestCache("bad_rankings2")); |
| DisableFirstCleanup(); |
| SetMaxSize(20 * 1024 * 1024); |
| SetNewEviction(); |
| InitCache(); |
| BackendDoomAll2(); |
| } |
| |
| // We should be able to create the same entry on multiple simultaneous instances |
| // of the cache. |
| TEST_F(DiskCacheTest, MultipleInstances) { |
| ScopedTestCache store1; |
| ScopedTestCache store2("cache_test2"); |
| ScopedTestCache store3("cache_test3"); |
| base::Thread cache_thread("CacheThread"); |
| ASSERT_TRUE(cache_thread.StartWithOptions( |
| base::Thread::Options(MessageLoop::TYPE_IO, 0))); |
| TestCompletionCallback cb; |
| |
| const int kNumberOfCaches = 2; |
| disk_cache::Backend* cache[kNumberOfCaches]; |
| |
| int rv = disk_cache::BackendImpl::CreateBackend( |
| store1.path(), false, 0, net::DISK_CACHE, disk_cache::kNone, |
| cache_thread.message_loop_proxy(), NULL, &cache[0], &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| rv = disk_cache::BackendImpl::CreateBackend( |
| store2.path(), false, 0, net::MEDIA_CACHE, disk_cache::kNone, |
| cache_thread.message_loop_proxy(), NULL, &cache[1], &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| |
| ASSERT_TRUE(cache[0] != NULL && cache[1] != NULL); |
| |
| std::string key("the first key"); |
| disk_cache::Entry* entry; |
| for (int i = 0; i < kNumberOfCaches; i++) { |
| rv = cache[i]->CreateEntry(key, &entry, &cb); |
| ASSERT_EQ(net::OK, cb.GetResult(rv)); |
| entry->Close(); |
| } |
| delete cache[0]; |
| delete cache[1]; |
| } |
| |
| // Test the six regions of the curve that determines the max cache size. |
| TEST_F(DiskCacheTest, AutomaticMaxSize) { |
| const int kDefaultSize = 80 * 1024 * 1024; |
| int64 large_size = kDefaultSize; |
| int64 largest_size = kint32max; |
| |
| // Region 1: expected = available * 0.8 |
| EXPECT_EQ((kDefaultSize - 1) * 8 / 10, |
| disk_cache::PreferedCacheSize(large_size - 1)); |
| EXPECT_EQ(kDefaultSize * 8 / 10, |
| disk_cache::PreferedCacheSize(large_size)); |
| EXPECT_EQ(kDefaultSize - 1, |
| disk_cache::PreferedCacheSize(large_size * 10 / 8 - 1)); |
| |
| // Region 2: expected = default_size |
| EXPECT_EQ(kDefaultSize, |
| disk_cache::PreferedCacheSize(large_size * 10 / 8)); |
| EXPECT_EQ(kDefaultSize, |
| disk_cache::PreferedCacheSize(large_size * 10 - 1)); |
| |
| // Region 3: expected = available * 0.1 |
| EXPECT_EQ(kDefaultSize, |
| disk_cache::PreferedCacheSize(large_size * 10)); |
| EXPECT_EQ((kDefaultSize * 25 - 1) / 10, |
| disk_cache::PreferedCacheSize(large_size * 25 - 1)); |
| |
| // Region 4: expected = default_size * 2.5 |
| EXPECT_EQ(kDefaultSize * 25 / 10, |
| disk_cache::PreferedCacheSize(large_size * 25)); |
| EXPECT_EQ(kDefaultSize * 25 / 10, |
| disk_cache::PreferedCacheSize(large_size * 100 - 1)); |
| EXPECT_EQ(kDefaultSize * 25 / 10, |
| disk_cache::PreferedCacheSize(large_size * 100)); |
| EXPECT_EQ(kDefaultSize * 25 / 10, |
| disk_cache::PreferedCacheSize(large_size * 250 - 1)); |
| |
| // Region 5: expected = available * 0.1 |
| EXPECT_EQ(kDefaultSize * 25 / 10, |
| disk_cache::PreferedCacheSize(large_size * 250)); |
| EXPECT_EQ(kint32max - 1, |
| disk_cache::PreferedCacheSize(largest_size * 100 - 1)); |
| |
| // Region 6: expected = kint32max |
| EXPECT_EQ(kint32max, |
| disk_cache::PreferedCacheSize(largest_size * 100)); |
| EXPECT_EQ(kint32max, |
| disk_cache::PreferedCacheSize(largest_size * 10000)); |
| } |
| |
| // Tests that we can "migrate" a running instance from one experiment group to |
| // another. |
| TEST_F(DiskCacheBackendTest, Histograms) { |
| SetDirectMode(); |
| InitCache(); |
| disk_cache::BackendImpl* backend_ = cache_impl_; // Needed be the macro. |
| |
| for (int i = 1; i < 3; i++) { |
| CACHE_UMA(HOURS, "FillupTime", i, 28); |
| } |
| } |
| |
| // Make sure that we keep the total memory used by the internal buffers under |
| // control. |
| TEST_F(DiskCacheBackendTest, TotalBuffersSize1) { |
| SetDirectMode(); |
| InitCache(); |
| std::string key("the first key"); |
| disk_cache::Entry* entry; |
| ASSERT_EQ(net::OK, CreateEntry(key, &entry)); |
| |
| const int kSize = 200; |
| scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize)); |
| CacheTestFillBuffer(buffer->data(), kSize, true); |
| |
| for (int i = 0; i < 10; i++) { |
| SCOPED_TRACE(i); |
| // Allocate 2MB for this entry. |
| EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer, kSize, true)); |
| EXPECT_EQ(kSize, WriteData(entry, 1, 0, buffer, kSize, true)); |
| EXPECT_EQ(kSize, WriteData(entry, 0, 1024 * 1024, buffer, kSize, false)); |
| EXPECT_EQ(kSize, WriteData(entry, 1, 1024 * 1024, buffer, kSize, false)); |
| |
| // Delete one of the buffers and truncate the other. |
| EXPECT_EQ(0, WriteData(entry, 0, 0, buffer, 0, true)); |
| EXPECT_EQ(0, WriteData(entry, 1, 10, buffer, 0, true)); |
| |
| // Delete the second buffer, writing 10 bytes to disk. |
| entry->Close(); |
| ASSERT_EQ(net::OK, OpenEntry(key, &entry)); |
| } |
| |
| entry->Close(); |
| EXPECT_EQ(0, cache_impl_->GetTotalBuffersSize()); |
| } |
| |
| // This test assumes at least 150MB of system memory. |
| TEST_F(DiskCacheBackendTest, TotalBuffersSize2) { |
| SetDirectMode(); |
| InitCache(); |
| |
| const int kOneMB = 1024 * 1024; |
| EXPECT_TRUE(cache_impl_->IsAllocAllowed(0, kOneMB)); |
| EXPECT_EQ(kOneMB, cache_impl_->GetTotalBuffersSize()); |
| |
| EXPECT_TRUE(cache_impl_->IsAllocAllowed(0, kOneMB)); |
| EXPECT_EQ(kOneMB * 2, cache_impl_->GetTotalBuffersSize()); |
| |
| EXPECT_TRUE(cache_impl_->IsAllocAllowed(0, kOneMB)); |
| EXPECT_EQ(kOneMB * 3, cache_impl_->GetTotalBuffersSize()); |
| |
| cache_impl_->BufferDeleted(kOneMB); |
| EXPECT_EQ(kOneMB * 2, cache_impl_->GetTotalBuffersSize()); |
| |
| // Check the upper limit. |
| EXPECT_FALSE(cache_impl_->IsAllocAllowed(0, 30 * kOneMB)); |
| |
| for (int i = 0; i < 30; i++) |
| cache_impl_->IsAllocAllowed(0, kOneMB); // Ignore the result. |
| |
| EXPECT_FALSE(cache_impl_->IsAllocAllowed(0, kOneMB)); |
| } |
| |
| // Tests that sharing of external files works and we are able to delete the |
| // files when we need to. |
| TEST_F(DiskCacheBackendTest, FileSharing) { |
| SetDirectMode(); |
| InitCache(); |
| |
| disk_cache::Addr address(0x80000001); |
| ASSERT_TRUE(cache_impl_->CreateExternalFile(&address)); |
| FilePath name = cache_impl_->GetFileName(address); |
| |
| scoped_refptr<disk_cache::File> file(new disk_cache::File(false)); |
| file->Init(name); |
| |
| #if defined(OS_WIN) |
| DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; |
| DWORD access = GENERIC_READ | GENERIC_WRITE; |
| base::win::ScopedHandle file2(CreateFile( |
| name.value().c_str(), access, sharing, NULL, OPEN_EXISTING, 0, NULL)); |
| EXPECT_FALSE(file2.IsValid()); |
| |
| sharing |= FILE_SHARE_DELETE; |
| file2.Set(CreateFile(name.value().c_str(), access, sharing, NULL, |
| OPEN_EXISTING, 0, NULL)); |
| EXPECT_TRUE(file2.IsValid()); |
| #endif |
| |
| EXPECT_TRUE(file_util::Delete(name, false)); |
| |
| // We should be able to use the file. |
| const int kSize = 200; |
| char buffer1[kSize]; |
| char buffer2[kSize]; |
| memset(buffer1, 't', kSize); |
| memset(buffer2, 0, kSize); |
| EXPECT_TRUE(file->Write(buffer1, kSize, 0)); |
| EXPECT_TRUE(file->Read(buffer2, kSize, 0)); |
| EXPECT_EQ(0, memcmp(buffer1, buffer2, kSize)); |
| |
| EXPECT_TRUE(disk_cache::DeleteCacheFile(name)); |
| } |