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 <vector>
      6 
      7 #include "base/files/scoped_temp_dir.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/memory/weak_ptr.h"
     10 #include "base/message_loop/message_loop_proxy.h"
     11 #include "base/run_loop.h"
     12 #include "content/browser/fileapi/mock_file_change_observer.h"
     13 #include "content/browser/quota/mock_quota_manager.h"
     14 #include "content/public/test/mock_blob_url_request_context.h"
     15 #include "content/public/test/test_file_system_backend.h"
     16 #include "content/public/test/test_file_system_context.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_job_factory_impl.h"
     21 #include "storage/browser/blob/blob_storage_context.h"
     22 #include "storage/browser/blob/blob_url_request_job.h"
     23 #include "storage/browser/fileapi/file_system_context.h"
     24 #include "storage/browser/fileapi/file_system_file_util.h"
     25 #include "storage/browser/fileapi/file_system_operation_context.h"
     26 #include "storage/browser/fileapi/file_system_operation_runner.h"
     27 #include "storage/browser/fileapi/local_file_util.h"
     28 #include "storage/common/blob/blob_data.h"
     29 #include "storage/common/fileapi/file_system_util.h"
     30 #include "testing/gtest/include/gtest/gtest.h"
     31 #include "url/gurl.h"
     32 
     33 using storage::FileSystemOperation;
     34 using storage::FileSystemOperationRunner;
     35 using storage::FileSystemURL;
     36 using content::MockBlobURLRequestContext;
     37 using content::ScopedTextBlob;
     38 
     39 namespace content {
     40 
     41 namespace {
     42 
     43 const GURL kOrigin("http://example.com");
     44 const storage::FileSystemType kFileSystemType = storage::kFileSystemTypeTest;
     45 
     46 void AssertStatusEq(base::File::Error expected,
     47                     base::File::Error actual) {
     48   ASSERT_EQ(expected, actual);
     49 }
     50 
     51 }  // namespace
     52 
     53 class FileSystemOperationImplWriteTest
     54     : public testing::Test {
     55  public:
     56   FileSystemOperationImplWriteTest()
     57       : status_(base::File::FILE_OK),
     58         cancel_status_(base::File::FILE_ERROR_FAILED),
     59         bytes_written_(0),
     60         complete_(false),
     61         weak_factory_(this) {
     62     change_observers_ =
     63         storage::MockFileChangeObserver::CreateList(&change_observer_);
     64   }
     65 
     66   virtual void SetUp() {
     67     ASSERT_TRUE(dir_.CreateUniqueTempDir());
     68 
     69     quota_manager_ =
     70         new MockQuotaManager(false /* is_incognito */,
     71                                     dir_.path(),
     72                                     base::MessageLoopProxy::current().get(),
     73                                     base::MessageLoopProxy::current().get(),
     74                                     NULL /* special storage policy */);
     75     virtual_path_ = base::FilePath(FILE_PATH_LITERAL("temporary file"));
     76 
     77     file_system_context_ = CreateFileSystemContextForTesting(
     78         quota_manager_->proxy(), dir_.path());
     79     url_request_context_.reset(
     80         new MockBlobURLRequestContext(file_system_context_.get()));
     81 
     82     file_system_context_->operation_runner()->CreateFile(
     83         URLForPath(virtual_path_), true /* exclusive */,
     84         base::Bind(&AssertStatusEq, base::File::FILE_OK));
     85 
     86     static_cast<TestFileSystemBackend*>(
     87         file_system_context_->GetFileSystemBackend(kFileSystemType))
     88         ->AddFileChangeObserver(change_observer());
     89   }
     90 
     91   virtual void TearDown() {
     92     quota_manager_ = NULL;
     93     file_system_context_ = NULL;
     94     base::RunLoop().RunUntilIdle();
     95   }
     96 
     97   base::File::Error status() const { return status_; }
     98   base::File::Error cancel_status() const { return cancel_status_; }
     99   void add_bytes_written(int64 bytes, bool complete) {
    100     bytes_written_ += bytes;
    101     EXPECT_FALSE(complete_);
    102     complete_ = complete;
    103   }
    104   int64 bytes_written() const { return bytes_written_; }
    105   bool complete() const { return complete_; }
    106 
    107  protected:
    108   const storage::ChangeObserverList& change_observers() const {
    109     return change_observers_;
    110   }
    111 
    112   storage::MockFileChangeObserver* change_observer() {
    113     return &change_observer_;
    114   }
    115 
    116   FileSystemURL URLForPath(const base::FilePath& path) const {
    117     return file_system_context_->CreateCrackedFileSystemURL(
    118         kOrigin, kFileSystemType, path);
    119   }
    120 
    121   // Callback function for recording test results.
    122   FileSystemOperation::WriteCallback RecordWriteCallback() {
    123     return base::Bind(&FileSystemOperationImplWriteTest::DidWrite,
    124                       weak_factory_.GetWeakPtr());
    125   }
    126 
    127   FileSystemOperation::StatusCallback RecordCancelCallback() {
    128     return base::Bind(&FileSystemOperationImplWriteTest::DidCancel,
    129                       weak_factory_.GetWeakPtr());
    130   }
    131 
    132   void DidWrite(base::File::Error status, int64 bytes, bool complete) {
    133     if (status == base::File::FILE_OK) {
    134       add_bytes_written(bytes, complete);
    135       if (complete)
    136         base::MessageLoop::current()->Quit();
    137     } else {
    138       EXPECT_FALSE(complete_);
    139       EXPECT_EQ(status_, base::File::FILE_OK);
    140       complete_ = true;
    141       status_ = status;
    142       if (base::MessageLoop::current()->is_running())
    143         base::MessageLoop::current()->Quit();
    144     }
    145   }
    146 
    147   void DidCancel(base::File::Error status) {
    148     cancel_status_ = status;
    149   }
    150 
    151   const MockBlobURLRequestContext& url_request_context() const {
    152     return *url_request_context_;
    153   }
    154 
    155   scoped_refptr<storage::FileSystemContext> file_system_context_;
    156   scoped_refptr<MockQuotaManager> quota_manager_;
    157 
    158   base::MessageLoopForIO loop_;
    159 
    160   base::ScopedTempDir dir_;
    161   base::FilePath virtual_path_;
    162 
    163   // For post-operation status.
    164   base::File::Error status_;
    165   base::File::Error cancel_status_;
    166   int64 bytes_written_;
    167   bool complete_;
    168 
    169   scoped_ptr<MockBlobURLRequestContext> url_request_context_;
    170 
    171   storage::MockFileChangeObserver change_observer_;
    172   storage::ChangeObserverList change_observers_;
    173 
    174   base::WeakPtrFactory<FileSystemOperationImplWriteTest> weak_factory_;
    175 
    176   DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImplWriteTest);
    177 };
    178 
    179 TEST_F(FileSystemOperationImplWriteTest, TestWriteSuccess) {
    180   ScopedTextBlob blob(url_request_context(),
    181                       "blob-id:success",
    182                       "Hello, world!\n");
    183   file_system_context_->operation_runner()->Write(
    184       &url_request_context(), URLForPath(virtual_path_),
    185       blob.GetBlobDataHandle(),
    186       0, RecordWriteCallback());
    187   base::MessageLoop::current()->Run();
    188 
    189   EXPECT_EQ(14, bytes_written());
    190   EXPECT_EQ(base::File::FILE_OK, status());
    191   EXPECT_TRUE(complete());
    192 
    193   EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
    194 }
    195 
    196 TEST_F(FileSystemOperationImplWriteTest, TestWriteZero) {
    197   ScopedTextBlob blob(url_request_context(), "blob_id:zero", "");
    198   file_system_context_->operation_runner()->Write(
    199       &url_request_context(), URLForPath(virtual_path_),
    200       blob.GetBlobDataHandle(), 0, RecordWriteCallback());
    201   base::MessageLoop::current()->Run();
    202 
    203   EXPECT_EQ(0, bytes_written());
    204   EXPECT_EQ(base::File::FILE_OK, status());
    205   EXPECT_TRUE(complete());
    206 
    207   EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
    208 }
    209 
    210 
    211 TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidBlobUrl) {
    212   scoped_ptr<storage::BlobDataHandle> null_handle;
    213   file_system_context_->operation_runner()->Write(
    214       &url_request_context(), URLForPath(virtual_path_),
    215       null_handle.Pass(), 0, RecordWriteCallback());
    216   base::MessageLoop::current()->Run();
    217 
    218   EXPECT_EQ(0, bytes_written());
    219   EXPECT_EQ(base::File::FILE_ERROR_FAILED, status());
    220   EXPECT_TRUE(complete());
    221 
    222   EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
    223 }
    224 
    225 TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidFile) {
    226   ScopedTextBlob blob(url_request_context(), "blob_id:writeinvalidfile",
    227                       "It\'ll not be written.");
    228   file_system_context_->operation_runner()->Write(
    229       &url_request_context(),
    230       URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))),
    231       blob.GetBlobDataHandle(), 0, RecordWriteCallback());
    232   base::MessageLoop::current()->Run();
    233 
    234   EXPECT_EQ(0, bytes_written());
    235   EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status());
    236   EXPECT_TRUE(complete());
    237 
    238   EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
    239 }
    240 
    241 TEST_F(FileSystemOperationImplWriteTest, TestWriteDir) {
    242   base::FilePath virtual_dir_path(FILE_PATH_LITERAL("d"));
    243   file_system_context_->operation_runner()->CreateDirectory(
    244       URLForPath(virtual_dir_path),
    245       true /* exclusive */, false /* recursive */,
    246       base::Bind(&AssertStatusEq, base::File::FILE_OK));
    247 
    248   ScopedTextBlob blob(url_request_context(), "blob:writedir",
    249                       "It\'ll not be written, too.");
    250   file_system_context_->operation_runner()->Write(
    251       &url_request_context(), URLForPath(virtual_dir_path),
    252       blob.GetBlobDataHandle(),  0, RecordWriteCallback());
    253   base::MessageLoop::current()->Run();
    254 
    255   EXPECT_EQ(0, bytes_written());
    256   // TODO(kinuko): This error code is platform- or fileutil- dependent
    257   // right now.  Make it return File::FILE_ERROR_NOT_A_FILE in every case.
    258   EXPECT_TRUE(status() == base::File::FILE_ERROR_NOT_A_FILE ||
    259               status() == base::File::FILE_ERROR_ACCESS_DENIED ||
    260               status() == base::File::FILE_ERROR_FAILED);
    261   EXPECT_TRUE(complete());
    262 
    263   EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
    264 }
    265 
    266 TEST_F(FileSystemOperationImplWriteTest, TestWriteFailureByQuota) {
    267   ScopedTextBlob blob(url_request_context(), "blob:success",
    268                       "Hello, world!\n");
    269   quota_manager_->SetQuota(
    270       kOrigin, FileSystemTypeToQuotaStorageType(kFileSystemType), 10);
    271   file_system_context_->operation_runner()->Write(
    272       &url_request_context(), URLForPath(virtual_path_),
    273       blob.GetBlobDataHandle(), 0, RecordWriteCallback());
    274   base::MessageLoop::current()->Run();
    275 
    276   EXPECT_EQ(10, bytes_written());
    277   EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, status());
    278   EXPECT_TRUE(complete());
    279 
    280   EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
    281 }
    282 
    283 TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelSuccessfulWrite) {
    284   ScopedTextBlob blob(url_request_context(), "blob:success",
    285                       "Hello, world!\n");
    286   FileSystemOperationRunner::OperationID id =
    287       file_system_context_->operation_runner()->Write(
    288           &url_request_context(), URLForPath(virtual_path_),
    289           blob.GetBlobDataHandle(), 0, RecordWriteCallback());
    290   file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback());
    291   // We use RunAllPendings() instead of Run() here, because we won't dispatch
    292   // callbacks after Cancel() is issued (so no chance to Quit) nor do we need
    293   // to run another write cycle.
    294   base::RunLoop().RunUntilIdle();
    295 
    296   // Issued Cancel() before receiving any response from Write(),
    297   // so nothing should have happen.
    298   EXPECT_EQ(0, bytes_written());
    299   EXPECT_EQ(base::File::FILE_ERROR_ABORT, status());
    300   EXPECT_EQ(base::File::FILE_OK, cancel_status());
    301   EXPECT_TRUE(complete());
    302 
    303   EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
    304 }
    305 
    306 TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelFailingWrite) {
    307   ScopedTextBlob blob(url_request_context(), "blob:writeinvalidfile",
    308                       "It\'ll not be written.");
    309   FileSystemOperationRunner::OperationID id =
    310       file_system_context_->operation_runner()->Write(
    311           &url_request_context(),
    312           URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))),
    313           blob.GetBlobDataHandle(), 0, RecordWriteCallback());
    314   file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback());
    315   // We use RunAllPendings() instead of Run() here, because we won't dispatch
    316   // callbacks after Cancel() is issued (so no chance to Quit) nor do we need
    317   // to run another write cycle.
    318   base::RunLoop().RunUntilIdle();
    319 
    320   // Issued Cancel() before receiving any response from Write(),
    321   // so nothing should have happen.
    322   EXPECT_EQ(0, bytes_written());
    323   EXPECT_EQ(base::File::FILE_ERROR_ABORT, status());
    324   EXPECT_EQ(base::File::FILE_OK, cancel_status());
    325   EXPECT_TRUE(complete());
    326 
    327   EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
    328 }
    329 
    330 // TODO(ericu,dmikurube,kinuko): Add more tests for cancel cases.
    331 
    332 }  // namespace content
    333