diff options
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/appcache/appcache_entry.h | 16 | ||||
-rw-r--r-- | webkit/appcache/appcache_group.h | 15 | ||||
-rw-r--r-- | webkit/appcache/appcache_response_unittest.cc | 44 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage.h | 34 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage_unittest.cc | 51 | ||||
-rw-r--r-- | webkit/appcache/mock_appcache_storage.cc | 267 | ||||
-rw-r--r-- | webkit/appcache/mock_appcache_storage.h | 51 | ||||
-rw-r--r-- | webkit/appcache/mock_appcache_storage_unittest.cc | 358 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gyp | 1 |
9 files changed, 690 insertions, 147 deletions
diff --git a/webkit/appcache/appcache_entry.h b/webkit/appcache/appcache_entry.h index 434568a..c57ab6b 100644 --- a/webkit/appcache/appcache_entry.h +++ b/webkit/appcache/appcache_entry.h @@ -5,6 +5,8 @@ #ifndef WEBKIT_APPCACHE_APPCACHE_ENTRY_H_ #define WEBKIT_APPCACHE_APPCACHE_ENTRY_H_ +#include "webkit/appcache/appcache_interfaces.h" + namespace appcache { // A cached entry is identified by a URL and is classified into one @@ -22,7 +24,13 @@ class AppCacheEntry { FALLBACK = 1 << 4, }; - explicit AppCacheEntry(int type) : types_(type) {} + AppCacheEntry() : types_(0), response_id_(kNoResponseId) {} + + explicit AppCacheEntry(int type) + : types_(type), response_id_(kNoResponseId) {} + + AppCacheEntry(int type, int64 response_id) + : types_(type), response_id_(response_id) {} int types() const { return types_; } void add_types(int added_types) { types_ |= added_types; } @@ -32,10 +40,12 @@ class AppCacheEntry { bool IsForeign() const { return (types_ & FOREIGN) != 0; } bool IsFallback() const { return (types_ & FALLBACK) != 0; } + int64 response_id() const { return response_id_; } + void set_response_id(int64 id) { response_id_ = id; } + private: int types_; - - // TODO(jennb): response storage key + int64 response_id_; }; } // namespace appcache diff --git a/webkit/appcache/appcache_group.h b/webkit/appcache/appcache_group.h index a5ff5fb..6f3c210 100644 --- a/webkit/appcache/appcache_group.h +++ b/webkit/appcache/appcache_group.h @@ -45,20 +45,18 @@ class AppCacheGroup : public base::RefCounted<AppCacheGroup> { void AddUpdateObserver(UpdateObserver* observer); void RemoveUpdateObserver(UpdateObserver* observer); - const GURL& manifest_url() { return manifest_url_; } + const GURL& manifest_url() const { return manifest_url_; } - bool is_obsolete() { return is_obsolete_; } + bool is_obsolete() const { return is_obsolete_; } void set_obsolete(bool value) { is_obsolete_ = value; } - AppCache* newest_complete_cache() { return newest_complete_cache_; } + AppCache* newest_complete_cache() const { return newest_complete_cache_; } void AddCache(AppCache* complete_cache); - void RemoveCache(AppCache* cache); + bool HasCache() const { return newest_complete_cache_ != NULL; } - bool HasCache() { return newest_complete_cache_ != NULL; } - - UpdateStatus update_status() { return update_status_; } + UpdateStatus update_status() const { return update_status_; } // Starts an update via update() javascript API. void StartUpdate() { @@ -79,13 +77,14 @@ class AppCacheGroup : public base::RefCounted<AppCacheGroup> { private: friend class AppCacheUpdateJob; friend class AppCacheUpdateJobTest; + friend class MockAppCacheStorage; // for old_caches() typedef std::vector<AppCache*> Caches; AppCacheUpdateJob* update_job() { return update_job_; } void SetUpdateStatus(UpdateStatus status); - const Caches& old_caches() { return old_caches_; } + const Caches& old_caches() const { return old_caches_; } GURL manifest_url_; UpdateStatus update_status_; diff --git a/webkit/appcache/appcache_response_unittest.cc b/webkit/appcache/appcache_response_unittest.cc index 854ac01..939086e 100644 --- a/webkit/appcache/appcache_response_unittest.cc +++ b/webkit/appcache/appcache_response_unittest.cc @@ -287,46 +287,6 @@ class AppCacheResponseTest : public testing::Test { // Most of the individual tests involve multiple async steps. Each test // is delineated with a section header. - // DelegateReferences ------------------------------------------------------- - // TODO(michaeln): maybe this one belongs in appcache_storage_unittest.cc - void DelegateReferences() { - typedef scoped_refptr<AppCacheStorage::DelegateReference> - ScopedDelegateReference; - MockStorageDelegate delegate(this); - ScopedDelegateReference delegate_reference1; - ScopedDelegateReference delegate_reference2; - - EXPECT_FALSE(service_->storage()->GetDelegateReference(&delegate)); - - delegate_reference1 = - service_->storage()->GetOrCreateDelegateReference(&delegate); - EXPECT_TRUE(delegate_reference1.get()); - EXPECT_TRUE(delegate_reference1->HasOneRef()); - EXPECT_TRUE(service_->storage()->GetDelegateReference(&delegate)); - EXPECT_EQ(&delegate, - service_->storage()->GetDelegateReference(&delegate)->delegate); - EXPECT_EQ(service_->storage()->GetDelegateReference(&delegate), - service_->storage()->GetOrCreateDelegateReference(&delegate)); - delegate_reference1 = NULL; - EXPECT_FALSE(service_->storage()->GetDelegateReference(&delegate)); - - delegate_reference1 = - service_->storage()->GetOrCreateDelegateReference(&delegate); - service_->storage()->CancelDelegateCallbacks(&delegate); - EXPECT_TRUE(delegate_reference1.get()); - EXPECT_TRUE(delegate_reference1->HasOneRef()); - EXPECT_FALSE(delegate_reference1->delegate); - EXPECT_FALSE(service_->storage()->GetDelegateReference(&delegate)); - - delegate_reference2 = - service_->storage()->GetOrCreateDelegateReference(&delegate); - EXPECT_TRUE(delegate_reference2.get()); - EXPECT_TRUE(delegate_reference2->HasOneRef()); - EXPECT_EQ(&delegate, delegate_reference2->delegate); - EXPECT_NE(delegate_reference1.get(), delegate_reference2.get()); - - TestFinished(); - } // ReadNonExistentResponse ------------------------------------------- void ReadNonExistentResponse() { @@ -680,10 +640,6 @@ class AppCacheResponseTest : public testing::Test { // static scoped_ptr<base::Thread> AppCacheResponseTest::io_thread_; -TEST_F(AppCacheResponseTest, DelegateReferences) { - RunTestOnIOThread(&AppCacheResponseTest::DelegateReferences); -} - TEST_F(AppCacheResponseTest, ReadNonExistentResponse) { RunTestOnIOThread(&AppCacheResponseTest::ReadNonExistentResponse); } diff --git a/webkit/appcache/appcache_storage.h b/webkit/appcache/appcache_storage.h index 912bdcb..c221054 100644 --- a/webkit/appcache/appcache_storage.h +++ b/webkit/appcache/appcache_storage.h @@ -12,6 +12,7 @@ #include "base/basictypes.h" #include "base/ref_counted.h" #include "net/base/net_errors.h" +#include "testing/gtest/include/gtest/gtest_prod.h" #include "webkit/appcache/appcache_response.h" #include "webkit/appcache/appcache_working_set.h" @@ -20,6 +21,7 @@ class GURL; namespace appcache { class AppCache; +class AppCacheEntry; class AppCacheGroup; class AppCacheService; @@ -41,18 +43,18 @@ class AppCacheStorage { virtual void OnGroupAndNewestCacheStored( AppCacheGroup* group, bool success) {} - // If the update fails, success will be false. - virtual void OnGroupMarkedAsObsolete(GURL& manifest_url, bool success) {} + // If the operation fails, success will be false. + virtual void OnGroupMadeObsolete(AppCacheGroup* group, bool success) {} // If a load fails the 'response_info' will be NULL. virtual void OnResponseInfoLoaded( AppCacheResponseInfo* response_info, int64 response_id) {} - // If no response is found, response_id will be kNoResponseId. + // If no response is found, entry.response_id() will be kNoResponseId. // If a response is found, the cache id and manifest url of the // containing cache and group are also returned. virtual void OnMainResponseFound( - const GURL& url, int64 response_id, bool is_fallback, + const GURL& url, const AppCacheEntry& entry, int64 cache_id, const GURL& mainfest_url) {} }; @@ -106,7 +108,7 @@ class AppCacheStorage { // Schedules a task to update persistent storage and doom the group and all // related caches and responses for deletion. Upon completion the in-memory // instance is marked as obsolete and the delegate callback is called. - virtual void MarkGroupAsObsolete( + virtual void MakeGroupObsolete( AppCacheGroup* group, Delegate* delegate) = 0; // Cancels all pending callbacks for the delegate. The delegate callbacks @@ -151,6 +153,7 @@ class AppCacheStorage { protected: friend class AppCacheResponseTest; + friend class AppCacheStorageTest; // Helper to call a collection of delegates. #define FOR_EACH_DELEGATE(delegates, func_and_args) \ @@ -289,29 +292,10 @@ class AppCacheStorage { // The set of last ids must be retrieved from storage prior to being used. static const int64 kUnitializedId = -1; + FRIEND_TEST(AppCacheStorageTest, DelegateReferences); DISALLOW_COPY_AND_ASSIGN(AppCacheStorage); }; -// TODO(michaeln): Maybe? -class AppCacheStoredItem { - public: - bool is_doomed() const { return is_doomed_; } - bool is_stored() const { return is_stored_; } - - protected: - AppCacheStoredItem() : is_doomed_(false), is_stored_(false) {} - - private: - friend class AppCacheStorage; - friend class MockAppCacheStorage; - - void set_is_doomed(bool b) { is_doomed_ = b; } - void set_is_stored(bool b) { is_stored_ = b; } - - bool is_doomed_; - bool is_stored_; -}; - } // namespace appcache #endif // WEBKIT_APPCACHE_APPCACHE_STORAGE_H_ diff --git a/webkit/appcache/appcache_storage_unittest.cc b/webkit/appcache/appcache_storage_unittest.cc index 6e06265..7025e75 100644 --- a/webkit/appcache/appcache_storage_unittest.cc +++ b/webkit/appcache/appcache_storage_unittest.cc @@ -2,19 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/message_loop.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/appcache/appcache.h" #include "webkit/appcache/appcache_group.h" -#include "webkit/appcache/appcache_storage.h" #include "webkit/appcache/appcache_response.h" +#include "webkit/appcache/appcache_storage.h" #include "webkit/appcache/mock_appcache_service.h" namespace appcache { class AppCacheStorageTest : public testing::Test { + public: + class MockStorageDelegate : public AppCacheStorage::Delegate { + public: + }; }; -TEST(AppCacheStorageTest, AddRemoveCache) { +TEST_F(AppCacheStorageTest, AddRemoveCache) { MockAppCacheService service; scoped_refptr<AppCache> cache = new AppCache(&service, 111); @@ -30,7 +35,7 @@ TEST(AppCacheStorageTest, AddRemoveCache) { dummy.storage()->working_set()->RemoveCache(cache); } -TEST(AppCacheStorageTest, AddRemoveGroup) { +TEST_F(AppCacheStorageTest, AddRemoveGroup) { MockAppCacheService service; scoped_refptr<AppCacheGroup> group = new AppCacheGroup(&service, GURL::EmptyGURL()); @@ -47,7 +52,7 @@ TEST(AppCacheStorageTest, AddRemoveGroup) { dummy.storage()->working_set()->RemoveGroup(group); } -TEST(AppCacheStorageTest, AddRemoveResponseInfo) { +TEST_F(AppCacheStorageTest, AddRemoveResponseInfo) { MockAppCacheService service; scoped_refptr<AppCacheResponseInfo> info = new AppCacheResponseInfo(&service, 111, new net::HttpResponseInfo); @@ -64,4 +69,42 @@ TEST(AppCacheStorageTest, AddRemoveResponseInfo) { dummy.storage()->working_set()->RemoveResponseInfo(info); } +TEST_F(AppCacheStorageTest, DelegateReferences) { + typedef scoped_refptr<AppCacheStorage::DelegateReference> + ScopedDelegateReference; + MockAppCacheService service; + MockStorageDelegate delegate; + ScopedDelegateReference delegate_reference1; + ScopedDelegateReference delegate_reference2; + + EXPECT_FALSE(service.storage()->GetDelegateReference(&delegate)); + + delegate_reference1 = + service.storage()->GetOrCreateDelegateReference(&delegate); + EXPECT_TRUE(delegate_reference1.get()); + EXPECT_TRUE(delegate_reference1->HasOneRef()); + EXPECT_TRUE(service.storage()->GetDelegateReference(&delegate)); + EXPECT_EQ(&delegate, + service.storage()->GetDelegateReference(&delegate)->delegate); + EXPECT_EQ(service.storage()->GetDelegateReference(&delegate), + service.storage()->GetOrCreateDelegateReference(&delegate)); + delegate_reference1 = NULL; + EXPECT_FALSE(service.storage()->GetDelegateReference(&delegate)); + + delegate_reference1 = + service.storage()->GetOrCreateDelegateReference(&delegate); + service.storage()->CancelDelegateCallbacks(&delegate); + EXPECT_TRUE(delegate_reference1.get()); + EXPECT_TRUE(delegate_reference1->HasOneRef()); + EXPECT_FALSE(delegate_reference1->delegate); + EXPECT_FALSE(service.storage()->GetDelegateReference(&delegate)); + + delegate_reference2 = + service.storage()->GetOrCreateDelegateReference(&delegate); + EXPECT_TRUE(delegate_reference2.get()); + EXPECT_TRUE(delegate_reference2->HasOneRef()); + EXPECT_EQ(&delegate, delegate_reference2->delegate); + EXPECT_NE(delegate_reference1.get(), delegate_reference2.get()); +} + } // namespace appcache diff --git a/webkit/appcache/mock_appcache_storage.cc b/webkit/appcache/mock_appcache_storage.cc index 3144179..3c626ec 100644 --- a/webkit/appcache/mock_appcache_storage.cc +++ b/webkit/appcache/mock_appcache_storage.cc @@ -5,86 +5,89 @@ #include "webkit/appcache/mock_appcache_storage.h" #include "base/logging.h" +#include "base/message_loop.h" #include "base/ref_counted.h" +#include "base/stl_util-inl.h" #include "webkit/appcache/appcache.h" #include "webkit/appcache/appcache_entry.h" #include "webkit/appcache/appcache_group.h" #include "webkit/appcache/appcache_response.h" +// This is a quick and easy 'mock' implementation of the storage interface +// that doesn't put anything to disk. +// +// We simply add an extra reference to objects when they're put in storage, +// and remove the extra reference when they are removed from storage. +// Responses are never really removed from the in-memory disk cache. +// Delegate callbacks are made asyncly to appropiately mimic what will +// happen with a real disk-backed storage impl that involves IO on a +// background thread. + namespace appcache { MockAppCacheStorage::MockAppCacheStorage(AppCacheService* service) - : AppCacheStorage(service) { + : AppCacheStorage(service), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { last_cache_id_ = 0; last_entry_id_ = 0; last_group_id_ = 0; last_response_id_ = 0; } +MockAppCacheStorage::~MockAppCacheStorage() { + STLDeleteElements(&pending_tasks_); +} + void MockAppCacheStorage::LoadCache(int64 id, Delegate* delegate) { + DCHECK(delegate); AppCache* cache = working_set_.GetCache(id); - if (cache->HasOneRef()) { - // TODO(michaeln): make the load look async + if (ShouldCacheLoadAppearAsync(cache)) { + ScheduleTask(method_factory_.NewRunnableMethod( + &MockAppCacheStorage::ProcessLoadCache, + id, GetOrCreateDelegateReference(delegate))); + return; } - delegate->OnCacheLoaded(cache, id); + ProcessLoadCache(id, GetOrCreateDelegateReference(delegate)); } void MockAppCacheStorage::LoadOrCreateGroup( const GURL& manifest_url, Delegate* delegate) { - scoped_refptr<AppCacheGroup> group = working_set_.GetGroup(manifest_url); - if (!group.get()) { - group = new AppCacheGroup(service_, manifest_url); - DCHECK(working_set_.GetGroup(manifest_url)); + DCHECK(delegate); + AppCacheGroup* group = working_set_.GetGroup(manifest_url); + if (ShouldGroupLoadAppearAsync(group)) { + ScheduleTask(method_factory_.NewRunnableMethod( + &MockAppCacheStorage::ProcessLoadOrCreateGroup, + manifest_url, GetOrCreateDelegateReference(delegate))); + return; } - // TODO(michaeln): make the load look async if all of the groups - // caches only have one ref - delegate->OnGroupLoaded(group.get(), manifest_url); + ProcessLoadOrCreateGroup( + manifest_url, GetOrCreateDelegateReference(delegate)); } void MockAppCacheStorage::StoreGroupAndNewestCache( AppCacheGroup* group, Delegate* delegate) { + DCHECK(group && delegate); DCHECK(group->newest_complete_cache()); - // TODO(michaeln): write me - // 'store' the group + newest - // 'unstore' the old caches - // figure out which responses can be doomed and doom them - // OldRepsonses - NewestResponse == ToBeDoomed - - // TODO(michaeln): Make this appear async - //AddStoredGroup(group); - //AddStoredCache(group->newest_complete_cache()); - // - //foreach(group->old_caches()) - // RemoveStoredCache(old_cache); - //std::set<int64> doomed_responses_ = responses from old caches - //std::set<int64> needed_responses_ = responses from newest cache - //foreach(needed_responses_) - // doomed_responses_.remove(needed_response_); - //DoomResponses(group->manifest_url(), doomed_responses_); - - delegate->OnGroupAndNewestCacheStored(group, false); + // Always make this operation look async. + ScheduleTask(method_factory_.NewRunnableMethod( + &MockAppCacheStorage::ProcessStoreGroupAndNewestCache, + group, group->newest_complete_cache(), + GetOrCreateDelegateReference(delegate))); } void MockAppCacheStorage::FindResponseForMainRequest( const GURL& url, Delegate* delegate) { - // TODO(michaeln): write me - // - //foreach(stored_group) { - // if (group->manifest_url()->origin() != url.GetOrigin()) - // continue; - // - // look for an entry - // look for a fallback namespace - // look for a online namespace - //} - delegate->OnMainResponseFound( - url, kNoResponseId, false, kNoCacheId, GURL::EmptyGURL()); + DCHECK(delegate); + + // Always make this operation look async. + ScheduleTask(method_factory_.NewRunnableMethod( + &MockAppCacheStorage::ProcessFindResponseForMainRequest, + url, GetOrCreateDelegateReference(delegate))); } void MockAppCacheStorage::MarkEntryAsForeign( const GURL& entry_url, int64 cache_id) { - // Update the working set. AppCache* cache = working_set_.GetCache(cache_id); if (cache) { AppCacheEntry* entry = cache->GetEntry(entry_url); @@ -96,14 +99,14 @@ void MockAppCacheStorage::MarkEntryAsForeign( // being loaded be sure to update the memory cache upon load completion. } -void MockAppCacheStorage::MarkGroupAsObsolete( +void MockAppCacheStorage::MakeGroupObsolete( AppCacheGroup* group, Delegate* delegate) { - // TODO(michaeln): write me - // remove from working_set - // remove from storage - // doom things - // group->set_obsolete(true); - // TODO(michaeln): Make this appear async + DCHECK(group && delegate); + + // Always make this method look async. + ScheduleTask(method_factory_.NewRunnableMethod( + &MockAppCacheStorage::ProcessMakeGroupObsolete, + group, GetOrCreateDelegateReference(delegate))); } AppCacheResponseReader* MockAppCacheStorage::CreateResponseReader( @@ -118,31 +121,185 @@ AppCacheResponseWriter* MockAppCacheStorage::CreateResponseWriter( void MockAppCacheStorage::DoomResponses( const GURL& manifest_url, const std::vector<int64>& response_ids) { - // We don't bother with deleting responses from mock storage. + // We don't bother with actually removing responses from the disk-cache, + // just keep track of which ids have been doomed. + std::vector<int64>::const_iterator it = response_ids.begin(); + while (it != response_ids.end()) { + doomed_response_ids_.insert(*it); + ++it; + } +} + +void MockAppCacheStorage::ProcessLoadCache( + int64 id, scoped_refptr<DelegateReference> delegate_ref) { + AppCache* cache = working_set_.GetCache(id); + if (delegate_ref->delegate) + delegate_ref->delegate->OnCacheLoaded(cache, id); +} + +void MockAppCacheStorage::ProcessLoadOrCreateGroup( + const GURL& manifest_url, scoped_refptr<DelegateReference> delegate_ref) { + scoped_refptr<AppCacheGroup> group = working_set_.GetGroup(manifest_url); + + // Newly created groups are not put in the stored_groups collection + // until StoreGroupAndNewestCache is called. + if (!group) + group = new AppCacheGroup(service_, manifest_url); + + if (delegate_ref->delegate) + delegate_ref->delegate->OnGroupLoaded(group, manifest_url); +} + +void MockAppCacheStorage::ProcessStoreGroupAndNewestCache( + scoped_refptr<AppCacheGroup> group, + scoped_refptr<AppCache> newest_cache, + scoped_refptr<DelegateReference> delegate_ref) { + DCHECK(group->newest_complete_cache() == newest_cache.get()); + + AddStoredGroup(group); + AddStoredCache(group->newest_complete_cache()); + + // Copy the collection prior to removal, on final release + // of a cache the group's collection will change. + AppCacheGroup::Caches copy = group->old_caches(); + RemoveStoredCaches(copy); + + if (delegate_ref->delegate) + delegate_ref->delegate->OnGroupAndNewestCacheStored(group, true); + + // We don't bother with removing responses from 'mock' storage + // TODO(michaeln): for 'real' storage... + // std::set<int64> doomed_responses_ = responses from old caches + // std::set<int64> needed_responses_ = responses from newest cache + // foreach(needed_responses_) + // doomed_responses_.remove(needed_response_); + // DoomResponses(group->manifest_url(), doomed_responses_); +} + +void MockAppCacheStorage::ProcessFindResponseForMainRequest( + const GURL& url, scoped_refptr<DelegateReference> delegate_ref) { + // TODO(michaeln): write me when doing AppCacheRequestHandler + // foreach(stored_group) { + // if (group->manifest_url()->origin() != url.GetOrigin()) + // continue; + // look for an entry + // look for a fallback namespace + // look for a online namespace + // } + if (delegate_ref->delegate) { + delegate_ref->delegate->OnMainResponseFound( + url, AppCacheEntry(), kNoCacheId, GURL()); + } +} + +void MockAppCacheStorage::ProcessMakeGroupObsolete( + scoped_refptr<AppCacheGroup> group, + scoped_refptr<DelegateReference> delegate_ref) { + RemoveStoredGroup(group); + if (group->newest_complete_cache()) + RemoveStoredCache(group->newest_complete_cache()); + + // Copy the collection prior to removal, on final release + // of a cache the group's collection will change. + AppCacheGroup::Caches copy = group->old_caches(); + RemoveStoredCaches(copy); + + group->set_obsolete(true); + + if (delegate_ref->delegate) + delegate_ref->delegate->OnGroupMadeObsolete(group, true); +} + +void MockAppCacheStorage::ScheduleTask(Task* task) { + pending_tasks_.push_back(task); + MessageLoop::current()->PostTask(FROM_HERE, + method_factory_.NewRunnableMethod( + &MockAppCacheStorage::RunOnePendingTask)); +} + +void MockAppCacheStorage::RunOnePendingTask() { + DCHECK(!pending_tasks_.empty()); + Task* task = pending_tasks_.front(); + pending_tasks_.pop_front(); + task->Run(); + delete task; } void MockAppCacheStorage::AddStoredCache(AppCache* cache) { -// cache->set_is_stored(true); int64 cache_id = cache->cache_id(); if (stored_caches_.find(cache_id) == stored_caches_.end()) stored_caches_.insert(StoredCacheMap::value_type(cache_id, cache)); } void MockAppCacheStorage::RemoveStoredCache(AppCache* cache) { -// cache->set_is_stored(false); + // Do not remove from the working set, active caches are still usable + // and may be looked up by id until they fall out of use. stored_caches_.erase(cache->cache_id()); } +void MockAppCacheStorage::RemoveStoredCaches( + const AppCacheGroup::Caches& caches) { + AppCacheGroup::Caches::const_iterator it = caches.begin(); + while (it != caches.end()) { + RemoveStoredCache(*it); + ++it; + } +} + void MockAppCacheStorage::AddStoredGroup(AppCacheGroup* group) { -// group->set_is_stored(true); const GURL& url = group->manifest_url(); if (stored_groups_.find(url) == stored_groups_.end()) stored_groups_.insert(StoredGroupMap::value_type(url, group)); } void MockAppCacheStorage::RemoveStoredGroup(AppCacheGroup* group) { -// group->set_is_stored(false); + // Also remove from the working set, caches for an 'obsolete' group + // may linger in use, but the group itself cannot be looked up by + // 'manifest_url' in the working set any longer. + working_set()->RemoveGroup(group); stored_groups_.erase(group->manifest_url()); } +bool MockAppCacheStorage::ShouldGroupLoadAppearAsync( + const AppCacheGroup* group) { + // We'll have to query the database to see if a group for the + // manifest_url exists on disk. So return true for async. + if (!group) + return true; + + // Groups without a newest cache can't have been put to disk yet, so + // we can synchronously return a reference we have in the working set. + if (!group->newest_complete_cache()) + return false; + + // The LoadGroup interface implies also loading the newest cache, so + // if loading the newest cache should appear async, so too must the + // loading of this group. + if (ShouldCacheLoadAppearAsync(group->newest_complete_cache())) + return true; + + + // If any of the old caches are "in use", then the group must also + // be memory resident and not require async loading. + const AppCacheGroup::Caches& old_caches = group->old_caches(); + AppCacheGroup::Caches::const_iterator it = old_caches.begin(); + while (it != old_caches.end()) { + // "in use" caches don't require async loading + if (!ShouldCacheLoadAppearAsync(*it)) + return false; + ++it; + } + + return true; +} + +bool MockAppCacheStorage::ShouldCacheLoadAppearAsync(const AppCache* cache) { + if (!cache) + return true; + + // If the 'stored' ref is the only ref, real storage will have to load from + // the database. + return IsCacheStored(cache) && cache->HasOneRef(); +} + } // namespace appcache diff --git a/webkit/appcache/mock_appcache_storage.h b/webkit/appcache/mock_appcache_storage.h index 8efb7d9..ca6bd70 100644 --- a/webkit/appcache/mock_appcache_storage.h +++ b/webkit/appcache/mock_appcache_storage.h @@ -5,10 +5,12 @@ #ifndef WEBKIT_APPCACHE_MOCK_APPCACHE_STORAGE_H_ #define WEBKIT_APPCACHE_MOCK_APPCACHE_STORAGE_H_ +#include <deque> #include <map> #include "base/hash_tables.h" #include "base/scoped_ptr.h" +#include "base/task.h" #include "net/disk_cache/disk_cache.h" #include "webkit/appcache/appcache.h" #include "webkit/appcache/appcache_group.h" @@ -23,13 +25,15 @@ namespace appcache { class MockAppCacheStorage : public AppCacheStorage { public: explicit MockAppCacheStorage(AppCacheService* service); + virtual ~MockAppCacheStorage(); + virtual void LoadCache(int64 id, Delegate* delegate); virtual void LoadOrCreateGroup(const GURL& manifest_url, Delegate* delegate); virtual void StoreGroupAndNewestCache( AppCacheGroup* group, Delegate* delegate); virtual void FindResponseForMainRequest(const GURL& url, Delegate* delegate); virtual void MarkEntryAsForeign(const GURL& entry_url, int64 cache_id); - virtual void MarkGroupAsObsolete(AppCacheGroup* group, Delegate* delegate); + virtual void MakeGroupObsolete(AppCacheGroup* group, Delegate* delegate); virtual AppCacheResponseReader* CreateResponseReader( const GURL& manifest_url, int64 response_id); virtual AppCacheResponseWriter* CreateResponseWriter(const GURL& origin); @@ -39,20 +43,40 @@ class MockAppCacheStorage : public AppCacheStorage { private: typedef base::hash_map<int64, scoped_refptr<AppCache> > StoredCacheMap; typedef std::map<GURL, scoped_refptr<AppCacheGroup> > StoredGroupMap; + typedef std::set<int64> DoomedResponseIds; + + void ProcessLoadCache( + int64 id, scoped_refptr<DelegateReference> delegate_ref); + void ProcessLoadOrCreateGroup( + const GURL& manifest_url, scoped_refptr<DelegateReference> delegate_ref); + void ProcessStoreGroupAndNewestCache( + scoped_refptr<AppCacheGroup> group, scoped_refptr<AppCache> newest_cache, + scoped_refptr<DelegateReference> delegate_ref); + void ProcessMakeGroupObsolete( + scoped_refptr<AppCacheGroup> group, + scoped_refptr<DelegateReference> delegate_ref); + void ProcessFindResponseForMainRequest( + const GURL& url, scoped_refptr<DelegateReference> delegate_ref); + + void ScheduleTask(Task* task); + void RunOnePendingTask(); void AddStoredCache(AppCache* cache); void RemoveStoredCache(AppCache* cache); - AppCache* GetStoredCache(int64 id) { - StoredCacheMap::iterator it = stored_caches_.find(id); - return (it != stored_caches_.end()) ? it->second : NULL; + void RemoveStoredCaches(const AppCacheGroup::Caches& caches); + bool IsCacheStored(const AppCache* cache) { + return stored_caches_.find(cache->cache_id()) != stored_caches_.end(); } void AddStoredGroup(AppCacheGroup* group); void RemoveStoredGroup(AppCacheGroup* group); - AppCacheGroup* GetStoredGroup(const GURL& manifest_url) { - StoredGroupMap::iterator it = stored_groups_.find(manifest_url); - return (it != stored_groups_.end()) ? it->second : NULL; - } + + // These helpers determine when certain operations should complete + // asynchronously vs synchronously to faithfully mimic, or mock, + // the behavior of the real implemenation of the AppCacheStorage + // interface. + bool ShouldGroupLoadAppearAsync(const AppCacheGroup* group); + bool ShouldCacheLoadAppearAsync(const AppCache* cache); // Lazily constructed in-memory disk cache. disk_cache::Backend* disk_cache() { @@ -65,7 +89,18 @@ class MockAppCacheStorage : public AppCacheStorage { StoredCacheMap stored_caches_; StoredGroupMap stored_groups_; + DoomedResponseIds doomed_response_ids_; scoped_ptr<disk_cache::Backend> disk_cache_; + std::deque<Task*> pending_tasks_; + ScopedRunnableMethodFactory<MockAppCacheStorage> method_factory_; + + FRIEND_TEST(MockAppCacheStorageTest, CreateGroup); + FRIEND_TEST(MockAppCacheStorageTest, LoadCache_FarHit); + FRIEND_TEST(MockAppCacheStorageTest, LoadGroupAndCache_FarHit); + FRIEND_TEST(MockAppCacheStorageTest, MakeGroupObsolete); + FRIEND_TEST(MockAppCacheStorageTest, StoreNewGroup); + FRIEND_TEST(MockAppCacheStorageTest, StoreExistingGroup); + DISALLOW_COPY_AND_ASSIGN(MockAppCacheStorage); }; } // namespace appcache diff --git a/webkit/appcache/mock_appcache_storage_unittest.cc b/webkit/appcache/mock_appcache_storage_unittest.cc new file mode 100644 index 0000000..60cd801 --- /dev/null +++ b/webkit/appcache/mock_appcache_storage_unittest.cc @@ -0,0 +1,358 @@ +// Copyright (c) 2009 The Chromium Authos. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/appcache/appcache.h" +#include "webkit/appcache/appcache_group.h" +#include "webkit/appcache/appcache_response.h" +#include "webkit/appcache/appcache_storage.h" +#include "webkit/appcache/mock_appcache_service.h" + +namespace appcache { + +class MockAppCacheStorageTest : public testing::Test { + public: + class MockStorageDelegate : public AppCacheStorage::Delegate { + public: + explicit MockStorageDelegate() + : loaded_cache_id_(0), stored_group_success_(false), + obsoleted_success_(false), found_cache_id_(kNoCacheId) { + } + + void OnCacheLoaded(AppCache* cache, int64 cache_id) { + loaded_cache_ = cache; + loaded_cache_id_ = cache_id; + } + + void OnGroupLoaded(AppCacheGroup* group, const GURL& manifest_url) { + loaded_group_ = group; + loaded_manifest_url_ = manifest_url; + } + + void OnGroupAndNewestCacheStored(AppCacheGroup* group, bool success) { + stored_group_ = group; + stored_group_success_ = success; + } + + void OnGroupMadeObsolete(AppCacheGroup* group, bool success) { + obsoleted_group_ = group; + obsoleted_success_ = success; + } + + void OnMainResponseFound(const GURL& url, const AppCacheEntry& entry, + int64 cache_id, const GURL& manifest_url) { + found_url_ = url; + found_entry_ = entry; + found_cache_id_ = cache_id; + found_manifest_url_ = manifest_url; + } + + scoped_refptr<AppCache> loaded_cache_; + int64 loaded_cache_id_; + scoped_refptr<AppCacheGroup> loaded_group_; + GURL loaded_manifest_url_; + scoped_refptr<AppCacheGroup> stored_group_; + bool stored_group_success_; + scoped_refptr<AppCacheGroup> obsoleted_group_; + bool obsoleted_success_; + GURL found_url_; + AppCacheEntry found_entry_; + int64 found_cache_id_; + GURL found_manifest_url_; + }; +}; + + +TEST_F(MockAppCacheStorageTest, LoadCache_Miss) { + // Attempt to load a cache that doesn't exist. Should + // complete asyncly. + MockAppCacheService service; + MockStorageDelegate delegate; + service.storage()->LoadCache(111, &delegate); + EXPECT_NE(111, delegate.loaded_cache_id_); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_EQ(111, delegate.loaded_cache_id_); + EXPECT_FALSE(delegate.loaded_cache_); +} + +TEST_F(MockAppCacheStorageTest, LoadCache_NearHit) { + // Attempt to load a cache that is currently in use + // and does not require loading from disk. This + // load should complete syncly. + MockAppCacheService service; + + // Setup some preconditions. Make an 'unstored' cache for + // us to load. The ctor should put it in the working set. + int64 cache_id = service.storage()->NewCacheId(); + scoped_refptr<AppCache> cache = new AppCache(&service, cache_id); + + // Conduct the test. + MockStorageDelegate delegate; + service.storage()->LoadCache(cache_id, &delegate); + EXPECT_EQ(cache_id, delegate.loaded_cache_id_); + EXPECT_EQ(cache.get(), delegate.loaded_cache_.get()); +} + +TEST_F(MockAppCacheStorageTest, CreateGroup) { + // Attempt to load/create a group that doesn't exist. + // Should complete asyncly. + MockAppCacheService service; + MockAppCacheStorage* storage = + reinterpret_cast<MockAppCacheStorage*>(service.storage()); + MockStorageDelegate delegate; + GURL manifest_url("http://blah/"); + service.storage()->LoadOrCreateGroup(manifest_url, &delegate); + EXPECT_NE(manifest_url, delegate.loaded_manifest_url_); + EXPECT_FALSE(delegate.loaded_group_.get()); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_); + EXPECT_TRUE(delegate.loaded_group_.get()); + EXPECT_TRUE(delegate.loaded_group_->HasOneRef()); + EXPECT_FALSE(delegate.loaded_group_->newest_complete_cache()); + EXPECT_TRUE(storage->stored_groups_.empty()); +} + +TEST_F(MockAppCacheStorageTest, LoadGroup_NearHit) { + // Attempt to load a group that is currently in use + // and does not require loading from disk. This + // load should complete syncly. + MockAppCacheService service; + MockStorageDelegate delegate; + + // Setup some preconditions. Create a group that appears + // to be "unstored" and "currently in use". + GURL manifest_url("http://blah/"); + service.storage()->LoadOrCreateGroup(manifest_url, &delegate); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_); + EXPECT_TRUE(delegate.loaded_group_.get()); + + // Reset our delegate, and take a reference to the new group. + scoped_refptr<AppCacheGroup> group; + group.swap(delegate.loaded_group_); + delegate.loaded_manifest_url_ = GURL(); + + // Conduct the test. + service.storage()->LoadOrCreateGroup(manifest_url, &delegate); + EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_); + EXPECT_EQ(group.get(), delegate.loaded_group_.get()); +} + +TEST_F(MockAppCacheStorageTest, LoadGroupAndCache_FarHit) { + // Attempt to load a cache that is not currently in use + // and does require loading from disk. This + // load should complete asyncly. + MockAppCacheService service; + MockAppCacheStorage* storage = + reinterpret_cast<MockAppCacheStorage*>(service.storage()); + + // Setup some preconditions. Create a group and newest cache that + // appears to be "stored" and "not currently in use". + GURL manifest_url("http://blah/"); + scoped_refptr<AppCacheGroup> group = + new AppCacheGroup(&service, manifest_url); + int64 cache_id = storage->NewCacheId(); + scoped_refptr<AppCache> cache = new AppCache(&service, cache_id); + cache->set_complete(true); + group->AddCache(cache); + storage->AddStoredGroup(group); + storage->AddStoredCache(cache); + + // Drop the references from above so the only refs to these + // objects are from within the storage class. This is to make + // these objects appear as "not currently in use". + AppCache* cache_ptr = cache.get(); + AppCacheGroup* group_ptr = group.get(); + cache = NULL; + group = NULL; + + // Setup a delegate to receive completion callbacks. + MockStorageDelegate delegate; + + // Conduct the cache load test. + EXPECT_NE(cache_id, delegate.loaded_cache_id_); + EXPECT_NE(cache_ptr, delegate.loaded_cache_.get()); + storage->LoadCache(cache_id, &delegate); + EXPECT_NE(cache_id, delegate.loaded_cache_id_); + EXPECT_NE(cache_ptr, delegate.loaded_cache_.get()); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_EQ(cache_id, delegate.loaded_cache_id_); + EXPECT_EQ(cache_ptr, delegate.loaded_cache_.get()); + delegate.loaded_cache_ = NULL; + + // Conduct the group load test. + EXPECT_NE(manifest_url, delegate.loaded_manifest_url_); + EXPECT_FALSE(delegate.loaded_group_.get()); + storage->LoadOrCreateGroup(manifest_url, &delegate); + EXPECT_NE(manifest_url, delegate.loaded_manifest_url_); + EXPECT_FALSE(delegate.loaded_group_.get()); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_EQ(manifest_url, delegate.loaded_manifest_url_); + EXPECT_EQ(group_ptr, delegate.loaded_group_.get()); +} + +TEST_F(MockAppCacheStorageTest, StoreNewGroup) { + // Store a group and its newest cache. Should complete asyncly. + MockAppCacheService service; + MockAppCacheStorage* storage = + reinterpret_cast<MockAppCacheStorage*>(service.storage()); + + // Setup some preconditions. Create a group and newest cache that + // appears to be "unstored". + GURL manifest_url("http://blah/"); + scoped_refptr<AppCacheGroup> group = + new AppCacheGroup(&service, manifest_url); + int64 cache_id = storage->NewCacheId(); + scoped_refptr<AppCache> cache = new AppCache(&service, cache_id); + cache->set_complete(true); + group->AddCache(cache); + // 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. + MockStorageDelegate delegate; + EXPECT_TRUE(storage->stored_caches_.empty()); + EXPECT_TRUE(storage->stored_groups_.empty()); + storage->StoreGroupAndNewestCache(group, &delegate); + EXPECT_FALSE(delegate.stored_group_success_); + EXPECT_TRUE(storage->stored_caches_.empty()); + EXPECT_TRUE(storage->stored_groups_.empty()); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_TRUE(delegate.stored_group_success_); + EXPECT_FALSE(storage->stored_caches_.empty()); + EXPECT_FALSE(storage->stored_groups_.empty()); +} + +TEST_F(MockAppCacheStorageTest, StoreExistingGroup) { + // Store a group and its newest cache. Should complete asyncly. + MockAppCacheService service; + MockAppCacheStorage* storage = + reinterpret_cast<MockAppCacheStorage*>(service.storage()); + + // Setup some preconditions. Create a group and old complete cache + // that appear to be "stored", and a newest unstored complete cache. + GURL manifest_url("http://blah/"); + scoped_refptr<AppCacheGroup> group = + new AppCacheGroup(&service, manifest_url); + int64 old_cache_id = storage->NewCacheId(); + scoped_refptr<AppCache> old_cache = new AppCache(&service, old_cache_id); + old_cache->set_complete(true); + group->AddCache(old_cache); + storage->AddStoredGroup(group); + storage->AddStoredCache(old_cache); + int64 new_cache_id = storage->NewCacheId(); + scoped_refptr<AppCache> new_cache = new AppCache(&service, new_cache_id); + new_cache->set_complete(true); + group->AddCache(new_cache); + EXPECT_EQ(new_cache.get(), group->newest_complete_cache()); + // Hold our refs to simulate the UpdateJob holding these refs. + + // Conduct the test. + MockStorageDelegate delegate; + EXPECT_EQ(size_t(1), storage->stored_caches_.size()); + EXPECT_EQ(size_t(1), storage->stored_groups_.size()); + EXPECT_TRUE(storage->IsCacheStored(old_cache)); + EXPECT_FALSE(storage->IsCacheStored(new_cache)); + storage->StoreGroupAndNewestCache(group, &delegate); + EXPECT_FALSE(delegate.stored_group_success_); + EXPECT_EQ(size_t(1), storage->stored_caches_.size()); + EXPECT_EQ(size_t(1), storage->stored_groups_.size()); + EXPECT_TRUE(storage->IsCacheStored(old_cache)); + EXPECT_FALSE(storage->IsCacheStored(new_cache)); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_TRUE(delegate.stored_group_success_); + EXPECT_EQ(size_t(1), storage->stored_caches_.size()); + EXPECT_EQ(size_t(1), storage->stored_groups_.size()); + EXPECT_FALSE(storage->IsCacheStored(old_cache)); + EXPECT_TRUE(storage->IsCacheStored(new_cache)); +} + +TEST_F(MockAppCacheStorageTest, MakeGroupObsolete) { + // Make a group obsolete, should complete asyncly. + MockAppCacheService service; + MockAppCacheStorage* storage = + reinterpret_cast<MockAppCacheStorage*>(service.storage()); + + // Setup some preconditions. Create a group and newest cache that + // appears to be "stored" and "currently in use". + GURL manifest_url("http://blah/"); + scoped_refptr<AppCacheGroup> group = + new AppCacheGroup(&service, manifest_url); + int64 cache_id = storage->NewCacheId(); + scoped_refptr<AppCache> cache = new AppCache(&service, cache_id); + cache->set_complete(true); + group->AddCache(cache); + storage->AddStoredGroup(group); + storage->AddStoredCache(cache); + // Hold our refs to simulate the UpdateJob holding these refs. + + // Conduct the test. + MockStorageDelegate delegate; + EXPECT_FALSE(group->is_obsolete()); + EXPECT_EQ(size_t(1), storage->stored_caches_.size()); + EXPECT_EQ(size_t(1), storage->stored_groups_.size()); + EXPECT_FALSE(cache->HasOneRef()); + EXPECT_FALSE(group->HasOneRef()); + storage->MakeGroupObsolete(group, &delegate); + EXPECT_FALSE(group->is_obsolete()); + EXPECT_EQ(size_t(1), storage->stored_caches_.size()); + EXPECT_EQ(size_t(1), storage->stored_groups_.size()); + EXPECT_FALSE(cache->HasOneRef()); + EXPECT_FALSE(group->HasOneRef()); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_TRUE(delegate.obsoleted_success_); + EXPECT_EQ(group.get(), delegate.obsoleted_group_.get()); + EXPECT_TRUE(group->is_obsolete()); + EXPECT_TRUE(storage->stored_caches_.empty()); + EXPECT_TRUE(storage->stored_groups_.empty()); + EXPECT_TRUE(cache->HasOneRef()); + EXPECT_FALSE(group->HasOneRef()); + delegate.obsoleted_group_ = NULL; + cache = NULL; + EXPECT_TRUE(group->HasOneRef()); +} + +TEST_F(MockAppCacheStorageTest, MarkEntryAsForeign) { + // Should complete syncly. + MockAppCacheService service; + MockAppCacheStorage* storage = + reinterpret_cast<MockAppCacheStorage*>(service.storage()); + + // Setup some preconditions. Create a cache with an entry. + GURL entry_url("http://blan/entry"); + int64 cache_id = storage->NewCacheId(); + scoped_refptr<AppCache> cache = new AppCache(&service, cache_id); + cache->AddEntry(entry_url, AppCacheEntry(AppCacheEntry::EXPLICIT)); + + // Conduct the test. + MockStorageDelegate delegate; + EXPECT_FALSE(cache->GetEntry(entry_url)->IsForeign()); + storage->MarkEntryAsForeign(entry_url, cache_id); + EXPECT_TRUE(cache->GetEntry(entry_url)->IsForeign()); + EXPECT_TRUE(cache->GetEntry(entry_url)->IsExplicit()); +} + +TEST_F(MockAppCacheStorageTest, FindNoMainResponse) { + // Should complete asyncly. + MockAppCacheService service; + MockAppCacheStorage* storage = + reinterpret_cast<MockAppCacheStorage*>(service.storage()); + + // Conduct the test. + MockStorageDelegate delegate; + GURL url("http://blah/some_url"); + EXPECT_NE(url, delegate.found_url_); + storage->FindResponseForMainRequest(url, &delegate); + EXPECT_NE(url, delegate.found_url_); + MessageLoop::current()->RunAllPending(); // Do async task execution. + EXPECT_EQ(url, 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(0, delegate.found_entry_.types()); +} + +} // namespace appcache + diff --git a/webkit/tools/test_shell/test_shell.gyp b/webkit/tools/test_shell/test_shell.gyp index 5c6e100..e33f443 100644 --- a/webkit/tools/test_shell/test_shell.gyp +++ b/webkit/tools/test_shell/test_shell.gyp @@ -372,6 +372,7 @@ '../../appcache/appcache_storage_unittest.cc', '../../appcache/appcache_update_job_unittest.cc', '../../appcache/mock_appcache_service.h', + '../../appcache/mock_appcache_storage_unittest.cc', '../../glue/bookmarklet_unittest.cc', '../../glue/context_menu_unittest.cc', '../../glue/cpp_bound_class_unittest.cc', |