Home | History | Annotate | Download | only in quota
      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.h"
     14 #include "base/message_loop/message_loop_proxy.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 
     20 namespace quota {
     21 
     22 class QuotaTemporaryStorageEvictorTest;
     23 
     24 namespace {
     25 
     26 class 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 }  // anonymous namespace
    135 
    136 class 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::MessageLoop::current()->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 
    228 TEST_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::MessageLoop::current()->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 
    247 TEST_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::MessageLoop::current()->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 
    267 TEST_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::MessageLoop::current()->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 
    301 TEST_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::MessageLoop::current()->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 
    332 TEST_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::MessageLoop::current()->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 
    367 TEST_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::MessageLoop::current()->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 
    387 TEST_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::MessageLoop::current()->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