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 16 namespace { 17 18 class 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 64 namespace appcache { 65 66 class 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 83 class 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 98 class AppCacheGroupTest : public testing::Test { 99 private: 100 base::MessageLoop message_loop_; 101 }; 102 103 TEST_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 173 TEST_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 214 TEST_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 235 TEST_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 254 TEST_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