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