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