// Copyright (c) 2012 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 "net/http/infinite_cache.h" #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/stringprintf.h" #include "base/threading/platform_thread.h" #include "base/time.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/http/http_transaction_unittest.h" #include "net/http/http_util.h" #include "testing/gtest/include/gtest/gtest.h" using base::Time; using base::TimeDelta; using net::InfiniteCache; using net::InfiniteCacheTransaction; namespace { void StartRequest(const MockTransaction& http_transaction, InfiniteCacheTransaction* transaction) { std::string standard_headers(http_transaction.status); standard_headers.push_back('\n'); standard_headers.append(http_transaction.response_headers); std::string raw_headers = net::HttpUtil::AssembleRawHeaders(standard_headers.c_str(), standard_headers.size()); scoped_refptr headers( new net::HttpResponseHeaders(raw_headers)); net::HttpResponseInfo response; response.headers = headers; response.request_time = http_transaction.request_time.is_null() ? Time::Now() : http_transaction.request_time; response.response_time = http_transaction.response_time.is_null() ? Time::Now() : http_transaction.response_time; MockHttpRequest request(http_transaction); transaction->OnRequestStart(&request); transaction->OnResponseReceived(&response); } void ProcessRequest(const MockTransaction& http_transaction, InfiniteCache* cache) { scoped_ptr transaction (cache->CreateInfiniteCacheTransaction()); StartRequest(http_transaction, transaction.get()); transaction->OnDataRead(http_transaction.data, strlen(http_transaction.data)); } void ProcessRequestWithTime(const MockTransaction& http_transaction, InfiniteCache* cache, Time time) { scoped_ptr transaction (cache->CreateInfiniteCacheTransaction()); MockTransaction timed_transaction = http_transaction; timed_transaction.request_time = time; timed_transaction.response_time = time; StartRequest(timed_transaction, transaction.get()); transaction->OnDataRead(http_transaction.data, strlen(http_transaction.data)); } } // namespace TEST(InfiniteCache, Basics) { InfiniteCache cache; cache.Init(base::FilePath()); scoped_ptr transaction (cache.CreateInfiniteCacheTransaction()); // Don't even Start() this transaction. transaction.reset(cache.CreateInfiniteCacheTransaction()); net::TestCompletionCallback cb; EXPECT_EQ(0, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); MockHttpRequest request(kTypicalGET_Transaction); transaction->OnRequestStart(&request); // Don't have a response yet. transaction.reset(cache.CreateInfiniteCacheTransaction()); EXPECT_EQ(0, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); net::HttpResponseInfo response; scoped_refptr headers( new net::HttpResponseHeaders(kTypicalGET_Transaction.response_headers)); response.headers = headers; transaction->OnRequestStart(&request); transaction->OnResponseReceived(&response); transaction.reset(cache.CreateInfiniteCacheTransaction()); EXPECT_EQ(1, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); // Hit the same URL again. transaction->OnRequestStart(&request); transaction->OnResponseReceived(&response); transaction.reset(cache.CreateInfiniteCacheTransaction()); EXPECT_EQ(1, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); // Now a different URL. MockHttpRequest request2(kSimpleGET_Transaction); transaction->OnRequestStart(&request2); transaction->OnResponseReceived(&response); transaction.reset(); EXPECT_EQ(2, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); } TEST(InfiniteCache, Save_Restore) { base::ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); base::FilePath path = dir.path().Append(FILE_PATH_LITERAL("infinite")); scoped_ptr cache(new InfiniteCache); cache->Init(path); net::TestCompletionCallback cb; ProcessRequest(kTypicalGET_Transaction, cache.get()); ProcessRequest(kSimpleGET_Transaction, cache.get()); ProcessRequest(kETagGET_Transaction, cache.get()); ProcessRequest(kSimplePOST_Transaction, cache.get()); EXPECT_EQ(3, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_EQ(net::OK, cb.GetResult(cache->FlushDataForTest(cb.callback()))); cache.reset(new InfiniteCache); cache->Init(path); EXPECT_EQ(3, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); ProcessRequest(kTypicalGET_Transaction, cache.get()); EXPECT_EQ(3, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_EQ(net::OK, cb.GetResult(cache->FlushDataForTest(cb.callback()))); } TEST(InfiniteCache, DoomMethod) { InfiniteCache cache; cache.Init(base::FilePath()); ProcessRequest(kTypicalGET_Transaction, &cache); ProcessRequest(kSimpleGET_Transaction, &cache); ProcessRequest(kETagGET_Transaction, &cache); net::TestCompletionCallback cb; EXPECT_EQ(3, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); MockTransaction request(kTypicalGET_Transaction); request.method = "PUT"; ProcessRequest(request, &cache); EXPECT_EQ(2, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); request.method = "POST"; request.url = kSimpleGET_Transaction.url; ProcessRequest(request, &cache); EXPECT_EQ(1, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); request.method = "DELETE"; request.url = kETagGET_Transaction.url; ProcessRequest(request, &cache); EXPECT_EQ(0, cb.GetResult(cache.QueryItemsForTest(cb.callback()))); } TEST(InfiniteCache, Delete) { base::ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); base::FilePath path = dir.path().Append(FILE_PATH_LITERAL("infinite")); scoped_ptr cache(new InfiniteCache); cache->Init(path); net::TestCompletionCallback cb; ProcessRequest(kTypicalGET_Transaction, cache.get()); ProcessRequest(kSimpleGET_Transaction, cache.get()); EXPECT_EQ(2, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_EQ(net::OK, cb.GetResult(cache->FlushDataForTest(cb.callback()))); EXPECT_TRUE(file_util::PathExists(path)); cache.reset(new InfiniteCache); cache->Init(path); EXPECT_EQ(2, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_EQ(net::OK, cb.GetResult(cache->DeleteData(cb.callback()))); EXPECT_EQ(0, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_FALSE(file_util::PathExists(path)); EXPECT_EQ(net::OK, cb.GetResult(cache->FlushDataForTest(cb.callback()))); } TEST(InfiniteCache, DeleteBetween) { #if !defined(OS_ANDROID) base::ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); base::FilePath path = dir.path().Append(FILE_PATH_LITERAL("infinite")); scoped_ptr cache(new InfiniteCache); cache->Init(path); net::TestCompletionCallback cb; Time::Exploded baseline = {}; baseline.year = 2012; baseline.month = 1; baseline.day_of_month = 1; Time base_time = Time::FromUTCExploded(baseline); ProcessRequestWithTime(kTypicalGET_Transaction, cache.get(), base_time); Time start = base_time + TimeDelta::FromSeconds(2); ProcessRequestWithTime(kSimpleGET_Transaction, cache.get(), start); Time end = start + TimeDelta::FromSeconds(2); ProcessRequestWithTime(kETagGET_Transaction, cache.get(), end + TimeDelta::FromSeconds(2)); EXPECT_EQ(3, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_EQ(net::OK, cb.GetResult(cache->DeleteDataBetween(start, end, cb.callback()))); EXPECT_EQ(2, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); // Make sure the data is deleted from disk. EXPECT_EQ(net::OK, cb.GetResult(cache->FlushDataForTest(cb.callback()))); cache.reset(new InfiniteCache); cache->Init(path); EXPECT_EQ(2, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); ProcessRequestWithTime(kETagGET_Transaction, cache.get(), end + TimeDelta::FromMinutes(5)); EXPECT_EQ(2, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_EQ(net::OK, cb.GetResult(cache->DeleteDataBetween(start, Time::Now(), cb.callback()))); EXPECT_EQ(1, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); // Make sure the data is deleted from disk. EXPECT_EQ(net::OK, cb.GetResult(cache->FlushDataForTest(cb.callback()))); cache.reset(new InfiniteCache); cache->Init(path); EXPECT_EQ(1, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); ProcessRequest(kTypicalGET_Transaction, cache.get()); EXPECT_EQ(1, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); #endif // OS_ANDROID } #if 0 // This test is too slow for the bots. TEST(InfiniteCache, FillUp) { base::ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); base::FilePath path = dir.path().Append(FILE_PATH_LITERAL("infinite")); scoped_ptr cache(new InfiniteCache); cache->Init(path); net::TestCompletionCallback cb; const int kNumEntries = 25000; for (int i = 0; i < kNumEntries; i++) { std::string url = StringPrintf("http://foo.com/%d/foo.html", i); MockTransaction transaction = kTypicalGET_Transaction; transaction.url = url.c_str(); ProcessRequest(transaction, cache.get()); } EXPECT_EQ(kNumEntries, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); EXPECT_EQ(net::OK, cb.GetResult(cache->FlushDataForTest(cb.callback()))); cache.reset(new InfiniteCache); cache->Init(path); EXPECT_EQ(kNumEntries, cb.GetResult(cache->QueryItemsForTest(cb.callback()))); } #endif