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