| // 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 <stack> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/message_loop.h" |
| #include "base/threading/thread.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "net/base/net_errors.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "webkit/appcache/appcache.h" |
| #include "webkit/appcache/appcache_database.h" |
| #include "webkit/appcache/appcache_entry.h" |
| #include "webkit/appcache/appcache_group.h" |
| #include "webkit/appcache/appcache_service.h" |
| #include "webkit/appcache/appcache_storage_impl.h" |
| #include "webkit/quota/quota_manager.h" |
| |
| namespace appcache { |
| |
| namespace { |
| |
| const base::Time kZeroTime; |
| const GURL kManifestUrl("http://blah/manifest"); |
| const GURL kManifestUrl2("http://blah/manifest2"); |
| const GURL kManifestUrl3("http://blah/manifest3"); |
| const GURL kEntryUrl("http://blah/entry"); |
| const GURL kEntryUrl2("http://blah/entry2"); |
| const GURL kFallbackNamespace("http://blah/fallback_namespace/"); |
| const GURL kFallbackNamespace2("http://blah/fallback_namespace/longer"); |
| const GURL kFallbackTestUrl("http://blah/fallback_namespace/longer/test"); |
| const GURL kOnlineNamespace("http://blah/online_namespace"); |
| const GURL kOnlineNamespaceWithinFallback( |
| "http://blah/fallback_namespace/online/"); |
| const GURL kInterceptNamespace("http://blah/intercept_namespace/"); |
| const GURL kInterceptNamespace2("http://blah/intercept_namespace/longer/"); |
| const GURL kInterceptTestUrl("http://blah/intercept_namespace/longer/test"); |
| const GURL kOrigin(kManifestUrl.GetOrigin()); |
| |
| const int kManifestEntryIdOffset = 100; |
| const int kFallbackEntryIdOffset = 1000; |
| |
| const GURL kDefaultEntryUrl("http://blah/makecacheandgroup_default_entry"); |
| const int kDefaultEntrySize = 10; |
| const int kDefaultEntryIdOffset = 12345; |
| |
| const int kMockQuota = 5000; |
| |
| scoped_ptr<base::Thread> io_thread; |
| scoped_ptr<base::Thread> db_thread; |
| |
| } // namespace |
| |
| class AppCacheStorageImplTest : public testing::Test { |
| public: |
| class MockStorageDelegate : public AppCacheStorage::Delegate { |
| public: |
| explicit MockStorageDelegate(AppCacheStorageImplTest* test) |
| : loaded_cache_id_(0), stored_group_success_(false), |
| would_exceed_quota_(false), obsoleted_success_(false), |
| found_cache_id_(kNoCacheId), test_(test) { |
| } |
| |
| void OnCacheLoaded(AppCache* cache, int64 cache_id) { |
| loaded_cache_ = cache; |
| loaded_cache_id_ = cache_id; |
| test_->ScheduleNextTask(); |
| } |
| |
| void OnGroupLoaded(AppCacheGroup* group, const GURL& manifest_url) { |
| loaded_group_ = group; |
| loaded_manifest_url_ = manifest_url; |
| loaded_groups_newest_cache_ = group ? group->newest_complete_cache() |
| : NULL; |
| test_->ScheduleNextTask(); |
| } |
| |
| void OnGroupAndNewestCacheStored( |
| AppCacheGroup* group, AppCache* newest_cache, bool success, |
| bool would_exceed_quota) { |
| stored_group_ = group; |
| stored_group_success_ = success; |
| would_exceed_quota_ = would_exceed_quota; |
| test_->ScheduleNextTask(); |
| } |
| |
| void OnGroupMadeObsolete(AppCacheGroup* group, bool success) { |
| obsoleted_group_ = group; |
| obsoleted_success_ = success; |
| test_->ScheduleNextTask(); |
| } |
| |
| void OnMainResponseFound(const GURL& url, const AppCacheEntry& entry, |
| const GURL& namespace_entry_url, |
| const AppCacheEntry& fallback_entry, |
| int64 cache_id, int64 group_id, |
| const GURL& manifest_url) { |
| found_url_ = url; |
| found_entry_ = entry; |
| found_namespace_entry_url_ = namespace_entry_url; |
| found_fallback_entry_ = fallback_entry; |
| found_cache_id_ = cache_id; |
| found_group_id_ = group_id; |
| found_manifest_url_ = manifest_url; |
| test_->ScheduleNextTask(); |
| } |
| |
| scoped_refptr<AppCache> loaded_cache_; |
| int64 loaded_cache_id_; |
| scoped_refptr<AppCacheGroup> loaded_group_; |
| GURL loaded_manifest_url_; |
| scoped_refptr<AppCache> loaded_groups_newest_cache_; |
| scoped_refptr<AppCacheGroup> stored_group_; |
| bool stored_group_success_; |
| bool would_exceed_quota_; |
| scoped_refptr<AppCacheGroup> obsoleted_group_; |
| bool obsoleted_success_; |
| GURL found_url_; |
| AppCacheEntry found_entry_; |
| GURL found_namespace_entry_url_; |
| AppCacheEntry found_fallback_entry_; |
| int64 found_cache_id_; |
| int64 found_group_id_; |
| GURL found_manifest_url_; |
| AppCacheStorageImplTest* test_; |
| }; |
| |
| class MockQuotaManager : public quota::QuotaManager { |
| public: |
| MockQuotaManager() |
| : QuotaManager(true /* is_incognito */, FilePath(), |
| io_thread->message_loop_proxy(), |
| db_thread->message_loop_proxy(), |
| NULL), |
| async_(false) {} |
| |
| virtual void GetUsageAndQuota( |
| const GURL& origin, quota::StorageType type, |
| const GetUsageAndQuotaCallback& callback) OVERRIDE { |
| EXPECT_EQ(kOrigin, origin); |
| EXPECT_EQ(quota::kStorageTypeTemporary, type); |
| if (async_) { |
| MessageLoop::current()->PostTask( |
| FROM_HERE, base::Bind(&MockQuotaManager::CallCallback, |
| base::Unretained(this), callback)); |
| return; |
| } |
| CallCallback(callback); |
| } |
| |
| void CallCallback(const GetUsageAndQuotaCallback& callback) { |
| callback.Run(quota::kQuotaStatusOk, 0, kMockQuota); |
| } |
| |
| bool async_; |
| |
| protected: |
| virtual ~MockQuotaManager() {} |
| }; |
| |
| class MockQuotaManagerProxy : public quota::QuotaManagerProxy { |
| public: |
| MockQuotaManagerProxy() |
| : QuotaManagerProxy(NULL, NULL), |
| notify_storage_accessed_count_(0), |
| notify_storage_modified_count_(0), |
| last_delta_(0), |
| mock_manager_(new MockQuotaManager) { |
| manager_ = mock_manager_; |
| } |
| |
| virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id, |
| const GURL& origin, |
| quota::StorageType type) OVERRIDE { |
| EXPECT_EQ(quota::QuotaClient::kAppcache, client_id); |
| EXPECT_EQ(quota::kStorageTypeTemporary, type); |
| ++notify_storage_accessed_count_; |
| last_origin_ = origin; |
| } |
| |
| virtual void NotifyStorageModified(quota::QuotaClient::ID client_id, |
| const GURL& origin, |
| quota::StorageType type, |
| int64 delta) OVERRIDE { |
| EXPECT_EQ(quota::QuotaClient::kAppcache, client_id); |
| EXPECT_EQ(quota::kStorageTypeTemporary, type); |
| ++notify_storage_modified_count_; |
| last_origin_ = origin; |
| last_delta_ = delta; |
| } |
| |
| // Not needed for our tests. |
| virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE {} |
| virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {} |
| virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {} |
| |
| int notify_storage_accessed_count_; |
| int notify_storage_modified_count_; |
| GURL last_origin_; |
| int last_delta_; |
| scoped_refptr<MockQuotaManager> mock_manager_; |
| |
| protected: |
| virtual ~MockQuotaManagerProxy() {} |
| }; |
| |
| template <class Method> |
| void RunMethod(Method method) { |
| (this->*method)(); |
| } |
| |
| // Helper callback to run a test on our io_thread. The io_thread is spun up |
| // once and reused for all tests. |
| template <class Method> |
| void MethodWrapper(Method method) { |
| SetUpTest(); |
| |
| // Ensure InitTask execution prior to conducting a test. |
| FlushDbThreadTasks(); |
| |
| // We also have to wait for InitTask completion call to be performed |
| // on the IO thread prior to running the test. Its guaranteed to be |
| // queued by this time. |
| MessageLoop::current()->PostTask( |
| FROM_HERE, base::Bind(&AppCacheStorageImplTest::RunMethod<Method>, |
| base::Unretained(this), method)); |
| } |
| |
| static void SetUpTestCase() { |
| io_thread.reset(new base::Thread("AppCacheTest.IOThread")); |
| base::Thread::Options options(MessageLoop::TYPE_IO, 0); |
| ASSERT_TRUE(io_thread->StartWithOptions(options)); |
| |
| db_thread.reset(new base::Thread("AppCacheTest::DBThread")); |
| ASSERT_TRUE(db_thread->Start()); |
| } |
| |
| static void TearDownTestCase() { |
| io_thread.reset(NULL); |
| db_thread.reset(NULL); |
| } |
| |
| // Test harness -------------------------------------------------- |
| |
| AppCacheStorageImplTest() { |
| } |
| |
| template <class Method> |
| void RunTestOnIOThread(Method method) { |
| test_finished_event_ .reset(new base::WaitableEvent(false, false)); |
| io_thread->message_loop()->PostTask( |
| FROM_HERE, base::Bind(&AppCacheStorageImplTest::MethodWrapper<Method>, |
| base::Unretained(this), method)); |
| test_finished_event_->Wait(); |
| } |
| |
| void SetUpTest() { |
| DCHECK(MessageLoop::current() == io_thread->message_loop()); |
| service_.reset(new AppCacheService(NULL)); |
| service_->Initialize(FilePath(), db_thread->message_loop_proxy(), NULL); |
| mock_quota_manager_proxy_ = new MockQuotaManagerProxy(); |
| service_->quota_manager_proxy_ = mock_quota_manager_proxy_; |
| delegate_.reset(new MockStorageDelegate(this)); |
| } |
| |
| void TearDownTest() { |
| DCHECK(MessageLoop::current() == io_thread->message_loop()); |
| storage()->CancelDelegateCallbacks(delegate()); |
| group_ = NULL; |
| cache_ = NULL; |
| cache2_ = NULL; |
| mock_quota_manager_proxy_ = NULL; |
| delegate_.reset(); |
| service_.reset(); |
| FlushDbThreadTasks(); |
| } |
| |
| void TestFinished() { |
| // We unwind the stack prior to finishing up to let stack |
| // based objects get deleted. |
| DCHECK(MessageLoop::current() == io_thread->message_loop()); |
| MessageLoop::current()->PostTask( |
| FROM_HERE, base::Bind(&AppCacheStorageImplTest::TestFinishedUnwound, |
| base::Unretained(this))); |
| } |
| |
| void TestFinishedUnwound() { |
| TearDownTest(); |
| test_finished_event_->Signal(); |
| } |
| |
| void PushNextTask(const base::Closure& task) { |
| task_stack_.push(task); |
| } |
| |
| void ScheduleNextTask() { |
| DCHECK(MessageLoop::current() == io_thread->message_loop()); |
| if (task_stack_.empty()) { |
| return; |
| } |
| MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top()); |
| task_stack_.pop(); |
| } |
| |
| static void SignalEvent(base::WaitableEvent* event) { |
| event->Signal(); |
| } |
| |
| void FlushDbThreadTasks() { |
| // We pump a task thru the db thread to ensure any tasks previously |
| // scheduled on that thread have been performed prior to return. |
| base::WaitableEvent event(false, false); |
| db_thread->message_loop()->PostTask( |
| FROM_HERE, base::Bind(&AppCacheStorageImplTest::SignalEvent, &event)); |
| event.Wait(); |
| } |
| |
| // LoadCache_Miss ---------------------------------------------------- |
| |
| void LoadCache_Miss() { |
| // Attempt to load a cache that doesn't exist. Should |
| // complete asynchronously. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadCache_Miss, |
| base::Unretained(this))); |
| |
| storage()->LoadCache(111, delegate()); |
| EXPECT_NE(111, delegate()->loaded_cache_id_); |
| } |
| |
| void Verify_LoadCache_Miss() { |
| EXPECT_EQ(111, delegate()->loaded_cache_id_); |
| EXPECT_FALSE(delegate()->loaded_cache_); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| TestFinished(); |
| } |
| |
| // LoadCache_NearHit ------------------------------------------------- |
| |
| void LoadCache_NearHit() { |
| // Attempt to load a cache that is currently in use |
| // and does not require loading from storage. This |
| // load should complete syncly. |
| |
| // Setup some preconditions. Make an 'unstored' cache for |
| // us to load. The ctor should put it in the working set. |
| int64 cache_id = storage()->NewCacheId(); |
| scoped_refptr<AppCache> cache(new AppCache(service(), cache_id)); |
| |
| // Conduct the test. |
| storage()->LoadCache(cache_id, delegate()); |
| EXPECT_EQ(cache_id, delegate()->loaded_cache_id_); |
| EXPECT_EQ(cache.get(), delegate()->loaded_cache_.get()); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| TestFinished(); |
| } |
| |
| // CreateGroup -------------------------------------------- |
| |
| void CreateGroupInEmptyOrigin() { |
| // Attempt to load a group that doesn't exist, one should |
| // be created for us, but not stored. |
| |
| // Since the origin has no groups, the storage class will respond |
| // syncly. |
| storage()->LoadOrCreateGroup(kManifestUrl, delegate()); |
| Verify_CreateGroup(); |
| } |
| |
| void CreateGroupInPopulatedOrigin() { |
| // Attempt to load a group that doesn't exist, one should |
| // be created for us, but not stored. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_CreateGroup, |
| base::Unretained(this))); |
| |
| // Since the origin has groups, storage class will have to |
| // consult the database and completion will be async. |
| storage()->usage_map_[kOrigin] = kDefaultEntrySize; |
| |
| storage()->LoadOrCreateGroup(kManifestUrl, delegate()); |
| EXPECT_FALSE(delegate()->loaded_group_.get()); |
| } |
| |
| void Verify_CreateGroup() { |
| EXPECT_EQ(kManifestUrl, delegate()->loaded_manifest_url_); |
| EXPECT_TRUE(delegate()->loaded_group_.get()); |
| EXPECT_TRUE(delegate()->loaded_group_->HasOneRef()); |
| EXPECT_FALSE(delegate()->loaded_group_->newest_complete_cache()); |
| |
| // Should not have been stored in the database. |
| AppCacheDatabase::GroupRecord record; |
| EXPECT_FALSE(database()->FindGroup( |
| delegate()->loaded_group_->group_id(), &record)); |
| |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| |
| TestFinished(); |
| } |
| |
| // LoadGroupAndCache_FarHit -------------------------------------- |
| |
| void LoadGroupAndCache_FarHit() { |
| // Attempt to load a cache that is not currently in use |
| // and does require loading from disk. This |
| // load should complete asynchronously. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadCache_Far_Hit, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a group and newest cache that |
| // appear to be "stored" and "not currently in use". |
| MakeCacheAndGroup(kManifestUrl, 1, 1, true); |
| group_ = NULL; |
| cache_ = NULL; |
| |
| // Conduct the cache load test, completes async |
| storage()->LoadCache(1, delegate()); |
| } |
| |
| void Verify_LoadCache_Far_Hit() { |
| EXPECT_TRUE(delegate()->loaded_cache_); |
| EXPECT_TRUE(delegate()->loaded_cache_->HasOneRef()); |
| EXPECT_EQ(1, delegate()->loaded_cache_id_); |
| |
| // The group should also have been loaded. |
| EXPECT_TRUE(delegate()->loaded_cache_->owning_group()); |
| EXPECT_TRUE(delegate()->loaded_cache_->owning_group()->HasOneRef()); |
| EXPECT_EQ(1, delegate()->loaded_cache_->owning_group()->group_id()); |
| |
| EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_accessed_count_); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| |
| // Drop things from the working set. |
| delegate()->loaded_cache_ = NULL; |
| EXPECT_FALSE(delegate()->loaded_group_); |
| |
| // Conduct the group load test, also complete asynchronously. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadGroup_Far_Hit, |
| base::Unretained(this))); |
| |
| storage()->LoadOrCreateGroup(kManifestUrl, delegate()); |
| } |
| |
| void Verify_LoadGroup_Far_Hit() { |
| EXPECT_TRUE(delegate()->loaded_group_); |
| EXPECT_EQ(kManifestUrl, delegate()->loaded_manifest_url_); |
| EXPECT_TRUE(delegate()->loaded_group_->newest_complete_cache()); |
| delegate()->loaded_groups_newest_cache_ = NULL; |
| EXPECT_TRUE(delegate()->loaded_group_->HasOneRef()); |
| EXPECT_EQ(2, mock_quota_manager_proxy_->notify_storage_accessed_count_); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| TestFinished(); |
| } |
| |
| // StoreNewGroup -------------------------------------- |
| |
| void StoreNewGroup() { |
| // Store a group and its newest cache. Should complete asynchronously. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_StoreNewGroup, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a group and newest cache that |
| // appear to be "unstored". |
| group_ = new AppCacheGroup( |
| service(), kManifestUrl, storage()->NewGroupId()); |
| cache_ = new AppCache(service(), storage()->NewCacheId()); |
| cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, 1, |
| kDefaultEntrySize)); |
| // Hold a ref to the cache simulate the UpdateJob holding that ref, |
| // and hold a ref to the group to simulate the CacheHost holding that ref. |
| |
| // Have the quota manager retrun asynchronously for this test. |
| mock_quota_manager_proxy_->mock_manager_->async_ = true; |
| |
| // Conduct the store test. |
| storage()->StoreGroupAndNewestCache(group_, cache_, delegate()); |
| EXPECT_FALSE(delegate()->stored_group_success_); |
| } |
| |
| void Verify_StoreNewGroup() { |
| EXPECT_TRUE(delegate()->stored_group_success_); |
| EXPECT_EQ(group_.get(), delegate()->stored_group_.get()); |
| EXPECT_EQ(cache_.get(), group_->newest_complete_cache()); |
| EXPECT_TRUE(cache_->is_complete()); |
| |
| // Should have been stored in the database. |
| AppCacheDatabase::GroupRecord group_record; |
| AppCacheDatabase::CacheRecord cache_record; |
| EXPECT_TRUE(database()->FindGroup(group_->group_id(), &group_record)); |
| EXPECT_TRUE(database()->FindCache(cache_->cache_id(), &cache_record)); |
| |
| // Verify quota bookkeeping |
| EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]); |
| EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_); |
| EXPECT_EQ(kDefaultEntrySize, mock_quota_manager_proxy_->last_delta_); |
| |
| TestFinished(); |
| } |
| |
| // StoreExistingGroup -------------------------------------- |
| |
| void StoreExistingGroup() { |
| // Store a group and its newest cache. Should complete asynchronously. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_StoreExistingGroup, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a group and old complete cache |
| // that appear to be "stored" |
| MakeCacheAndGroup(kManifestUrl, 1, 1, true); |
| EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]); |
| |
| // And a newest unstored complete cache. |
| cache2_ = new AppCache(service(), 2); |
| cache2_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::MASTER, 1, |
| kDefaultEntrySize + 100)); |
| |
| // Conduct the test. |
| storage()->StoreGroupAndNewestCache(group_, cache2_, delegate()); |
| EXPECT_FALSE(delegate()->stored_group_success_); |
| } |
| |
| void Verify_StoreExistingGroup() { |
| EXPECT_TRUE(delegate()->stored_group_success_); |
| EXPECT_EQ(group_.get(), delegate()->stored_group_.get()); |
| EXPECT_EQ(cache2_.get(), group_->newest_complete_cache()); |
| EXPECT_TRUE(cache2_->is_complete()); |
| |
| // The new cache should have been stored in the database. |
| AppCacheDatabase::GroupRecord group_record; |
| AppCacheDatabase::CacheRecord cache_record; |
| EXPECT_TRUE(database()->FindGroup(1, &group_record)); |
| EXPECT_TRUE(database()->FindCache(2, &cache_record)); |
| |
| // The old cache should have been deleted |
| EXPECT_FALSE(database()->FindCache(1, &cache_record)); |
| |
| // Verify quota bookkeeping |
| EXPECT_EQ(kDefaultEntrySize + 100, storage()->usage_map_[kOrigin]); |
| EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_); |
| EXPECT_EQ(100, mock_quota_manager_proxy_->last_delta_); |
| |
| TestFinished(); |
| } |
| |
| // StoreExistingGroupExistingCache ------------------------------- |
| |
| void StoreExistingGroupExistingCache() { |
| // Store a group with updates to its existing newest complete cache. |
| // Setup some preconditions. Create a group and a complete cache that |
| // appear to be "stored". |
| |
| // Setup some preconditions. Create a group and old complete cache |
| // that appear to be "stored" |
| MakeCacheAndGroup(kManifestUrl, 1, 1, true); |
| EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]); |
| |
| // Change the cache. |
| base::Time now = base::Time::Now(); |
| cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::MASTER, 1, 100)); |
| cache_->set_update_time(now); |
| |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_StoreExistingGroupExistingCache, |
| base::Unretained(this), now)); |
| |
| // Conduct the test. |
| EXPECT_EQ(cache_, group_->newest_complete_cache()); |
| storage()->StoreGroupAndNewestCache(group_, cache_, delegate()); |
| EXPECT_FALSE(delegate()->stored_group_success_); |
| } |
| |
| void Verify_StoreExistingGroupExistingCache( |
| base::Time expected_update_time) { |
| EXPECT_TRUE(delegate()->stored_group_success_); |
| EXPECT_EQ(cache_, group_->newest_complete_cache()); |
| |
| AppCacheDatabase::CacheRecord cache_record; |
| EXPECT_TRUE(database()->FindCache(1, &cache_record)); |
| EXPECT_EQ(1, cache_record.cache_id); |
| EXPECT_EQ(1, cache_record.group_id); |
| EXPECT_FALSE(cache_record.online_wildcard); |
| EXPECT_TRUE(expected_update_time == cache_record.update_time); |
| EXPECT_EQ(100 + kDefaultEntrySize, cache_record.cache_size); |
| |
| std::vector<AppCacheDatabase::EntryRecord> entry_records; |
| EXPECT_TRUE(database()->FindEntriesForCache(1, &entry_records)); |
| EXPECT_EQ(2U, entry_records.size()); |
| if (entry_records[0].url == kDefaultEntryUrl) |
| entry_records.erase(entry_records.begin()); |
| EXPECT_EQ(1 , entry_records[0].cache_id); |
| EXPECT_EQ(kEntryUrl, entry_records[0].url); |
| EXPECT_EQ(AppCacheEntry::MASTER, entry_records[0].flags); |
| EXPECT_EQ(1, entry_records[0].response_id); |
| EXPECT_EQ(100, entry_records[0].response_size); |
| |
| // Verify quota bookkeeping |
| EXPECT_EQ(100 + kDefaultEntrySize, storage()->usage_map_[kOrigin]); |
| EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_); |
| EXPECT_EQ(100, mock_quota_manager_proxy_->last_delta_); |
| |
| TestFinished(); |
| } |
| |
| // FailStoreGroup -------------------------------------- |
| |
| void FailStoreGroup() { |
| // Store a group and its newest cache. Should complete asynchronously. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_FailStoreGroup, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a group and newest cache that |
| // appear to be "unstored" and big enough to exceed the 5M limit. |
| const int64 kTooBig = 10 * 1024 * 1024; // 10M |
| group_ = new AppCacheGroup( |
| service(), kManifestUrl, storage()->NewGroupId()); |
| cache_ = new AppCache(service(), storage()->NewCacheId()); |
| cache_->AddEntry(kManifestUrl, |
| AppCacheEntry(AppCacheEntry::MANIFEST, 1, kTooBig)); |
| // Hold a ref to the cache simulate the UpdateJob holding that ref, |
| // and hold a ref to the group to simulate the CacheHost holding that ref. |
| |
| // Conduct the store test. |
| storage()->StoreGroupAndNewestCache(group_, cache_, delegate()); |
| EXPECT_FALSE(delegate()->stored_group_success_); // Expected to be async. |
| } |
| |
| void Verify_FailStoreGroup() { |
| EXPECT_FALSE(delegate()->stored_group_success_); |
| EXPECT_TRUE(delegate()->would_exceed_quota_); |
| |
| // Should not have been stored in the database. |
| AppCacheDatabase::GroupRecord group_record; |
| AppCacheDatabase::CacheRecord cache_record; |
| EXPECT_FALSE(database()->FindGroup(group_->group_id(), &group_record)); |
| EXPECT_FALSE(database()->FindCache(cache_->cache_id(), &cache_record)); |
| |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_); |
| EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| |
| TestFinished(); |
| } |
| |
| // MakeGroupObsolete ------------------------------- |
| |
| void MakeGroupObsolete() { |
| // Make a group obsolete, should complete asynchronously. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_MakeGroupObsolete, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a group and newest cache that |
| // appears to be "stored" and "currently in use". |
| MakeCacheAndGroup(kManifestUrl, 1, 1, true); |
| EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]); |
| |
| // Also insert some related records. |
| AppCacheDatabase::EntryRecord entry_record; |
| entry_record.cache_id = 1; |
| entry_record.flags = AppCacheEntry::FALLBACK; |
| entry_record.response_id = 1; |
| entry_record.url = kEntryUrl; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| |
| AppCacheDatabase::NamespaceRecord fallback_namespace_record; |
| fallback_namespace_record.cache_id = 1; |
| fallback_namespace_record.target_url = kEntryUrl; |
| fallback_namespace_record.namespace_url = kFallbackNamespace; |
| fallback_namespace_record.origin = kManifestUrl.GetOrigin(); |
| EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record)); |
| |
| AppCacheDatabase::OnlineWhiteListRecord online_whitelist_record; |
| online_whitelist_record.cache_id = 1; |
| online_whitelist_record.namespace_url = kOnlineNamespace; |
| EXPECT_TRUE(database()->InsertOnlineWhiteList(&online_whitelist_record)); |
| |
| // Conduct the test. |
| storage()->MakeGroupObsolete(group_, delegate()); |
| EXPECT_FALSE(group_->is_obsolete()); |
| } |
| |
| void Verify_MakeGroupObsolete() { |
| EXPECT_TRUE(delegate()->obsoleted_success_); |
| EXPECT_EQ(group_.get(), delegate()->obsoleted_group_.get()); |
| EXPECT_TRUE(group_->is_obsolete()); |
| EXPECT_TRUE(storage()->usage_map_.empty()); |
| |
| // The cache and group have been deleted from the database. |
| AppCacheDatabase::GroupRecord group_record; |
| AppCacheDatabase::CacheRecord cache_record; |
| EXPECT_FALSE(database()->FindGroup(1, &group_record)); |
| EXPECT_FALSE(database()->FindCache(1, &cache_record)); |
| |
| // The related records should have been deleted too. |
| std::vector<AppCacheDatabase::EntryRecord> entry_records; |
| database()->FindEntriesForCache(1, &entry_records); |
| EXPECT_TRUE(entry_records.empty()); |
| std::vector<AppCacheDatabase::NamespaceRecord> intercept_records; |
| std::vector<AppCacheDatabase::NamespaceRecord> fallback_records; |
| database()->FindNamespacesForCache( |
| 1, &intercept_records, &fallback_records); |
| EXPECT_TRUE(fallback_records.empty()); |
| std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelist_records; |
| database()->FindOnlineWhiteListForCache(1, &whitelist_records); |
| EXPECT_TRUE(whitelist_records.empty()); |
| |
| // Verify quota bookkeeping |
| EXPECT_TRUE(storage()->usage_map_.empty()); |
| EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_); |
| EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_); |
| EXPECT_EQ(-kDefaultEntrySize, mock_quota_manager_proxy_->last_delta_); |
| |
| TestFinished(); |
| } |
| |
| // MarkEntryAsForeign ------------------------------- |
| |
| void MarkEntryAsForeign() { |
| // Setup some preconditions. Create a cache with an entry |
| // in storage and in the working set. |
| MakeCacheAndGroup(kManifestUrl, 1, 1, true); |
| cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT)); |
| AppCacheDatabase::EntryRecord entry_record; |
| entry_record.cache_id = 1; |
| entry_record.url = kEntryUrl; |
| entry_record.flags = AppCacheEntry::EXPLICIT; |
| entry_record.response_id = 0; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| EXPECT_FALSE(cache_->GetEntry(kEntryUrl)->IsForeign()); |
| |
| // Conduct the test. |
| storage()->MarkEntryAsForeign(kEntryUrl, 1); |
| |
| // The entry in the working set should have been updated syncly. |
| EXPECT_TRUE(cache_->GetEntry(kEntryUrl)->IsForeign()); |
| EXPECT_TRUE(cache_->GetEntry(kEntryUrl)->IsExplicit()); |
| |
| // And the entry in storage should also be updated, but that |
| // happens asynchronously on the db thread. |
| FlushDbThreadTasks(); |
| AppCacheDatabase::EntryRecord entry_record2; |
| EXPECT_TRUE(database()->FindEntry(1, kEntryUrl, &entry_record2)); |
| EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, |
| entry_record2.flags); |
| TestFinished(); |
| } |
| |
| // MarkEntryAsForeignWithLoadInProgress ------------------------------- |
| |
| void MarkEntryAsForeignWithLoadInProgress() { |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_MarkEntryAsForeignWithLoadInProgress, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a cache with an entry |
| // in storage, but not in the working set. |
| MakeCacheAndGroup(kManifestUrl, 1, 1, true); |
| cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT)); |
| AppCacheDatabase::EntryRecord entry_record; |
| entry_record.cache_id = 1; |
| entry_record.url = kEntryUrl; |
| entry_record.flags = AppCacheEntry::EXPLICIT; |
| entry_record.response_id = 0; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| EXPECT_FALSE(cache_->GetEntry(kEntryUrl)->IsForeign()); |
| EXPECT_TRUE(cache_->HasOneRef()); |
| cache_ = NULL; |
| group_ = NULL; |
| |
| // Conduct the test, start a cache load, and prior to completion |
| // of that load, mark the entry as foreign. |
| storage()->LoadCache(1, delegate()); |
| storage()->MarkEntryAsForeign(kEntryUrl, 1); |
| } |
| |
| void Verify_MarkEntryAsForeignWithLoadInProgress() { |
| EXPECT_EQ(1, delegate()->loaded_cache_id_); |
| EXPECT_TRUE(delegate()->loaded_cache_.get()); |
| |
| // The entry in the working set should have been updated upon load. |
| EXPECT_TRUE(delegate()->loaded_cache_->GetEntry(kEntryUrl)->IsForeign()); |
| EXPECT_TRUE(delegate()->loaded_cache_->GetEntry(kEntryUrl)->IsExplicit()); |
| |
| // And the entry in storage should also be updated. |
| FlushDbThreadTasks(); |
| AppCacheDatabase::EntryRecord entry_record; |
| EXPECT_TRUE(database()->FindEntry(1, kEntryUrl, &entry_record)); |
| EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, |
| entry_record.flags); |
| TestFinished(); |
| } |
| |
| // FindNoMainResponse ------------------------------- |
| |
| void FindNoMainResponse() { |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_FindNoMainResponse, |
| base::Unretained(this))); |
| |
| // Conduct the test. |
| storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate()); |
| EXPECT_NE(kEntryUrl, delegate()->found_url_); |
| } |
| |
| void Verify_FindNoMainResponse() { |
| EXPECT_EQ(kEntryUrl, delegate()->found_url_); |
| EXPECT_TRUE(delegate()->found_manifest_url_.is_empty()); |
| EXPECT_EQ(kNoCacheId, delegate()->found_cache_id_); |
| EXPECT_EQ(kNoResponseId, delegate()->found_entry_.response_id()); |
| EXPECT_EQ(kNoResponseId, delegate()->found_fallback_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty()); |
| EXPECT_EQ(0, delegate()->found_entry_.types()); |
| EXPECT_EQ(0, delegate()->found_fallback_entry_.types()); |
| TestFinished(); |
| } |
| |
| // BasicFindMainResponse ------------------------------- |
| |
| void BasicFindMainResponseInDatabase() { |
| BasicFindMainResponse(true); |
| } |
| |
| void BasicFindMainResponseInWorkingSet() { |
| BasicFindMainResponse(false); |
| } |
| |
| void BasicFindMainResponse(bool drop_from_working_set) { |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_BasicFindMainResponse, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a complete cache with an entry |
| // in storage. |
| MakeCacheAndGroup(kManifestUrl, 2, 1, true); |
| cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, 1)); |
| AppCacheDatabase::EntryRecord entry_record; |
| entry_record.cache_id = 1; |
| entry_record.url = kEntryUrl; |
| entry_record.flags = AppCacheEntry::EXPLICIT; |
| entry_record.response_id = 1; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| |
| // Optionally drop the cache/group pair from the working set. |
| if (drop_from_working_set) { |
| EXPECT_TRUE(cache_->HasOneRef()); |
| cache_ = NULL; |
| EXPECT_TRUE(group_->HasOneRef()); |
| group_ = NULL; |
| } |
| |
| // Conduct the test. |
| storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate()); |
| EXPECT_NE(kEntryUrl, delegate()->found_url_); |
| } |
| |
| void Verify_BasicFindMainResponse() { |
| EXPECT_EQ(kEntryUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_); |
| EXPECT_EQ(1, delegate()->found_cache_id_); |
| EXPECT_EQ(2, delegate()->found_group_id_); |
| EXPECT_EQ(1, delegate()->found_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_entry_.IsExplicit()); |
| EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id()); |
| TestFinished(); |
| } |
| |
| // BasicFindMainFallbackResponse ------------------------------- |
| |
| void BasicFindMainFallbackResponseInDatabase() { |
| BasicFindMainFallbackResponse(true); |
| } |
| |
| void BasicFindMainFallbackResponseInWorkingSet() { |
| BasicFindMainFallbackResponse(false); |
| } |
| |
| void BasicFindMainFallbackResponse(bool drop_from_working_set) { |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_BasicFindMainFallbackResponse, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a complete cache with a |
| // fallback namespace and entry. |
| MakeCacheAndGroup(kManifestUrl, 2, 1, true); |
| cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1)); |
| cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2)); |
| cache_->fallback_namespaces_.push_back( |
| Namespace(FALLBACK_NAMESPACE, kFallbackNamespace2, kEntryUrl2)); |
| cache_->fallback_namespaces_.push_back( |
| Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl)); |
| AppCacheDatabase::CacheRecord cache_record; |
| std::vector<AppCacheDatabase::EntryRecord> entries; |
| std::vector<AppCacheDatabase::NamespaceRecord> intercepts; |
| std::vector<AppCacheDatabase::NamespaceRecord> fallbacks; |
| std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists; |
| cache_->ToDatabaseRecords(group_, |
| &cache_record, &entries, &intercepts, &fallbacks, &whitelists); |
| |
| std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter = |
| entries.begin(); |
| while (iter != entries.end()) { |
| // MakeCacheAndGroup has inserted the default entry record already. |
| if (iter->url != kDefaultEntryUrl) |
| EXPECT_TRUE(database()->InsertEntry(&(*iter))); |
| ++iter; |
| } |
| |
| EXPECT_TRUE(database()->InsertNamespaceRecords(fallbacks)); |
| EXPECT_TRUE(database()->InsertOnlineWhiteListRecords(whitelists)); |
| if (drop_from_working_set) { |
| EXPECT_TRUE(cache_->HasOneRef()); |
| cache_ = NULL; |
| EXPECT_TRUE(group_->HasOneRef()); |
| group_ = NULL; |
| } |
| |
| // Conduct the test. The test url is in both fallback namespace urls, |
| // but should match the longer of the two. |
| storage()->FindResponseForMainRequest(kFallbackTestUrl, GURL(), delegate()); |
| EXPECT_NE(kFallbackTestUrl, delegate()->found_url_); |
| } |
| |
| void Verify_BasicFindMainFallbackResponse() { |
| EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_); |
| EXPECT_EQ(1, delegate()->found_cache_id_); |
| EXPECT_EQ(2, delegate()->found_group_id_); |
| EXPECT_FALSE(delegate()->found_entry_.has_response_id()); |
| EXPECT_EQ(2, delegate()->found_fallback_entry_.response_id()); |
| EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_); |
| EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback()); |
| TestFinished(); |
| } |
| |
| // BasicFindMainInterceptResponse ------------------------------- |
| |
| void BasicFindMainInterceptResponseInDatabase() { |
| BasicFindMainInterceptResponse(true); |
| } |
| |
| void BasicFindMainInterceptResponseInWorkingSet() { |
| BasicFindMainInterceptResponse(false); |
| } |
| |
| void BasicFindMainInterceptResponse(bool drop_from_working_set) { |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_BasicFindMainInterceptResponse, |
| base::Unretained(this))); |
| |
| // Setup some preconditions. Create a complete cache with an |
| // intercept namespace and entry. |
| MakeCacheAndGroup(kManifestUrl, 2, 1, true); |
| cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1)); |
| cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::INTERCEPT, 2)); |
| cache_->intercept_namespaces_.push_back( |
| Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace2, kEntryUrl2)); |
| cache_->intercept_namespaces_.push_back( |
| Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace, kEntryUrl)); |
| AppCacheDatabase::CacheRecord cache_record; |
| std::vector<AppCacheDatabase::EntryRecord> entries; |
| std::vector<AppCacheDatabase::NamespaceRecord> intercepts; |
| std::vector<AppCacheDatabase::NamespaceRecord> fallbacks; |
| std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists; |
| cache_->ToDatabaseRecords(group_, |
| &cache_record, &entries, &intercepts, &fallbacks, &whitelists); |
| |
| std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter = |
| entries.begin(); |
| while (iter != entries.end()) { |
| // MakeCacheAndGroup has inserted the default entry record already |
| if (iter->url != kDefaultEntryUrl) |
| EXPECT_TRUE(database()->InsertEntry(&(*iter))); |
| ++iter; |
| } |
| |
| EXPECT_TRUE(database()->InsertNamespaceRecords(intercepts)); |
| EXPECT_TRUE(database()->InsertOnlineWhiteListRecords(whitelists)); |
| if (drop_from_working_set) { |
| EXPECT_TRUE(cache_->HasOneRef()); |
| cache_ = NULL; |
| EXPECT_TRUE(group_->HasOneRef()); |
| group_ = NULL; |
| } |
| |
| // Conduct the test. The test url is in both intercept namespaces, |
| // but should match the longer of the two. |
| storage()->FindResponseForMainRequest( |
| kInterceptTestUrl, GURL(), delegate()); |
| EXPECT_NE(kInterceptTestUrl, delegate()->found_url_); |
| } |
| |
| void Verify_BasicFindMainInterceptResponse() { |
| EXPECT_EQ(kInterceptTestUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_); |
| EXPECT_EQ(1, delegate()->found_cache_id_); |
| EXPECT_EQ(2, delegate()->found_group_id_); |
| EXPECT_EQ(2, delegate()->found_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_entry_.IsIntercept()); |
| EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_); |
| EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id()); |
| TestFinished(); |
| } |
| // FindMainResponseWithMultipleHits ------------------------------- |
| |
| void FindMainResponseWithMultipleHits() { |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits, |
| base::Unretained(this))); |
| |
| // Setup some preconditions, create a few caches with an identical set |
| // of entries and fallback namespaces. Only the last one remains in |
| // the working set to simulate appearing as "in use". |
| MakeMultipleHitCacheAndGroup(kManifestUrl, 1); |
| MakeMultipleHitCacheAndGroup(kManifestUrl2, 2); |
| MakeMultipleHitCacheAndGroup(kManifestUrl3, 3); |
| |
| // Conduct the test, we should find the response from the last cache |
| // since it's "in use". |
| storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate()); |
| EXPECT_NE(kEntryUrl, delegate()->found_url_); |
| } |
| |
| void MakeMultipleHitCacheAndGroup(const GURL& manifest_url, int id) { |
| MakeCacheAndGroup(manifest_url, id, id, true); |
| AppCacheDatabase::EntryRecord entry_record; |
| |
| // Add an entry for kEntryUrl |
| entry_record.cache_id = id; |
| entry_record.url = kEntryUrl; |
| entry_record.flags = AppCacheEntry::EXPLICIT; |
| entry_record.response_id = id; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| cache_->AddEntry( |
| entry_record.url, |
| AppCacheEntry(entry_record.flags, entry_record.response_id)); |
| |
| // Add an entry for the manifestUrl |
| entry_record.cache_id = id; |
| entry_record.url = manifest_url; |
| entry_record.flags = AppCacheEntry::MANIFEST; |
| entry_record.response_id = id + kManifestEntryIdOffset; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| cache_->AddEntry( |
| entry_record.url, |
| AppCacheEntry(entry_record.flags, entry_record.response_id)); |
| |
| // Add a fallback entry and namespace |
| entry_record.cache_id = id; |
| entry_record.url = kEntryUrl2; |
| entry_record.flags = AppCacheEntry::FALLBACK; |
| entry_record.response_id = id + kFallbackEntryIdOffset; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| cache_->AddEntry( |
| entry_record.url, |
| AppCacheEntry(entry_record.flags, entry_record.response_id)); |
| AppCacheDatabase::NamespaceRecord fallback_namespace_record; |
| fallback_namespace_record.cache_id = id; |
| fallback_namespace_record.target_url = entry_record.url; |
| fallback_namespace_record.namespace_url = kFallbackNamespace; |
| fallback_namespace_record.origin = manifest_url.GetOrigin(); |
| EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record)); |
| cache_->fallback_namespaces_.push_back( |
| Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl2)); |
| } |
| |
| void Verify_FindMainResponseWithMultipleHits() { |
| EXPECT_EQ(kEntryUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl3, delegate()->found_manifest_url_); |
| EXPECT_EQ(3, delegate()->found_cache_id_); |
| EXPECT_EQ(3, delegate()->found_group_id_); |
| EXPECT_EQ(3, delegate()->found_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_entry_.IsExplicit()); |
| EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id()); |
| |
| // Conduct another test perferring kManifestUrl |
| delegate_.reset(new MockStorageDelegate(this)); |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits2, |
| base::Unretained(this))); |
| storage()->FindResponseForMainRequest(kEntryUrl, kManifestUrl, delegate()); |
| EXPECT_NE(kEntryUrl, delegate()->found_url_); |
| } |
| |
| void Verify_FindMainResponseWithMultipleHits2() { |
| EXPECT_EQ(kEntryUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_); |
| EXPECT_EQ(1, delegate()->found_cache_id_); |
| EXPECT_EQ(1, delegate()->found_group_id_); |
| EXPECT_EQ(1, delegate()->found_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_entry_.IsExplicit()); |
| EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id()); |
| |
| // Conduct the another test perferring kManifestUrl2 |
| delegate_.reset(new MockStorageDelegate(this)); |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits3, |
| base::Unretained(this))); |
| storage()->FindResponseForMainRequest(kEntryUrl, kManifestUrl2, delegate()); |
| EXPECT_NE(kEntryUrl, delegate()->found_url_); |
| } |
| |
| void Verify_FindMainResponseWithMultipleHits3() { |
| EXPECT_EQ(kEntryUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl2, delegate()->found_manifest_url_); |
| EXPECT_EQ(2, delegate()->found_cache_id_); |
| EXPECT_EQ(2, delegate()->found_group_id_); |
| EXPECT_EQ(2, delegate()->found_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_entry_.IsExplicit()); |
| EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id()); |
| |
| // Conduct another test with no preferred manifest that hits the fallback. |
| delegate_.reset(new MockStorageDelegate(this)); |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits4, |
| base::Unretained(this))); |
| storage()->FindResponseForMainRequest( |
| kFallbackTestUrl, GURL(), delegate()); |
| EXPECT_NE(kFallbackTestUrl, delegate()->found_url_); |
| } |
| |
| void Verify_FindMainResponseWithMultipleHits4() { |
| EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl3, delegate()->found_manifest_url_); |
| EXPECT_EQ(3, delegate()->found_cache_id_); |
| EXPECT_EQ(3, delegate()->found_group_id_); |
| EXPECT_FALSE(delegate()->found_entry_.has_response_id()); |
| EXPECT_EQ(3 + kFallbackEntryIdOffset, |
| delegate()->found_fallback_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback()); |
| EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_); |
| |
| // Conduct another test preferring kManifestUrl2 that hits the fallback. |
| delegate_.reset(new MockStorageDelegate(this)); |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits5, |
| base::Unretained(this))); |
| storage()->FindResponseForMainRequest( |
| kFallbackTestUrl, kManifestUrl2, delegate()); |
| EXPECT_NE(kFallbackTestUrl, delegate()->found_url_); |
| } |
| |
| void Verify_FindMainResponseWithMultipleHits5() { |
| EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_); |
| EXPECT_EQ(kManifestUrl2, delegate()->found_manifest_url_); |
| EXPECT_EQ(2, delegate()->found_cache_id_); |
| EXPECT_EQ(2, delegate()->found_group_id_); |
| EXPECT_FALSE(delegate()->found_entry_.has_response_id()); |
| EXPECT_EQ(2 + kFallbackEntryIdOffset, |
| delegate()->found_fallback_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback()); |
| EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_); |
| |
| TestFinished(); |
| } |
| |
| // FindMainResponseExclusions ------------------------------- |
| |
| void FindMainResponseExclusionsInDatabase() { |
| FindMainResponseExclusions(true); |
| } |
| |
| void FindMainResponseExclusionsInWorkingSet() { |
| FindMainResponseExclusions(false); |
| } |
| |
| void FindMainResponseExclusions(bool drop_from_working_set) { |
| // Setup some preconditions. Create a complete cache with a |
| // foreign entry, an online namespace, and a second online |
| // namespace nested within a fallback namespace. |
| MakeCacheAndGroup(kManifestUrl, 1, 1, true); |
| cache_->AddEntry(kEntryUrl, |
| AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, 1)); |
| cache_->online_whitelist_namespaces_.push_back(kOnlineNamespace); |
| cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2)); |
| cache_->fallback_namespaces_.push_back( |
| Namespace(FALLBACK_NAMESPACE, kFallbackNamespace, kEntryUrl2)); |
| cache_->online_whitelist_namespaces_.push_back(kOnlineNamespace); |
| cache_->online_whitelist_namespaces_.push_back( |
| kOnlineNamespaceWithinFallback); |
| |
| AppCacheDatabase::EntryRecord entry_record; |
| entry_record.cache_id = 1; |
| entry_record.url = kEntryUrl; |
| entry_record.flags = AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN; |
| entry_record.response_id = 1; |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| AppCacheDatabase::OnlineWhiteListRecord whitelist_record; |
| whitelist_record.cache_id = 1; |
| whitelist_record.namespace_url = kOnlineNamespace; |
| EXPECT_TRUE(database()->InsertOnlineWhiteList(&whitelist_record)); |
| AppCacheDatabase::NamespaceRecord fallback_namespace_record; |
| fallback_namespace_record.cache_id = 1; |
| fallback_namespace_record.target_url = kEntryUrl2; |
| fallback_namespace_record.namespace_url = kFallbackNamespace; |
| fallback_namespace_record.origin = kManifestUrl.GetOrigin(); |
| EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record)); |
| whitelist_record.cache_id = 1; |
| whitelist_record.namespace_url = kOnlineNamespaceWithinFallback; |
| EXPECT_TRUE(database()->InsertOnlineWhiteList(&whitelist_record)); |
| if (drop_from_working_set) { |
| cache_ = NULL; |
| group_ = NULL; |
| } |
| |
| // We should not find anything for the foreign entry. |
| PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_ExclusionNotFound, |
| base::Unretained(this), kEntryUrl, 1)); |
| storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate()); |
| } |
| |
| void Verify_ExclusionNotFound(GURL expected_url, int phase) { |
| EXPECT_EQ(expected_url, delegate()->found_url_); |
| EXPECT_TRUE(delegate()->found_manifest_url_.is_empty()); |
| EXPECT_EQ(kNoCacheId, delegate()->found_cache_id_); |
| EXPECT_EQ(0, delegate()->found_group_id_); |
| EXPECT_EQ(kNoResponseId, delegate()->found_entry_.response_id()); |
| EXPECT_EQ(kNoResponseId, delegate()->found_fallback_entry_.response_id()); |
| EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty()); |
| EXPECT_EQ(0, delegate()->found_entry_.types()); |
| EXPECT_EQ(0, delegate()->found_fallback_entry_.types()); |
| |
| if (phase == 1) { |
| // We should not find anything for the online namespace. |
| PushNextTask( |
| base::Bind(&AppCacheStorageImplTest::Verify_ExclusionNotFound, |
| base::Unretained(this), kOnlineNamespace, 2)); |
| storage()->FindResponseForMainRequest( |
| kOnlineNamespace, GURL(), delegate()); |
| return; |
| } |
| if (phase == 2) { |
| // We should not find anything for the online namespace nested within |
| // the fallback namespace. |
| PushNextTask(base::Bind( |
| &AppCacheStorageImplTest::Verify_ExclusionNotFound, |
| base::Unretained(this), kOnlineNamespaceWithinFallback, 3)); |
| storage()->FindResponseForMainRequest( |
| kOnlineNamespaceWithinFallback, GURL(), delegate()); |
| return; |
| } |
| |
| TestFinished(); |
| } |
| |
| // Test case helpers -------------------------------------------------- |
| |
| AppCacheService* service() { |
| return service_.get(); |
| } |
| |
| AppCacheStorageImpl* storage() { |
| return static_cast<AppCacheStorageImpl*>(service()->storage()); |
| } |
| |
| AppCacheDatabase* database() { |
| return storage()->database_; |
| } |
| |
| MockStorageDelegate* delegate() { |
| return delegate_.get(); |
| } |
| |
| void MakeCacheAndGroup( |
| const GURL& manifest_url, int64 group_id, int64 cache_id, |
| bool add_to_database) { |
| AppCacheEntry default_entry( |
| AppCacheEntry::EXPLICIT, cache_id + kDefaultEntryIdOffset, |
| kDefaultEntrySize); |
| group_ = new AppCacheGroup(service(), manifest_url, group_id); |
| cache_ = new AppCache(service(), cache_id); |
| cache_->AddEntry(kDefaultEntryUrl, default_entry); |
| cache_->set_complete(true); |
| group_->AddCache(cache_); |
| if (add_to_database) { |
| AppCacheDatabase::GroupRecord group_record; |
| group_record.group_id = group_id; |
| group_record.manifest_url = manifest_url; |
| group_record.origin = manifest_url.GetOrigin(); |
| EXPECT_TRUE(database()->InsertGroup(&group_record)); |
| AppCacheDatabase::CacheRecord cache_record; |
| cache_record.cache_id = cache_id; |
| cache_record.group_id = group_id; |
| cache_record.online_wildcard = false; |
| cache_record.update_time = kZeroTime; |
| cache_record.cache_size = kDefaultEntrySize; |
| EXPECT_TRUE(database()->InsertCache(&cache_record)); |
| AppCacheDatabase::EntryRecord entry_record; |
| entry_record.cache_id = cache_id; |
| entry_record.url = kDefaultEntryUrl; |
| entry_record.flags = default_entry.types(); |
| entry_record.response_id = default_entry.response_id(); |
| entry_record.response_size = default_entry.response_size(); |
| EXPECT_TRUE(database()->InsertEntry(&entry_record)); |
| |
| storage()->usage_map_[manifest_url.GetOrigin()] = |
| default_entry.response_size(); |
| } |
| } |
| |
| // Data members -------------------------------------------------- |
| |
| scoped_ptr<base::WaitableEvent> test_finished_event_; |
| std::stack<base::Closure> task_stack_; |
| scoped_ptr<AppCacheService> service_; |
| scoped_ptr<MockStorageDelegate> delegate_; |
| scoped_refptr<MockQuotaManagerProxy> mock_quota_manager_proxy_; |
| scoped_refptr<AppCacheGroup> group_; |
| scoped_refptr<AppCache> cache_; |
| scoped_refptr<AppCache> cache2_; |
| }; |
| |
| |
| TEST_F(AppCacheStorageImplTest, LoadCache_Miss) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::LoadCache_Miss); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, LoadCache_NearHit) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::LoadCache_NearHit); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, CreateGroupInEmptyOrigin) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::CreateGroupInEmptyOrigin); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, CreateGroupInPopulatedOrigin) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::CreateGroupInPopulatedOrigin); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, LoadGroupAndCache_FarHit) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::LoadGroupAndCache_FarHit); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, StoreNewGroup) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::StoreNewGroup); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, StoreExistingGroup) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::StoreExistingGroup); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, StoreExistingGroupExistingCache) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::StoreExistingGroupExistingCache); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, FailStoreGroup) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::FailStoreGroup); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, MakeGroupObsolete) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::MakeGroupObsolete); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, MarkEntryAsForeign) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::MarkEntryAsForeign); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, MarkEntryAsForeignWithLoadInProgress) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::MarkEntryAsForeignWithLoadInProgress); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, FindNoMainResponse) { |
| RunTestOnIOThread(&AppCacheStorageImplTest::FindNoMainResponse); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, BasicFindMainResponseInDatabase) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::BasicFindMainResponseInDatabase); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, BasicFindMainResponseInWorkingSet) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::BasicFindMainResponseInWorkingSet); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, BasicFindMainFallbackResponseInDatabase) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::BasicFindMainFallbackResponseInDatabase); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, BasicFindMainFallbackResponseInWorkingSet) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::BasicFindMainFallbackResponseInWorkingSet); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, BasicFindMainInterceptResponseInDatabase) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::BasicFindMainInterceptResponseInDatabase); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, BasicFindMainInterceptResponseInWorkingSet) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::BasicFindMainInterceptResponseInWorkingSet); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, FindMainResponseWithMultipleHits) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::FindMainResponseWithMultipleHits); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInDatabase) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::FindMainResponseExclusionsInDatabase); |
| } |
| |
| TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInWorkingSet) { |
| RunTestOnIOThread( |
| &AppCacheStorageImplTest::FindMainResponseExclusionsInWorkingSet); |
| } |
| |
| // That's all folks! |
| |
| } // namespace appcache |