summaryrefslogtreecommitdiffstats
path: root/webkit/appcache/appcache_storage_impl_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/appcache/appcache_storage_impl_unittest.cc')
-rw-r--r--webkit/appcache/appcache_storage_impl_unittest.cc1007
1 files changed, 1007 insertions, 0 deletions
diff --git a/webkit/appcache/appcache_storage_impl_unittest.cc b/webkit/appcache/appcache_storage_impl_unittest.cc
new file mode 100644
index 0000000..1db4aa8
--- /dev/null
+++ b/webkit/appcache/appcache_storage_impl_unittest.cc
@@ -0,0 +1,1007 @@
+// Copyright (c) 2009 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 "base/message_loop.h"
+#include "base/thread.h"
+#include "base/waitable_event.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/tools/test_shell/simple_appcache_system.h"
+
+namespace appcache {
+
+namespace {
+
+const base::TimeTicks kZeroTimeTicks;
+const GURL kManifestUrl("http://blah/manifest");
+const GURL kManifestUrl2("http://blah/manifest2");
+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");
+
+// For the duration of this test case, we hijack the AppCacheThread API
+// calls and implement them in terms of the io and db threads created here.
+
+scoped_ptr<base::Thread> io_thread;
+scoped_ptr<base::Thread> db_thread;
+
+class TestThreadProvider : public SimpleAppCacheSystem::ThreadProvider {
+ public:
+ virtual bool PostTask(
+ int id,
+ const tracked_objects::Location& from_here,
+ Task* task) {
+ GetMessageLoop(id)->PostTask(from_here, task);
+ return true;
+ }
+
+ virtual bool CurrentlyOn(int id) {
+ return MessageLoop::current() == GetMessageLoop(id);
+ }
+
+ MessageLoop* GetMessageLoop(int id) {
+ DCHECK(io_thread.get() && db_thread.get());
+ if (id == SimpleAppCacheSystem::IO_THREAD_ID)
+ return io_thread->message_loop();
+ if (id == SimpleAppCacheSystem::DB_THREAD_ID)
+ return db_thread->message_loop();
+ NOTREACHED() << "Invalid AppCacheThreadID value";
+ return NULL;
+ }
+};
+
+TestThreadProvider thread_provider;
+
+} // 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),
+ 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, bool success) {
+ stored_group_ = group;
+ stored_group_success_ = success;
+ 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 AppCacheEntry& fallback_entry,
+ int64 cache_id, const GURL& manifest_url) {
+ found_url_ = url;
+ found_entry_ = entry;
+ found_fallback_entry_ = fallback_entry;
+ found_cache_id_ = cache_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_;
+ scoped_refptr<AppCacheGroup> obsoleted_group_;
+ bool obsoleted_success_;
+ GURL found_url_;
+ AppCacheEntry found_entry_;
+ AppCacheEntry found_fallback_entry_;
+ int64 found_cache_id_;
+ GURL found_manifest_url_;
+ AppCacheStorageImplTest* test_;
+ };
+
+ // Helper class run a test on our io_thread. The io_thread
+ // is spun up once and reused for all tests.
+ template <class Method>
+ class WrapperTask : public Task {
+ public:
+ WrapperTask(AppCacheStorageImplTest* test, Method method)
+ : test_(test), method_(method) {
+ }
+
+ virtual void Run() {
+ test_->SetUpTest();
+
+ // Ensure InitTask execution prior to conducting a test.
+ 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,
+ NewRunnableFunction(&RunMethod, test_, method_));
+ }
+
+ static void RunMethod(AppCacheStorageImplTest* test, Method method) {
+ (test->*method)();
+ }
+
+ private:
+ AppCacheStorageImplTest* test_;
+ Method 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());
+
+ SimpleAppCacheSystem::set_thread_provider(&thread_provider);
+ }
+
+ static void TearDownTestCase() {
+ SimpleAppCacheSystem::set_thread_provider(NULL);
+ io_thread.reset(NULL);
+ db_thread.reset(NULL);
+ }
+
+ // Test harness --------------------------------------------------
+
+ AppCacheStorageImplTest()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
+ }
+
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ test_finished_event_ .reset(new base::WaitableEvent(false, false));
+ io_thread->message_loop()->PostTask(
+ FROM_HERE, new WrapperTask<Method>(this, method));
+ test_finished_event_->Wait();
+ }
+
+ void SetUpTest() {
+ DCHECK(MessageLoop::current() == io_thread->message_loop());
+ service_.reset(new AppCacheService);
+ service_->Initialize(FilePath());
+ delegate_.reset(new MockStorageDelegate(this));
+ }
+
+ void TearDownTest() {
+ DCHECK(MessageLoop::current() == io_thread->message_loop());
+ storage()->CancelDelegateCallbacks(delegate());
+ group_ = NULL;
+ cache_ = NULL;
+ cache2_ = 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,
+ method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::TestFinishedUnwound));
+ }
+
+ void TestFinishedUnwound() {
+ TearDownTest();
+ test_finished_event_->Signal();
+ }
+
+ void PushNextTask(Task* 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,
+ NewRunnableFunction(&AppCacheStorageImplTest::SignalEvent,
+ &event));
+ event.Wait();
+ }
+
+ // LoadCache_Miss ----------------------------------------------------
+
+ void LoadCache_Miss() {
+ // Attempt to load a cache that doesn't exist. Should
+ // complete asyncly.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_LoadCache_Miss));
+
+ 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_);
+ 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());
+ 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(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_CreateGroup));
+
+ // Since the origin has groups, storage class will have to
+ // consult the database and completion will be async.
+ storage()->origins_with_groups_.insert(kManifestUrl.GetOrigin());
+
+ 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));
+
+ 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 asyncly.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_LoadCache_Far_Hit));
+
+ // 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());
+
+ // Drop things from the working set.
+ delegate()->loaded_cache_ = NULL;
+ EXPECT_FALSE(delegate()->loaded_group_);
+
+ // Conduct the group load test, also complete asyncly.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_LoadGroup_Far_Hit));
+
+ 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());
+ TestFinished();
+ }
+
+ // StoreNewGroup --------------------------------------
+
+ void StoreNewGroup() {
+ // Store a group and its newest cache. Should complete asyncly.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_StoreNewGroup));
+
+ // 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_->set_complete(true);
+ // 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_);
+ }
+
+ 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());
+
+ // 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));
+ TestFinished();
+ }
+
+ // StoreExistingGroup --------------------------------------
+
+ void StoreExistingGroup() {
+ // Store a group and its newest cache. Should complete asyncly.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_StoreExistingGroup));
+
+ // Setup some preconditions. Create a group and old complete cache
+ // that appear to be "stored"
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+
+ // And a newest unstored complete cache.
+ cache2_ = new AppCache(service(), 2);
+ cache2_->set_complete(true);
+
+ // 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());
+
+ // 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));
+ 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);
+
+ // Change the cache.
+ base::TimeTicks now = base::TimeTicks::Now();
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::MASTER));
+ cache_->set_update_time(now);
+
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_StoreExistingGroupExistingCache,
+ 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::TimeTicks 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);
+
+ std::vector<AppCacheDatabase::EntryRecord> entry_records;
+ EXPECT_TRUE(database()->FindEntriesForCache(1, &entry_records));
+ EXPECT_EQ(1U, entry_records.size());
+ 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(0, entry_records[0].response_id);
+
+ TestFinished();
+ }
+
+ // MakeGroupObsolete -------------------------------
+
+ void MakeGroupObsolete() {
+ // Make a group obsolete, should complete asyncly.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_MakeGroupObsolete));
+
+ // Setup some preconditions. Create a group and newest cache that
+ // appears to be "stored" and "currently in use".
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ EXPECT_FALSE(storage()->origins_with_groups_.empty());
+
+ // 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::FallbackNameSpaceRecord fallback_namespace_record;
+ fallback_namespace_record.cache_id = 1;
+ fallback_namespace_record.fallback_entry_url = kEntryUrl;
+ fallback_namespace_record.namespace_url = kFallbackNamespace;
+ fallback_namespace_record.origin = kManifestUrl.GetOrigin();
+ EXPECT_TRUE(
+ database()->InsertFallbackNameSpace(&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()->origins_with_groups_.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::FallbackNameSpaceRecord> fallback_records;
+ database()->FindFallbackNameSpacesForCache(1, &fallback_records);
+ EXPECT_TRUE(fallback_records.empty());
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelist_records;
+ database()->FindOnlineWhiteListForCache(1, &whitelist_records);
+ EXPECT_TRUE(whitelist_records.empty());
+
+ 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 asyncly 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(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_MarkEntryAsForeignWithLoadInProgress));
+
+ // 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(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_FindNoMainResponse));
+
+ // Conduct the test.
+ storage()->FindResponseForMainRequest(kEntryUrl, 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_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(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_BasicFindMainResponse));
+
+ // Setup some preconditions. Create a complete cache with an entry
+ // in storage.
+ MakeCacheAndGroup(kManifestUrl, 1, 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, 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(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(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_BasicFindMainFallbackResponse));
+
+ // Setup some preconditions. Create a complete cache with a
+ // fallback namespace and entry.
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1));
+ cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2));
+ cache_->fallback_namespaces_.push_back(
+ FallbackNamespace(kFallbackNamespace2, kEntryUrl2));
+ cache_->fallback_namespaces_.push_back(
+ FallbackNamespace(kFallbackNamespace, kEntryUrl));
+ AppCacheDatabase::CacheRecord cache_record;
+ std::vector<AppCacheDatabase::EntryRecord> entries;
+ std::vector<AppCacheDatabase::FallbackNameSpaceRecord> fallbacks;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
+ cache_->ToDatabaseRecords(group_,
+ &cache_record, &entries, &fallbacks, &whitelists);
+ EXPECT_TRUE(database()->InsertEntryRecords(entries));
+ EXPECT_TRUE(database()->InsertFallbackNameSpaceRecords(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, 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_FALSE(delegate()->found_entry_.has_response_id());
+ EXPECT_EQ(2, delegate()->found_fallback_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
+ TestFinished();
+ }
+
+ // FindMainResponseWithMultipleHits -------------------------------
+
+ void FindMainResponseWithMultipleHits() {
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits));
+
+ // Setup some preconditions. Create 2 complete caches with an entry
+ // for the same url.
+
+ // The first cache, in the database but not in the working set.
+ MakeCacheAndGroup(kManifestUrl, 1, 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));
+ cache_ = NULL;
+ group_ = NULL;
+
+ // The second cache, in the database and working set.
+ MakeCacheAndGroup(kManifestUrl2, 2, 2, true);
+ cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, 2));
+ entry_record.cache_id = 2;
+ entry_record.url = kEntryUrl;
+ entry_record.flags = AppCacheEntry::EXPLICIT;
+ entry_record.response_id = 2;
+ EXPECT_TRUE(database()->InsertEntry(&entry_record));
+
+ // Conduct the test, we should find the response from the second cache
+ // since it's "in use".
+ storage()->FindResponseForMainRequest(kEntryUrl, delegate());
+ EXPECT_NE(kEntryUrl, delegate()->found_url_);
+ }
+
+ void Verify_FindMainResponseWithMultipleHits() {
+ 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_entry_.response_id());
+ EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
+ EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
+ 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 and an online namespace.
+ MakeCacheAndGroup(kManifestUrl, 1, 1, true);
+ cache_->AddEntry(kEntryUrl,
+ AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, 1));
+ cache_->online_whitelist_namespaces_.push_back(kOnlineNamespace);
+ 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));
+ if (drop_from_working_set) {
+ cache_ = NULL;
+ group_ = NULL;
+ }
+
+ // We should not find anything for the foreign entry.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_NotFound, kEntryUrl, false));
+ storage()->FindResponseForMainRequest(kEntryUrl, delegate());
+ }
+
+ void Verify_NotFound(GURL expected_url, bool test_finished) {
+ EXPECT_EQ(expected_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(kNoResponseId, delegate()->found_fallback_entry_.response_id());
+ EXPECT_EQ(0, delegate()->found_entry_.types());
+ EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
+
+ if (!test_finished) {
+ // We should not find anything for the online namespace.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheStorageImplTest::Verify_NotFound, kOnlineNamespace, true));
+ storage()->FindResponseForMainRequest(kOnlineNamespace, 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) {
+ group_ = new AppCacheGroup(service(), manifest_url, group_id);
+ cache_ = new AppCache(service(), cache_id);
+ 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 = kZeroTimeTicks;
+ EXPECT_TRUE(database()->InsertCache(&cache_record));
+ storage()->origins_with_groups_.insert(manifest_url.GetOrigin());
+ }
+ }
+
+ // Data members --------------------------------------------------
+
+ ScopedRunnableMethodFactory<AppCacheStorageImplTest> method_factory_;
+ scoped_ptr<base::WaitableEvent> test_finished_event_;
+ std::stack<Task*> task_stack_;
+ scoped_ptr<AppCacheService> service_;
+ scoped_ptr<MockStorageDelegate> delegate_;
+ 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, 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, FindMainResponseWithMultipleHits) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindMainResponseWithMultipleHits);
+}
+
+TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInDatabase) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindMainResponseExclusionsInDatabase);
+}
+
+TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInWorkingSet) {
+ RunTestOnIOThread(
+ &AppCacheStorageImplTest::FindMainResponseExclusionsInWorkingSet);
+}
+
+// That's all folks!
+
+} // namespace appcache
+