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 "webkit/browser/fileapi/quota/quota_reservation_manager.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/file_util.h" 10 #include "base/files/file.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 "testing/gtest/include/gtest/gtest.h" 15 #include "webkit/browser/fileapi/quota/open_file_handle.h" 16 #include "webkit/browser/fileapi/quota/quota_reservation.h" 17 18 using fileapi::kFileSystemTypeTemporary; 19 using fileapi::OpenFileHandle; 20 using fileapi::QuotaReservation; 21 using fileapi::QuotaReservationManager; 22 23 namespace content { 24 25 namespace { 26 27 const char kOrigin[] = "http://example.com"; 28 const fileapi::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 fileapi::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 fileapi::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 fileapi::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 fileapi::FileSystemType type) OVERRIDE {} 84 virtual void DecrementDirtyCount(const GURL& origin, 85 fileapi::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, 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, 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