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