summaryrefslogtreecommitdiffstats
path: root/webkit
diff options
context:
space:
mode:
Diffstat (limited to 'webkit')
-rw-r--r--webkit/appcache/appcache_entry.h16
-rw-r--r--webkit/appcache/appcache_group.h15
-rw-r--r--webkit/appcache/appcache_response_unittest.cc44
-rw-r--r--webkit/appcache/appcache_storage.h34
-rw-r--r--webkit/appcache/appcache_storage_unittest.cc51
-rw-r--r--webkit/appcache/mock_appcache_storage.cc267
-rw-r--r--webkit/appcache/mock_appcache_storage.h51
-rw-r--r--webkit/appcache/mock_appcache_storage_unittest.cc358
-rw-r--r--webkit/tools/test_shell/test_shell.gyp1
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',