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