1// Copyright 2013 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 <list> 6#include <map> 7#include <utility> 8 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/memory/scoped_ptr.h" 12#include "base/memory/weak_ptr.h" 13#include "base/message_loop/message_loop_proxy.h" 14#include "base/run_loop.h" 15#include "testing/gtest/include/gtest/gtest.h" 16#include "webkit/browser/quota/mock_storage_client.h" 17#include "webkit/browser/quota/quota_manager.h" 18#include "webkit/browser/quota/quota_temporary_storage_evictor.h" 19 20namespace quota { 21 22class QuotaTemporaryStorageEvictorTest; 23 24namespace { 25 26class MockQuotaEvictionHandler : public quota::QuotaEvictionHandler { 27 public: 28 explicit MockQuotaEvictionHandler(QuotaTemporaryStorageEvictorTest *test) 29 : quota_(0), 30 available_space_(0), 31 error_on_evict_origin_data_(false), 32 error_on_get_usage_and_quota_(false) {} 33 34 virtual void EvictOriginData( 35 const GURL& origin, 36 StorageType type, 37 const EvictOriginDataCallback& callback) OVERRIDE { 38 if (error_on_evict_origin_data_) { 39 callback.Run(quota::kQuotaErrorInvalidModification); 40 return; 41 } 42 int64 origin_usage = EnsureOriginRemoved(origin); 43 if (origin_usage >= 0) 44 available_space_ += origin_usage; 45 callback.Run(quota::kQuotaStatusOk); 46 } 47 48 virtual void GetUsageAndQuotaForEviction( 49 const UsageAndQuotaCallback& callback) OVERRIDE { 50 if (error_on_get_usage_and_quota_) { 51 callback.Run(quota::kQuotaErrorInvalidAccess, UsageAndQuota()); 52 return; 53 } 54 if (!task_for_get_usage_and_quota_.is_null()) 55 task_for_get_usage_and_quota_.Run(); 56 UsageAndQuota quota_and_usage(-1, GetUsage(), quota_, available_space_); 57 callback.Run(quota::kQuotaStatusOk, quota_and_usage); 58 } 59 60 virtual void GetLRUOrigin( 61 StorageType type, 62 const GetLRUOriginCallback& callback) OVERRIDE { 63 if (origin_order_.empty()) 64 callback.Run(GURL()); 65 else 66 callback.Run(GURL(origin_order_.front())); 67 } 68 69 int64 GetUsage() const { 70 int64 total_usage = 0; 71 for (std::map<GURL, int64>::const_iterator p = origins_.begin(); 72 p != origins_.end(); 73 ++p) 74 total_usage += p->second; 75 return total_usage; 76 } 77 78 void set_quota(int64 quota) { 79 quota_ = quota; 80 } 81 void set_available_space(int64 available_space) { 82 available_space_ = available_space; 83 } 84 void set_task_for_get_usage_and_quota(const base::Closure& task) { 85 task_for_get_usage_and_quota_= task; 86 } 87 void set_error_on_evict_origin_data(bool error_on_evict_origin_data) { 88 error_on_evict_origin_data_ = error_on_evict_origin_data; 89 } 90 void set_error_on_get_usage_and_quota(bool error_on_get_usage_and_quota) { 91 error_on_get_usage_and_quota_ = error_on_get_usage_and_quota; 92 } 93 94 // Simulates an access to |origin|. It reorders the internal LRU list. 95 // It internally uses AddOrigin(). 96 void AccessOrigin(const GURL& origin) { 97 std::map<GURL, int64>::iterator found = origins_.find(origin); 98 EXPECT_TRUE(origins_.end() != found); 99 AddOrigin(origin, found->second); 100 } 101 102 // Simulates adding or overwriting the |origin| to the internal origin set 103 // with the |usage|. It also adds or moves the |origin| to the end of the 104 // LRU list. 105 void AddOrigin(const GURL& origin, int64 usage) { 106 EnsureOriginRemoved(origin); 107 origin_order_.push_back(origin); 108 origins_[origin] = usage; 109 } 110 111 private: 112 int64 EnsureOriginRemoved(const GURL& origin) { 113 int64 origin_usage; 114 if (origins_.find(origin) == origins_.end()) 115 return -1; 116 else 117 origin_usage = origins_[origin]; 118 119 origins_.erase(origin); 120 origin_order_.remove(origin); 121 return origin_usage; 122 } 123 124 int64 quota_; 125 int64 available_space_; 126 std::list<GURL> origin_order_; 127 std::map<GURL, int64> origins_; 128 bool error_on_evict_origin_data_; 129 bool error_on_get_usage_and_quota_; 130 131 base::Closure task_for_get_usage_and_quota_; 132}; 133 134} // namespace 135 136class QuotaTemporaryStorageEvictorTest : public testing::Test { 137 public: 138 QuotaTemporaryStorageEvictorTest() 139 : num_get_usage_and_quota_for_eviction_(0), 140 weak_factory_(this) {} 141 142 virtual void SetUp() { 143 quota_eviction_handler_.reset(new MockQuotaEvictionHandler(this)); 144 145 // Run multiple evictions in a single RunUntilIdle() when interval_ms == 0 146 temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor( 147 quota_eviction_handler_.get(), 0)); 148 } 149 150 virtual void TearDown() { 151 temporary_storage_evictor_.reset(); 152 quota_eviction_handler_.reset(); 153 base::RunLoop().RunUntilIdle(); 154 } 155 156 void TaskForRepeatedEvictionTest( 157 const std::pair<GURL, int64>& origin_to_be_added, 158 const GURL& origin_to_be_accessed, 159 int expected_usage_after_first, 160 int expected_usage_after_second) { 161 EXPECT_GE(4, num_get_usage_and_quota_for_eviction_); 162 switch (num_get_usage_and_quota_for_eviction_) { 163 case 2: 164 EXPECT_EQ(expected_usage_after_first, 165 quota_eviction_handler()->GetUsage()); 166 if (!origin_to_be_added.first.is_empty()) 167 quota_eviction_handler()->AddOrigin(origin_to_be_added.first, 168 origin_to_be_added.second); 169 if (!origin_to_be_accessed.is_empty()) 170 quota_eviction_handler()->AccessOrigin(origin_to_be_accessed); 171 break; 172 case 3: 173 EXPECT_EQ(expected_usage_after_second, 174 quota_eviction_handler()->GetUsage()); 175 temporary_storage_evictor()->set_repeated_eviction(false); 176 break; 177 } 178 ++num_get_usage_and_quota_for_eviction_; 179 } 180 181 protected: 182 MockQuotaEvictionHandler* quota_eviction_handler() const { 183 return static_cast<MockQuotaEvictionHandler*>( 184 quota_eviction_handler_.get()); 185 } 186 187 QuotaTemporaryStorageEvictor* temporary_storage_evictor() const { 188 return temporary_storage_evictor_.get(); 189 } 190 191 const QuotaTemporaryStorageEvictor::Statistics& statistics() const { 192 return temporary_storage_evictor()->statistics_; 193 } 194 195 void set_repeated_eviction(bool repeated_eviction) const { 196 return temporary_storage_evictor_->set_repeated_eviction(repeated_eviction); 197 } 198 199 int num_get_usage_and_quota_for_eviction() const { 200 return num_get_usage_and_quota_for_eviction_; 201 } 202 203 int64 default_min_available_disk_space_to_start_eviction() const { 204 return 1000 * 1000 * 500; 205 } 206 207 void set_min_available_disk_space_to_start_eviction(int64 value) const { 208 temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction( 209 value); 210 } 211 212 void reset_min_available_disk_space_to_start_eviction() const { 213 temporary_storage_evictor_-> 214 reset_min_available_disk_space_to_start_eviction(); 215 } 216 217 base::MessageLoop message_loop_; 218 scoped_ptr<MockQuotaEvictionHandler> quota_eviction_handler_; 219 scoped_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_; 220 221 int num_get_usage_and_quota_for_eviction_; 222 223 base::WeakPtrFactory<QuotaTemporaryStorageEvictorTest> weak_factory_; 224 225 DISALLOW_COPY_AND_ASSIGN(QuotaTemporaryStorageEvictorTest); 226}; 227 228TEST_F(QuotaTemporaryStorageEvictorTest, SimpleEvictionTest) { 229 quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 3000); 230 quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 200); 231 quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 500); 232 quota_eviction_handler()->set_quota(4000); 233 quota_eviction_handler()->set_available_space(1000000000); 234 EXPECT_EQ(3000 + 200 + 500, quota_eviction_handler()->GetUsage()); 235 set_repeated_eviction(false); 236 temporary_storage_evictor()->Start(); 237 base::RunLoop().RunUntilIdle(); 238 EXPECT_EQ(200 + 500, quota_eviction_handler()->GetUsage()); 239 240 EXPECT_EQ(0, statistics().num_errors_on_evicting_origin); 241 EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota); 242 EXPECT_EQ(1, statistics().num_evicted_origins); 243 EXPECT_EQ(1, statistics().num_eviction_rounds); 244 EXPECT_EQ(0, statistics().num_skipped_eviction_rounds); 245} 246 247TEST_F(QuotaTemporaryStorageEvictorTest, MultipleEvictionTest) { 248 quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 20); 249 quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 2900); 250 quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450); 251 quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 400); 252 quota_eviction_handler()->set_quota(4000); 253 quota_eviction_handler()->set_available_space(1000000000); 254 EXPECT_EQ(20 + 2900 + 450 + 400, quota_eviction_handler()->GetUsage()); 255 set_repeated_eviction(false); 256 temporary_storage_evictor()->Start(); 257 base::RunLoop().RunUntilIdle(); 258 EXPECT_EQ(450 + 400, quota_eviction_handler()->GetUsage()); 259 260 EXPECT_EQ(0, statistics().num_errors_on_evicting_origin); 261 EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota); 262 EXPECT_EQ(2, statistics().num_evicted_origins); 263 EXPECT_EQ(1, statistics().num_eviction_rounds); 264 EXPECT_EQ(0, statistics().num_skipped_eviction_rounds); 265} 266 267TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionTest) { 268 const int64 a_size = 400; 269 const int64 b_size = 150; 270 const int64 c_size = 120; 271 const int64 d_size = 292; 272 const int64 initial_total_size = a_size + b_size + c_size + d_size; 273 const int64 e_size = 275; 274 275 quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size); 276 quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size); 277 quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size); 278 quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size); 279 quota_eviction_handler()->set_quota(1000); 280 quota_eviction_handler()->set_available_space(1000000000); 281 quota_eviction_handler()->set_task_for_get_usage_and_quota( 282 base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest, 283 weak_factory_.GetWeakPtr(), 284 std::make_pair(GURL("http://www.e.com"), e_size), GURL(), 285 initial_total_size - d_size, 286 initial_total_size - d_size + e_size - c_size)); 287 EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage()); 288 temporary_storage_evictor()->Start(); 289 base::RunLoop().RunUntilIdle(); 290 EXPECT_EQ(initial_total_size - d_size + e_size - c_size - b_size, 291 quota_eviction_handler()->GetUsage()); 292 EXPECT_EQ(5, num_get_usage_and_quota_for_eviction()); 293 294 EXPECT_EQ(0, statistics().num_errors_on_evicting_origin); 295 EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota); 296 EXPECT_EQ(3, statistics().num_evicted_origins); 297 EXPECT_EQ(2, statistics().num_eviction_rounds); 298 EXPECT_EQ(0, statistics().num_skipped_eviction_rounds); 299} 300 301TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionSkippedTest) { 302 const int64 a_size = 400; 303 const int64 b_size = 150; 304 const int64 c_size = 120; 305 const int64 d_size = 292; 306 const int64 initial_total_size = a_size + b_size + c_size + d_size; 307 308 quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size); 309 quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size); 310 quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size); 311 quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size); 312 quota_eviction_handler()->set_quota(1000); 313 quota_eviction_handler()->set_available_space(1000000000); 314 quota_eviction_handler()->set_task_for_get_usage_and_quota( 315 base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest, 316 weak_factory_.GetWeakPtr(), std::make_pair(GURL(), 0), GURL(), 317 initial_total_size - d_size, initial_total_size - d_size)); 318 EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage()); 319 set_repeated_eviction(true); 320 temporary_storage_evictor()->Start(); 321 base::RunLoop().RunUntilIdle(); 322 EXPECT_EQ(initial_total_size - d_size, quota_eviction_handler()->GetUsage()); 323 EXPECT_EQ(4, num_get_usage_and_quota_for_eviction()); 324 325 EXPECT_EQ(0, statistics().num_errors_on_evicting_origin); 326 EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota); 327 EXPECT_EQ(1, statistics().num_evicted_origins); 328 EXPECT_EQ(3, statistics().num_eviction_rounds); 329 EXPECT_EQ(2, statistics().num_skipped_eviction_rounds); 330} 331 332TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionWithAccessOriginTest) { 333 const int64 a_size = 400; 334 const int64 b_size = 150; 335 const int64 c_size = 120; 336 const int64 d_size = 292; 337 const int64 initial_total_size = a_size + b_size + c_size + d_size; 338 const int64 e_size = 275; 339 340 quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size); 341 quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size); 342 quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size); 343 quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size); 344 quota_eviction_handler()->set_quota(1000); 345 quota_eviction_handler()->set_available_space(1000000000); 346 quota_eviction_handler()->set_task_for_get_usage_and_quota( 347 base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest, 348 weak_factory_.GetWeakPtr(), 349 std::make_pair(GURL("http://www.e.com"), e_size), 350 GURL("http://www.c.com"), 351 initial_total_size - d_size, 352 initial_total_size - d_size + e_size - b_size)); 353 EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage()); 354 temporary_storage_evictor()->Start(); 355 base::RunLoop().RunUntilIdle(); 356 EXPECT_EQ(initial_total_size - d_size + e_size - b_size - a_size, 357 quota_eviction_handler()->GetUsage()); 358 EXPECT_EQ(5, num_get_usage_and_quota_for_eviction()); 359 360 EXPECT_EQ(0, statistics().num_errors_on_evicting_origin); 361 EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota); 362 EXPECT_EQ(3, statistics().num_evicted_origins); 363 EXPECT_EQ(2, statistics().num_eviction_rounds); 364 EXPECT_EQ(0, statistics().num_skipped_eviction_rounds); 365} 366 367TEST_F(QuotaTemporaryStorageEvictorTest, DiskSpaceNonEvictionTest) { 368 quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 414); 369 quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450); 370 quota_eviction_handler()->set_quota(10000); 371 quota_eviction_handler()->set_available_space( 372 default_min_available_disk_space_to_start_eviction() - 350); 373 EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage()); 374 reset_min_available_disk_space_to_start_eviction(); 375 set_repeated_eviction(false); 376 temporary_storage_evictor()->Start(); 377 base::RunLoop().RunUntilIdle(); 378 EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage()); 379 380 EXPECT_EQ(0, statistics().num_errors_on_evicting_origin); 381 EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota); 382 EXPECT_EQ(0, statistics().num_evicted_origins); 383 EXPECT_EQ(1, statistics().num_eviction_rounds); 384 EXPECT_EQ(1, statistics().num_skipped_eviction_rounds); 385} 386 387TEST_F(QuotaTemporaryStorageEvictorTest, DiskSpaceEvictionTest) { 388 quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 294); 389 quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 120); 390 quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 150); 391 quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 300); 392 quota_eviction_handler()->set_quota(10000); 393 quota_eviction_handler()->set_available_space( 394 default_min_available_disk_space_to_start_eviction() - 350); 395 EXPECT_EQ(294 + 120 + 150 + 300, quota_eviction_handler()->GetUsage()); 396 set_min_available_disk_space_to_start_eviction( 397 default_min_available_disk_space_to_start_eviction()); 398 set_repeated_eviction(false); 399 temporary_storage_evictor()->Start(); 400 base::RunLoop().RunUntilIdle(); 401 EXPECT_EQ(150 + 300, quota_eviction_handler()->GetUsage()); 402 403 EXPECT_EQ(0, statistics().num_errors_on_evicting_origin); 404 EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota); 405 EXPECT_EQ(2, statistics().num_evicted_origins); 406 EXPECT_EQ(1, statistics().num_eviction_rounds); 407 EXPECT_EQ(0, statistics().num_skipped_eviction_rounds); 408} 409 410} // namespace quota 411