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 "content/browser/renderer_host/pepper/quota_reservation.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/quota_reservation.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17using storage::QuotaReservationManager;
18
19namespace content {
20
21namespace {
22
23const char kOrigin[] = "http://example.com";
24const storage::FileSystemType kType = storage::kFileSystemTypeTemporary;
25
26const base::FilePath::StringType file1_name = FILE_PATH_LITERAL("file1");
27const base::FilePath::StringType file2_name = FILE_PATH_LITERAL("file2");
28const base::FilePath::StringType file3_name = FILE_PATH_LITERAL("file3");
29const int kFile1ID = 1;
30const int kFile2ID = 2;
31const int kFile3ID = 3;
32
33class FakeBackend : public QuotaReservationManager::QuotaBackend {
34 public:
35  FakeBackend() {}
36  virtual ~FakeBackend() {}
37
38  virtual void ReserveQuota(
39      const GURL& origin,
40      storage::FileSystemType type,
41      int64 delta,
42      const QuotaReservationManager::ReserveQuotaCallback& callback) OVERRIDE {
43    base::MessageLoopProxy::current()->PostTask(
44        FROM_HERE,
45        base::Bind(base::IgnoreResult(callback), base::File::FILE_OK, delta));
46  }
47
48  virtual void ReleaseReservedQuota(const GURL& origin,
49                                    storage::FileSystemType type,
50                                    int64 size) OVERRIDE {}
51
52  virtual void CommitQuotaUsage(const GURL& origin,
53                                storage::FileSystemType type,
54                                int64 delta) OVERRIDE {}
55
56  virtual void IncrementDirtyCount(const GURL& origin,
57                                   storage::FileSystemType type) OVERRIDE {}
58  virtual void DecrementDirtyCount(const GURL& origin,
59                                   storage::FileSystemType type) OVERRIDE {}
60
61 private:
62  DISALLOW_COPY_AND_ASSIGN(FakeBackend);
63};
64
65}  // namespace
66
67class QuotaReservationTest : public testing::Test {
68 public:
69  QuotaReservationTest() {}
70  virtual ~QuotaReservationTest() {}
71
72  virtual void SetUp() OVERRIDE {
73    ASSERT_TRUE(work_dir_.CreateUniqueTempDir());
74
75    reservation_manager_.reset(new QuotaReservationManager(
76        scoped_ptr<QuotaReservationManager::QuotaBackend>(new FakeBackend)));
77  }
78
79  virtual void TearDown() OVERRIDE {
80    reservation_manager_.reset();
81    base::RunLoop().RunUntilIdle();
82  }
83
84  base::FilePath MakeFilePath(const base::FilePath::StringType& file_name) {
85    return work_dir_.path().Append(file_name);
86  }
87
88  storage::FileSystemURL MakeFileSystemURL(
89      const base::FilePath::StringType& file_name) {
90    return storage::FileSystemURL::CreateForTest(
91        GURL(kOrigin), kType, MakeFilePath(file_name));
92  }
93
94  scoped_refptr<QuotaReservation> CreateQuotaReservation(
95      scoped_refptr<storage::QuotaReservation> reservation,
96      const GURL& origin,
97      storage::FileSystemType type) {
98    // Sets reservation_ as a side effect.
99    return scoped_refptr<QuotaReservation>(
100        new QuotaReservation(reservation, origin, type));
101  }
102
103  void SetFileSize(const base::FilePath::StringType& file_name, int64 size) {
104    base::File file(MakeFilePath(file_name),
105                    base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
106    ASSERT_TRUE(file.IsValid());
107    ASSERT_TRUE(file.SetLength(size));
108  }
109
110  QuotaReservationManager* reservation_manager() {
111    return reservation_manager_.get();
112  }
113
114 private:
115  base::MessageLoop message_loop_;
116  base::ScopedTempDir work_dir_;
117  scoped_ptr<storage::QuotaReservationManager> reservation_manager_;
118
119  DISALLOW_COPY_AND_ASSIGN(QuotaReservationTest);
120};
121
122void GotReservedQuota(int64* reserved_quota_ptr,
123                      ppapi::FileGrowthMap* file_growths_ptr,
124                      int64 reserved_quota,
125                      const ppapi::FileSizeMap& maximum_written_offsets) {
126  *reserved_quota_ptr = reserved_quota;
127
128  file_growths_ptr->clear();
129  for (ppapi::FileSizeMap::const_iterator it = maximum_written_offsets.begin();
130       it != maximum_written_offsets.end();
131       ++it)
132    (*file_growths_ptr)[it->first] = ppapi::FileGrowth(it->second, 0);
133}
134
135void ReserveQuota(scoped_refptr<QuotaReservation> quota_reservation,
136                  int64 amount,
137                  int64* reserved_quota,
138                  ppapi::FileGrowthMap* file_growths) {
139  quota_reservation->ReserveQuota(
140      amount,
141      *file_growths,
142      base::Bind(&GotReservedQuota, reserved_quota, file_growths));
143  base::RunLoop().RunUntilIdle();
144}
145
146// Tests that:
147// 1) We can reserve quota with no files open.
148// 2) Open a file, grow it, close it, and reserve quota with correct sizes.
149TEST_F(QuotaReservationTest, ReserveQuota) {
150  GURL origin(kOrigin);
151  storage::FileSystemType type = kType;
152
153  scoped_refptr<storage::QuotaReservation> reservation(
154      reservation_manager()->CreateReservation(origin, type));
155  scoped_refptr<QuotaReservation> test =
156      CreateQuotaReservation(reservation, origin, type);
157
158  // Reserve quota with no files open.
159  int64 amount = 100;
160  int64 reserved_quota;
161  ppapi::FileGrowthMap file_growths;
162  ReserveQuota(test, amount, &reserved_quota, &file_growths);
163  EXPECT_EQ(amount, reserved_quota);
164  EXPECT_EQ(0U, file_growths.size());
165
166  // Open a file, refresh the reservation, extend the file, and close it.
167  int64 file_size = 10;
168  SetFileSize(file1_name, file_size);
169  int64 open_file_size =
170      test->OpenFile(kFile1ID, MakeFileSystemURL(file1_name));
171  EXPECT_EQ(file_size, open_file_size);
172
173  file_growths[kFile1ID] = ppapi::FileGrowth(file_size, 0);  // 1 file open.
174  ReserveQuota(test, amount, &reserved_quota, &file_growths);
175  EXPECT_EQ(amount, reserved_quota);
176  EXPECT_EQ(1U, file_growths.size());
177  EXPECT_EQ(file_size, file_growths[kFile1ID].max_written_offset);
178
179  int64 new_file_size = 30;
180  SetFileSize(file1_name, new_file_size);
181
182  EXPECT_EQ(amount, reservation->remaining_quota());
183  test->CloseFile(kFile1ID, ppapi::FileGrowth(new_file_size, 0));
184  EXPECT_EQ(amount - (new_file_size - file_size),
185            reservation->remaining_quota());
186}
187
188// Tests that:
189// 1) We can open and close multiple files.
190TEST_F(QuotaReservationTest, MultipleFiles) {
191  GURL origin(kOrigin);
192  storage::FileSystemType type = kType;
193
194  scoped_refptr<storage::QuotaReservation> reservation(
195      reservation_manager()->CreateReservation(origin, type));
196  scoped_refptr<QuotaReservation> test =
197      CreateQuotaReservation(reservation, origin, type);
198
199  // Open some files of different sizes.
200  int64 file1_size = 10;
201  SetFileSize(file1_name, file1_size);
202  int64 open_file1_size =
203      test->OpenFile(kFile1ID, MakeFileSystemURL(file1_name));
204  EXPECT_EQ(file1_size, open_file1_size);
205  int64 file2_size = 20;
206  SetFileSize(file2_name, file2_size);
207  int64 open_file2_size =
208      test->OpenFile(kFile2ID, MakeFileSystemURL(file2_name));
209  EXPECT_EQ(file2_size, open_file2_size);
210  int64 file3_size = 30;
211  SetFileSize(file3_name, file3_size);
212  int64 open_file3_size =
213      test->OpenFile(kFile3ID, MakeFileSystemURL(file3_name));
214  EXPECT_EQ(file3_size, open_file3_size);
215
216  // Reserve quota.
217  int64 amount = 100;
218  int64 reserved_quota;
219  ppapi::FileGrowthMap file_growths;
220  file_growths[kFile1ID] = ppapi::FileGrowth(file1_size, 0);  // 3 files open.
221  file_growths[kFile2ID] = ppapi::FileGrowth(file2_size, 0);
222  file_growths[kFile3ID] = ppapi::FileGrowth(file3_size, 0);
223
224  ReserveQuota(test, amount, &reserved_quota, &file_growths);
225  EXPECT_EQ(amount, reserved_quota);
226  EXPECT_EQ(3U, file_growths.size());
227  EXPECT_EQ(file1_size, file_growths[kFile1ID].max_written_offset);
228  EXPECT_EQ(file2_size, file_growths[kFile2ID].max_written_offset);
229  EXPECT_EQ(file3_size, file_growths[kFile3ID].max_written_offset);
230
231  test->CloseFile(kFile2ID, ppapi::FileGrowth(file2_size, 0));
232
233  file_growths.erase(kFile2ID);
234  ReserveQuota(test, amount, &reserved_quota, &file_growths);
235  EXPECT_EQ(amount, reserved_quota);
236  EXPECT_EQ(2U, file_growths.size());
237  EXPECT_EQ(file1_size, file_growths[kFile1ID].max_written_offset);
238  EXPECT_EQ(file3_size, file_growths[kFile3ID].max_written_offset);
239
240  test->CloseFile(kFile1ID, ppapi::FileGrowth(file1_size, 0));
241  test->CloseFile(kFile3ID, ppapi::FileGrowth(file3_size, 0));
242}
243
244}  // namespace content
245