1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stack>
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/callback.h"
10#include "base/files/file_util.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/message_loop/message_loop.h"
14#include "base/synchronization/waitable_event.h"
15#include "base/threading/thread.h"
16#include "content/browser/appcache/appcache.h"
17#include "content/browser/appcache/appcache_backend_impl.h"
18#include "content/browser/appcache/appcache_database.h"
19#include "content/browser/appcache/appcache_entry.h"
20#include "content/browser/appcache/appcache_group.h"
21#include "content/browser/appcache/appcache_host.h"
22#include "content/browser/appcache/appcache_interceptor.h"
23#include "content/browser/appcache/appcache_request_handler.h"
24#include "content/browser/appcache/appcache_service_impl.h"
25#include "content/browser/appcache/appcache_storage_impl.h"
26#include "net/base/net_errors.h"
27#include "net/base/request_priority.h"
28#include "net/http/http_response_headers.h"
29#include "net/url_request/url_request_error_job.h"
30#include "net/url_request/url_request_job_factory_impl.h"
31#include "net/url_request/url_request_test_job.h"
32#include "net/url_request/url_request_test_util.h"
33#include "sql/test/test_helpers.h"
34#include "storage/browser/quota/quota_manager.h"
35#include "testing/gtest/include/gtest/gtest.h"
36
37namespace content {
38
39namespace {
40
41const base::Time kZeroTime;
42const GURL kManifestUrl("http://blah/manifest");
43const GURL kManifestUrl2("http://blah/manifest2");
44const GURL kManifestUrl3("http://blah/manifest3");
45const GURL kEntryUrl("http://blah/entry");
46const GURL kEntryUrl2("http://blah/entry2");
47const GURL kFallbackNamespace("http://blah/fallback_namespace/");
48const GURL kFallbackNamespace2("http://blah/fallback_namespace/longer");
49const GURL kFallbackTestUrl("http://blah/fallback_namespace/longer/test");
50const GURL kOnlineNamespace("http://blah/online_namespace");
51const GURL kOnlineNamespaceWithinFallback(
52    "http://blah/fallback_namespace/online/");
53const GURL kInterceptNamespace("http://blah/intercept_namespace/");
54const GURL kInterceptNamespace2("http://blah/intercept_namespace/longer/");
55const GURL kInterceptTestUrl("http://blah/intercept_namespace/longer/test");
56const GURL kInterceptPatternNamespace("http://blah/intercept_pattern/*/bar");
57const GURL kInterceptPatternTestPositiveUrl(
58    "http://blah/intercept_pattern/foo/bar");
59const GURL kInterceptPatternTestNegativeUrl(
60    "http://blah/intercept_pattern/foo/not_bar");
61const GURL kFallbackPatternNamespace("http://blah/fallback_pattern/*/bar");
62const GURL kFallbackPatternTestPositiveUrl(
63    "http://blah/fallback_pattern/foo/bar");
64const GURL kFallbackPatternTestNegativeUrl(
65    "http://blah/fallback_pattern/foo/not_bar");
66const GURL kOrigin(kManifestUrl.GetOrigin());
67
68const int kManifestEntryIdOffset = 100;
69const int kFallbackEntryIdOffset = 1000;
70
71const GURL kDefaultEntryUrl("http://blah/makecacheandgroup_default_entry");
72const int kDefaultEntrySize = 10;
73const int kDefaultEntryIdOffset = 12345;
74
75const int kMockQuota = 5000;
76
77// The Reinitialize test needs some http accessible resources to run,
78// we mock stuff inprocess for that.
79class MockHttpServer {
80 public:
81  static GURL GetMockUrl(const std::string& path) {
82    return GURL("http://mockhost/" + path);
83  }
84
85  static net::URLRequestJob* CreateJob(
86      net::URLRequest* request, net::NetworkDelegate* network_delegate) {
87    if (request->url().host() != "mockhost")
88      return new net::URLRequestErrorJob(request, network_delegate, -100);
89
90    std::string headers, body;
91    GetMockResponse(request->url().path(), &headers, &body);
92    return new net::URLRequestTestJob(
93        request, network_delegate, headers, body, true);
94  }
95
96 private:
97  static void GetMockResponse(const std::string& path,
98                              std::string* headers,
99                              std::string* body) {
100    const char manifest_headers[] =
101        "HTTP/1.1 200 OK\0"
102        "Content-type: text/cache-manifest\0"
103        "\0";
104    const char page_headers[] =
105        "HTTP/1.1 200 OK\0"
106        "Content-type: text/html\0"
107        "\0";
108    const char not_found_headers[] =
109        "HTTP/1.1 404 NOT FOUND\0"
110        "\0";
111
112    if (path == "/manifest") {
113      (*headers) = std::string(manifest_headers, arraysize(manifest_headers));
114      (*body) = "CACHE MANIFEST\n";
115    } else if (path == "/empty.html") {
116      (*headers) = std::string(page_headers, arraysize(page_headers));
117      (*body) = "";
118    } else {
119      (*headers) = std::string(not_found_headers,
120                               arraysize(not_found_headers));
121      (*body) = "";
122    }
123  }
124};
125
126class MockHttpServerJobFactory
127    : public net::URLRequestJobFactory::ProtocolHandler {
128 public:
129  virtual net::URLRequestJob* MaybeCreateJob(
130      net::URLRequest* request,
131      net::NetworkDelegate* network_delegate) const OVERRIDE {
132    return MockHttpServer::CreateJob(request, network_delegate);
133  }
134};
135
136class IOThread : public base::Thread {
137 public:
138  explicit IOThread(const char* name)
139      : base::Thread(name) {
140  }
141
142  virtual ~IOThread() {
143    Stop();
144  }
145
146  net::URLRequestContext* request_context() {
147    return request_context_.get();
148  }
149
150  virtual void Init() OVERRIDE {
151    scoped_ptr<net::URLRequestJobFactoryImpl> factory(
152        new net::URLRequestJobFactoryImpl());
153    factory->SetProtocolHandler("http", new MockHttpServerJobFactory);
154    job_factory_ = factory.Pass();
155    request_context_.reset(new net::TestURLRequestContext());
156    request_context_->set_job_factory(job_factory_.get());
157    AppCacheInterceptor::EnsureRegistered();
158  }
159
160  virtual void CleanUp() OVERRIDE {
161    request_context_.reset();
162    job_factory_.reset();
163  }
164
165 private:
166  scoped_ptr<net::URLRequestJobFactory> job_factory_;
167  scoped_ptr<net::URLRequestContext> request_context_;
168};
169
170scoped_ptr<IOThread> io_thread;
171scoped_ptr<base::Thread> db_thread;
172
173}  // namespace
174
175class AppCacheStorageImplTest : public testing::Test {
176 public:
177  class MockStorageDelegate : public AppCacheStorage::Delegate {
178   public:
179    explicit MockStorageDelegate(AppCacheStorageImplTest* test)
180        : loaded_cache_id_(0), stored_group_success_(false),
181          would_exceed_quota_(false), obsoleted_success_(false),
182          found_cache_id_(kAppCacheNoCacheId), test_(test) {
183    }
184
185    virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) OVERRIDE {
186      loaded_cache_ = cache;
187      loaded_cache_id_ = cache_id;
188      test_->ScheduleNextTask();
189    }
190
191    virtual void OnGroupLoaded(AppCacheGroup* group,
192                               const GURL& manifest_url) OVERRIDE {
193      loaded_group_ = group;
194      loaded_manifest_url_ = manifest_url;
195      loaded_groups_newest_cache_ = group ? group->newest_complete_cache()
196                                          : NULL;
197      test_->ScheduleNextTask();
198    }
199
200    virtual void OnGroupAndNewestCacheStored(
201        AppCacheGroup* group, AppCache* newest_cache, bool success,
202        bool would_exceed_quota) OVERRIDE {
203      stored_group_ = group;
204      stored_group_success_ = success;
205      would_exceed_quota_ = would_exceed_quota;
206      test_->ScheduleNextTask();
207    }
208
209    virtual void OnGroupMadeObsolete(AppCacheGroup* group,
210                                     bool success,
211                                     int response_code) OVERRIDE {
212      obsoleted_group_ = group;
213      obsoleted_success_ = success;
214      test_->ScheduleNextTask();
215    }
216
217    virtual void OnMainResponseFound(const GURL& url,
218                                     const AppCacheEntry& entry,
219                                     const GURL& namespace_entry_url,
220                                     const AppCacheEntry& fallback_entry,
221                                     int64 cache_id,
222                                     int64 group_id,
223                                     const GURL& manifest_url) OVERRIDE {
224      found_url_ = url;
225      found_entry_ = entry;
226      found_namespace_entry_url_ = namespace_entry_url;
227      found_fallback_entry_ = fallback_entry;
228      found_cache_id_ = cache_id;
229      found_group_id_ = group_id;
230      found_manifest_url_ = manifest_url;
231      test_->ScheduleNextTask();
232    }
233
234    scoped_refptr<AppCache> loaded_cache_;
235    int64 loaded_cache_id_;
236    scoped_refptr<AppCacheGroup> loaded_group_;
237    GURL loaded_manifest_url_;
238    scoped_refptr<AppCache> loaded_groups_newest_cache_;
239    scoped_refptr<AppCacheGroup> stored_group_;
240    bool stored_group_success_;
241    bool would_exceed_quota_;
242    scoped_refptr<AppCacheGroup> obsoleted_group_;
243    bool obsoleted_success_;
244    GURL found_url_;
245    AppCacheEntry found_entry_;
246    GURL found_namespace_entry_url_;
247    AppCacheEntry found_fallback_entry_;
248    int64 found_cache_id_;
249    int64 found_group_id_;
250    GURL found_manifest_url_;
251    AppCacheStorageImplTest* test_;
252  };
253
254  class MockQuotaManager : public storage::QuotaManager {
255   public:
256    MockQuotaManager()
257        : QuotaManager(true /* is_incognito */,
258                       base::FilePath(),
259                       io_thread->message_loop_proxy().get(),
260                       db_thread->message_loop_proxy().get(),
261                       NULL),
262          async_(false) {}
263
264    virtual void GetUsageAndQuota(
265        const GURL& origin,
266        storage::StorageType type,
267        const GetUsageAndQuotaCallback& callback) OVERRIDE {
268      EXPECT_EQ(storage::kStorageTypeTemporary, type);
269      if (async_) {
270        base::MessageLoop::current()->PostTask(
271            FROM_HERE,
272            base::Bind(&MockQuotaManager::CallCallback,
273                       base::Unretained(this),
274                       callback));
275        return;
276      }
277      CallCallback(callback);
278    }
279
280    void CallCallback(const GetUsageAndQuotaCallback& callback) {
281      callback.Run(storage::kQuotaStatusOk, 0, kMockQuota);
282    }
283
284    bool async_;
285
286   protected:
287    virtual ~MockQuotaManager() {}
288  };
289
290  class MockQuotaManagerProxy : public storage::QuotaManagerProxy {
291   public:
292    MockQuotaManagerProxy()
293        : QuotaManagerProxy(NULL, NULL),
294          notify_storage_accessed_count_(0),
295          notify_storage_modified_count_(0),
296          last_delta_(0),
297          mock_manager_(new MockQuotaManager) {
298      manager_ = mock_manager_.get();
299    }
300
301    virtual void NotifyStorageAccessed(storage::QuotaClient::ID client_id,
302                                       const GURL& origin,
303                                       storage::StorageType type) OVERRIDE {
304      EXPECT_EQ(storage::QuotaClient::kAppcache, client_id);
305      EXPECT_EQ(storage::kStorageTypeTemporary, type);
306      ++notify_storage_accessed_count_;
307      last_origin_ = origin;
308    }
309
310    virtual void NotifyStorageModified(storage::QuotaClient::ID client_id,
311                                       const GURL& origin,
312                                       storage::StorageType type,
313                                       int64 delta) OVERRIDE {
314      EXPECT_EQ(storage::QuotaClient::kAppcache, client_id);
315      EXPECT_EQ(storage::kStorageTypeTemporary, type);
316      ++notify_storage_modified_count_;
317      last_origin_ = origin;
318      last_delta_ = delta;
319    }
320
321    // Not needed for our tests.
322    virtual void RegisterClient(storage::QuotaClient* client) OVERRIDE {}
323    virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {}
324    virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {}
325    virtual void SetUsageCacheEnabled(storage::QuotaClient::ID client_id,
326                                      const GURL& origin,
327                                      storage::StorageType type,
328                                      bool enabled) OVERRIDE {}
329    virtual void GetUsageAndQuota(
330        base::SequencedTaskRunner* original_task_runner,
331        const GURL& origin,
332        storage::StorageType type,
333        const GetUsageAndQuotaCallback& callback) OVERRIDE {}
334
335    int notify_storage_accessed_count_;
336    int notify_storage_modified_count_;
337    GURL last_origin_;
338    int last_delta_;
339    scoped_refptr<MockQuotaManager> mock_manager_;
340
341   protected:
342    virtual ~MockQuotaManagerProxy() {}
343  };
344
345  template <class Method>
346  void RunMethod(Method method) {
347    (this->*method)();
348  }
349
350  // Helper callback to run a test on our io_thread. The io_thread is spun up
351  // once and reused for all tests.
352  template <class Method>
353  void MethodWrapper(Method method) {
354    SetUpTest();
355
356    // Ensure InitTask execution prior to conducting a test.
357    FlushDbThreadTasks();
358
359    // We also have to wait for InitTask completion call to be performed
360    // on the IO thread prior to running the test. Its guaranteed to be
361    // queued by this time.
362    base::MessageLoop::current()->PostTask(
363        FROM_HERE,
364        base::Bind(&AppCacheStorageImplTest::RunMethod<Method>,
365                   base::Unretained(this),
366                   method));
367  }
368
369  static void SetUpTestCase() {
370    // We start both threads as TYPE_IO because we also use the db_thead
371    // for the disk_cache which needs to be of TYPE_IO.
372    base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
373    io_thread.reset(new IOThread("AppCacheTest.IOThread"));
374    ASSERT_TRUE(io_thread->StartWithOptions(options));
375    db_thread.reset(new base::Thread("AppCacheTest::DBThread"));
376    ASSERT_TRUE(db_thread->StartWithOptions(options));
377  }
378
379  static void TearDownTestCase() {
380    io_thread.reset(NULL);
381    db_thread.reset(NULL);
382  }
383
384  // Test harness --------------------------------------------------
385
386  AppCacheStorageImplTest() {
387  }
388
389  template <class Method>
390  void RunTestOnIOThread(Method method) {
391    test_finished_event_ .reset(new base::WaitableEvent(false, false));
392    io_thread->message_loop()->PostTask(
393        FROM_HERE, base::Bind(&AppCacheStorageImplTest::MethodWrapper<Method>,
394                              base::Unretained(this), method));
395    test_finished_event_->Wait();
396  }
397
398  void SetUpTest() {
399    DCHECK(base::MessageLoop::current() == io_thread->message_loop());
400    service_.reset(new AppCacheServiceImpl(NULL));
401    service_->Initialize(base::FilePath(), db_thread->task_runner(), NULL);
402    mock_quota_manager_proxy_ = new MockQuotaManagerProxy();
403    service_->quota_manager_proxy_ = mock_quota_manager_proxy_;
404    delegate_.reset(new MockStorageDelegate(this));
405  }
406
407  void TearDownTest() {
408    DCHECK(base::MessageLoop::current() == io_thread->message_loop());
409    storage()->CancelDelegateCallbacks(delegate());
410    group_ = NULL;
411    cache_ = NULL;
412    cache2_ = NULL;
413    mock_quota_manager_proxy_ = NULL;
414    delegate_.reset();
415    service_.reset();
416    FlushDbThreadTasks();
417  }
418
419  void TestFinished() {
420    // We unwind the stack prior to finishing up to let stack
421    // based objects get deleted.
422    DCHECK(base::MessageLoop::current() == io_thread->message_loop());
423    base::MessageLoop::current()->PostTask(
424        FROM_HERE,
425        base::Bind(&AppCacheStorageImplTest::TestFinishedUnwound,
426                   base::Unretained(this)));
427  }
428
429  void TestFinishedUnwound() {
430    TearDownTest();
431    test_finished_event_->Signal();
432  }
433
434  void PushNextTask(const base::Closure& task) {
435    task_stack_.push(task);
436  }
437
438  void ScheduleNextTask() {
439    DCHECK(base::MessageLoop::current() == io_thread->message_loop());
440    if (task_stack_.empty()) {
441      return;
442    }
443    base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top());
444    task_stack_.pop();
445  }
446
447  static void SignalEvent(base::WaitableEvent* event) {
448    event->Signal();
449  }
450
451  void FlushDbThreadTasks() {
452    // We pump a task thru the db thread to ensure any tasks previously
453    // scheduled on that thread have been performed prior to return.
454    base::WaitableEvent event(false, false);
455    db_thread->message_loop()->PostTask(
456        FROM_HERE, base::Bind(&AppCacheStorageImplTest::SignalEvent, &event));
457    event.Wait();
458  }
459
460  // LoadCache_Miss ----------------------------------------------------
461
462  void LoadCache_Miss() {
463    // Attempt to load a cache that doesn't exist. Should
464    // complete asynchronously.
465    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadCache_Miss,
466                            base::Unretained(this)));
467
468    storage()->LoadCache(111, delegate());
469    EXPECT_NE(111, delegate()->loaded_cache_id_);
470  }
471
472  void Verify_LoadCache_Miss() {
473    EXPECT_EQ(111, delegate()->loaded_cache_id_);
474    EXPECT_FALSE(delegate()->loaded_cache_.get());
475    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
476    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
477    TestFinished();
478  }
479
480  // LoadCache_NearHit -------------------------------------------------
481
482  void LoadCache_NearHit() {
483    // Attempt to load a cache that is currently in use
484    // and does not require loading from storage. This
485    // load should complete syncly.
486
487    // Setup some preconditions. Make an 'unstored' cache for
488    // us to load. The ctor should put it in the working set.
489    int64 cache_id = storage()->NewCacheId();
490    scoped_refptr<AppCache> cache(new AppCache(storage(), cache_id));
491
492    // Conduct the test.
493    storage()->LoadCache(cache_id, delegate());
494    EXPECT_EQ(cache_id, delegate()->loaded_cache_id_);
495    EXPECT_EQ(cache.get(), delegate()->loaded_cache_.get());
496    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
497    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
498    TestFinished();
499  }
500
501  // CreateGroup  --------------------------------------------
502
503  void CreateGroupInEmptyOrigin() {
504    // Attempt to load a group that doesn't exist, one should
505    // be created for us, but not stored.
506
507    // Since the origin has no groups, the storage class will respond
508    // syncly.
509    storage()->LoadOrCreateGroup(kManifestUrl, delegate());
510    Verify_CreateGroup();
511  }
512
513  void CreateGroupInPopulatedOrigin() {
514    // Attempt to load a group that doesn't exist, one should
515    // be created for us, but not stored.
516    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_CreateGroup,
517                            base::Unretained(this)));
518
519    // Since the origin has groups, storage class will have to
520    // consult the database and completion will be async.
521    storage()->usage_map_[kOrigin] = kDefaultEntrySize;
522
523    storage()->LoadOrCreateGroup(kManifestUrl, delegate());
524    EXPECT_FALSE(delegate()->loaded_group_.get());
525  }
526
527  void Verify_CreateGroup() {
528    EXPECT_EQ(kManifestUrl, delegate()->loaded_manifest_url_);
529    EXPECT_TRUE(delegate()->loaded_group_.get());
530    EXPECT_TRUE(delegate()->loaded_group_->HasOneRef());
531    EXPECT_FALSE(delegate()->loaded_group_->newest_complete_cache());
532
533    // Should not have been stored in the database.
534    AppCacheDatabase::GroupRecord record;
535    EXPECT_FALSE(database()->FindGroup(
536        delegate()->loaded_group_->group_id(), &record));
537
538    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
539    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
540
541    TestFinished();
542  }
543
544  // LoadGroupAndCache_FarHit  --------------------------------------
545
546  void LoadGroupAndCache_FarHit() {
547    // Attempt to load a cache that is not currently in use
548    // and does require loading from disk. This
549    // load should complete asynchronously.
550    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadCache_Far_Hit,
551                            base::Unretained(this)));
552
553    // Setup some preconditions. Create a group and newest cache that
554    // appear to be "stored" and "not currently in use".
555    MakeCacheAndGroup(kManifestUrl, 1, 1, true);
556    group_ = NULL;
557    cache_ = NULL;
558
559    // Conduct the cache load test, completes async
560    storage()->LoadCache(1, delegate());
561  }
562
563  void Verify_LoadCache_Far_Hit() {
564    EXPECT_TRUE(delegate()->loaded_cache_.get());
565    EXPECT_TRUE(delegate()->loaded_cache_->HasOneRef());
566    EXPECT_EQ(1, delegate()->loaded_cache_id_);
567
568    // The group should also have been loaded.
569    EXPECT_TRUE(delegate()->loaded_cache_->owning_group());
570    EXPECT_TRUE(delegate()->loaded_cache_->owning_group()->HasOneRef());
571    EXPECT_EQ(1, delegate()->loaded_cache_->owning_group()->group_id());
572
573    EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_accessed_count_);
574    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
575
576    // Drop things from the working set.
577    delegate()->loaded_cache_ = NULL;
578    EXPECT_FALSE(delegate()->loaded_group_.get());
579
580    // Conduct the group load test, also complete asynchronously.
581    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_LoadGroup_Far_Hit,
582                            base::Unretained(this)));
583
584    storage()->LoadOrCreateGroup(kManifestUrl, delegate());
585  }
586
587  void Verify_LoadGroup_Far_Hit() {
588    EXPECT_TRUE(delegate()->loaded_group_.get());
589    EXPECT_EQ(kManifestUrl, delegate()->loaded_manifest_url_);
590    EXPECT_TRUE(delegate()->loaded_group_->newest_complete_cache());
591    delegate()->loaded_groups_newest_cache_ = NULL;
592    EXPECT_TRUE(delegate()->loaded_group_->HasOneRef());
593    EXPECT_EQ(2, mock_quota_manager_proxy_->notify_storage_accessed_count_);
594    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
595    TestFinished();
596  }
597
598  // StoreNewGroup  --------------------------------------
599
600  void StoreNewGroup() {
601    // Store a group and its newest cache. Should complete asynchronously.
602    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_StoreNewGroup,
603                            base::Unretained(this)));
604
605    // Setup some preconditions. Create a group and newest cache that
606    // appear to be "unstored".
607    group_ = new AppCacheGroup(
608        storage(), kManifestUrl, storage()->NewGroupId());
609    cache_ = new AppCache(storage(), storage()->NewCacheId());
610    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, 1,
611                                              kDefaultEntrySize));
612    // Hold a ref to the cache simulate the UpdateJob holding that ref,
613    // and hold a ref to the group to simulate the CacheHost holding that ref.
614
615    // Have the quota manager retrun asynchronously for this test.
616    mock_quota_manager_proxy_->mock_manager_->async_ = true;
617
618    // Conduct the store test.
619    storage()->StoreGroupAndNewestCache(group_.get(), cache_.get(), delegate());
620    EXPECT_FALSE(delegate()->stored_group_success_);
621  }
622
623  void Verify_StoreNewGroup() {
624    EXPECT_TRUE(delegate()->stored_group_success_);
625    EXPECT_EQ(group_.get(), delegate()->stored_group_.get());
626    EXPECT_EQ(cache_.get(), group_->newest_complete_cache());
627    EXPECT_TRUE(cache_->is_complete());
628
629    // Should have been stored in the database.
630    AppCacheDatabase::GroupRecord group_record;
631    AppCacheDatabase::CacheRecord cache_record;
632    EXPECT_TRUE(database()->FindGroup(group_->group_id(), &group_record));
633    EXPECT_TRUE(database()->FindCache(cache_->cache_id(), &cache_record));
634
635    // Verify quota bookkeeping
636    EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
637    EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
638    EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
639    EXPECT_EQ(kDefaultEntrySize, mock_quota_manager_proxy_->last_delta_);
640
641    TestFinished();
642  }
643
644  // StoreExistingGroup  --------------------------------------
645
646  void StoreExistingGroup() {
647    // Store a group and its newest cache. Should complete asynchronously.
648    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_StoreExistingGroup,
649                            base::Unretained(this)));
650
651    // Setup some preconditions. Create a group and old complete cache
652    // that appear to be "stored"
653    MakeCacheAndGroup(kManifestUrl, 1, 1, true);
654    EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
655
656    // And a newest unstored complete cache.
657    cache2_ = new AppCache(storage(), 2);
658    cache2_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::MASTER, 1,
659                                               kDefaultEntrySize + 100));
660
661    // Conduct the test.
662    storage()->StoreGroupAndNewestCache(
663        group_.get(), cache2_.get(), delegate());
664    EXPECT_FALSE(delegate()->stored_group_success_);
665  }
666
667  void Verify_StoreExistingGroup() {
668    EXPECT_TRUE(delegate()->stored_group_success_);
669    EXPECT_EQ(group_.get(), delegate()->stored_group_.get());
670    EXPECT_EQ(cache2_.get(), group_->newest_complete_cache());
671    EXPECT_TRUE(cache2_->is_complete());
672
673    // The new cache should have been stored in the database.
674    AppCacheDatabase::GroupRecord group_record;
675    AppCacheDatabase::CacheRecord cache_record;
676    EXPECT_TRUE(database()->FindGroup(1, &group_record));
677    EXPECT_TRUE(database()->FindCache(2, &cache_record));
678
679    // The old cache should have been deleted
680    EXPECT_FALSE(database()->FindCache(1, &cache_record));
681
682    // Verify quota bookkeeping
683    EXPECT_EQ(kDefaultEntrySize + 100, storage()->usage_map_[kOrigin]);
684    EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
685    EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
686    EXPECT_EQ(100, mock_quota_manager_proxy_->last_delta_);
687
688    TestFinished();
689  }
690
691  // StoreExistingGroupExistingCache  -------------------------------
692
693  void StoreExistingGroupExistingCache() {
694    // Store a group with updates to its existing newest complete cache.
695    // Setup some preconditions. Create a group and a complete cache that
696    // appear to be "stored".
697
698    // Setup some preconditions. Create a group and old complete cache
699    // that appear to be "stored"
700    MakeCacheAndGroup(kManifestUrl, 1, 1, true);
701    EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
702
703    // Change the cache.
704    base::Time now = base::Time::Now();
705    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::MASTER, 1, 100));
706    cache_->set_update_time(now);
707
708    PushNextTask(base::Bind(
709        &AppCacheStorageImplTest::Verify_StoreExistingGroupExistingCache,
710        base::Unretained(this), now));
711
712    // Conduct the test.
713    EXPECT_EQ(cache_.get(), group_->newest_complete_cache());
714    storage()->StoreGroupAndNewestCache(group_.get(), cache_.get(), delegate());
715    EXPECT_FALSE(delegate()->stored_group_success_);
716  }
717
718  void Verify_StoreExistingGroupExistingCache(
719      base::Time expected_update_time) {
720    EXPECT_TRUE(delegate()->stored_group_success_);
721    EXPECT_EQ(cache_.get(), group_->newest_complete_cache());
722
723    AppCacheDatabase::CacheRecord cache_record;
724    EXPECT_TRUE(database()->FindCache(1, &cache_record));
725    EXPECT_EQ(1, cache_record.cache_id);
726    EXPECT_EQ(1, cache_record.group_id);
727    EXPECT_FALSE(cache_record.online_wildcard);
728    EXPECT_TRUE(expected_update_time == cache_record.update_time);
729    EXPECT_EQ(100 + kDefaultEntrySize, cache_record.cache_size);
730
731    std::vector<AppCacheDatabase::EntryRecord> entry_records;
732    EXPECT_TRUE(database()->FindEntriesForCache(1, &entry_records));
733    EXPECT_EQ(2U, entry_records.size());
734    if (entry_records[0].url == kDefaultEntryUrl)
735      entry_records.erase(entry_records.begin());
736    EXPECT_EQ(1 , entry_records[0].cache_id);
737    EXPECT_EQ(kEntryUrl, entry_records[0].url);
738    EXPECT_EQ(AppCacheEntry::MASTER, entry_records[0].flags);
739    EXPECT_EQ(1, entry_records[0].response_id);
740    EXPECT_EQ(100, entry_records[0].response_size);
741
742    // Verify quota bookkeeping
743    EXPECT_EQ(100 + kDefaultEntrySize, storage()->usage_map_[kOrigin]);
744    EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
745    EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
746    EXPECT_EQ(100, mock_quota_manager_proxy_->last_delta_);
747
748    TestFinished();
749  }
750
751  // FailStoreGroup  --------------------------------------
752
753  void FailStoreGroup() {
754    // Store a group and its newest cache. Should complete asynchronously.
755    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_FailStoreGroup,
756                            base::Unretained(this)));
757
758    // Setup some preconditions. Create a group and newest cache that
759    // appear to be "unstored" and big enough to exceed the 5M limit.
760    const int64 kTooBig = 10 * 1024 * 1024;  // 10M
761    group_ = new AppCacheGroup(
762        storage(), kManifestUrl, storage()->NewGroupId());
763    cache_ = new AppCache(storage(), storage()->NewCacheId());
764    cache_->AddEntry(kManifestUrl,
765                     AppCacheEntry(AppCacheEntry::MANIFEST, 1, kTooBig));
766    // Hold a ref to the cache simulate the UpdateJob holding that ref,
767    // and hold a ref to the group to simulate the CacheHost holding that ref.
768
769    // Conduct the store test.
770    storage()->StoreGroupAndNewestCache(group_.get(), cache_.get(), delegate());
771    EXPECT_FALSE(delegate()->stored_group_success_);  // Expected to be async.
772  }
773
774  void Verify_FailStoreGroup() {
775    EXPECT_FALSE(delegate()->stored_group_success_);
776    EXPECT_TRUE(delegate()->would_exceed_quota_);
777
778    // Should not have been stored in the database.
779    AppCacheDatabase::GroupRecord group_record;
780    AppCacheDatabase::CacheRecord cache_record;
781    EXPECT_FALSE(database()->FindGroup(group_->group_id(), &group_record));
782    EXPECT_FALSE(database()->FindCache(cache_->cache_id(), &cache_record));
783
784    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_accessed_count_);
785    EXPECT_EQ(0, mock_quota_manager_proxy_->notify_storage_modified_count_);
786
787    TestFinished();
788  }
789
790  // MakeGroupObsolete  -------------------------------
791
792  void MakeGroupObsolete() {
793    // Make a group obsolete, should complete asynchronously.
794    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_MakeGroupObsolete,
795                            base::Unretained(this)));
796
797    // Setup some preconditions. Create a group and newest cache that
798    // appears to be "stored" and "currently in use".
799    MakeCacheAndGroup(kManifestUrl, 1, 1, true);
800    EXPECT_EQ(kDefaultEntrySize, storage()->usage_map_[kOrigin]);
801
802    // Also insert some related records.
803    AppCacheDatabase::EntryRecord entry_record;
804    entry_record.cache_id = 1;
805    entry_record.flags = AppCacheEntry::FALLBACK;
806    entry_record.response_id = 1;
807    entry_record.url = kEntryUrl;
808    EXPECT_TRUE(database()->InsertEntry(&entry_record));
809
810    AppCacheDatabase::NamespaceRecord fallback_namespace_record;
811    fallback_namespace_record.cache_id = 1;
812    fallback_namespace_record.namespace_.target_url = kEntryUrl;
813    fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace;
814    fallback_namespace_record.origin = kManifestUrl.GetOrigin();
815    EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record));
816
817    AppCacheDatabase::OnlineWhiteListRecord online_whitelist_record;
818    online_whitelist_record.cache_id = 1;
819    online_whitelist_record.namespace_url = kOnlineNamespace;
820    EXPECT_TRUE(database()->InsertOnlineWhiteList(&online_whitelist_record));
821
822    // Conduct the test.
823    storage()->MakeGroupObsolete(group_.get(), delegate(), 0);
824    EXPECT_FALSE(group_->is_obsolete());
825  }
826
827  void Verify_MakeGroupObsolete() {
828    EXPECT_TRUE(delegate()->obsoleted_success_);
829    EXPECT_EQ(group_.get(), delegate()->obsoleted_group_.get());
830    EXPECT_TRUE(group_->is_obsolete());
831    EXPECT_TRUE(storage()->usage_map_.empty());
832
833    // The cache and group have been deleted from the database.
834    AppCacheDatabase::GroupRecord group_record;
835    AppCacheDatabase::CacheRecord cache_record;
836    EXPECT_FALSE(database()->FindGroup(1, &group_record));
837    EXPECT_FALSE(database()->FindCache(1, &cache_record));
838
839    // The related records should have been deleted too.
840    std::vector<AppCacheDatabase::EntryRecord> entry_records;
841    database()->FindEntriesForCache(1, &entry_records);
842    EXPECT_TRUE(entry_records.empty());
843    std::vector<AppCacheDatabase::NamespaceRecord> intercept_records;
844    std::vector<AppCacheDatabase::NamespaceRecord> fallback_records;
845    database()->FindNamespacesForCache(
846        1, &intercept_records, &fallback_records);
847    EXPECT_TRUE(fallback_records.empty());
848    std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelist_records;
849    database()->FindOnlineWhiteListForCache(1, &whitelist_records);
850    EXPECT_TRUE(whitelist_records.empty());
851
852    // Verify quota bookkeeping
853    EXPECT_TRUE(storage()->usage_map_.empty());
854    EXPECT_EQ(1, mock_quota_manager_proxy_->notify_storage_modified_count_);
855    EXPECT_EQ(kOrigin, mock_quota_manager_proxy_->last_origin_);
856    EXPECT_EQ(-kDefaultEntrySize, mock_quota_manager_proxy_->last_delta_);
857
858    TestFinished();
859  }
860
861  // MarkEntryAsForeign  -------------------------------
862
863  void MarkEntryAsForeign() {
864    // Setup some preconditions. Create a cache with an entry
865    // in storage and in the working set.
866    MakeCacheAndGroup(kManifestUrl, 1, 1, true);
867    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT));
868    AppCacheDatabase::EntryRecord entry_record;
869    entry_record.cache_id = 1;
870    entry_record.url = kEntryUrl;
871    entry_record.flags = AppCacheEntry::EXPLICIT;
872    entry_record.response_id = 0;
873    EXPECT_TRUE(database()->InsertEntry(&entry_record));
874    EXPECT_FALSE(cache_->GetEntry(kEntryUrl)->IsForeign());
875
876    // Conduct the test.
877    storage()->MarkEntryAsForeign(kEntryUrl, 1);
878
879    // The entry in the working set should have been updated syncly.
880    EXPECT_TRUE(cache_->GetEntry(kEntryUrl)->IsForeign());
881    EXPECT_TRUE(cache_->GetEntry(kEntryUrl)->IsExplicit());
882
883    // And the entry in storage should also be updated, but that
884    // happens asynchronously on the db thread.
885    FlushDbThreadTasks();
886    AppCacheDatabase::EntryRecord entry_record2;
887    EXPECT_TRUE(database()->FindEntry(1, kEntryUrl, &entry_record2));
888    EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN,
889              entry_record2.flags);
890    TestFinished();
891  }
892
893  // MarkEntryAsForeignWithLoadInProgress  -------------------------------
894
895  void MarkEntryAsForeignWithLoadInProgress() {
896    PushNextTask(base::Bind(
897       &AppCacheStorageImplTest::Verify_MarkEntryAsForeignWithLoadInProgress,
898       base::Unretained(this)));
899
900    // Setup some preconditions. Create a cache with an entry
901    // in storage, but not in the working set.
902    MakeCacheAndGroup(kManifestUrl, 1, 1, true);
903    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT));
904    AppCacheDatabase::EntryRecord entry_record;
905    entry_record.cache_id = 1;
906    entry_record.url = kEntryUrl;
907    entry_record.flags = AppCacheEntry::EXPLICIT;
908    entry_record.response_id = 0;
909    EXPECT_TRUE(database()->InsertEntry(&entry_record));
910    EXPECT_FALSE(cache_->GetEntry(kEntryUrl)->IsForeign());
911    EXPECT_TRUE(cache_->HasOneRef());
912    cache_ = NULL;
913    group_ = NULL;
914
915    // Conduct the test, start a cache load, and prior to completion
916    // of that load, mark the entry as foreign.
917    storage()->LoadCache(1, delegate());
918    storage()->MarkEntryAsForeign(kEntryUrl, 1);
919  }
920
921  void Verify_MarkEntryAsForeignWithLoadInProgress() {
922    EXPECT_EQ(1, delegate()->loaded_cache_id_);
923    EXPECT_TRUE(delegate()->loaded_cache_.get());
924
925    // The entry in the working set should have been updated upon load.
926    EXPECT_TRUE(delegate()->loaded_cache_->GetEntry(kEntryUrl)->IsForeign());
927    EXPECT_TRUE(delegate()->loaded_cache_->GetEntry(kEntryUrl)->IsExplicit());
928
929    // And the entry in storage should also be updated.
930    FlushDbThreadTasks();
931    AppCacheDatabase::EntryRecord entry_record;
932    EXPECT_TRUE(database()->FindEntry(1, kEntryUrl, &entry_record));
933    EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN,
934              entry_record.flags);
935    TestFinished();
936  }
937
938  // FindNoMainResponse  -------------------------------
939
940  void FindNoMainResponse() {
941    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_FindNoMainResponse,
942                            base::Unretained(this)));
943
944    // Conduct the test.
945    storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
946    EXPECT_NE(kEntryUrl, delegate()->found_url_);
947  }
948
949  void Verify_FindNoMainResponse() {
950    EXPECT_EQ(kEntryUrl, delegate()->found_url_);
951    EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
952    EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
953    EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
954    EXPECT_EQ(kAppCacheNoResponseId,
955        delegate()->found_fallback_entry_.response_id());
956    EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
957    EXPECT_EQ(0, delegate()->found_entry_.types());
958    EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
959    TestFinished();
960  }
961
962  // BasicFindMainResponse  -------------------------------
963
964  void BasicFindMainResponseInDatabase() {
965    BasicFindMainResponse(true);
966  }
967
968  void BasicFindMainResponseInWorkingSet() {
969    BasicFindMainResponse(false);
970  }
971
972  void BasicFindMainResponse(bool drop_from_working_set) {
973    PushNextTask(base::Bind(
974        &AppCacheStorageImplTest::Verify_BasicFindMainResponse,
975        base::Unretained(this)));
976
977    // Setup some preconditions. Create a complete cache with an entry
978    // in storage.
979    MakeCacheAndGroup(kManifestUrl, 2, 1, true);
980    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, 1));
981    AppCacheDatabase::EntryRecord entry_record;
982    entry_record.cache_id = 1;
983    entry_record.url = kEntryUrl;
984    entry_record.flags = AppCacheEntry::EXPLICIT;
985    entry_record.response_id = 1;
986    EXPECT_TRUE(database()->InsertEntry(&entry_record));
987
988    // Optionally drop the cache/group pair from the working set.
989    if (drop_from_working_set) {
990      EXPECT_TRUE(cache_->HasOneRef());
991      cache_ = NULL;
992      EXPECT_TRUE(group_->HasOneRef());
993      group_ = NULL;
994    }
995
996    // Conduct the test.
997    storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
998    EXPECT_NE(kEntryUrl,  delegate()->found_url_);
999  }
1000
1001  void Verify_BasicFindMainResponse() {
1002    EXPECT_EQ(kEntryUrl, delegate()->found_url_);
1003    EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
1004    EXPECT_EQ(1, delegate()->found_cache_id_);
1005    EXPECT_EQ(2, delegate()->found_group_id_);
1006    EXPECT_EQ(1, delegate()->found_entry_.response_id());
1007    EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
1008    EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
1009    TestFinished();
1010  }
1011
1012  // BasicFindMainFallbackResponse  -------------------------------
1013
1014  void BasicFindMainFallbackResponseInDatabase() {
1015    BasicFindMainFallbackResponse(true);
1016  }
1017
1018  void BasicFindMainFallbackResponseInWorkingSet() {
1019    BasicFindMainFallbackResponse(false);
1020  }
1021
1022  void BasicFindMainFallbackResponse(bool drop_from_working_set) {
1023    PushNextTask(base::Bind(
1024        &AppCacheStorageImplTest::Verify_BasicFindMainFallbackResponse,
1025        base::Unretained(this)));
1026
1027    // Setup some preconditions. Create a complete cache with a
1028    // fallback namespace and entry.
1029    MakeCacheAndGroup(kManifestUrl, 2, 1, true);
1030    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1));
1031    cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2));
1032    cache_->fallback_namespaces_.push_back(
1033        AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
1034                  kFallbackNamespace2,
1035                  kEntryUrl2,
1036                  false));
1037    cache_->fallback_namespaces_.push_back(
1038        AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
1039                  kFallbackNamespace,
1040                  kEntryUrl,
1041                  false));
1042    AppCacheDatabase::CacheRecord cache_record;
1043    std::vector<AppCacheDatabase::EntryRecord> entries;
1044    std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
1045    std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
1046    std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
1047    cache_->ToDatabaseRecords(group_.get(),
1048                              &cache_record,
1049                              &entries,
1050                              &intercepts,
1051                              &fallbacks,
1052                              &whitelists);
1053
1054    std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
1055        entries.begin();
1056    while (iter != entries.end()) {
1057      // MakeCacheAndGroup has inserted the default entry record already.
1058      if (iter->url != kDefaultEntryUrl)
1059        EXPECT_TRUE(database()->InsertEntry(&(*iter)));
1060      ++iter;
1061    }
1062
1063    EXPECT_TRUE(database()->InsertNamespaceRecords(fallbacks));
1064    EXPECT_TRUE(database()->InsertOnlineWhiteListRecords(whitelists));
1065    if (drop_from_working_set) {
1066      EXPECT_TRUE(cache_->HasOneRef());
1067      cache_ = NULL;
1068      EXPECT_TRUE(group_->HasOneRef());
1069      group_ = NULL;
1070    }
1071
1072    // Conduct the test. The test url is in both fallback namespace urls,
1073    // but should match the longer of the two.
1074    storage()->FindResponseForMainRequest(kFallbackTestUrl, GURL(), delegate());
1075    EXPECT_NE(kFallbackTestUrl, delegate()->found_url_);
1076  }
1077
1078  void Verify_BasicFindMainFallbackResponse() {
1079    EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_);
1080    EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
1081    EXPECT_EQ(1, delegate()->found_cache_id_);
1082    EXPECT_EQ(2, delegate()->found_group_id_);
1083    EXPECT_FALSE(delegate()->found_entry_.has_response_id());
1084    EXPECT_EQ(2, delegate()->found_fallback_entry_.response_id());
1085    EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
1086    EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
1087    TestFinished();
1088  }
1089
1090  // BasicFindMainInterceptResponse  -------------------------------
1091
1092  void BasicFindMainInterceptResponseInDatabase() {
1093    BasicFindMainInterceptResponse(true);
1094  }
1095
1096  void BasicFindMainInterceptResponseInWorkingSet() {
1097    BasicFindMainInterceptResponse(false);
1098  }
1099
1100  void BasicFindMainInterceptResponse(bool drop_from_working_set) {
1101    PushNextTask(base::Bind(
1102        &AppCacheStorageImplTest::Verify_BasicFindMainInterceptResponse,
1103        base::Unretained(this)));
1104
1105    // Setup some preconditions. Create a complete cache with an
1106    // intercept namespace and entry.
1107    MakeCacheAndGroup(kManifestUrl, 2, 1, true);
1108    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1));
1109    cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::INTERCEPT, 2));
1110    cache_->intercept_namespaces_.push_back(
1111        AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace2,
1112                  kEntryUrl2, false));
1113    cache_->intercept_namespaces_.push_back(
1114        AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace,
1115                  kEntryUrl, false));
1116    AppCacheDatabase::CacheRecord cache_record;
1117    std::vector<AppCacheDatabase::EntryRecord> entries;
1118    std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
1119    std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
1120    std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
1121    cache_->ToDatabaseRecords(group_.get(),
1122                              &cache_record,
1123                              &entries,
1124                              &intercepts,
1125                              &fallbacks,
1126                              &whitelists);
1127
1128    std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
1129        entries.begin();
1130    while (iter != entries.end()) {
1131      // MakeCacheAndGroup has inserted  the default entry record already
1132      if (iter->url != kDefaultEntryUrl)
1133        EXPECT_TRUE(database()->InsertEntry(&(*iter)));
1134      ++iter;
1135    }
1136
1137    EXPECT_TRUE(database()->InsertNamespaceRecords(intercepts));
1138    EXPECT_TRUE(database()->InsertOnlineWhiteListRecords(whitelists));
1139    if (drop_from_working_set) {
1140      EXPECT_TRUE(cache_->HasOneRef());
1141      cache_ = NULL;
1142      EXPECT_TRUE(group_->HasOneRef());
1143      group_ = NULL;
1144    }
1145
1146    // Conduct the test. The test url is in both intercept namespaces,
1147    // but should match the longer of the two.
1148    storage()->FindResponseForMainRequest(
1149        kInterceptTestUrl, GURL(), delegate());
1150    EXPECT_NE(kInterceptTestUrl, delegate()->found_url_);
1151  }
1152
1153  void Verify_BasicFindMainInterceptResponse() {
1154    EXPECT_EQ(kInterceptTestUrl, delegate()->found_url_);
1155    EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
1156    EXPECT_EQ(1, delegate()->found_cache_id_);
1157    EXPECT_EQ(2, delegate()->found_group_id_);
1158    EXPECT_EQ(2, delegate()->found_entry_.response_id());
1159    EXPECT_TRUE(delegate()->found_entry_.IsIntercept());
1160    EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
1161    EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
1162    TestFinished();
1163  }
1164
1165  // FindInterceptPatternMatch ----------------------------------------
1166
1167  void FindInterceptPatternMatchInDatabase() {
1168    FindInterceptPatternMatch(true);
1169  }
1170
1171  void FindInterceptPatternMatchInWorkingSet() {
1172    FindInterceptPatternMatch(false);
1173  }
1174
1175  void FindInterceptPatternMatch(bool drop_from_working_set) {
1176    // Setup some preconditions. Create a complete cache with an
1177    // pattern matching intercept namespace and entry.
1178    MakeCacheAndGroup(kManifestUrl, 2, 1, true);
1179    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1));
1180    cache_->intercept_namespaces_.push_back(
1181        AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE,
1182            kInterceptPatternNamespace, kEntryUrl, true));
1183    AppCacheDatabase::CacheRecord cache_record;
1184    std::vector<AppCacheDatabase::EntryRecord> entries;
1185    std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
1186    std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
1187    std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
1188    cache_->ToDatabaseRecords(group_.get(),
1189                              &cache_record,
1190                              &entries,
1191                              &intercepts,
1192                              &fallbacks,
1193                              &whitelists);
1194
1195    std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
1196        entries.begin();
1197    while (iter != entries.end()) {
1198      // MakeCacheAndGroup has inserted  the default entry record already
1199      if (iter->url != kDefaultEntryUrl)
1200        EXPECT_TRUE(database()->InsertEntry(&(*iter)));
1201      ++iter;
1202    }
1203
1204    EXPECT_TRUE(database()->InsertNamespaceRecords(intercepts));
1205    if (drop_from_working_set) {
1206      EXPECT_TRUE(cache_->HasOneRef());
1207      cache_ = NULL;
1208      EXPECT_TRUE(group_->HasOneRef());
1209      group_ = NULL;
1210    }
1211
1212    // First test something that does not match the pattern.
1213    PushNextTask(base::Bind(
1214        &AppCacheStorageImplTest::Verify_FindInterceptPatternMatchNegative,
1215        base::Unretained(this)));
1216    storage()->FindResponseForMainRequest(
1217        kInterceptPatternTestNegativeUrl, GURL(), delegate());
1218    EXPECT_EQ(GURL(), delegate()->found_url_);  // Is always async.
1219  }
1220
1221  void Verify_FindInterceptPatternMatchNegative() {
1222    EXPECT_EQ(kInterceptPatternTestNegativeUrl, delegate()->found_url_);
1223    EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
1224    EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
1225    EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
1226    EXPECT_EQ(kAppCacheNoResponseId,
1227        delegate()->found_fallback_entry_.response_id());
1228    EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
1229    EXPECT_EQ(0, delegate()->found_entry_.types());
1230    EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
1231
1232    // Then test something that matches.
1233    PushNextTask(base::Bind(
1234        &AppCacheStorageImplTest::Verify_FindInterceptPatternMatchPositive,
1235        base::Unretained(this)));
1236    storage()->FindResponseForMainRequest(
1237        kInterceptPatternTestPositiveUrl, GURL(), delegate());
1238  }
1239
1240  void Verify_FindInterceptPatternMatchPositive() {
1241    EXPECT_EQ(kInterceptPatternTestPositiveUrl, delegate()->found_url_);
1242    EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
1243    EXPECT_EQ(1, delegate()->found_cache_id_);
1244    EXPECT_EQ(2, delegate()->found_group_id_);
1245    EXPECT_EQ(1, delegate()->found_entry_.response_id());
1246    EXPECT_TRUE(delegate()->found_entry_.IsIntercept());
1247    EXPECT_EQ(kEntryUrl, delegate()->found_namespace_entry_url_);
1248    EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
1249    TestFinished();
1250  }
1251
1252  // FindFallbackPatternMatch  -------------------------------
1253
1254  void FindFallbackPatternMatchInDatabase() {
1255    FindFallbackPatternMatch(true);
1256  }
1257
1258  void FindFallbackPatternMatchInWorkingSet() {
1259    FindFallbackPatternMatch(false);
1260  }
1261
1262  void FindFallbackPatternMatch(bool drop_from_working_set) {
1263    // Setup some preconditions. Create a complete cache with a
1264    // pattern matching fallback namespace and entry.
1265    MakeCacheAndGroup(kManifestUrl, 2, 1, true);
1266    cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1));
1267    cache_->fallback_namespaces_.push_back(
1268        AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
1269            kFallbackPatternNamespace, kEntryUrl, true));
1270    AppCacheDatabase::CacheRecord cache_record;
1271    std::vector<AppCacheDatabase::EntryRecord> entries;
1272    std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
1273    std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
1274    std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
1275    cache_->ToDatabaseRecords(group_.get(),
1276                              &cache_record,
1277                              &entries,
1278                              &intercepts,
1279                              &fallbacks,
1280                              &whitelists);
1281
1282    std::vector<AppCacheDatabase::EntryRecord>::const_iterator iter =
1283        entries.begin();
1284    while (iter != entries.end()) {
1285      // MakeCacheAndGroup has inserted the default entry record already.
1286      if (iter->url != kDefaultEntryUrl)
1287        EXPECT_TRUE(database()->InsertEntry(&(*iter)));
1288      ++iter;
1289    }
1290
1291    EXPECT_TRUE(database()->InsertNamespaceRecords(fallbacks));
1292    if (drop_from_working_set) {
1293      EXPECT_TRUE(cache_->HasOneRef());
1294      cache_ = NULL;
1295      EXPECT_TRUE(group_->HasOneRef());
1296      group_ = NULL;
1297    }
1298
1299    // First test something that does not match the pattern.
1300    PushNextTask(base::Bind(
1301        &AppCacheStorageImplTest::Verify_FindFallbackPatternMatchNegative,
1302        base::Unretained(this)));
1303    storage()->FindResponseForMainRequest(
1304        kFallbackPatternTestNegativeUrl, GURL(), delegate());
1305    EXPECT_EQ(GURL(), delegate()->found_url_);  // Is always async.
1306  }
1307
1308  void Verify_FindFallbackPatternMatchNegative() {
1309    EXPECT_EQ(kFallbackPatternTestNegativeUrl, delegate()->found_url_);
1310      EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
1311      EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
1312      EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
1313      EXPECT_EQ(kAppCacheNoResponseId,
1314          delegate()->found_fallback_entry_.response_id());
1315      EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
1316      EXPECT_EQ(0, delegate()->found_entry_.types());
1317      EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
1318
1319      // Then test something that matches.
1320      PushNextTask(base::Bind(
1321          &AppCacheStorageImplTest::Verify_FindFallbackPatternMatchPositive,
1322          base::Unretained(this)));
1323      storage()->FindResponseForMainRequest(
1324          kFallbackPatternTestPositiveUrl, GURL(), delegate());
1325  }
1326
1327  void Verify_FindFallbackPatternMatchPositive() {
1328    EXPECT_EQ(kFallbackPatternTestPositiveUrl, delegate()->found_url_);
1329    EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
1330    EXPECT_EQ(1, delegate()->found_cache_id_);
1331    EXPECT_EQ(2, delegate()->found_group_id_);
1332    EXPECT_EQ(1, delegate()->found_fallback_entry_.response_id());
1333    EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
1334    EXPECT_EQ(kEntryUrl, delegate()->found_namespace_entry_url_);
1335    EXPECT_FALSE(delegate()->found_entry_.has_response_id());
1336    TestFinished();
1337  }
1338
1339  // FindMainResponseWithMultipleHits  -------------------------------
1340
1341  void FindMainResponseWithMultipleHits() {
1342    PushNextTask(base::Bind(
1343        &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits,
1344        base::Unretained(this)));
1345
1346    // Setup some preconditions, create a few caches with an identical set
1347    // of entries and fallback namespaces. Only the last one remains in
1348    // the working set to simulate appearing as "in use".
1349    MakeMultipleHitCacheAndGroup(kManifestUrl, 1);
1350    MakeMultipleHitCacheAndGroup(kManifestUrl2, 2);
1351    MakeMultipleHitCacheAndGroup(kManifestUrl3, 3);
1352
1353    // Conduct the test, we should find the response from the last cache
1354    // since it's "in use".
1355    storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
1356    EXPECT_NE(kEntryUrl, delegate()->found_url_);
1357  }
1358
1359  void MakeMultipleHitCacheAndGroup(const GURL& manifest_url, int id) {
1360    MakeCacheAndGroup(manifest_url, id, id, true);
1361    AppCacheDatabase::EntryRecord entry_record;
1362
1363    // Add an entry for kEntryUrl
1364    entry_record.cache_id = id;
1365    entry_record.url = kEntryUrl;
1366    entry_record.flags = AppCacheEntry::EXPLICIT;
1367    entry_record.response_id = id;
1368    EXPECT_TRUE(database()->InsertEntry(&entry_record));
1369    cache_->AddEntry(
1370        entry_record.url,
1371        AppCacheEntry(entry_record.flags, entry_record.response_id));
1372
1373    // Add an entry for the manifestUrl
1374    entry_record.cache_id = id;
1375    entry_record.url = manifest_url;
1376    entry_record.flags = AppCacheEntry::MANIFEST;
1377    entry_record.response_id = id + kManifestEntryIdOffset;
1378    EXPECT_TRUE(database()->InsertEntry(&entry_record));
1379    cache_->AddEntry(
1380        entry_record.url,
1381        AppCacheEntry(entry_record.flags, entry_record.response_id));
1382
1383    // Add a fallback entry and namespace
1384    entry_record.cache_id = id;
1385    entry_record.url = kEntryUrl2;
1386    entry_record.flags = AppCacheEntry::FALLBACK;
1387    entry_record.response_id = id + kFallbackEntryIdOffset;
1388    EXPECT_TRUE(database()->InsertEntry(&entry_record));
1389    cache_->AddEntry(
1390        entry_record.url,
1391        AppCacheEntry(entry_record.flags, entry_record.response_id));
1392    AppCacheDatabase::NamespaceRecord fallback_namespace_record;
1393    fallback_namespace_record.cache_id = id;
1394    fallback_namespace_record.namespace_.target_url = entry_record.url;
1395    fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace;
1396    fallback_namespace_record.origin = manifest_url.GetOrigin();
1397    EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record));
1398    cache_->fallback_namespaces_.push_back(
1399        AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
1400                  kFallbackNamespace,
1401                  kEntryUrl2,
1402                  false));
1403  }
1404
1405  void Verify_FindMainResponseWithMultipleHits() {
1406    EXPECT_EQ(kEntryUrl, delegate()->found_url_);
1407    EXPECT_EQ(kManifestUrl3, delegate()->found_manifest_url_);
1408    EXPECT_EQ(3, delegate()->found_cache_id_);
1409    EXPECT_EQ(3, delegate()->found_group_id_);
1410    EXPECT_EQ(3, delegate()->found_entry_.response_id());
1411    EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
1412    EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
1413
1414    // Conduct another test preferring kManifestUrl
1415    delegate_.reset(new MockStorageDelegate(this));
1416    PushNextTask(base::Bind(
1417        &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits2,
1418        base::Unretained(this)));
1419    storage()->FindResponseForMainRequest(kEntryUrl, kManifestUrl, delegate());
1420    EXPECT_NE(kEntryUrl, delegate()->found_url_);
1421  }
1422
1423  void Verify_FindMainResponseWithMultipleHits2() {
1424    EXPECT_EQ(kEntryUrl, delegate()->found_url_);
1425    EXPECT_EQ(kManifestUrl, delegate()->found_manifest_url_);
1426    EXPECT_EQ(1, delegate()->found_cache_id_);
1427    EXPECT_EQ(1, delegate()->found_group_id_);
1428    EXPECT_EQ(1, delegate()->found_entry_.response_id());
1429    EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
1430    EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
1431
1432    // Conduct the another test preferring kManifestUrl2
1433    delegate_.reset(new MockStorageDelegate(this));
1434    PushNextTask(base::Bind(
1435        &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits3,
1436        base::Unretained(this)));
1437    storage()->FindResponseForMainRequest(kEntryUrl, kManifestUrl2, delegate());
1438    EXPECT_NE(kEntryUrl, delegate()->found_url_);
1439  }
1440
1441  void Verify_FindMainResponseWithMultipleHits3() {
1442    EXPECT_EQ(kEntryUrl, delegate()->found_url_);
1443    EXPECT_EQ(kManifestUrl2, delegate()->found_manifest_url_);
1444    EXPECT_EQ(2, delegate()->found_cache_id_);
1445    EXPECT_EQ(2, delegate()->found_group_id_);
1446    EXPECT_EQ(2, delegate()->found_entry_.response_id());
1447    EXPECT_TRUE(delegate()->found_entry_.IsExplicit());
1448    EXPECT_FALSE(delegate()->found_fallback_entry_.has_response_id());
1449
1450    // Conduct another test with no preferred manifest that hits the fallback.
1451    delegate_.reset(new MockStorageDelegate(this));
1452    PushNextTask(base::Bind(
1453        &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits4,
1454        base::Unretained(this)));
1455    storage()->FindResponseForMainRequest(
1456        kFallbackTestUrl, GURL(), delegate());
1457    EXPECT_NE(kFallbackTestUrl, delegate()->found_url_);
1458  }
1459
1460  void Verify_FindMainResponseWithMultipleHits4() {
1461    EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_);
1462    EXPECT_EQ(kManifestUrl3, delegate()->found_manifest_url_);
1463    EXPECT_EQ(3, delegate()->found_cache_id_);
1464    EXPECT_EQ(3, delegate()->found_group_id_);
1465    EXPECT_FALSE(delegate()->found_entry_.has_response_id());
1466    EXPECT_EQ(3 + kFallbackEntryIdOffset,
1467              delegate()->found_fallback_entry_.response_id());
1468    EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
1469    EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
1470
1471    // Conduct another test preferring kManifestUrl2 that hits the fallback.
1472    delegate_.reset(new MockStorageDelegate(this));
1473    PushNextTask(base::Bind(
1474        &AppCacheStorageImplTest::Verify_FindMainResponseWithMultipleHits5,
1475        base::Unretained(this)));
1476    storage()->FindResponseForMainRequest(
1477        kFallbackTestUrl, kManifestUrl2, delegate());
1478    EXPECT_NE(kFallbackTestUrl, delegate()->found_url_);
1479  }
1480
1481  void Verify_FindMainResponseWithMultipleHits5() {
1482    EXPECT_EQ(kFallbackTestUrl, delegate()->found_url_);
1483    EXPECT_EQ(kManifestUrl2, delegate()->found_manifest_url_);
1484    EXPECT_EQ(2, delegate()->found_cache_id_);
1485    EXPECT_EQ(2, delegate()->found_group_id_);
1486    EXPECT_FALSE(delegate()->found_entry_.has_response_id());
1487    EXPECT_EQ(2 + kFallbackEntryIdOffset,
1488              delegate()->found_fallback_entry_.response_id());
1489    EXPECT_TRUE(delegate()->found_fallback_entry_.IsFallback());
1490    EXPECT_EQ(kEntryUrl2, delegate()->found_namespace_entry_url_);
1491
1492    TestFinished();
1493  }
1494
1495  // FindMainResponseExclusions  -------------------------------
1496
1497  void FindMainResponseExclusionsInDatabase() {
1498    FindMainResponseExclusions(true);
1499  }
1500
1501  void FindMainResponseExclusionsInWorkingSet() {
1502    FindMainResponseExclusions(false);
1503  }
1504
1505  void FindMainResponseExclusions(bool drop_from_working_set) {
1506    // Setup some preconditions. Create a complete cache with a
1507    // foreign entry, an online namespace, and a second online
1508    // namespace nested within a fallback namespace.
1509    MakeCacheAndGroup(kManifestUrl, 1, 1, true);
1510    cache_->AddEntry(kEntryUrl,
1511        AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, 1));
1512    cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2));
1513    cache_->fallback_namespaces_.push_back(
1514        AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
1515                  kFallbackNamespace,
1516                  kEntryUrl2,
1517                  false));
1518    cache_->online_whitelist_namespaces_.push_back(
1519        AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespace,
1520                  GURL(), false));
1521    cache_->online_whitelist_namespaces_.push_back(
1522        AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE,
1523            kOnlineNamespaceWithinFallback, GURL(), false));
1524
1525    AppCacheDatabase::EntryRecord entry_record;
1526    entry_record.cache_id = 1;
1527    entry_record.url = kEntryUrl;
1528    entry_record.flags = AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN;
1529    entry_record.response_id = 1;
1530    EXPECT_TRUE(database()->InsertEntry(&entry_record));
1531    AppCacheDatabase::OnlineWhiteListRecord whitelist_record;
1532    whitelist_record.cache_id = 1;
1533    whitelist_record.namespace_url = kOnlineNamespace;
1534    EXPECT_TRUE(database()->InsertOnlineWhiteList(&whitelist_record));
1535    AppCacheDatabase::NamespaceRecord fallback_namespace_record;
1536    fallback_namespace_record.cache_id = 1;
1537    fallback_namespace_record.namespace_.target_url = kEntryUrl2;
1538    fallback_namespace_record.namespace_.namespace_url = kFallbackNamespace;
1539    fallback_namespace_record.origin = kManifestUrl.GetOrigin();
1540    EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record));
1541    whitelist_record.cache_id = 1;
1542    whitelist_record.namespace_url = kOnlineNamespaceWithinFallback;
1543    EXPECT_TRUE(database()->InsertOnlineWhiteList(&whitelist_record));
1544    if (drop_from_working_set) {
1545      cache_ = NULL;
1546      group_ = NULL;
1547    }
1548
1549    // We should not find anything for the foreign entry.
1550    PushNextTask(base::Bind(&AppCacheStorageImplTest::Verify_ExclusionNotFound,
1551                            base::Unretained(this), kEntryUrl, 1));
1552    storage()->FindResponseForMainRequest(kEntryUrl, GURL(), delegate());
1553  }
1554
1555  void Verify_ExclusionNotFound(GURL expected_url, int phase) {
1556    EXPECT_EQ(expected_url, delegate()->found_url_);
1557    EXPECT_TRUE(delegate()->found_manifest_url_.is_empty());
1558    EXPECT_EQ(kAppCacheNoCacheId, delegate()->found_cache_id_);
1559    EXPECT_EQ(0, delegate()->found_group_id_);
1560    EXPECT_EQ(kAppCacheNoResponseId, delegate()->found_entry_.response_id());
1561    EXPECT_EQ(kAppCacheNoResponseId,
1562        delegate()->found_fallback_entry_.response_id());
1563    EXPECT_TRUE(delegate()->found_namespace_entry_url_.is_empty());
1564    EXPECT_EQ(0, delegate()->found_entry_.types());
1565    EXPECT_EQ(0, delegate()->found_fallback_entry_.types());
1566
1567    if (phase == 1) {
1568      // We should not find anything for the online namespace.
1569      PushNextTask(
1570          base::Bind(&AppCacheStorageImplTest::Verify_ExclusionNotFound,
1571                     base::Unretained(this), kOnlineNamespace, 2));
1572      storage()->FindResponseForMainRequest(
1573          kOnlineNamespace, GURL(), delegate());
1574      return;
1575    }
1576    if (phase == 2) {
1577      // We should not find anything for the online namespace nested within
1578      // the fallback namespace.
1579      PushNextTask(base::Bind(
1580          &AppCacheStorageImplTest::Verify_ExclusionNotFound,
1581          base::Unretained(this), kOnlineNamespaceWithinFallback, 3));
1582      storage()->FindResponseForMainRequest(
1583          kOnlineNamespaceWithinFallback, GURL(), delegate());
1584      return;
1585    }
1586
1587    TestFinished();
1588  }
1589
1590  // Reinitialize -------------------------------
1591  // These tests are somewhat of a system integration test.
1592  // They rely on running a mock http server on our IO thread,
1593  // and involves other appcache classes to get some code
1594  // coverage thruout when Reinitialize happens.
1595
1596  class MockServiceObserver : public AppCacheServiceImpl::Observer {
1597   public:
1598    explicit MockServiceObserver(AppCacheStorageImplTest* test)
1599        : test_(test) {}
1600
1601    virtual void OnServiceReinitialized(
1602        AppCacheStorageReference* old_storage_ref) OVERRIDE {
1603      observed_old_storage_ = old_storage_ref;
1604      test_->ScheduleNextTask();
1605    }
1606
1607    scoped_refptr<AppCacheStorageReference> observed_old_storage_;
1608    AppCacheStorageImplTest* test_;
1609  };
1610
1611  class MockAppCacheFrontend : public AppCacheFrontend {
1612   public:
1613    MockAppCacheFrontend() : error_event_was_raised_(false) {}
1614
1615    virtual void OnCacheSelected(
1616        int host_id, const AppCacheInfo& info) OVERRIDE {}
1617    virtual void OnStatusChanged(const std::vector<int>& host_ids,
1618                                 AppCacheStatus status) OVERRIDE {}
1619    virtual void OnEventRaised(const std::vector<int>& host_ids,
1620                               AppCacheEventID event_id) OVERRIDE {}
1621    virtual void OnProgressEventRaised(
1622        const std::vector<int>& host_ids,
1623        const GURL& url,
1624        int num_total, int num_complete) OVERRIDE {}
1625    virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
1626                                    const AppCacheErrorDetails& details)
1627        OVERRIDE {
1628      error_event_was_raised_ = true;
1629    }
1630    virtual void OnLogMessage(int host_id, AppCacheLogLevel log_level,
1631                              const std::string& message) OVERRIDE {}
1632    virtual void OnContentBlocked(
1633        int host_id, const GURL& manifest_url) OVERRIDE {}
1634
1635    bool error_event_was_raised_;
1636  };
1637
1638  enum ReinitTestCase {
1639     CORRUPT_CACHE_ON_INSTALL,
1640     CORRUPT_CACHE_ON_LOAD_EXISTING,
1641     CORRUPT_SQL_ON_INSTALL
1642  };
1643
1644  void Reinitialize1() {
1645    // Recover from a corrupt disk cache discovered while
1646    // installing a new appcache.
1647    Reinitialize(CORRUPT_CACHE_ON_INSTALL);
1648  }
1649
1650  void Reinitialize2() {
1651    // Recover from a corrupt disk cache discovered while
1652    // trying to load a resource from an existing appcache.
1653    Reinitialize(CORRUPT_CACHE_ON_LOAD_EXISTING);
1654  }
1655
1656  void Reinitialize3() {
1657    // Recover from a corrupt sql database discovered while
1658    // installing a new appcache.
1659    Reinitialize(CORRUPT_SQL_ON_INSTALL);
1660  }
1661
1662  void Reinitialize(ReinitTestCase test_case) {
1663    // Unlike all of the other tests, this one actually read/write files.
1664    ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
1665
1666    AppCacheDatabase db(temp_directory_.path().AppendASCII("Index"));
1667    EXPECT_TRUE(db.LazyOpen(true));
1668
1669    if (test_case == CORRUPT_CACHE_ON_INSTALL ||
1670        test_case == CORRUPT_CACHE_ON_LOAD_EXISTING) {
1671      // Create a corrupt/unopenable disk_cache index file.
1672      const std::string kCorruptData("deadbeef");
1673      base::FilePath disk_cache_directory =
1674          temp_directory_.path().AppendASCII("Cache");
1675      ASSERT_TRUE(base::CreateDirectory(disk_cache_directory));
1676      base::FilePath index_file = disk_cache_directory.AppendASCII("index");
1677      EXPECT_EQ(static_cast<int>(kCorruptData.length()),
1678                base::WriteFile(
1679                    index_file, kCorruptData.data(), kCorruptData.length()));
1680    }
1681
1682    // Create records for a degenerate cached manifest that only contains
1683    // one entry for the manifest file resource.
1684    if (test_case == CORRUPT_CACHE_ON_LOAD_EXISTING) {
1685      AppCacheDatabase db(temp_directory_.path().AppendASCII("Index"));
1686      GURL manifest_url = MockHttpServer::GetMockUrl("manifest");
1687
1688      AppCacheDatabase::GroupRecord group_record;
1689      group_record.group_id = 1;
1690      group_record.manifest_url = manifest_url;
1691      group_record.origin = manifest_url.GetOrigin();
1692      EXPECT_TRUE(db.InsertGroup(&group_record));
1693      AppCacheDatabase::CacheRecord cache_record;
1694      cache_record.cache_id = 1;
1695      cache_record.group_id = 1;
1696      cache_record.online_wildcard = false;
1697      cache_record.update_time = kZeroTime;
1698      cache_record.cache_size = kDefaultEntrySize;
1699      EXPECT_TRUE(db.InsertCache(&cache_record));
1700      AppCacheDatabase::EntryRecord entry_record;
1701      entry_record.cache_id = 1;
1702      entry_record.url = manifest_url;
1703      entry_record.flags = AppCacheEntry::MANIFEST;
1704      entry_record.response_id = 1;
1705      entry_record.response_size = kDefaultEntrySize;
1706      EXPECT_TRUE(db.InsertEntry(&entry_record));
1707    }
1708
1709    // Recreate the service to point at the db and corruption on disk.
1710    service_.reset(new AppCacheServiceImpl(NULL));
1711    service_->set_request_context(io_thread->request_context());
1712    service_->Initialize(temp_directory_.path(),
1713                         db_thread->task_runner(),
1714                         db_thread->task_runner());
1715    mock_quota_manager_proxy_ = new MockQuotaManagerProxy();
1716    service_->quota_manager_proxy_ = mock_quota_manager_proxy_;
1717    delegate_.reset(new MockStorageDelegate(this));
1718
1719    // Additional setup to observe reinitailize happens.
1720    observer_.reset(new MockServiceObserver(this));
1721    service_->AddObserver(observer_.get());
1722
1723    // We continue after the init task is complete including the callback
1724    // on the current thread.
1725    FlushDbThreadTasks();
1726    base::MessageLoop::current()->PostTask(
1727        FROM_HERE,
1728        base::Bind(&AppCacheStorageImplTest::Continue_Reinitialize,
1729                   base::Unretained(this),
1730                   test_case));
1731  }
1732
1733  void Continue_Reinitialize(ReinitTestCase test_case) {
1734    const int kMockProcessId = 1;
1735    backend_.reset(new AppCacheBackendImpl);
1736    backend_->Initialize(service_.get(), &frontend_, kMockProcessId);
1737
1738    if (test_case == CORRUPT_SQL_ON_INSTALL) {
1739      // Break the db file
1740      EXPECT_FALSE(database()->was_corruption_detected());
1741      ASSERT_TRUE(sql::test::CorruptSizeInHeader(
1742          temp_directory_.path().AppendASCII("Index")));
1743    }
1744
1745    if (test_case == CORRUPT_CACHE_ON_INSTALL  ||
1746        test_case == CORRUPT_SQL_ON_INSTALL) {
1747      // Try to create a new appcache, the resulting update job will
1748      // eventually fail when it gets to disk cache initialization.
1749      backend_->RegisterHost(1);
1750      AppCacheHost* host1 = backend_->GetHost(1);
1751      const GURL kEmptyPageUrl(MockHttpServer::GetMockUrl("empty.html"));
1752      host1->first_party_url_ = kEmptyPageUrl;
1753      host1->SelectCache(kEmptyPageUrl,
1754                         kAppCacheNoCacheId,
1755                         MockHttpServer::GetMockUrl("manifest"));
1756    } else {
1757      ASSERT_EQ(CORRUPT_CACHE_ON_LOAD_EXISTING, test_case);
1758      // Try to access the existing cache manifest.
1759      // The URLRequestJob  will eventually fail when it gets to disk
1760      // cache initialization.
1761      backend_->RegisterHost(2);
1762      AppCacheHost* host2 = backend_->GetHost(2);
1763      GURL manifest_url = MockHttpServer::GetMockUrl("manifest");
1764      request_ = service()->request_context()->CreateRequest(
1765          manifest_url, net::DEFAULT_PRIORITY, NULL, NULL);
1766      AppCacheInterceptor::SetExtraRequestInfo(
1767          request_.get(), service_.get(),
1768          backend_->process_id(), host2->host_id(),
1769          RESOURCE_TYPE_MAIN_FRAME);
1770      request_->Start();
1771    }
1772
1773    PushNextTask(base::Bind(
1774        &AppCacheStorageImplTest::Verify_Reinitialized,
1775        base::Unretained(this),
1776        test_case));
1777  }
1778
1779  void Verify_Reinitialized(ReinitTestCase test_case) {
1780    // Verify we got notified of reinit and a new storage instance is created,
1781    // and that the old data has been deleted.
1782    EXPECT_TRUE(observer_->observed_old_storage_.get());
1783    EXPECT_TRUE(observer_->observed_old_storage_->storage() != storage());
1784    EXPECT_FALSE(PathExists(
1785        temp_directory_.path().AppendASCII("Cache").AppendASCII("index")));
1786    EXPECT_FALSE(PathExists(
1787        temp_directory_.path().AppendASCII("Index")));
1788
1789    if (test_case == CORRUPT_SQL_ON_INSTALL) {
1790      AppCacheStorageImpl* storage = static_cast<AppCacheStorageImpl*>(
1791          observer_->observed_old_storage_->storage());
1792      EXPECT_TRUE(storage->database_->was_corruption_detected());
1793    }
1794
1795    // Verify that the hosts saw appropriate events.
1796    if (test_case == CORRUPT_CACHE_ON_INSTALL ||
1797        test_case == CORRUPT_SQL_ON_INSTALL) {
1798      EXPECT_TRUE(frontend_.error_event_was_raised_);
1799      AppCacheHost* host1 = backend_->GetHost(1);
1800      EXPECT_FALSE(host1->associated_cache());
1801      EXPECT_FALSE(host1->group_being_updated_.get());
1802      EXPECT_TRUE(host1->disabled_storage_reference_.get());
1803    } else {
1804      ASSERT_EQ(CORRUPT_CACHE_ON_LOAD_EXISTING, test_case);
1805      AppCacheHost* host2 = backend_->GetHost(2);
1806      EXPECT_EQ(1, host2->main_resource_cache_->cache_id());
1807      EXPECT_TRUE(host2->disabled_storage_reference_.get());
1808    }
1809
1810    // Cleanup and claim victory.
1811    service_->RemoveObserver(observer_.get());
1812    request_.reset();
1813    backend_.reset();
1814    observer_.reset();
1815    TestFinished();
1816  }
1817
1818  // Test case helpers --------------------------------------------------
1819
1820  AppCacheServiceImpl* service() {
1821    return service_.get();
1822  }
1823
1824  AppCacheStorageImpl* storage() {
1825    return static_cast<AppCacheStorageImpl*>(service()->storage());
1826  }
1827
1828  AppCacheDatabase* database() {
1829    return storage()->database_;
1830  }
1831
1832  MockStorageDelegate* delegate() {
1833    return delegate_.get();
1834  }
1835
1836  void MakeCacheAndGroup(
1837      const GURL& manifest_url, int64 group_id, int64 cache_id,
1838      bool add_to_database) {
1839    AppCacheEntry default_entry(
1840        AppCacheEntry::EXPLICIT, cache_id + kDefaultEntryIdOffset,
1841        kDefaultEntrySize);
1842    group_ = new AppCacheGroup(storage(), manifest_url, group_id);
1843    cache_ = new AppCache(storage(), cache_id);
1844    cache_->AddEntry(kDefaultEntryUrl, default_entry);
1845    cache_->set_complete(true);
1846    group_->AddCache(cache_.get());
1847    if (add_to_database) {
1848      AppCacheDatabase::GroupRecord group_record;
1849      group_record.group_id = group_id;
1850      group_record.manifest_url = manifest_url;
1851      group_record.origin = manifest_url.GetOrigin();
1852      EXPECT_TRUE(database()->InsertGroup(&group_record));
1853      AppCacheDatabase::CacheRecord cache_record;
1854      cache_record.cache_id = cache_id;
1855      cache_record.group_id = group_id;
1856      cache_record.online_wildcard = false;
1857      cache_record.update_time = kZeroTime;
1858      cache_record.cache_size = kDefaultEntrySize;
1859      EXPECT_TRUE(database()->InsertCache(&cache_record));
1860      AppCacheDatabase::EntryRecord entry_record;
1861      entry_record.cache_id = cache_id;
1862      entry_record.url = kDefaultEntryUrl;
1863      entry_record.flags = default_entry.types();
1864      entry_record.response_id = default_entry.response_id();
1865      entry_record.response_size = default_entry.response_size();
1866      EXPECT_TRUE(database()->InsertEntry(&entry_record));
1867
1868      storage()->usage_map_[manifest_url.GetOrigin()] =
1869          default_entry.response_size();
1870    }
1871  }
1872
1873  // Data members --------------------------------------------------
1874
1875  scoped_ptr<base::WaitableEvent> test_finished_event_;
1876  std::stack<base::Closure> task_stack_;
1877  scoped_ptr<AppCacheServiceImpl> service_;
1878  scoped_ptr<MockStorageDelegate> delegate_;
1879  scoped_refptr<MockQuotaManagerProxy> mock_quota_manager_proxy_;
1880  scoped_refptr<AppCacheGroup> group_;
1881  scoped_refptr<AppCache> cache_;
1882  scoped_refptr<AppCache> cache2_;
1883
1884  // Specifically for the Reinitalize test.
1885  base::ScopedTempDir temp_directory_;
1886  scoped_ptr<MockServiceObserver> observer_;
1887  MockAppCacheFrontend frontend_;
1888  scoped_ptr<AppCacheBackendImpl> backend_;
1889  scoped_ptr<net::URLRequest> request_;
1890};
1891
1892
1893TEST_F(AppCacheStorageImplTest, LoadCache_Miss) {
1894  RunTestOnIOThread(&AppCacheStorageImplTest::LoadCache_Miss);
1895}
1896
1897TEST_F(AppCacheStorageImplTest, LoadCache_NearHit) {
1898  RunTestOnIOThread(&AppCacheStorageImplTest::LoadCache_NearHit);
1899}
1900
1901TEST_F(AppCacheStorageImplTest, CreateGroupInEmptyOrigin) {
1902  RunTestOnIOThread(&AppCacheStorageImplTest::CreateGroupInEmptyOrigin);
1903}
1904
1905TEST_F(AppCacheStorageImplTest, CreateGroupInPopulatedOrigin) {
1906  RunTestOnIOThread(&AppCacheStorageImplTest::CreateGroupInPopulatedOrigin);
1907}
1908
1909TEST_F(AppCacheStorageImplTest, LoadGroupAndCache_FarHit) {
1910  RunTestOnIOThread(&AppCacheStorageImplTest::LoadGroupAndCache_FarHit);
1911}
1912
1913TEST_F(AppCacheStorageImplTest, StoreNewGroup) {
1914  RunTestOnIOThread(&AppCacheStorageImplTest::StoreNewGroup);
1915}
1916
1917TEST_F(AppCacheStorageImplTest, StoreExistingGroup) {
1918  RunTestOnIOThread(&AppCacheStorageImplTest::StoreExistingGroup);
1919}
1920
1921TEST_F(AppCacheStorageImplTest, StoreExistingGroupExistingCache) {
1922  RunTestOnIOThread(&AppCacheStorageImplTest::StoreExistingGroupExistingCache);
1923}
1924
1925TEST_F(AppCacheStorageImplTest, FailStoreGroup) {
1926  RunTestOnIOThread(&AppCacheStorageImplTest::FailStoreGroup);
1927}
1928
1929TEST_F(AppCacheStorageImplTest, MakeGroupObsolete) {
1930  RunTestOnIOThread(&AppCacheStorageImplTest::MakeGroupObsolete);
1931}
1932
1933TEST_F(AppCacheStorageImplTest, MarkEntryAsForeign) {
1934  RunTestOnIOThread(&AppCacheStorageImplTest::MarkEntryAsForeign);
1935}
1936
1937TEST_F(AppCacheStorageImplTest, MarkEntryAsForeignWithLoadInProgress) {
1938  RunTestOnIOThread(
1939      &AppCacheStorageImplTest::MarkEntryAsForeignWithLoadInProgress);
1940}
1941
1942TEST_F(AppCacheStorageImplTest, FindNoMainResponse) {
1943  RunTestOnIOThread(&AppCacheStorageImplTest::FindNoMainResponse);
1944}
1945
1946TEST_F(AppCacheStorageImplTest, BasicFindMainResponseInDatabase) {
1947  RunTestOnIOThread(
1948      &AppCacheStorageImplTest::BasicFindMainResponseInDatabase);
1949}
1950
1951TEST_F(AppCacheStorageImplTest, BasicFindMainResponseInWorkingSet) {
1952  RunTestOnIOThread(
1953      &AppCacheStorageImplTest::BasicFindMainResponseInWorkingSet);
1954}
1955
1956TEST_F(AppCacheStorageImplTest, BasicFindMainFallbackResponseInDatabase) {
1957  RunTestOnIOThread(
1958      &AppCacheStorageImplTest::BasicFindMainFallbackResponseInDatabase);
1959}
1960
1961TEST_F(AppCacheStorageImplTest, BasicFindMainFallbackResponseInWorkingSet) {
1962  RunTestOnIOThread(
1963      &AppCacheStorageImplTest::BasicFindMainFallbackResponseInWorkingSet);
1964}
1965
1966TEST_F(AppCacheStorageImplTest, BasicFindMainInterceptResponseInDatabase) {
1967  RunTestOnIOThread(
1968      &AppCacheStorageImplTest::BasicFindMainInterceptResponseInDatabase);
1969}
1970
1971TEST_F(AppCacheStorageImplTest, BasicFindMainInterceptResponseInWorkingSet) {
1972  RunTestOnIOThread(
1973      &AppCacheStorageImplTest::BasicFindMainInterceptResponseInWorkingSet);
1974}
1975
1976TEST_F(AppCacheStorageImplTest, FindMainResponseWithMultipleHits) {
1977  RunTestOnIOThread(
1978      &AppCacheStorageImplTest::FindMainResponseWithMultipleHits);
1979}
1980
1981TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInDatabase) {
1982  RunTestOnIOThread(
1983      &AppCacheStorageImplTest::FindMainResponseExclusionsInDatabase);
1984}
1985
1986TEST_F(AppCacheStorageImplTest, FindMainResponseExclusionsInWorkingSet) {
1987  RunTestOnIOThread(
1988      &AppCacheStorageImplTest::FindMainResponseExclusionsInWorkingSet);
1989}
1990
1991TEST_F(AppCacheStorageImplTest, FindInterceptPatternMatchInWorkingSet) {
1992  RunTestOnIOThread(
1993      &AppCacheStorageImplTest::FindInterceptPatternMatchInWorkingSet);
1994}
1995
1996TEST_F(AppCacheStorageImplTest, FindInterceptPatternMatchInDatabase) {
1997  RunTestOnIOThread(
1998      &AppCacheStorageImplTest::FindInterceptPatternMatchInDatabase);
1999}
2000
2001TEST_F(AppCacheStorageImplTest, FindFallbackPatternMatchInWorkingSet) {
2002  RunTestOnIOThread(
2003      &AppCacheStorageImplTest::FindFallbackPatternMatchInWorkingSet);
2004}
2005
2006TEST_F(AppCacheStorageImplTest, FindFallbackPatternMatchInDatabase) {
2007  RunTestOnIOThread(
2008      &AppCacheStorageImplTest::FindFallbackPatternMatchInDatabase);
2009}
2010
2011TEST_F(AppCacheStorageImplTest, Reinitialize1) {
2012  RunTestOnIOThread(&AppCacheStorageImplTest::Reinitialize1);
2013}
2014
2015TEST_F(AppCacheStorageImplTest, Reinitialize2) {
2016  RunTestOnIOThread(&AppCacheStorageImplTest::Reinitialize2);
2017}
2018
2019TEST_F(AppCacheStorageImplTest, Reinitialize3) {
2020  RunTestOnIOThread(&AppCacheStorageImplTest::Reinitialize3);
2021}
2022
2023// That's all folks!
2024
2025}  // namespace content
2026