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