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