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