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 <string>
6
7#include "base/message_loop/message_loop.h"
8#include "content/browser/appcache/mock_appcache_service.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "webkit/browser/appcache/appcache.h"
11#include "webkit/browser/appcache/appcache_group.h"
12#include "webkit/browser/appcache/appcache_host.h"
13#include "webkit/browser/appcache/appcache_update_job.h"
14#include "webkit/common/appcache/appcache_interfaces.h"
15
16using appcache::AppCache;
17using appcache::AppCacheFrontend;
18using appcache::AppCacheGroup;
19using appcache::AppCacheHost;
20using appcache::AppCacheServiceImpl;
21using appcache::AppCacheUpdateJob;
22
23namespace {
24
25class TestAppCacheFrontend : public appcache::AppCacheFrontend {
26 public:
27  TestAppCacheFrontend()
28      : last_host_id_(-1), last_cache_id_(-1),
29        last_status_(appcache::APPCACHE_STATUS_OBSOLETE) {
30  }
31
32  virtual void OnCacheSelected(
33      int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
34    last_host_id_ = host_id;
35    last_cache_id_ = info.cache_id;
36    last_status_ = info.status;
37  }
38
39  virtual void OnStatusChanged(const std::vector<int>& host_ids,
40                               appcache::AppCacheStatus status) OVERRIDE {
41  }
42
43  virtual void OnEventRaised(const std::vector<int>& host_ids,
44                             appcache::AppCacheEventID event_id) OVERRIDE {
45  }
46
47  virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
48                                  const appcache::AppCacheErrorDetails& details)
49      OVERRIDE {}
50
51  virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
52                                     const GURL& url,
53                                     int num_total, int num_complete) OVERRIDE {
54  }
55
56  virtual void OnLogMessage(int host_id, appcache::AppCacheLogLevel log_level,
57                            const std::string& message) OVERRIDE {
58  }
59
60  virtual void OnContentBlocked(int host_id,
61                                const GURL& manifest_url) OVERRIDE {
62  }
63
64  int last_host_id_;
65  int64 last_cache_id_;
66  appcache::AppCacheStatus last_status_;
67};
68
69}  // namespace anon
70
71namespace content {
72
73class TestUpdateObserver : public AppCacheGroup::UpdateObserver {
74 public:
75  TestUpdateObserver() : update_completed_(false), group_has_cache_(false) {
76  }
77
78  virtual void OnUpdateComplete(AppCacheGroup* group) OVERRIDE {
79    update_completed_ = true;
80    group_has_cache_ = group->HasCache();
81  }
82
83  virtual void OnContentBlocked(AppCacheGroup* group) {
84  }
85
86  bool update_completed_;
87  bool group_has_cache_;
88};
89
90class TestAppCacheHost : public AppCacheHost {
91 public:
92  TestAppCacheHost(int host_id, AppCacheFrontend* frontend,
93                   AppCacheServiceImpl* service)
94      : AppCacheHost(host_id, frontend, service),
95        update_completed_(false) {
96  }
97
98  virtual void OnUpdateComplete(AppCacheGroup* group) OVERRIDE {
99    update_completed_ = true;
100  }
101
102  bool update_completed_;
103};
104
105class AppCacheGroupTest : public testing::Test {
106 private:
107  base::MessageLoop message_loop_;
108};
109
110TEST_F(AppCacheGroupTest, AddRemoveCache) {
111  MockAppCacheService service;
112  scoped_refptr<AppCacheGroup> group(
113      new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
114
115  base::Time now = base::Time::Now();
116
117  scoped_refptr<AppCache> cache1(new AppCache(service.storage(), 111));
118  cache1->set_complete(true);
119  cache1->set_update_time(now);
120  group->AddCache(cache1.get());
121  EXPECT_EQ(cache1, group->newest_complete_cache());
122
123  // Adding older cache does not change newest complete cache.
124  scoped_refptr<AppCache> cache2(new AppCache(service.storage(), 222));
125  cache2->set_complete(true);
126  cache2->set_update_time(now - base::TimeDelta::FromDays(1));
127  group->AddCache(cache2.get());
128  EXPECT_EQ(cache1, group->newest_complete_cache());
129
130  // Adding newer cache does change newest complete cache.
131  scoped_refptr<AppCache> cache3(new AppCache(service.storage(), 333));
132  cache3->set_complete(true);
133  cache3->set_update_time(now + base::TimeDelta::FromDays(1));
134  group->AddCache(cache3.get());
135  EXPECT_EQ(cache3, group->newest_complete_cache());
136
137  // Adding cache with same update time uses one with larger ID.
138  scoped_refptr<AppCache> cache4(new AppCache(service.storage(), 444));
139  cache4->set_complete(true);
140  cache4->set_update_time(now + base::TimeDelta::FromDays(1));  // same as 3
141  group->AddCache(cache4.get());
142  EXPECT_EQ(cache4, group->newest_complete_cache());
143
144  // smaller id
145  scoped_refptr<AppCache> cache5(new AppCache(service.storage(), 55));
146  cache5->set_complete(true);
147  cache5->set_update_time(now + base::TimeDelta::FromDays(1));  // same as 4
148  group->AddCache(cache5.get());
149  EXPECT_EQ(cache4, group->newest_complete_cache());  // no change
150
151  // Old caches can always be removed.
152  group->RemoveCache(cache1.get());
153  EXPECT_FALSE(cache1->owning_group());
154  EXPECT_EQ(cache4, group->newest_complete_cache());  // newest unchanged
155
156  // Remove rest of caches.
157  group->RemoveCache(cache2.get());
158  EXPECT_FALSE(cache2->owning_group());
159  EXPECT_EQ(cache4, group->newest_complete_cache());  // newest unchanged
160  group->RemoveCache(cache3.get());
161  EXPECT_FALSE(cache3->owning_group());
162  EXPECT_EQ(cache4, group->newest_complete_cache());  // newest unchanged
163  group->RemoveCache(cache5.get());
164  EXPECT_FALSE(cache5->owning_group());
165  EXPECT_EQ(cache4, group->newest_complete_cache());  // newest unchanged
166  group->RemoveCache(cache4.get());                   // newest removed
167  EXPECT_FALSE(cache4->owning_group());
168  EXPECT_FALSE(group->newest_complete_cache());       // no more newest cache
169
170  // Can remove newest cache if there are older caches.
171  group->AddCache(cache1.get());
172  EXPECT_EQ(cache1, group->newest_complete_cache());
173  group->AddCache(cache4.get());
174  EXPECT_EQ(cache4, group->newest_complete_cache());
175  group->RemoveCache(cache4.get());  // remove newest
176  EXPECT_FALSE(cache4->owning_group());
177  EXPECT_FALSE(group->newest_complete_cache());  // newest removed
178}
179
180TEST_F(AppCacheGroupTest, CleanupUnusedGroup) {
181  MockAppCacheService service;
182  TestAppCacheFrontend frontend;
183  AppCacheGroup* group =
184      new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111);
185
186  AppCacheHost host1(1, &frontend, &service);
187  AppCacheHost host2(2, &frontend, &service);
188
189  base::Time now = base::Time::Now();
190
191  AppCache* cache1 = new AppCache(service.storage(), 111);
192  cache1->set_complete(true);
193  cache1->set_update_time(now);
194  group->AddCache(cache1);
195  EXPECT_EQ(cache1, group->newest_complete_cache());
196
197  host1.AssociateCompleteCache(cache1);
198  EXPECT_EQ(frontend.last_host_id_, host1.host_id());
199  EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
200  EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_IDLE);
201
202  host2.AssociateCompleteCache(cache1);
203  EXPECT_EQ(frontend.last_host_id_, host2.host_id());
204  EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
205  EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_IDLE);
206
207  AppCache* cache2 = new AppCache(service.storage(), 222);
208  cache2->set_complete(true);
209  cache2->set_update_time(now + base::TimeDelta::FromDays(1));
210  group->AddCache(cache2);
211  EXPECT_EQ(cache2, group->newest_complete_cache());
212
213  // Unassociate all hosts from older cache.
214  host1.AssociateNoCache(GURL());
215  host2.AssociateNoCache(GURL());
216  EXPECT_EQ(frontend.last_host_id_, host2.host_id());
217  EXPECT_EQ(frontend.last_cache_id_, appcache::kAppCacheNoCacheId);
218  EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_UNCACHED);
219}
220
221TEST_F(AppCacheGroupTest, StartUpdate) {
222  MockAppCacheService service;
223  scoped_refptr<AppCacheGroup> group(
224      new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
225
226  // Set state to checking to prevent update job from executing fetches.
227  group->update_status_ = AppCacheGroup::CHECKING;
228  group->StartUpdate();
229  AppCacheUpdateJob* update = group->update_job_;
230  EXPECT_TRUE(update != NULL);
231
232  // Start another update, check that same update job is in use.
233  group->StartUpdateWithHost(NULL);
234  EXPECT_EQ(update, group->update_job_);
235
236  // Deleting the update should restore the group to APPCACHE_STATUS_IDLE.
237  delete update;
238  EXPECT_TRUE(group->update_job_ == NULL);
239  EXPECT_EQ(AppCacheGroup::IDLE, group->update_status());
240}
241
242TEST_F(AppCacheGroupTest, CancelUpdate) {
243  MockAppCacheService service;
244  scoped_refptr<AppCacheGroup> group(
245      new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
246
247  // Set state to checking to prevent update job from executing fetches.
248  group->update_status_ = AppCacheGroup::CHECKING;
249  group->StartUpdate();
250  AppCacheUpdateJob* update = group->update_job_;
251  EXPECT_TRUE(update != NULL);
252
253  // Deleting the group should cancel the update.
254  TestUpdateObserver observer;
255  group->AddUpdateObserver(&observer);
256  group = NULL;  // causes group to be deleted
257  EXPECT_TRUE(observer.update_completed_);
258  EXPECT_FALSE(observer.group_has_cache_);
259}
260
261TEST_F(AppCacheGroupTest, QueueUpdate) {
262  MockAppCacheService service;
263  scoped_refptr<AppCacheGroup> group(
264      new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111));
265
266  // Set state to checking to prevent update job from executing fetches.
267  group->update_status_ = AppCacheGroup::CHECKING;
268  group->StartUpdate();
269  EXPECT_TRUE(group->update_job_);
270
271  // Pretend group's update job is terminating so that next update is queued.
272  group->update_job_->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
273  EXPECT_TRUE(group->update_job_->IsTerminating());
274
275  TestAppCacheFrontend frontend;
276  TestAppCacheHost host(1, &frontend, &service);
277  host.new_master_entry_url_ = GURL("http://foo.com/bar.txt");
278  group->StartUpdateWithNewMasterEntry(&host, host.new_master_entry_url_);
279  EXPECT_FALSE(group->queued_updates_.empty());
280
281  group->AddUpdateObserver(&host);
282  EXPECT_FALSE(group->FindObserver(&host, group->observers_));
283  EXPECT_TRUE(group->FindObserver(&host, group->queued_observers_));
284
285  // Delete update to cause it to complete. Verify no update complete notice
286  // sent to host.
287  delete group->update_job_;
288  EXPECT_EQ(AppCacheGroup::IDLE, group->update_status_);
289  EXPECT_FALSE(group->restart_update_task_.IsCancelled());
290  EXPECT_FALSE(host.update_completed_);
291
292  // Start another update. Cancels task and will run queued updates.
293  group->update_status_ = AppCacheGroup::CHECKING;  // prevent actual fetches
294  group->StartUpdate();
295  EXPECT_TRUE(group->update_job_);
296  EXPECT_TRUE(group->restart_update_task_.IsCancelled());
297  EXPECT_TRUE(group->queued_updates_.empty());
298  EXPECT_FALSE(group->update_job_->pending_master_entries_.empty());
299  EXPECT_FALSE(group->FindObserver(&host, group->queued_observers_));
300  EXPECT_TRUE(group->FindObserver(&host, group->observers_));
301
302  // Delete update to cause it to complete. Verify host is notified.
303  delete group->update_job_;
304  EXPECT_EQ(AppCacheGroup::IDLE, group->update_status_);
305  EXPECT_TRUE(group->restart_update_task_.IsCancelled());
306  EXPECT_TRUE(host.update_completed_);
307}
308
309}  // namespace content
310