Home | History | Annotate | Download | only in quota
      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 "storage/browser/fileapi/quota/quota_reservation_manager.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/files/file.h"
     10 #include "base/files/file_util.h"
     11 #include "base/files/scoped_temp_dir.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/run_loop.h"
     14 #include "storage/browser/fileapi/quota/open_file_handle.h"
     15 #include "storage/browser/fileapi/quota/quota_reservation.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 using storage::kFileSystemTypeTemporary;
     19 using storage::OpenFileHandle;
     20 using storage::QuotaReservation;
     21 using storage::QuotaReservationManager;
     22 
     23 namespace content {
     24 
     25 namespace {
     26 
     27 const char kOrigin[] = "http://example.com";
     28 const storage::FileSystemType kType = kFileSystemTypeTemporary;
     29 const int64 kInitialFileSize = 1;
     30 
     31 typedef QuotaReservationManager::ReserveQuotaCallback ReserveQuotaCallback;
     32 
     33 int64 GetFileSize(const base::FilePath& path) {
     34   int64 size = 0;
     35   base::GetFileSize(path, &size);
     36   return size;
     37 }
     38 
     39 void SetFileSize(const base::FilePath& path, int64 size) {
     40   base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
     41   ASSERT_TRUE(file.IsValid());
     42   ASSERT_TRUE(file.SetLength(size));
     43 }
     44 
     45 class FakeBackend : public QuotaReservationManager::QuotaBackend {
     46  public:
     47   FakeBackend()
     48       : on_memory_usage_(kInitialFileSize),
     49         on_disk_usage_(kInitialFileSize) {}
     50   virtual ~FakeBackend() {}
     51 
     52   virtual void ReserveQuota(const GURL& origin,
     53                             storage::FileSystemType type,
     54                             int64 delta,
     55                             const ReserveQuotaCallback& callback) OVERRIDE {
     56     EXPECT_EQ(GURL(kOrigin), origin);
     57     EXPECT_EQ(kType, type);
     58     on_memory_usage_ += delta;
     59     base::MessageLoopProxy::current()->PostTask(
     60         FROM_HERE,
     61         base::Bind(base::IgnoreResult(callback), base::File::FILE_OK, delta));
     62   }
     63 
     64   virtual void ReleaseReservedQuota(const GURL& origin,
     65                                     storage::FileSystemType type,
     66                                     int64 size) OVERRIDE {
     67     EXPECT_LE(0, size);
     68     EXPECT_EQ(GURL(kOrigin), origin);
     69     EXPECT_EQ(kType, type);
     70     on_memory_usage_ -= size;
     71   }
     72 
     73   virtual void CommitQuotaUsage(const GURL& origin,
     74                                 storage::FileSystemType type,
     75                                 int64 delta) OVERRIDE {
     76     EXPECT_EQ(GURL(kOrigin), origin);
     77     EXPECT_EQ(kType, type);
     78     on_disk_usage_ += delta;
     79     on_memory_usage_ += delta;
     80   }
     81 
     82   virtual void IncrementDirtyCount(const GURL& origin,
     83                                    storage::FileSystemType type) OVERRIDE {}
     84   virtual void DecrementDirtyCount(const GURL& origin,
     85                                    storage::FileSystemType type) OVERRIDE {}
     86 
     87   int64 on_memory_usage() { return on_memory_usage_; }
     88   int64 on_disk_usage() { return on_disk_usage_; }
     89 
     90  private:
     91   int64 on_memory_usage_;
     92   int64 on_disk_usage_;
     93 
     94   DISALLOW_COPY_AND_ASSIGN(FakeBackend);
     95 };
     96 
     97 class FakeWriter {
     98  public:
     99   explicit FakeWriter(scoped_ptr<OpenFileHandle> handle)
    100       : handle_(handle.Pass()),
    101         path_(handle_->platform_path()),
    102         max_written_offset_(handle_->GetEstimatedFileSize()),
    103         append_mode_write_amount_(0),
    104         dirty_(false) {
    105   }
    106 
    107   ~FakeWriter() {
    108     if (handle_)
    109       EXPECT_FALSE(dirty_);
    110   }
    111 
    112   int64 Truncate(int64 length) {
    113     int64 consumed = 0;
    114 
    115     if (max_written_offset_ < length) {
    116       consumed = length - max_written_offset_;
    117       max_written_offset_ = length;
    118     }
    119     SetFileSize(path_, length);
    120     return consumed;
    121   }
    122 
    123   int64 Write(int64 max_offset) {
    124     dirty_ = true;
    125 
    126     int64 consumed = 0;
    127     if (max_written_offset_ < max_offset) {
    128       consumed = max_offset - max_written_offset_;
    129       max_written_offset_ = max_offset;
    130     }
    131     if (GetFileSize(path_) < max_offset)
    132       SetFileSize(path_, max_offset);
    133     return consumed;
    134   }
    135 
    136   int64 Append(int64 amount) {
    137     dirty_ = true;
    138     append_mode_write_amount_ += amount;
    139     SetFileSize(path_, GetFileSize(path_) + amount);
    140     return amount;
    141   }
    142 
    143   void ReportUsage() {
    144     handle_->UpdateMaxWrittenOffset(max_written_offset_);
    145     handle_->AddAppendModeWriteAmount(append_mode_write_amount_);
    146     max_written_offset_ = handle_->GetEstimatedFileSize();
    147     append_mode_write_amount_ = 0;
    148     dirty_ = false;
    149   }
    150 
    151   void ClearWithoutUsageReport() {
    152     handle_.reset();
    153   }
    154 
    155  private:
    156   scoped_ptr<OpenFileHandle> handle_;
    157   base::FilePath path_;
    158   int64 max_written_offset_;
    159   int64 append_mode_write_amount_;
    160   bool dirty_;
    161 };
    162 
    163 void ExpectSuccess(bool* done, base::File::Error error) {
    164   EXPECT_FALSE(*done);
    165   *done = true;
    166   EXPECT_EQ(base::File::FILE_OK, error);
    167 }
    168 
    169 void RefreshReservation(QuotaReservation* reservation, int64 size) {
    170   DCHECK(reservation);
    171 
    172   bool done = false;
    173   reservation->RefreshReservation(size, base::Bind(&ExpectSuccess, &done));
    174   base::RunLoop().RunUntilIdle();
    175   EXPECT_TRUE(done);
    176 }
    177 
    178 }  // namespace
    179 
    180 class QuotaReservationManagerTest : public testing::Test {
    181  public:
    182   QuotaReservationManagerTest() {}
    183   virtual ~QuotaReservationManagerTest() {}
    184 
    185   virtual void SetUp() OVERRIDE {
    186     ASSERT_TRUE(work_dir_.CreateUniqueTempDir());
    187     file_path_ = work_dir_.path().Append(FILE_PATH_LITERAL("hoge"));
    188     SetFileSize(file_path_, kInitialFileSize);
    189 
    190     scoped_ptr<QuotaReservationManager::QuotaBackend> backend(new FakeBackend);
    191     reservation_manager_.reset(new QuotaReservationManager(backend.Pass()));
    192   }
    193 
    194   virtual void TearDown() OVERRIDE {
    195     reservation_manager_.reset();
    196   }
    197 
    198   FakeBackend* fake_backend() {
    199     return static_cast<FakeBackend*>(reservation_manager_->backend_.get());
    200   }
    201 
    202   QuotaReservationManager* reservation_manager() {
    203     return reservation_manager_.get();
    204   }
    205 
    206   const base::FilePath& file_path() const {
    207     return file_path_;
    208   }
    209 
    210  private:
    211   base::MessageLoop message_loop_;
    212   base::ScopedTempDir work_dir_;
    213   base::FilePath file_path_;
    214   scoped_ptr<QuotaReservationManager> reservation_manager_;
    215 
    216   DISALLOW_COPY_AND_ASSIGN(QuotaReservationManagerTest);
    217 };
    218 
    219 TEST_F(QuotaReservationManagerTest, BasicTest) {
    220   scoped_refptr<QuotaReservation> reservation =
    221       reservation_manager()->CreateReservation(GURL(kOrigin), kType);
    222 
    223   {
    224     RefreshReservation(reservation.get(), 10 + 20 + 3);
    225     int64 cached_reserved_quota = reservation->remaining_quota();
    226     FakeWriter writer(reservation->GetOpenFileHandle(file_path()));
    227 
    228     cached_reserved_quota -= writer.Write(kInitialFileSize + 10);
    229     EXPECT_LE(0, cached_reserved_quota);
    230     cached_reserved_quota -= writer.Append(20);
    231     EXPECT_LE(0, cached_reserved_quota);
    232 
    233     writer.ReportUsage();
    234   }
    235 
    236   EXPECT_EQ(3, reservation->remaining_quota());
    237   EXPECT_EQ(kInitialFileSize + 10 + 20, GetFileSize(file_path()));
    238   EXPECT_EQ(kInitialFileSize + 10 + 20, fake_backend()->on_disk_usage());
    239   EXPECT_EQ(kInitialFileSize + 10 + 20 + 3, fake_backend()->on_memory_usage());
    240 
    241   {
    242     RefreshReservation(reservation.get(), 5);
    243     FakeWriter writer(reservation->GetOpenFileHandle(file_path()));
    244 
    245     EXPECT_EQ(0, writer.Truncate(3));
    246 
    247     writer.ReportUsage();
    248   }
    249 
    250   EXPECT_EQ(5, reservation->remaining_quota());
    251   EXPECT_EQ(3, GetFileSize(file_path()));
    252   EXPECT_EQ(3, fake_backend()->on_disk_usage());
    253   EXPECT_EQ(3 + 5, fake_backend()->on_memory_usage());
    254 
    255   reservation = NULL;
    256 
    257   EXPECT_EQ(3, fake_backend()->on_memory_usage());
    258 }
    259 
    260 TEST_F(QuotaReservationManagerTest, MultipleWriter) {
    261   scoped_refptr<QuotaReservation> reservation =
    262       reservation_manager()->CreateReservation(GURL(kOrigin), kType);
    263 
    264   {
    265     RefreshReservation(reservation.get(), 10 + 20 + 30 + 40 + 5);
    266     int64 cached_reserved_quota = reservation->remaining_quota();
    267     FakeWriter writer1(reservation->GetOpenFileHandle(file_path()));
    268     FakeWriter writer2(reservation->GetOpenFileHandle(file_path()));
    269     FakeWriter writer3(reservation->GetOpenFileHandle(file_path()));
    270 
    271     cached_reserved_quota -= writer1.Write(kInitialFileSize + 10);
    272     EXPECT_LE(0, cached_reserved_quota);
    273     cached_reserved_quota -= writer2.Write(kInitialFileSize + 20);
    274     cached_reserved_quota -= writer3.Append(30);
    275     EXPECT_LE(0, cached_reserved_quota);
    276     cached_reserved_quota -= writer3.Append(40);
    277     EXPECT_LE(0, cached_reserved_quota);
    278 
    279     writer1.ReportUsage();
    280     writer2.ReportUsage();
    281     writer3.ReportUsage();
    282   }
    283 
    284   EXPECT_EQ(kInitialFileSize + 20 + 30 + 40, GetFileSize(file_path()));
    285   EXPECT_EQ(kInitialFileSize + 10 + 20 + 30 + 40 + 5,
    286             fake_backend()->on_memory_usage());
    287   EXPECT_EQ(kInitialFileSize + 20 + 30 + 40, fake_backend()->on_disk_usage());
    288 
    289   reservation = NULL;
    290 
    291   EXPECT_EQ(kInitialFileSize + 20 + 30 + 40, fake_backend()->on_disk_usage());
    292 }
    293 
    294 TEST_F(QuotaReservationManagerTest, MultipleClient) {
    295   scoped_refptr<QuotaReservation> reservation1 =
    296       reservation_manager()->CreateReservation(GURL(kOrigin), kType);
    297   RefreshReservation(reservation1.get(), 10);
    298   int64 cached_reserved_quota1 = reservation1->remaining_quota();
    299 
    300   scoped_refptr<QuotaReservation> reservation2 =
    301       reservation_manager()->CreateReservation(GURL(kOrigin), kType);
    302   RefreshReservation(reservation2.get(), 20);
    303   int64 cached_reserved_quota2 = reservation2->remaining_quota();
    304 
    305   scoped_ptr<FakeWriter> writer1(
    306       new FakeWriter(reservation1->GetOpenFileHandle(file_path())));
    307 
    308   scoped_ptr<FakeWriter> writer2(
    309       new FakeWriter(reservation2->GetOpenFileHandle(file_path())));
    310 
    311   cached_reserved_quota1 -= writer1->Write(kInitialFileSize + 10);
    312   EXPECT_LE(0, cached_reserved_quota1);
    313 
    314   cached_reserved_quota2 -= writer2->Append(20);
    315   EXPECT_LE(0, cached_reserved_quota2);
    316 
    317   writer1->ReportUsage();
    318   RefreshReservation(reservation1.get(), 2);
    319   cached_reserved_quota1 = reservation1->remaining_quota();
    320 
    321   writer2->ReportUsage();
    322   RefreshReservation(reservation2.get(), 3);
    323   cached_reserved_quota2 = reservation2->remaining_quota();
    324 
    325   writer1.reset();
    326   writer2.reset();
    327 
    328   EXPECT_EQ(kInitialFileSize + 10 + 20, GetFileSize(file_path()));
    329   EXPECT_EQ(kInitialFileSize + 10 + 20 + 2 + 3,
    330             fake_backend()->on_memory_usage());
    331   EXPECT_EQ(kInitialFileSize + 10 + 20, fake_backend()->on_disk_usage());
    332 
    333   reservation1 = NULL;
    334   EXPECT_EQ(kInitialFileSize + 10 + 20 + 3, fake_backend()->on_memory_usage());
    335 
    336   reservation2 = NULL;
    337   EXPECT_EQ(kInitialFileSize + 10 + 20, fake_backend()->on_memory_usage());
    338 }
    339 
    340 TEST_F(QuotaReservationManagerTest, ClientCrash) {
    341   scoped_refptr<QuotaReservation> reservation1 =
    342       reservation_manager()->CreateReservation(GURL(kOrigin), kType);
    343   RefreshReservation(reservation1.get(), 15);
    344 
    345   scoped_refptr<QuotaReservation> reservation2 =
    346       reservation_manager()->CreateReservation(GURL(kOrigin), kType);
    347   RefreshReservation(reservation2.get(), 20);
    348 
    349   {
    350     FakeWriter writer(reservation1->GetOpenFileHandle(file_path()));
    351 
    352     writer.Write(kInitialFileSize + 10);
    353 
    354     reservation1->OnClientCrash();
    355     writer.ClearWithoutUsageReport();
    356   }
    357   reservation1 = NULL;
    358 
    359   EXPECT_EQ(kInitialFileSize + 10, GetFileSize(file_path()));
    360   EXPECT_EQ(kInitialFileSize + 15 + 20, fake_backend()->on_memory_usage());
    361   EXPECT_EQ(kInitialFileSize + 10, fake_backend()->on_disk_usage());
    362 
    363   reservation2 = NULL;
    364   EXPECT_EQ(kInitialFileSize + 10, fake_backend()->on_memory_usage());
    365 }
    366 
    367 }  // namespace content
    368