Home | History | Annotate | Download | only in fileapi
      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