1 // Copyright (c) 2012 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 <string> 6 #include <vector> 7 8 #include "base/basictypes.h" 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/files/scoped_temp_dir.h" 12 #include "base/message_loop/message_loop.h" 13 #include "net/base/io_buffer.h" 14 #include "net/url_request/url_request.h" 15 #include "net/url_request/url_request_context.h" 16 #include "net/url_request/url_request_job.h" 17 #include "net/url_request/url_request_status.h" 18 #include "testing/platform_test.h" 19 #include "url/gurl.h" 20 #include "webkit/browser/fileapi/file_system_context.h" 21 #include "webkit/browser/fileapi/file_system_file_util.h" 22 #include "webkit/browser/fileapi/file_system_operation_context.h" 23 #include "webkit/browser/fileapi/file_system_quota_util.h" 24 #include "webkit/browser/fileapi/file_writer_delegate.h" 25 #include "webkit/browser/fileapi/mock_file_system_context.h" 26 #include "webkit/browser/fileapi/sandbox_file_stream_writer.h" 27 28 namespace fileapi { 29 30 namespace { 31 32 const GURL kOrigin("http://example.com"); 33 const FileSystemType kFileSystemType = kFileSystemTypeTest; 34 35 const char kData[] = "The quick brown fox jumps over the lazy dog.\n"; 36 const int kDataSize = ARRAYSIZE_UNSAFE(kData) - 1; 37 38 class Result { 39 public: 40 Result() 41 : status_(base::PLATFORM_FILE_OK), 42 bytes_written_(0), 43 write_status_(FileWriterDelegate::SUCCESS_IO_PENDING) {} 44 45 base::PlatformFileError status() const { return status_; } 46 int64 bytes_written() const { return bytes_written_; } 47 FileWriterDelegate::WriteProgressStatus write_status() const { 48 return write_status_; 49 } 50 51 void DidWrite(base::PlatformFileError status, int64 bytes, 52 FileWriterDelegate::WriteProgressStatus write_status) { 53 write_status_ = write_status; 54 if (status == base::PLATFORM_FILE_OK) { 55 bytes_written_ += bytes; 56 if (write_status_ != FileWriterDelegate::SUCCESS_IO_PENDING) 57 base::MessageLoop::current()->Quit(); 58 } else { 59 EXPECT_EQ(base::PLATFORM_FILE_OK, status_); 60 status_ = status; 61 base::MessageLoop::current()->Quit(); 62 } 63 } 64 65 private: 66 // For post-operation status. 67 base::PlatformFileError status_; 68 int64 bytes_written_; 69 FileWriterDelegate::WriteProgressStatus write_status_; 70 }; 71 72 } // namespace (anonymous) 73 74 class FileWriterDelegateTest : public PlatformTest { 75 public: 76 FileWriterDelegateTest() 77 : loop_(base::MessageLoop::TYPE_IO) {} 78 79 protected: 80 virtual void SetUp() OVERRIDE; 81 virtual void TearDown() OVERRIDE; 82 83 FileSystemFileUtil* file_util() { 84 return file_system_context_->GetFileUtil(kFileSystemType); 85 } 86 87 int64 usage() { 88 return file_system_context_->GetQuotaUtil(kFileSystemType) 89 ->GetOriginUsageOnFileThread( 90 file_system_context_.get(), kOrigin, kFileSystemType); 91 } 92 93 int64 GetFileSizeOnDisk(const char* test_file_path) { 94 // There might be in-flight flush/write. 95 base::MessageLoop::current()->PostTask( 96 FROM_HERE, base::Bind(&base::DoNothing)); 97 base::MessageLoop::current()->RunUntilIdle(); 98 99 FileSystemURL url = GetFileSystemURL(test_file_path); 100 base::PlatformFileInfo file_info; 101 base::FilePath platform_path; 102 EXPECT_EQ(base::PLATFORM_FILE_OK, 103 file_util()->GetFileInfo(NewOperationContext().get(), url, 104 &file_info, &platform_path)); 105 return file_info.size; 106 } 107 108 FileSystemURL GetFileSystemURL(const char* file_name) const { 109 return file_system_context_->CreateCrackedFileSystemURL( 110 kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name)); 111 } 112 113 scoped_ptr<FileSystemOperationContext> NewOperationContext() { 114 FileSystemOperationContext* context = 115 new FileSystemOperationContext(file_system_context_.get()); 116 context->set_update_observers( 117 *file_system_context_->GetUpdateObservers(kFileSystemType)); 118 context->set_root_path(dir_.path()); 119 return make_scoped_ptr(context); 120 } 121 122 FileWriterDelegate* CreateWriterDelegate( 123 const char* test_file_path, 124 int64 offset, 125 int64 allowed_growth) { 126 SandboxFileStreamWriter* writer = new SandboxFileStreamWriter( 127 file_system_context_.get(), 128 GetFileSystemURL(test_file_path), 129 offset, 130 *file_system_context_->GetUpdateObservers(kFileSystemType)); 131 writer->set_default_quota(allowed_growth); 132 return new FileWriterDelegate(scoped_ptr<FileStreamWriter>(writer)); 133 } 134 135 FileWriterDelegate::DelegateWriteCallback GetWriteCallback(Result* result) { 136 return base::Bind(&Result::DidWrite, base::Unretained(result)); 137 } 138 139 // Creates and sets up a FileWriterDelegate for writing the given |blob_url|, 140 // and creates a new FileWriterDelegate for the file. 141 void PrepareForWrite(const char* test_file_path, 142 const GURL& blob_url, 143 int64 offset, 144 int64 allowed_growth) { 145 file_writer_delegate_.reset( 146 CreateWriterDelegate(test_file_path, offset, allowed_growth)); 147 request_.reset(empty_context_.CreateRequest( 148 blob_url, file_writer_delegate_.get())); 149 } 150 151 static net::URLRequest::ProtocolFactory Factory; 152 153 // This should be alive until the very end of this instance. 154 base::MessageLoop loop_; 155 156 scoped_refptr<FileSystemContext> file_system_context_; 157 158 net::URLRequestContext empty_context_; 159 scoped_ptr<FileWriterDelegate> file_writer_delegate_; 160 scoped_ptr<net::URLRequest> request_; 161 162 base::ScopedTempDir dir_; 163 164 static const char* content_; 165 }; 166 167 const char* FileWriterDelegateTest::content_ = NULL; 168 169 namespace { 170 171 static std::string g_content; 172 173 class FileWriterDelegateTestJob : public net::URLRequestJob { 174 public: 175 FileWriterDelegateTestJob(net::URLRequest* request, 176 net::NetworkDelegate* network_delegate, 177 const std::string& content) 178 : net::URLRequestJob(request, network_delegate), 179 content_(content), 180 remaining_bytes_(content.length()), 181 cursor_(0) { 182 } 183 184 virtual void Start() OVERRIDE { 185 base::MessageLoop::current()->PostTask( 186 FROM_HERE, 187 base::Bind(&FileWriterDelegateTestJob::NotifyHeadersComplete, this)); 188 } 189 190 virtual bool ReadRawData(net::IOBuffer* buf, 191 int buf_size, 192 int *bytes_read) OVERRIDE { 193 if (remaining_bytes_ < buf_size) 194 buf_size = static_cast<int>(remaining_bytes_); 195 196 for (int i = 0; i < buf_size; ++i) 197 buf->data()[i] = content_[cursor_++]; 198 remaining_bytes_ -= buf_size; 199 200 SetStatus(net::URLRequestStatus()); 201 *bytes_read = buf_size; 202 return true; 203 } 204 205 virtual int GetResponseCode() const OVERRIDE { 206 return 200; 207 } 208 209 protected: 210 virtual ~FileWriterDelegateTestJob() {} 211 212 private: 213 std::string content_; 214 int remaining_bytes_; 215 int cursor_; 216 }; 217 218 } // namespace (anonymous) 219 220 // static 221 net::URLRequestJob* FileWriterDelegateTest::Factory( 222 net::URLRequest* request, 223 net::NetworkDelegate* network_delegate, 224 const std::string& scheme) { 225 return new FileWriterDelegateTestJob( 226 request, network_delegate, FileWriterDelegateTest::content_); 227 } 228 229 void FileWriterDelegateTest::SetUp() { 230 ASSERT_TRUE(dir_.CreateUniqueTempDir()); 231 232 file_system_context_ = CreateFileSystemContextForTesting( 233 NULL, dir_.path()); 234 235 bool created = false; 236 scoped_ptr<FileSystemOperationContext> context = NewOperationContext(); 237 context->set_allowed_bytes_growth(kint64max); 238 base::PlatformFileError error = file_util()->EnsureFileExists( 239 context.get(), 240 GetFileSystemURL("test"), 241 &created); 242 ASSERT_EQ(base::PLATFORM_FILE_OK, error); 243 ASSERT_TRUE(created); 244 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory); 245 } 246 247 void FileWriterDelegateTest::TearDown() { 248 net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL); 249 file_system_context_ = NULL; 250 base::MessageLoop::current()->RunUntilIdle(); 251 } 252 253 TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) { 254 const GURL kBlobURL("blob:nolimit"); 255 content_ = kData; 256 257 PrepareForWrite("test", kBlobURL, 0, kint64max); 258 259 Result result; 260 ASSERT_EQ(0, usage()); 261 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 262 base::MessageLoop::current()->Run(); 263 264 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 265 file_writer_delegate_.reset(); 266 267 ASSERT_EQ(kDataSize, usage()); 268 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 269 EXPECT_EQ(kDataSize, result.bytes_written()); 270 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 271 } 272 273 TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) { 274 const GURL kBlobURL("blob:just"); 275 content_ = kData; 276 const int64 kAllowedGrowth = kDataSize; 277 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth); 278 279 Result result; 280 ASSERT_EQ(0, usage()); 281 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 282 base::MessageLoop::current()->Run(); 283 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 284 file_writer_delegate_.reset(); 285 286 ASSERT_EQ(kAllowedGrowth, usage()); 287 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 288 289 EXPECT_EQ(kAllowedGrowth, result.bytes_written()); 290 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 291 } 292 293 TEST_F(FileWriterDelegateTest, DISABLED_WriteFailureByQuota) { 294 const GURL kBlobURL("blob:failure"); 295 content_ = kData; 296 const int64 kAllowedGrowth = kDataSize - 1; 297 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth); 298 299 Result result; 300 ASSERT_EQ(0, usage()); 301 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 302 base::MessageLoop::current()->Run(); 303 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status()); 304 file_writer_delegate_.reset(); 305 306 ASSERT_EQ(kAllowedGrowth, usage()); 307 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 308 309 EXPECT_EQ(kAllowedGrowth, result.bytes_written()); 310 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result.status()); 311 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status()); 312 } 313 314 TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) { 315 const GURL kBlobURL("blob:zero"); 316 content_ = ""; 317 int64 kAllowedGrowth = 0; 318 PrepareForWrite("test", kBlobURL, 0, kAllowedGrowth); 319 320 Result result; 321 ASSERT_EQ(0, usage()); 322 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 323 base::MessageLoop::current()->Run(); 324 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 325 file_writer_delegate_.reset(); 326 327 ASSERT_EQ(kAllowedGrowth, usage()); 328 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 329 330 EXPECT_EQ(kAllowedGrowth, result.bytes_written()); 331 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 332 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 333 } 334 335 TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) { 336 scoped_ptr<FileWriterDelegate> file_writer_delegate2; 337 scoped_ptr<net::URLRequest> request2; 338 339 bool created = false; 340 file_util()->EnsureFileExists(NewOperationContext().get(), 341 GetFileSystemURL("test2"), 342 &created); 343 ASSERT_TRUE(created); 344 345 const GURL kBlobURL("blob:nolimitconcurrent"); 346 const GURL kBlobURL2("blob:nolimitconcurrent2"); 347 content_ = kData; 348 349 PrepareForWrite("test", kBlobURL, 0, kint64max); 350 351 // Credate another FileWriterDelegate for concurrent write. 352 file_writer_delegate2.reset(CreateWriterDelegate("test2", 0, kint64max)); 353 request2.reset(empty_context_.CreateRequest( 354 kBlobURL2, file_writer_delegate2.get())); 355 356 Result result, result2; 357 ASSERT_EQ(0, usage()); 358 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 359 file_writer_delegate2->Start(request2.Pass(), GetWriteCallback(&result2)); 360 base::MessageLoop::current()->Run(); 361 if (result.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING || 362 result2.write_status() == FileWriterDelegate::SUCCESS_IO_PENDING) 363 base::MessageLoop::current()->Run(); 364 365 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 366 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result2.write_status()); 367 file_writer_delegate_.reset(); 368 file_writer_delegate2.reset(); 369 370 ASSERT_EQ(kDataSize * 2, usage()); 371 EXPECT_EQ(GetFileSizeOnDisk("test") + GetFileSizeOnDisk("test2"), usage()); 372 373 EXPECT_EQ(kDataSize, result.bytes_written()); 374 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 375 EXPECT_EQ(kDataSize, result2.bytes_written()); 376 EXPECT_EQ(base::PLATFORM_FILE_OK, result2.status()); 377 } 378 379 TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { 380 const GURL kBlobURL("blob:failure-with-updated-quota"); 381 content_ = kData; 382 383 // Writing kDataSize (=45) bytes data while allowed_growth is 100. 384 int64 offset = 0; 385 int64 allowed_growth = 100; 386 ASSERT_LT(kDataSize, allowed_growth); 387 PrepareForWrite("test", kBlobURL, offset, allowed_growth); 388 389 { 390 Result result; 391 ASSERT_EQ(0, usage()); 392 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 393 base::MessageLoop::current()->Run(); 394 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 395 file_writer_delegate_.reset(); 396 397 ASSERT_EQ(kDataSize, usage()); 398 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 399 EXPECT_EQ(kDataSize, result.bytes_written()); 400 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 401 } 402 403 // Trying to overwrite kDataSize bytes data while allowed_growth is 20. 404 offset = 0; 405 allowed_growth = 20; 406 PrepareForWrite("test", kBlobURL, offset, allowed_growth); 407 408 { 409 Result result; 410 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 411 base::MessageLoop::current()->Run(); 412 EXPECT_EQ(kDataSize, usage()); 413 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 414 EXPECT_EQ(kDataSize, result.bytes_written()); 415 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 416 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 417 } 418 419 // Trying to write kDataSize bytes data from offset 25 while 420 // allowed_growth is 55. 421 offset = 25; 422 allowed_growth = 55; 423 PrepareForWrite("test", kBlobURL, offset, allowed_growth); 424 425 { 426 Result result; 427 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 428 base::MessageLoop::current()->Run(); 429 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 430 file_writer_delegate_.reset(); 431 432 EXPECT_EQ(offset + kDataSize, usage()); 433 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 434 EXPECT_EQ(kDataSize, result.bytes_written()); 435 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 436 } 437 438 // Trying to overwrite 45 bytes data while allowed_growth is -20. 439 offset = 0; 440 allowed_growth = -20; 441 PrepareForWrite("test", kBlobURL, offset, allowed_growth); 442 int64 pre_write_usage = GetFileSizeOnDisk("test"); 443 444 { 445 Result result; 446 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 447 base::MessageLoop::current()->Run(); 448 ASSERT_EQ(FileWriterDelegate::SUCCESS_COMPLETED, result.write_status()); 449 file_writer_delegate_.reset(); 450 451 EXPECT_EQ(pre_write_usage, usage()); 452 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 453 EXPECT_EQ(kDataSize, result.bytes_written()); 454 EXPECT_EQ(base::PLATFORM_FILE_OK, result.status()); 455 } 456 457 // Trying to overwrite 45 bytes data with offset pre_write_usage - 20, 458 // while allowed_growth is 10. 459 const int kOverlap = 20; 460 offset = pre_write_usage - kOverlap; 461 allowed_growth = 10; 462 PrepareForWrite("test", kBlobURL, offset, allowed_growth); 463 464 { 465 Result result; 466 file_writer_delegate_->Start(request_.Pass(), GetWriteCallback(&result)); 467 base::MessageLoop::current()->Run(); 468 ASSERT_EQ(FileWriterDelegate::ERROR_WRITE_STARTED, result.write_status()); 469 file_writer_delegate_.reset(); 470 471 EXPECT_EQ(pre_write_usage + allowed_growth, usage()); 472 EXPECT_EQ(GetFileSizeOnDisk("test"), usage()); 473 EXPECT_EQ(kOverlap + allowed_growth, result.bytes_written()); 474 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result.status()); 475 } 476 } 477 478 } // namespace fileapi 479