| // Copyright 2014 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 "content/browser/cache_storage/cache_storage_manager.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <list> |
| #include <set> |
| #include <utility> |
| |
| #include "base/files/file_enumerator.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/guid.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/sha1.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/browser/blob_storage/chrome_blob_storage_context.h" |
| #include "content/browser/cache_storage/cache_storage.h" |
| #include "content/browser/cache_storage/cache_storage.pb.h" |
| #include "content/browser/cache_storage/cache_storage_cache_handle.h" |
| #include "content/browser/cache_storage/cache_storage_index.h" |
| #include "content/browser/cache_storage/cache_storage_quota_client.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/cache_storage_usage_info.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "net/url_request/url_request_job_factory_impl.h" |
| #include "storage/browser/blob/blob_data_builder.h" |
| #include "storage/browser/blob/blob_data_handle.h" |
| #include "storage/browser/blob/blob_storage_context.h" |
| #include "storage/browser/blob/blob_url_request_job_factory.h" |
| #include "storage/browser/quota/quota_manager_proxy.h" |
| #include "storage/browser/test/mock_quota_manager_proxy.h" |
| #include "storage/browser/test/mock_special_storage_policy.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| bool IsIndexFileCurrent(const base::FilePath& cache_dir) { |
| base::File::Info info; |
| const base::FilePath index_path = |
| cache_dir.AppendASCII(CacheStorage::kIndexFileName); |
| if (!GetFileInfo(index_path, &info)) |
| return false; |
| base::Time index_last_modified = info.last_modified; |
| |
| base::FileEnumerator enumerator(cache_dir, false, |
| base::FileEnumerator::DIRECTORIES); |
| for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); |
| file_path = enumerator.Next()) { |
| if (!GetFileInfo(file_path, &info)) |
| return false; |
| if (index_last_modified < info.last_modified) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void CopyCacheStorageIndex(CacheStorageIndex* dest, |
| const CacheStorageIndex& src) { |
| DCHECK_EQ(0U, dest->num_entries()); |
| for (const auto& cache_metadata : src.ordered_cache_metadata()) |
| dest->Insert(cache_metadata); |
| } |
| |
| } // anonymous namespace |
| |
| // Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns |
| // the memory. |
| std::unique_ptr<storage::BlobProtocolHandler> CreateMockBlobProtocolHandler( |
| storage::BlobStorageContext* blob_storage_context) { |
| return base::WrapUnique( |
| new storage::BlobProtocolHandler(blob_storage_context, nullptr)); |
| } |
| |
| class CacheStorageManagerTest : public testing::Test { |
| public: |
| CacheStorageManagerTest() |
| : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), |
| blob_storage_context_(nullptr), |
| callback_bool_(false), |
| callback_error_(CACHE_STORAGE_OK), |
| origin1_("http://example1.com"), |
| origin2_("http://example2.com") {} |
| |
| void SetUp() override { |
| base::FilePath temp_dir_path; |
| if (!MemoryOnly()) |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| |
| CreateStorageManager(); |
| } |
| |
| void TearDown() override { DestroyStorageManager(); } |
| |
| virtual bool MemoryOnly() { return false; } |
| |
| void BoolCallback(base::RunLoop* run_loop, bool value) { |
| callback_bool_ = value; |
| run_loop->Quit(); |
| } |
| |
| void BoolAndErrorCallback(base::RunLoop* run_loop, |
| bool value, |
| CacheStorageError error) { |
| callback_bool_ = value; |
| callback_error_ = error; |
| run_loop->Quit(); |
| } |
| |
| void CacheAndErrorCallback( |
| base::RunLoop* run_loop, |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle, |
| CacheStorageError error) { |
| callback_cache_handle_ = std::move(cache_handle); |
| callback_error_ = error; |
| run_loop->Quit(); |
| } |
| |
| void CacheMetadataCallback(base::RunLoop* run_loop, |
| const CacheStorageIndex& cache_index) { |
| callback_cache_index_ = CacheStorageIndex(); |
| CopyCacheStorageIndex(&callback_cache_index_, cache_index); |
| run_loop->Quit(); |
| } |
| |
| const std::string& GetFirstIndexName() const { |
| return callback_cache_index_.ordered_cache_metadata().front().name; |
| } |
| |
| std::vector<std::string> GetIndexNames() const { |
| std::vector<std::string> cache_names; |
| for (const auto& metadata : callback_cache_index_.ordered_cache_metadata()) |
| cache_names.push_back(metadata.name); |
| return cache_names; |
| } |
| |
| void CachePutCallback(base::RunLoop* run_loop, CacheStorageError error) { |
| callback_error_ = error; |
| run_loop->Quit(); |
| } |
| |
| void CacheMatchCallback( |
| base::RunLoop* run_loop, |
| CacheStorageError error, |
| std::unique_ptr<ServiceWorkerResponse> response, |
| std::unique_ptr<storage::BlobDataHandle> blob_data_handle) { |
| callback_error_ = error; |
| callback_cache_handle_response_ = std::move(response); |
| callback_data_handle_ = std::move(blob_data_handle); |
| run_loop->Quit(); |
| } |
| |
| void CreateStorageManager() { |
| ChromeBlobStorageContext* blob_storage_context( |
| ChromeBlobStorageContext::GetFor(&browser_context_)); |
| // Wait for ChromeBlobStorageContext to finish initializing. |
| base::RunLoop().RunUntilIdle(); |
| |
| blob_storage_context_ = blob_storage_context->context(); |
| |
| url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl); |
| url_request_job_factory_->SetProtocolHandler( |
| "blob", CreateMockBlobProtocolHandler(blob_storage_context->context())); |
| |
| net::URLRequestContext* url_request_context = |
| BrowserContext::GetDefaultStoragePartition(&browser_context_)-> |
| GetURLRequestContext()->GetURLRequestContext(); |
| |
| url_request_context->set_job_factory(url_request_job_factory_.get()); |
| |
| base::FilePath temp_dir_path; |
| if (!MemoryOnly()) |
| temp_dir_path = temp_dir_.GetPath(); |
| |
| quota_policy_ = new MockSpecialStoragePolicy; |
| mock_quota_manager_ = new MockQuotaManager( |
| MemoryOnly(), temp_dir_path, base::ThreadTaskRunnerHandle::Get().get(), |
| quota_policy_.get()); |
| mock_quota_manager_->SetQuota( |
| GURL(origin1_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); |
| mock_quota_manager_->SetQuota( |
| GURL(origin2_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); |
| |
| quota_manager_proxy_ = new MockQuotaManagerProxy( |
| mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); |
| |
| cache_manager_ = CacheStorageManager::Create( |
| temp_dir_path, base::ThreadTaskRunnerHandle::Get(), |
| quota_manager_proxy_); |
| |
| cache_manager_->SetBlobParametersForCache( |
| BrowserContext::GetDefaultStoragePartition(&browser_context_) |
| ->GetURLRequestContext(), |
| blob_storage_context->context()->AsWeakPtr()); |
| } |
| |
| bool FlushCacheStorageIndex(const GURL& origin) { |
| callback_bool_ = false; |
| base::RunLoop loop; |
| bool write_was_scheduled = |
| CacheStorageForOrigin(origin)->InitiateScheduledIndexWriteForTest( |
| base::BindOnce(&CacheStorageManagerTest::BoolCallback, |
| base::Unretained(this), &loop)); |
| loop.Run(); |
| DCHECK(callback_bool_); |
| return write_was_scheduled; |
| } |
| |
| void DestroyStorageManager() { |
| if (quota_manager_proxy_) |
| quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |
| base::RunLoop().RunUntilIdle(); |
| quota_manager_proxy_ = nullptr; |
| |
| url_request_job_factory_.reset(); |
| blob_storage_context_ = nullptr; |
| |
| quota_policy_ = nullptr; |
| mock_quota_manager_ = nullptr; |
| |
| callback_cache_handle_ = nullptr; |
| callback_bool_ = false; |
| callback_cache_handle_response_ = nullptr; |
| callback_data_handle_ = nullptr; |
| callback_cache_index_ = CacheStorageIndex(); |
| callback_all_origins_usage_.clear(); |
| |
| cache_manager_ = nullptr; |
| } |
| |
| bool Open(const GURL& origin, const std::string& cache_name) { |
| base::RunLoop loop; |
| cache_manager_->OpenCache( |
| origin, cache_name, |
| base::BindOnce(&CacheStorageManagerTest::CacheAndErrorCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| |
| bool error = callback_error_ != CACHE_STORAGE_OK; |
| if (error) |
| EXPECT_FALSE(callback_cache_handle_); |
| else |
| EXPECT_TRUE(callback_cache_handle_); |
| return !error; |
| } |
| |
| bool Has(const GURL& origin, const std::string& cache_name) { |
| base::RunLoop loop; |
| cache_manager_->HasCache( |
| origin, cache_name, |
| base::BindOnce(&CacheStorageManagerTest::BoolAndErrorCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| |
| return callback_bool_; |
| } |
| |
| bool Delete(const GURL& origin, const std::string& cache_name) { |
| base::RunLoop loop; |
| cache_manager_->DeleteCache( |
| origin, cache_name, |
| base::BindOnce(&CacheStorageManagerTest::BoolAndErrorCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| |
| return callback_bool_; |
| } |
| |
| size_t Keys(const GURL& origin) { |
| base::RunLoop loop; |
| cache_manager_->EnumerateCaches( |
| origin, |
| base::BindOnce(&CacheStorageManagerTest::CacheMetadataCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| return callback_cache_index_.num_entries(); |
| } |
| |
| bool StorageMatch(const GURL& origin, |
| const std::string& cache_name, |
| const GURL& url, |
| const CacheStorageCacheQueryParams& match_params = |
| CacheStorageCacheQueryParams()) { |
| ServiceWorkerFetchRequest request; |
| request.url = url; |
| return StorageMatchWithRequest(origin, cache_name, request, match_params); |
| } |
| |
| bool StorageMatchWithRequest( |
| const GURL& origin, |
| const std::string& cache_name, |
| const ServiceWorkerFetchRequest& request, |
| const CacheStorageCacheQueryParams& match_params = |
| CacheStorageCacheQueryParams()) { |
| std::unique_ptr<ServiceWorkerFetchRequest> unique_request = |
| base::MakeUnique<ServiceWorkerFetchRequest>(request); |
| |
| base::RunLoop loop; |
| cache_manager_->MatchCache( |
| origin, cache_name, std::move(unique_request), match_params, |
| base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| |
| return callback_error_ == CACHE_STORAGE_OK; |
| } |
| |
| bool StorageMatchAll(const GURL& origin, |
| const GURL& url, |
| const CacheStorageCacheQueryParams& match_params = |
| CacheStorageCacheQueryParams()) { |
| ServiceWorkerFetchRequest request; |
| request.url = url; |
| return StorageMatchAllWithRequest(origin, request, match_params); |
| } |
| |
| bool StorageMatchAllWithRequest( |
| const GURL& origin, |
| const ServiceWorkerFetchRequest& request, |
| const CacheStorageCacheQueryParams& match_params = |
| CacheStorageCacheQueryParams()) { |
| std::unique_ptr<ServiceWorkerFetchRequest> unique_request = |
| base::MakeUnique<ServiceWorkerFetchRequest>(request); |
| base::RunLoop loop; |
| cache_manager_->MatchAllCaches( |
| origin, std::move(unique_request), match_params, |
| base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| |
| return callback_error_ == CACHE_STORAGE_OK; |
| } |
| |
| bool CachePut(CacheStorageCache* cache, const GURL& url) { |
| ServiceWorkerFetchRequest request; |
| request.url = url; |
| |
| return CachePutWithStatusCode(cache, request, 200); |
| } |
| |
| bool CachePutWithRequestAndHeaders( |
| CacheStorageCache* cache, |
| const ServiceWorkerFetchRequest& request, |
| const ServiceWorkerHeaderMap& response_headers) { |
| return CachePutWithStatusCode(cache, request, 200, response_headers); |
| } |
| |
| bool CachePutWithStatusCode(CacheStorageCache* cache, |
| const ServiceWorkerFetchRequest& request, |
| int status_code, |
| const ServiceWorkerHeaderMap& response_headers = |
| ServiceWorkerHeaderMap()) { |
| std::unique_ptr<storage::BlobDataBuilder> blob_data( |
| new storage::BlobDataBuilder(base::GenerateGUID())); |
| blob_data->AppendData(request.url.spec()); |
| |
| std::unique_ptr<storage::BlobDataHandle> blob_handle = |
| blob_storage_context_->AddFinishedBlob(blob_data.get()); |
| std::unique_ptr<std::vector<GURL>> url_list = |
| base::MakeUnique<std::vector<GURL>>(); |
| url_list->push_back(request.url); |
| ServiceWorkerResponse response( |
| std::move(url_list), status_code, "OK", |
| blink::mojom::FetchResponseType::kDefault, |
| base::MakeUnique<ServiceWorkerHeaderMap>(response_headers), |
| blob_handle->uuid(), request.url.spec().size(), |
| blink::kWebServiceWorkerResponseErrorUnknown, base::Time(), |
| false /* is_in_cache_storage */, |
| std::string() /* cache_storage_cache_name */, |
| base::MakeUnique< |
| ServiceWorkerHeaderList>() /* cors_exposed_header_names */); |
| |
| CacheStorageBatchOperation operation; |
| operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; |
| operation.request = request; |
| operation.response = response; |
| |
| base::RunLoop loop; |
| cache->BatchOperation( |
| std::vector<CacheStorageBatchOperation>(1, operation), |
| base::BindOnce(&CacheStorageManagerTest::CachePutCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| |
| return callback_error_ == CACHE_STORAGE_OK; |
| } |
| |
| bool CacheMatch(CacheStorageCache* cache, const GURL& url) { |
| std::unique_ptr<ServiceWorkerFetchRequest> request( |
| new ServiceWorkerFetchRequest()); |
| request->url = url; |
| base::RunLoop loop; |
| cache->Match( |
| std::move(request), CacheStorageCacheQueryParams(), |
| base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| |
| return callback_error_ == CACHE_STORAGE_OK; |
| } |
| |
| CacheStorage* CacheStorageForOrigin(const GURL& origin) { |
| return cache_manager_->FindOrCreateCacheStorage(origin); |
| } |
| |
| int64_t GetOriginUsage(const GURL& origin) { |
| base::RunLoop loop; |
| cache_manager_->GetOriginUsage( |
| origin, base::Bind(&CacheStorageManagerTest::UsageCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| return callback_usage_; |
| } |
| |
| void UsageCallback(base::RunLoop* run_loop, int64_t usage) { |
| callback_usage_ = usage; |
| run_loop->Quit(); |
| } |
| |
| std::vector<CacheStorageUsageInfo> GetAllOriginsUsage() { |
| base::RunLoop loop; |
| cache_manager_->GetAllOriginsUsage( |
| base::Bind(&CacheStorageManagerTest::AllOriginsUsageCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| return callback_all_origins_usage_; |
| } |
| |
| void AllOriginsUsageCallback( |
| base::RunLoop* run_loop, |
| const std::vector<CacheStorageUsageInfo>& usage) { |
| callback_all_origins_usage_ = usage; |
| run_loop->Quit(); |
| } |
| |
| int64_t GetSizeThenCloseAllCaches(const GURL& origin) { |
| base::RunLoop loop; |
| CacheStorage* cache_storage = CacheStorageForOrigin(origin); |
| cache_storage->GetSizeThenCloseAllCaches( |
| base::BindOnce(&CacheStorageManagerTest::UsageCallback, |
| base::Unretained(this), &loop)); |
| loop.Run(); |
| return callback_usage_; |
| } |
| |
| int64_t Size(const GURL& origin) { |
| base::RunLoop loop; |
| CacheStorage* cache_storage = CacheStorageForOrigin(origin); |
| cache_storage->Size(base::BindOnce(&CacheStorageManagerTest::UsageCallback, |
| base::Unretained(this), &loop)); |
| loop.Run(); |
| return callback_usage_; |
| } |
| |
| protected: |
| // Temporary directory must be allocated first so as to be destroyed last. |
| base::ScopedTempDir temp_dir_; |
| |
| TestBrowserThreadBundle browser_thread_bundle_; |
| TestBrowserContext browser_context_; |
| std::unique_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_; |
| storage::BlobStorageContext* blob_storage_context_; |
| |
| scoped_refptr<MockSpecialStoragePolicy> quota_policy_; |
| scoped_refptr<MockQuotaManager> mock_quota_manager_; |
| scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_; |
| std::unique_ptr<CacheStorageManager> cache_manager_; |
| |
| std::unique_ptr<CacheStorageCacheHandle> callback_cache_handle_; |
| int callback_bool_; |
| CacheStorageError callback_error_; |
| std::unique_ptr<ServiceWorkerResponse> callback_cache_handle_response_; |
| std::unique_ptr<storage::BlobDataHandle> callback_data_handle_; |
| CacheStorageIndex callback_cache_index_; |
| |
| const GURL origin1_; |
| const GURL origin2_; |
| |
| int64_t callback_usage_; |
| std::vector<CacheStorageUsageInfo> callback_all_origins_usage_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(CacheStorageManagerTest); |
| }; |
| |
| class CacheStorageManagerMemoryOnlyTest : public CacheStorageManagerTest { |
| public: |
| bool MemoryOnly() override { return true; } |
| }; |
| |
| class CacheStorageManagerTestP : public CacheStorageManagerTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| bool MemoryOnly() override { return !GetParam(); } |
| }; |
| |
| TEST_F(CacheStorageManagerTest, TestsRunOnIOThread) { |
| EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, OpenCache) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, OpenTwoCaches) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, CachePointersDiffer) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle = |
| std::move(callback_cache_handle_); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_NE(callback_cache_handle_->value(), cache_handle->value()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, Open2CachesSameNameDiffOrigins) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle = |
| std::move(callback_cache_handle_); |
| EXPECT_TRUE(Open(origin2_, "foo")); |
| EXPECT_NE(cache_handle->value(), callback_cache_handle_->value()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, OpenExistingCache) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle = |
| std::move(callback_cache_handle_); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_EQ(callback_cache_handle_->value(), cache_handle->value()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, HasCache) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Has(origin1_, "foo")); |
| EXPECT_TRUE(callback_bool_); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, HasNonExistent) { |
| EXPECT_FALSE(Has(origin1_, "foo")); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DeleteCache) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| EXPECT_FALSE(Has(origin1_, "foo")); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DeleteTwice) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| EXPECT_FALSE(Delete(origin1_, "foo")); |
| EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DeleteCacheReducesOriginSize) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| // The quota manager gets updated after the put operation runs its callback so |
| // run the event loop. |
| base::RunLoop().RunUntilIdle(); |
| int64_t put_delta = quota_manager_proxy_->last_notified_delta(); |
| EXPECT_LT(0, put_delta); |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| |
| // Drop the cache handle so that the cache can be erased from disk. |
| callback_cache_handle_ = nullptr; |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(-1 * quota_manager_proxy_->last_notified_delta(), put_delta); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, EmptyKeys) { |
| EXPECT_EQ(0u, Keys(origin1_)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, SomeKeys) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(Open(origin2_, "baz")); |
| EXPECT_EQ(2u, Keys(origin1_)); |
| std::vector<std::string> expected_keys; |
| expected_keys.push_back("foo"); |
| expected_keys.push_back("bar"); |
| EXPECT_EQ(expected_keys, GetIndexNames()); |
| EXPECT_EQ(1u, Keys(origin2_)); |
| EXPECT_STREQ("baz", GetFirstIndexName().c_str()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DeletedKeysGone) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(Open(origin2_, "baz")); |
| EXPECT_TRUE(Delete(origin1_, "bar")); |
| EXPECT_EQ(1u, Keys(origin1_)); |
| EXPECT_STREQ("foo", GetFirstIndexName().c_str()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchEntryExists) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_TRUE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"))); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchNoEntry) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/bar"))); |
| EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchNoCache) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_FALSE(StorageMatch(origin1_, "bar", GURL("http://example.com/foo"))); |
| EXPECT_EQ(CACHE_STORAGE_ERROR_CACHE_NAME_NOT_FOUND, callback_error_); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchAllEntryExists) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_TRUE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchAllNoEntry) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_FALSE(StorageMatchAll(origin1_, GURL("http://example.com/bar"))); |
| EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchAllNoCaches) { |
| EXPECT_FALSE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); |
| EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); |
| } |
| |
| TEST_F(CacheStorageManagerTest, StorageReuseCacheName) { |
| // Deleting a cache and creating one with the same name and adding an entry |
| // with the same URL should work. (see crbug.com/542668) |
| const GURL kTestURL = GURL("http://example.com/foo"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), kTestURL)); |
| EXPECT_TRUE(CacheMatch(callback_cache_handle_->value(), kTestURL)); |
| std::unique_ptr<storage::BlobDataHandle> data_handle = |
| std::move(callback_data_handle_); |
| |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| // The cache is deleted but the handle to one of its entries is still |
| // open. Creating a new cache in the same directory would fail on Windows. |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), kTestURL)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DropRefAfterNewCacheWithSameNameCreated) { |
| // Make sure that dropping the final cache handle to a doomed cache doesn't |
| // affect newer caches with the same name. (see crbug.com/631467) |
| |
| // 1. Create cache A and hang onto the handle |
| const GURL kTestURL = GURL("http://example.com/foo"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle = |
| std::move(callback_cache_handle_); |
| |
| // 2. Doom the cache |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| |
| // 3. Create cache B (with the same name) |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| |
| // 4. Drop handle to A |
| cache_handle.reset(); |
| |
| // 5. Verify that B still works |
| EXPECT_FALSE(CacheMatch(callback_cache_handle_->value(), kTestURL)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DeleteCorrectDirectory) { |
| // This test reproduces crbug.com/630036. |
| // 1. Cache A with name "foo" is created |
| const GURL kTestURL = GURL("http://example.com/foo"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle = |
| std::move(callback_cache_handle_); |
| |
| // 2. Cache A is doomed, but js hangs onto the handle. |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| |
| // 3. Cache B with name "foo" is created |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| |
| // 4. Cache B is doomed, and both handles are reset. |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| cache_handle.reset(); |
| callback_cache_handle_.reset(); |
| |
| // Do some busy work on a different cache to move the cache pool threads |
| // along and trigger the bug. |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), kTestURL)); |
| EXPECT_TRUE(CacheMatch(callback_cache_handle_->value(), kTestURL)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchAllEntryExistsTwice) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| ServiceWorkerFetchRequest request; |
| request.url = GURL("http://example.com/foo"); |
| EXPECT_TRUE( |
| CachePutWithStatusCode(callback_cache_handle_->value(), request, 200)); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE( |
| CachePutWithStatusCode(callback_cache_handle_->value(), request, 201)); |
| |
| EXPECT_TRUE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); |
| |
| // The caches need to be searched in order of creation, so verify that the |
| // response came from the first cache. |
| EXPECT_EQ(200, callback_cache_handle_response_->status_code); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchInOneOfMany) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_TRUE(Open(origin1_, "baz")); |
| |
| EXPECT_TRUE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, Chinese) { |
| EXPECT_TRUE(Open(origin1_, "ä½ å¥½")); |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle = |
| std::move(callback_cache_handle_); |
| EXPECT_TRUE(Open(origin1_, "ä½ å¥½")); |
| EXPECT_EQ(callback_cache_handle_->value(), cache_handle->value()); |
| EXPECT_EQ(1u, Keys(origin1_)); |
| EXPECT_STREQ("ä½ å¥½", GetFirstIndexName().c_str()); |
| } |
| |
| TEST_F(CacheStorageManagerTest, EmptyKey) { |
| EXPECT_TRUE(Open(origin1_, "")); |
| std::unique_ptr<CacheStorageCacheHandle> cache_handle = |
| std::move(callback_cache_handle_); |
| EXPECT_TRUE(Open(origin1_, "")); |
| EXPECT_EQ(cache_handle->value(), callback_cache_handle_->value()); |
| EXPECT_EQ(1u, Keys(origin1_)); |
| EXPECT_STREQ("", GetFirstIndexName().c_str()); |
| EXPECT_TRUE(Has(origin1_, "")); |
| EXPECT_TRUE(Delete(origin1_, "")); |
| EXPECT_EQ(0u, Keys(origin1_)); |
| } |
| |
| TEST_F(CacheStorageManagerTest, DataPersists) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(Open(origin1_, "baz")); |
| EXPECT_TRUE(Open(origin2_, "raz")); |
| EXPECT_TRUE(Delete(origin1_, "bar")); |
| quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |
| cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); |
| EXPECT_EQ(2u, Keys(origin1_)); |
| std::vector<std::string> expected_keys; |
| expected_keys.push_back("foo"); |
| expected_keys.push_back("baz"); |
| EXPECT_EQ(expected_keys, GetIndexNames()); |
| } |
| |
| TEST_F(CacheStorageManagerMemoryOnlyTest, DataLostWhenMemoryOnly) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Open(origin2_, "baz")); |
| quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |
| cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); |
| EXPECT_EQ(0u, Keys(origin1_)); |
| } |
| |
| TEST_F(CacheStorageManagerTest, BadCacheName) { |
| // Since the implementation writes cache names to disk, ensure that we don't |
| // escape the directory. |
| const std::string bad_name = "../../../../../../../../../../../../../../foo"; |
| EXPECT_TRUE(Open(origin1_, bad_name)); |
| EXPECT_EQ(1u, Keys(origin1_)); |
| EXPECT_STREQ(bad_name.c_str(), GetFirstIndexName().c_str()); |
| } |
| |
| TEST_F(CacheStorageManagerTest, BadOriginName) { |
| // Since the implementation writes origin names to disk, ensure that we don't |
| // escape the directory. |
| GURL bad_origin("http://../../../../../../../../../../../../../../foo"); |
| EXPECT_TRUE(Open(bad_origin, "foo")); |
| EXPECT_EQ(1u, Keys(bad_origin)); |
| EXPECT_STREQ("foo", GetFirstIndexName().c_str()); |
| } |
| |
| // With a persistent cache if the client drops its reference to a |
| // CacheStorageCache it should be deleted. |
| TEST_F(CacheStorageManagerTest, DropReference) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| base::WeakPtr<CacheStorageCache> cache = |
| callback_cache_handle_->value()->AsWeakPtr(); |
| // Run a cache operation to ensure that the cache has finished initializing so |
| // that when the handle is dropped it can close immediately. |
| EXPECT_FALSE(CacheMatch(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| |
| callback_cache_handle_ = nullptr; |
| EXPECT_FALSE(cache); |
| } |
| |
| // A cache continues to work so long as there is a handle to it. Only after the |
| // last cache handle is deleted can the cache be freed. |
| TEST_P(CacheStorageManagerTestP, CacheWorksAfterDelete) { |
| const GURL kFooURL("http://example.com/foo"); |
| const GURL kBarURL("http://example.com/bar"); |
| const GURL kBazURL("http://example.com/baz"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| std::unique_ptr<CacheStorageCacheHandle> original_handle = |
| std::move(callback_cache_handle_); |
| EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| |
| // Verify that the existing cache handle still works. |
| EXPECT_TRUE(CacheMatch(original_handle->value(), kFooURL)); |
| EXPECT_TRUE(CachePut(original_handle->value(), kBarURL)); |
| EXPECT_TRUE(CacheMatch(original_handle->value(), kBarURL)); |
| |
| // The cache shouldn't be visible to subsequent storage operations. |
| EXPECT_EQ(0u, Keys(origin1_)); |
| |
| // Open a new cache with the same name, it should create a new cache, but not |
| // interfere with the original cache. |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| std::unique_ptr<CacheStorageCacheHandle> new_handle = |
| std::move(callback_cache_handle_); |
| EXPECT_TRUE(CachePut(new_handle->value(), kBazURL)); |
| |
| EXPECT_FALSE(CacheMatch(new_handle->value(), kFooURL)); |
| EXPECT_FALSE(CacheMatch(new_handle->value(), kBarURL)); |
| EXPECT_TRUE(CacheMatch(new_handle->value(), kBazURL)); |
| |
| EXPECT_TRUE(CacheMatch(original_handle->value(), kFooURL)); |
| EXPECT_TRUE(CacheMatch(original_handle->value(), kBarURL)); |
| EXPECT_FALSE(CacheMatch(original_handle->value(), kBazURL)); |
| } |
| |
| // Deleted caches can still be modified, but all changes will eventually be |
| // thrown away when all references are released. |
| TEST_F(CacheStorageManagerTest, DeletedCacheIgnoredInIndex) { |
| const GURL kFooURL("http://example.com/foo"); |
| const GURL kBarURL("http://example.com/bar"); |
| const GURL kBazURL("http://example.com/baz"); |
| const std::string kCacheName = "foo"; |
| |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| auto original_handle = std::move(callback_cache_handle_); |
| EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); |
| EXPECT_TRUE(Delete(origin1_, kCacheName)); |
| |
| // Now a second cache using the same name, but with different data. |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| auto new_handle = std::move(callback_cache_handle_); |
| EXPECT_TRUE(CachePut(new_handle->value(), kFooURL)); |
| EXPECT_TRUE(CachePut(new_handle->value(), kBarURL)); |
| EXPECT_TRUE(CachePut(new_handle->value(), kBazURL)); |
| auto new_cache_size = Size(origin1_); |
| |
| // Now modify the first cache. |
| EXPECT_TRUE(CachePut(original_handle->value(), kBarURL)); |
| |
| // Now deref both caches, and recreate the storage manager. |
| original_handle = nullptr; |
| new_handle = nullptr; |
| EXPECT_TRUE(FlushCacheStorageIndex(origin1_)); |
| DestroyStorageManager(); |
| CreateStorageManager(); |
| |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| EXPECT_EQ(new_cache_size, Size(origin1_)); |
| } |
| |
| TEST_F(CacheStorageManagerTest, CacheSizeCorrectAfterReopen) { |
| const GURL kFooURL("http://example.com/foo"); |
| const std::string kCacheName = "foo"; |
| |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| auto original_handle = std::move(callback_cache_handle_); |
| EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); |
| auto size_before_close = Size(origin1_); |
| EXPECT_GT(size_before_close, 0); |
| |
| DestroyStorageManager(); |
| CreateStorageManager(); |
| |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| EXPECT_EQ(size_before_close, Size(origin1_)); |
| } |
| |
| // With a memory cache the cache can't be freed from memory until the client |
| // calls delete. |
| TEST_F(CacheStorageManagerMemoryOnlyTest, MemoryLosesReferenceOnlyAfterDelete) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| base::WeakPtr<CacheStorageCache> cache = |
| callback_cache_handle_->value()->AsWeakPtr(); |
| callback_cache_handle_ = nullptr; |
| EXPECT_TRUE(cache); |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(cache); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DeleteBeforeRelease) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Delete(origin1_, "foo")); |
| EXPECT_TRUE(callback_cache_handle_->value()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, OpenRunsSerially) { |
| EXPECT_FALSE(Delete(origin1_, "tmp")); // Init storage. |
| CacheStorage* cache_storage = CacheStorageForOrigin(origin1_); |
| cache_storage->StartAsyncOperationForTesting(); |
| |
| base::RunLoop open_loop; |
| cache_manager_->OpenCache( |
| origin1_, "foo", |
| base::BindOnce(&CacheStorageManagerTest::CacheAndErrorCallback, |
| base::Unretained(this), base::Unretained(&open_loop))); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(callback_cache_handle_); |
| |
| cache_storage->CompleteAsyncOperationForTesting(); |
| open_loop.Run(); |
| EXPECT_TRUE(callback_cache_handle_); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, GetOriginUsage) { |
| EXPECT_EQ(0, GetOriginUsage(origin1_)); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_EQ(0, GetOriginUsage(origin1_)); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| int64_t foo_size = GetOriginUsage(origin1_); |
| EXPECT_LT(0, GetOriginUsage(origin1_)); |
| EXPECT_EQ(0, GetOriginUsage(origin2_)); |
| |
| // Add the same entry into a second cache, the size should double. |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_EQ(2 * foo_size, GetOriginUsage(origin1_)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, GetAllOriginsUsage) { |
| EXPECT_EQ(0ULL, GetAllOriginsUsage().size()); |
| // Put one entry in a cache on origin 1. |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| |
| // Put two entries (of identical size) in a cache on origin 2. |
| EXPECT_TRUE(Open(origin2_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/bar"))); |
| |
| std::vector<CacheStorageUsageInfo> usage = GetAllOriginsUsage(); |
| EXPECT_EQ(2ULL, usage.size()); |
| |
| int origin1_index = usage[0].origin == origin1_ ? 0 : 1; |
| int origin2_index = usage[1].origin == origin2_ ? 1 : 0; |
| EXPECT_NE(origin1_index, origin2_index); |
| |
| int64_t origin1_size = usage[origin1_index].total_size_bytes; |
| int64_t origin2_size = usage[origin2_index].total_size_bytes; |
| EXPECT_EQ(2 * origin1_size, origin2_size); |
| |
| if (MemoryOnly()) { |
| EXPECT_TRUE(usage[origin1_index].last_modified.is_null()); |
| EXPECT_TRUE(usage[origin2_index].last_modified.is_null()); |
| } else { |
| EXPECT_FALSE(usage[origin1_index].last_modified.is_null()); |
| EXPECT_FALSE(usage[origin2_index].last_modified.is_null()); |
| } |
| } |
| |
| TEST_F(CacheStorageManagerTest, GetAllOriginsUsageWithOldIndex) { |
| // Write a single value (V1) to the cache. |
| const GURL kFooURL = origin1_.Resolve("foo"); |
| const std::string kCacheName = "foo"; |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| std::unique_ptr<CacheStorageCacheHandle> original_handle = |
| std::move(callback_cache_handle_); |
| |
| EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); |
| int64_t cache_size_v1 = Size(origin1_); |
| base::FilePath storage_dir = original_handle->value()->path().DirName(); |
| original_handle = nullptr; |
| EXPECT_GE(cache_size_v1, 0); |
| |
| // Close the caches and cache manager. |
| EXPECT_TRUE(FlushCacheStorageIndex(origin1_)); |
| DestroyStorageManager(); |
| |
| // Save a copy of the V1 index. |
| EXPECT_TRUE(IsIndexFileCurrent(storage_dir)); |
| base::FilePath index_path = storage_dir.AppendASCII("index.txt"); |
| EXPECT_TRUE(base::PathExists(index_path)); |
| base::FilePath backup_index_path = storage_dir.AppendASCII("index.txt.bak"); |
| EXPECT_TRUE(base::CopyFile(index_path, backup_index_path)); |
| |
| // Create a new CacheStorageManager that hasn't yet loaded the origin. |
| CreateStorageManager(); |
| quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |
| cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); |
| |
| // Create a second value (V2) in the cache. |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| original_handle = std::move(callback_cache_handle_); |
| const GURL kBarURL = origin1_.Resolve("bar"); |
| EXPECT_TRUE(CachePut(original_handle->value(), kBarURL)); |
| original_handle = nullptr; |
| |
| std::vector<CacheStorageUsageInfo> usage = GetAllOriginsUsage(); |
| ASSERT_EQ(1ULL, usage.size()); |
| int64_t usage_before_close = usage[0].total_size_bytes; |
| EXPECT_GT(usage_before_close, 0); |
| |
| // Close the caches and cache manager. |
| DestroyStorageManager(); |
| |
| // Restore the index to the V1 state. Make the access/mod times of index file |
| // older than the other directories in the store to trigger size |
| // recalculation. |
| EXPECT_TRUE(base::CopyFile(backup_index_path, index_path)); |
| base::Time t = base::Time::Now() - base::TimeDelta::FromHours(1); |
| EXPECT_TRUE(base::TouchFile(index_path, t, t)); |
| EXPECT_FALSE(IsIndexFileCurrent(storage_dir)); |
| |
| CreateStorageManager(); |
| usage = GetAllOriginsUsage(); |
| ASSERT_EQ(1ULL, usage.size()); |
| |
| EXPECT_EQ(usage_before_close, usage[0].total_size_bytes); |
| |
| EXPECT_FALSE(usage[0].last_modified.is_null()); |
| } |
| |
| TEST_F(CacheStorageManagerTest, GetOriginSizeWithOldIndex) { |
| // Write a single value (V1) to the cache. |
| const GURL kFooURL = origin1_.Resolve("foo"); |
| const std::string kCacheName = "foo"; |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| std::unique_ptr<CacheStorageCacheHandle> original_handle = |
| std::move(callback_cache_handle_); |
| |
| EXPECT_TRUE(CachePut(original_handle->value(), kFooURL)); |
| int64_t cache_size_v1 = Size(origin1_); |
| base::FilePath storage_dir = original_handle->value()->path().DirName(); |
| original_handle = nullptr; |
| EXPECT_GE(cache_size_v1, 0); |
| |
| // Close the caches and cache manager. |
| EXPECT_TRUE(FlushCacheStorageIndex(origin1_)); |
| DestroyStorageManager(); |
| |
| // Save a copy of the V1 index. |
| EXPECT_TRUE(IsIndexFileCurrent(storage_dir)); |
| base::FilePath index_path = storage_dir.AppendASCII("index.txt"); |
| EXPECT_TRUE(base::PathExists(index_path)); |
| base::FilePath backup_index_path = storage_dir.AppendASCII("index.txt.bak"); |
| EXPECT_TRUE(base::CopyFile(index_path, backup_index_path)); |
| |
| // Create a new CacheStorageManager that hasn't yet loaded the origin. |
| CreateStorageManager(); |
| quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |
| cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); |
| |
| // Reopen the cache and write a second value (V2). |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| original_handle = std::move(callback_cache_handle_); |
| const GURL kBarURL = origin1_.Resolve("bar"); |
| EXPECT_TRUE(CachePut(original_handle->value(), kBarURL)); |
| original_handle = nullptr; |
| int64_t cache_size_v2 = Size(origin1_); |
| EXPECT_GE(cache_size_v2, 0); |
| |
| // Close the caches and cache manager. |
| DestroyStorageManager(); |
| |
| // Restore the index to the V1 state. |
| EXPECT_TRUE(base::CopyFile(backup_index_path, index_path)); |
| |
| // Make the access/mod times of index file older than the other files in the |
| // cache to trigger size recalculation. |
| base::Time t = base::Time::Now() - base::TimeDelta::FromHours(1); |
| EXPECT_TRUE(base::TouchFile(index_path, t, t)); |
| EXPECT_FALSE(IsIndexFileCurrent(storage_dir)); |
| |
| // Reopen the cache and ensure the size is correct for the V2 value. |
| CreateStorageManager(); |
| EXPECT_TRUE(Open(origin1_, kCacheName)); |
| EXPECT_EQ(cache_size_v2, Size(origin1_)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, GetSizeThenCloseAllCaches) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo2"))); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/bar"))); |
| |
| int64_t origin_size = GetOriginUsage(origin1_); |
| EXPECT_LT(0, origin_size); |
| |
| EXPECT_EQ(origin_size, GetSizeThenCloseAllCaches(origin1_)); |
| EXPECT_FALSE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/baz"))); |
| } |
| |
| TEST_F(CacheStorageManagerTest, DeleteUnreferencedCacheDirectories) { |
| // Create a referenced cache. |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| |
| // Create an unreferenced directory next to the referenced one. |
| base::FilePath origin_path = CacheStorageManager::ConstructOriginPath( |
| cache_manager_->root_path(), origin1_); |
| base::FilePath unreferenced_path = origin_path.AppendASCII("bar"); |
| EXPECT_TRUE(CreateDirectory(unreferenced_path)); |
| EXPECT_TRUE(base::DirectoryExists(unreferenced_path)); |
| |
| // Create a new StorageManager so that the next time the cache is opened |
| // the unreferenced directory can be deleted. |
| quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |
| cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); |
| |
| // Verify that the referenced cache still works. |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CacheMatch(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| |
| // Verify that the unreferenced cache is gone. |
| EXPECT_FALSE(base::DirectoryExists(unreferenced_path)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, OpenCacheStorageAccessed) { |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, HasStorageAccessed) { |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| EXPECT_FALSE(Has(origin1_, "foo")); |
| EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, DeleteStorageAccessed) { |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| EXPECT_FALSE(Delete(origin1_, "foo")); |
| EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, KeysStorageAccessed) { |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| EXPECT_EQ(0u, Keys(origin1_)); |
| EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, MatchCacheStorageAccessed) { |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"))); |
| EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, MatchAllCachesStorageAccessed) { |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| EXPECT_FALSE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); |
| EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, SizeStorageAccessed) { |
| EXPECT_EQ(0, Size(origin1_)); |
| // Size is not part of the web API and should not notify the quota manager of |
| // an access. |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, SizeThenCloseStorageAccessed) { |
| EXPECT_EQ(0, GetSizeThenCloseAllCaches(origin1_)); |
| // GetSizeThenCloseAllCaches is not part of the web API and should not notify |
| // the quota manager of an access. |
| EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreSearch) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo?bar"))); |
| |
| EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"))); |
| |
| CacheStorageCacheQueryParams match_params; |
| match_params.ignore_search = true; |
| EXPECT_TRUE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"), |
| match_params)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreMethod) { |
| GURL url = GURL("http://example.com/foo"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), url)); |
| |
| ServiceWorkerFetchRequest post_request; |
| post_request.url = url; |
| post_request.method = "POST"; |
| EXPECT_FALSE(StorageMatchWithRequest(origin1_, "foo", post_request)); |
| |
| CacheStorageCacheQueryParams match_params; |
| match_params.ignore_method = true; |
| EXPECT_TRUE( |
| StorageMatchWithRequest(origin1_, "foo", post_request, match_params)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreVary) { |
| GURL url = GURL("http://example.com/foo"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| |
| ServiceWorkerFetchRequest request; |
| request.url = url; |
| request.headers["vary_foo"] = "foo"; |
| |
| ServiceWorkerHeaderMap response_headers; |
| response_headers["vary"] = "vary_foo"; |
| |
| EXPECT_TRUE(CachePutWithRequestAndHeaders(callback_cache_handle_->value(), |
| request, response_headers)); |
| EXPECT_TRUE(StorageMatchWithRequest(origin1_, "foo", request)); |
| |
| request.headers["vary_foo"] = "bar"; |
| EXPECT_FALSE(StorageMatchWithRequest(origin1_, "foo", request)); |
| |
| CacheStorageCacheQueryParams match_params; |
| match_params.ignore_vary = true; |
| EXPECT_TRUE(StorageMatchWithRequest(origin1_, "foo", request, match_params)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreSearch) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo?bar"))); |
| |
| EXPECT_FALSE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); |
| |
| CacheStorageCacheQueryParams match_params; |
| match_params.ignore_search = true; |
| EXPECT_TRUE( |
| StorageMatchAll(origin1_, GURL("http://example.com/foo"), match_params)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreMethod) { |
| GURL url = GURL("http://example.com/foo"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), url)); |
| |
| ServiceWorkerFetchRequest post_request; |
| post_request.url = url; |
| post_request.method = "POST"; |
| EXPECT_FALSE(StorageMatchAllWithRequest(origin1_, post_request)); |
| |
| CacheStorageCacheQueryParams match_params; |
| match_params.ignore_method = true; |
| EXPECT_TRUE(StorageMatchAllWithRequest(origin1_, post_request, match_params)); |
| } |
| |
| TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreVary) { |
| GURL url = GURL("http://example.com/foo"); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| |
| ServiceWorkerFetchRequest request; |
| request.url = url; |
| request.headers["vary_foo"] = "foo"; |
| |
| ServiceWorkerHeaderMap response_headers; |
| response_headers["vary"] = "vary_foo"; |
| |
| EXPECT_TRUE(CachePutWithRequestAndHeaders(callback_cache_handle_->value(), |
| request, response_headers)); |
| EXPECT_TRUE(StorageMatchAllWithRequest(origin1_, request)); |
| |
| request.headers["vary_foo"] = "bar"; |
| EXPECT_FALSE(StorageMatchAllWithRequest(origin1_, request)); |
| |
| CacheStorageCacheQueryParams match_params; |
| match_params.ignore_vary = true; |
| EXPECT_TRUE(StorageMatchAllWithRequest(origin1_, request, match_params)); |
| } |
| |
| class CacheStorageQuotaClientTest : public CacheStorageManagerTest { |
| protected: |
| CacheStorageQuotaClientTest() {} |
| |
| void SetUp() override { |
| CacheStorageManagerTest::SetUp(); |
| quota_client_.reset( |
| new CacheStorageQuotaClient(cache_manager_->AsWeakPtr())); |
| } |
| |
| void QuotaUsageCallback(base::RunLoop* run_loop, int64_t usage) { |
| callback_quota_usage_ = usage; |
| run_loop->Quit(); |
| } |
| |
| void OriginsCallback(base::RunLoop* run_loop, const std::set<GURL>& origins) { |
| callback_origins_ = origins; |
| run_loop->Quit(); |
| } |
| |
| void DeleteOriginCallback(base::RunLoop* run_loop, |
| storage::QuotaStatusCode status) { |
| callback_status_ = status; |
| run_loop->Quit(); |
| } |
| |
| int64_t QuotaGetOriginUsage(const GURL& origin) { |
| base::RunLoop loop; |
| quota_client_->GetOriginUsage( |
| origin, storage::kStorageTypeTemporary, |
| base::Bind(&CacheStorageQuotaClientTest::QuotaUsageCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| return callback_quota_usage_; |
| } |
| |
| size_t QuotaGetOriginsForType() { |
| base::RunLoop loop; |
| quota_client_->GetOriginsForType( |
| storage::kStorageTypeTemporary, |
| base::Bind(&CacheStorageQuotaClientTest::OriginsCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| return callback_origins_.size(); |
| } |
| |
| size_t QuotaGetOriginsForHost(const std::string& host) { |
| base::RunLoop loop; |
| quota_client_->GetOriginsForHost( |
| storage::kStorageTypeTemporary, host, |
| base::Bind(&CacheStorageQuotaClientTest::OriginsCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| return callback_origins_.size(); |
| } |
| |
| bool QuotaDeleteOriginData(const GURL& origin) { |
| base::RunLoop loop; |
| quota_client_->DeleteOriginData( |
| origin, storage::kStorageTypeTemporary, |
| base::Bind(&CacheStorageQuotaClientTest::DeleteOriginCallback, |
| base::Unretained(this), base::Unretained(&loop))); |
| loop.Run(); |
| return callback_status_ == storage::kQuotaStatusOk; |
| } |
| |
| bool QuotaDoesSupport(storage::StorageType type) { |
| return quota_client_->DoesSupport(type); |
| } |
| |
| std::unique_ptr<CacheStorageQuotaClient> quota_client_; |
| |
| storage::QuotaStatusCode callback_status_; |
| int64_t callback_quota_usage_ = 0; |
| std::set<GURL> callback_origins_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(CacheStorageQuotaClientTest); |
| }; |
| |
| class CacheStorageQuotaClientDiskOnlyTest : public CacheStorageQuotaClientTest { |
| public: |
| bool MemoryOnly() override { return false; } |
| }; |
| |
| class CacheStorageQuotaClientTestP : public CacheStorageQuotaClientTest, |
| public testing::WithParamInterface<bool> { |
| bool MemoryOnly() override { return !GetParam(); } |
| }; |
| |
| TEST_P(CacheStorageQuotaClientTestP, QuotaID) { |
| EXPECT_EQ(storage::QuotaClient::kServiceWorkerCache, quota_client_->id()); |
| } |
| |
| TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginUsage) { |
| EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_LT(0, QuotaGetOriginUsage(origin1_)); |
| } |
| |
| TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginsForType) { |
| EXPECT_EQ(0u, QuotaGetOriginsForType()); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(Open(origin2_, "foo")); |
| EXPECT_EQ(2u, QuotaGetOriginsForType()); |
| } |
| |
| TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginsForHost) { |
| EXPECT_EQ(0u, QuotaGetOriginsForHost("example.com")); |
| EXPECT_TRUE(Open(GURL("http://example.com:8080"), "foo")); |
| EXPECT_TRUE(Open(GURL("http://example.com:9000"), "foo")); |
| EXPECT_TRUE(Open(GURL("ftp://example.com"), "foo")); |
| EXPECT_TRUE(Open(GURL("http://example2.com"), "foo")); |
| EXPECT_EQ(3u, QuotaGetOriginsForHost("example.com")); |
| EXPECT_EQ(1u, QuotaGetOriginsForHost("example2.com")); |
| EXPECT_TRUE(callback_origins_.find(GURL("http://example2.com")) != |
| callback_origins_.end()); |
| EXPECT_EQ(0u, QuotaGetOriginsForHost("unknown.com")); |
| } |
| |
| TEST_P(CacheStorageQuotaClientTestP, QuotaDeleteOriginData) { |
| EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| // Call put to test that initialized caches are properly deleted too. |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| EXPECT_TRUE(Open(origin1_, "bar")); |
| EXPECT_TRUE(Open(origin2_, "baz")); |
| |
| int64_t origin1_size = QuotaGetOriginUsage(origin1_); |
| EXPECT_LT(0, origin1_size); |
| |
| EXPECT_TRUE(QuotaDeleteOriginData(origin1_)); |
| |
| EXPECT_EQ(-1 * origin1_size, quota_manager_proxy_->last_notified_delta()); |
| EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); |
| EXPECT_FALSE(Has(origin1_, "foo")); |
| EXPECT_FALSE(Has(origin1_, "bar")); |
| EXPECT_TRUE(Has(origin2_, "baz")); |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| } |
| |
| TEST_P(CacheStorageQuotaClientTestP, QuotaDeleteEmptyOrigin) { |
| EXPECT_TRUE(QuotaDeleteOriginData(origin1_)); |
| } |
| |
| TEST_F(CacheStorageQuotaClientDiskOnlyTest, QuotaDeleteUnloadedOriginData) { |
| EXPECT_TRUE(Open(origin1_, "foo")); |
| // Call put to test that initialized caches are properly deleted too. |
| EXPECT_TRUE(CachePut(callback_cache_handle_->value(), |
| GURL("http://example.com/foo"))); |
| |
| // Close the cache backend so that it writes out its index to disk. |
| base::RunLoop run_loop; |
| callback_cache_handle_->value()->Close(run_loop.QuitClosure()); |
| run_loop.Run(); |
| |
| // Create a new CacheStorageManager that hasn't yet loaded the origin. |
| quota_manager_proxy_->SimulateQuotaManagerDestroyed(); |
| cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); |
| quota_client_.reset(new CacheStorageQuotaClient(cache_manager_->AsWeakPtr())); |
| |
| EXPECT_TRUE(QuotaDeleteOriginData(origin1_)); |
| EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); |
| } |
| |
| TEST_P(CacheStorageQuotaClientTestP, QuotaDoesSupport) { |
| EXPECT_TRUE(QuotaDoesSupport(storage::kStorageTypeTemporary)); |
| EXPECT_FALSE(QuotaDoesSupport(storage::kStorageTypePersistent)); |
| EXPECT_FALSE(QuotaDoesSupport(storage::kStorageTypeSyncable)); |
| EXPECT_FALSE(QuotaDoesSupport(storage::kStorageTypeQuotaNotManaged)); |
| EXPECT_FALSE(QuotaDoesSupport(storage::kStorageTypeUnknown)); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(CacheStorageManagerTests, |
| CacheStorageManagerTestP, |
| ::testing::Values(false, true)); |
| |
| INSTANTIATE_TEST_CASE_P(CacheStorageQuotaClientTests, |
| CacheStorageQuotaClientTestP, |
| ::testing::Values(false, true)); |
| |
| } // namespace content |