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