Home | History | Annotate | Download | only in fileapi
      1 // Copyright 2014 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 "webkit/browser/blob/local_file_stream_reader.h"
      6 
      7 #include <string>
      8 
      9 #include "base/file_util.h"
     10 #include "base/files/file.h"
     11 #include "base/files/file_path.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/run_loop.h"
     15 #include "base/threading/thread.h"
     16 #include "net/base/io_buffer.h"
     17 #include "net/base/net_errors.h"
     18 #include "net/base/test_completion_callback.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 using webkit_blob::LocalFileStreamReader;
     22 
     23 namespace content {
     24 
     25 namespace {
     26 
     27 const char kTestData[] = "0123456789";
     28 const int kTestDataSize = arraysize(kTestData) - 1;
     29 
     30 void ReadFromReader(LocalFileStreamReader* reader,
     31                     std::string* data, size_t size,
     32                     int* result) {
     33   ASSERT_TRUE(reader != NULL);
     34   ASSERT_TRUE(result != NULL);
     35   *result = net::OK;
     36   net::TestCompletionCallback callback;
     37   size_t total_bytes_read = 0;
     38   while (total_bytes_read < size) {
     39     scoped_refptr<net::IOBufferWithSize> buf(
     40         new net::IOBufferWithSize(size - total_bytes_read));
     41     int rv = reader->Read(buf.get(), buf->size(), callback.callback());
     42     if (rv == net::ERR_IO_PENDING)
     43       rv = callback.WaitForResult();
     44     if (rv < 0)
     45       *result = rv;
     46     if (rv <= 0)
     47       break;
     48     total_bytes_read += rv;
     49     data->append(buf->data(), rv);
     50   }
     51 }
     52 
     53 void NeverCalled(int) { ADD_FAILURE(); }
     54 void EmptyCallback() {}
     55 
     56 void QuitLoop() {
     57   base::MessageLoop::current()->Quit();
     58 }
     59 
     60 }  // namespace
     61 
     62 class LocalFileStreamReaderTest : public testing::Test {
     63  public:
     64   LocalFileStreamReaderTest()
     65       : file_thread_("FileUtilProxyTestFileThread") {}
     66 
     67   virtual void SetUp() OVERRIDE {
     68     ASSERT_TRUE(file_thread_.Start());
     69     ASSERT_TRUE(dir_.CreateUniqueTempDir());
     70 
     71     base::WriteFile(test_path(), kTestData, kTestDataSize);
     72     base::File::Info info;
     73     ASSERT_TRUE(base::GetFileInfo(test_path(), &info));
     74     test_file_modification_time_ = info.last_modified;
     75   }
     76 
     77   virtual void TearDown() OVERRIDE {
     78     // Give another chance for deleted streams to perform Close.
     79     base::RunLoop().RunUntilIdle();
     80     file_thread_.Stop();
     81     base::RunLoop().RunUntilIdle();
     82   }
     83 
     84  protected:
     85   LocalFileStreamReader* CreateFileReader(
     86       const base::FilePath& path,
     87       int64 initial_offset,
     88       const base::Time& expected_modification_time) {
     89     return new LocalFileStreamReader(
     90             file_task_runner(),
     91             path,
     92             initial_offset,
     93             expected_modification_time);
     94   }
     95 
     96   void TouchTestFile() {
     97     base::Time new_modified_time =
     98         test_file_modification_time() - base::TimeDelta::FromSeconds(1);
     99     ASSERT_TRUE(base::TouchFile(test_path(),
    100                                 test_file_modification_time(),
    101                                 new_modified_time));
    102   }
    103 
    104   base::MessageLoopProxy* file_task_runner() const {
    105     return file_thread_.message_loop_proxy().get();
    106   }
    107 
    108   base::FilePath test_dir() const { return dir_.path(); }
    109   base::FilePath test_path() const { return dir_.path().AppendASCII("test"); }
    110   base::Time test_file_modification_time() const {
    111     return test_file_modification_time_;
    112   }
    113 
    114   void EnsureFileTaskFinished() {
    115     file_task_runner()->PostTaskAndReply(
    116         FROM_HERE, base::Bind(&EmptyCallback), base::Bind(&QuitLoop));
    117     base::MessageLoop::current()->Run();
    118   }
    119 
    120  private:
    121   base::MessageLoopForIO message_loop_;
    122   base::Thread file_thread_;
    123   base::ScopedTempDir dir_;
    124   base::Time test_file_modification_time_;
    125 };
    126 
    127 TEST_F(LocalFileStreamReaderTest, NonExistent) {
    128   base::FilePath nonexistent_path = test_dir().AppendASCII("nonexistent");
    129   scoped_ptr<LocalFileStreamReader> reader(
    130       CreateFileReader(nonexistent_path, 0, base::Time()));
    131   int result = 0;
    132   std::string data;
    133   ReadFromReader(reader.get(), &data, 10, &result);
    134   ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result);
    135   ASSERT_EQ(0U, data.size());
    136 }
    137 
    138 TEST_F(LocalFileStreamReaderTest, Empty) {
    139   base::FilePath empty_path = test_dir().AppendASCII("empty");
    140   base::File file(empty_path, base::File::FLAG_CREATE | base::File::FLAG_READ);
    141   ASSERT_TRUE(file.IsValid());
    142   file.Close();
    143 
    144   scoped_ptr<LocalFileStreamReader> reader(
    145       CreateFileReader(empty_path, 0, base::Time()));
    146   int result = 0;
    147   std::string data;
    148   ReadFromReader(reader.get(), &data, 10, &result);
    149   ASSERT_EQ(net::OK, result);
    150   ASSERT_EQ(0U, data.size());
    151 
    152   net::TestInt64CompletionCallback callback;
    153   int64 length_result = reader->GetLength(callback.callback());
    154   if (length_result == net::ERR_IO_PENDING)
    155     length_result = callback.WaitForResult();
    156   ASSERT_EQ(0, result);
    157 }
    158 
    159 TEST_F(LocalFileStreamReaderTest, GetLengthNormal) {
    160   scoped_ptr<LocalFileStreamReader> reader(
    161       CreateFileReader(test_path(), 0, test_file_modification_time()));
    162   net::TestInt64CompletionCallback callback;
    163   int64 result = reader->GetLength(callback.callback());
    164   if (result == net::ERR_IO_PENDING)
    165     result = callback.WaitForResult();
    166   ASSERT_EQ(kTestDataSize, result);
    167 }
    168 
    169 TEST_F(LocalFileStreamReaderTest, GetLengthAfterModified) {
    170   // Touch file so that the file's modification time becomes different
    171   // from what we expect.
    172   TouchTestFile();
    173 
    174   scoped_ptr<LocalFileStreamReader> reader(
    175       CreateFileReader(test_path(), 0, test_file_modification_time()));
    176   net::TestInt64CompletionCallback callback;
    177   int64 result = reader->GetLength(callback.callback());
    178   if (result == net::ERR_IO_PENDING)
    179     result = callback.WaitForResult();
    180   ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
    181 
    182   // With NULL expected modification time this should work.
    183   reader.reset(CreateFileReader(test_path(), 0, base::Time()));
    184   result = reader->GetLength(callback.callback());
    185   if (result == net::ERR_IO_PENDING)
    186     result = callback.WaitForResult();
    187   ASSERT_EQ(kTestDataSize, result);
    188 }
    189 
    190 TEST_F(LocalFileStreamReaderTest, GetLengthWithOffset) {
    191   scoped_ptr<LocalFileStreamReader> reader(
    192       CreateFileReader(test_path(), 3, base::Time()));
    193   net::TestInt64CompletionCallback callback;
    194   int64 result = reader->GetLength(callback.callback());
    195   if (result == net::ERR_IO_PENDING)
    196     result = callback.WaitForResult();
    197   // Initial offset does not affect the result of GetLength.
    198   ASSERT_EQ(kTestDataSize, result);
    199 }
    200 
    201 TEST_F(LocalFileStreamReaderTest, ReadNormal) {
    202   scoped_ptr<LocalFileStreamReader> reader(
    203       CreateFileReader(test_path(), 0, test_file_modification_time()));
    204   int result = 0;
    205   std::string data;
    206   ReadFromReader(reader.get(), &data, kTestDataSize, &result);
    207   ASSERT_EQ(net::OK, result);
    208   ASSERT_EQ(kTestData, data);
    209 }
    210 
    211 TEST_F(LocalFileStreamReaderTest, ReadAfterModified) {
    212   // Touch file so that the file's modification time becomes different
    213   // from what we expect.
    214   TouchTestFile();
    215 
    216   scoped_ptr<LocalFileStreamReader> reader(
    217       CreateFileReader(test_path(), 0, test_file_modification_time()));
    218   int result = 0;
    219   std::string data;
    220   ReadFromReader(reader.get(), &data, kTestDataSize, &result);
    221   ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
    222   ASSERT_EQ(0U, data.size());
    223 
    224   // With NULL expected modification time this should work.
    225   data.clear();
    226   reader.reset(CreateFileReader(test_path(), 0, base::Time()));
    227   ReadFromReader(reader.get(), &data, kTestDataSize, &result);
    228   ASSERT_EQ(net::OK, result);
    229   ASSERT_EQ(kTestData, data);
    230 }
    231 
    232 TEST_F(LocalFileStreamReaderTest, ReadWithOffset) {
    233   scoped_ptr<LocalFileStreamReader> reader(
    234       CreateFileReader(test_path(), 3, base::Time()));
    235   int result = 0;
    236   std::string data;
    237   ReadFromReader(reader.get(), &data, kTestDataSize, &result);
    238   ASSERT_EQ(net::OK, result);
    239   ASSERT_EQ(&kTestData[3], data);
    240 }
    241 
    242 TEST_F(LocalFileStreamReaderTest, DeleteWithUnfinishedRead) {
    243   scoped_ptr<LocalFileStreamReader> reader(
    244       CreateFileReader(test_path(), 0, base::Time()));
    245 
    246   net::TestCompletionCallback callback;
    247   scoped_refptr<net::IOBufferWithSize> buf(
    248       new net::IOBufferWithSize(kTestDataSize));
    249   int rv = reader->Read(buf.get(), buf->size(), base::Bind(&NeverCalled));
    250   ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0);
    251 
    252   // Delete immediately.
    253   // Should not crash; nor should NeverCalled be callback.
    254   reader.reset();
    255   EnsureFileTaskFinished();
    256 }
    257 
    258 }  // namespace content
    259