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/fileapi/file_system_file_stream_reader.h" 6 7 #include <limits> 8 #include <string> 9 10 #include "base/files/scoped_temp_dir.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/platform_file.h" 14 #include "net/base/io_buffer.h" 15 #include "net/base/net_errors.h" 16 #include "net/base/test_completion_callback.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 #include "webkit/browser/fileapi/external_mount_points.h" 19 #include "webkit/browser/fileapi/file_system_context.h" 20 #include "webkit/browser/fileapi/file_system_file_util.h" 21 #include "webkit/browser/fileapi/file_system_operation_context.h" 22 #include "webkit/browser/fileapi/mock_file_system_context.h" 23 24 namespace fileapi { 25 26 namespace { 27 28 const char kURLOrigin[] = "http://remote/"; 29 const char kTestFileName[] = "test.dat"; 30 const char kTestData[] = "0123456789"; 31 const int kTestDataSize = arraysize(kTestData) - 1; 32 33 void ReadFromReader(FileSystemFileStreamReader* reader, 34 std::string* data, 35 size_t size, 36 int* result) { 37 ASSERT_TRUE(reader != NULL); 38 ASSERT_TRUE(result != NULL); 39 *result = net::OK; 40 net::TestCompletionCallback callback; 41 size_t total_bytes_read = 0; 42 while (total_bytes_read < size) { 43 scoped_refptr<net::IOBufferWithSize> buf( 44 new net::IOBufferWithSize(size - total_bytes_read)); 45 int rv = reader->Read(buf.get(), buf->size(), callback.callback()); 46 if (rv == net::ERR_IO_PENDING) 47 rv = callback.WaitForResult(); 48 if (rv < 0) 49 *result = rv; 50 if (rv <= 0) 51 break; 52 total_bytes_read += rv; 53 data->append(buf->data(), rv); 54 } 55 } 56 57 void NeverCalled(int unused) { ADD_FAILURE(); } 58 59 } // namespace 60 61 class FileSystemFileStreamReaderTest : public testing::Test { 62 public: 63 FileSystemFileStreamReaderTest() 64 : message_loop_(base::MessageLoop::TYPE_IO) {} 65 66 virtual void SetUp() OVERRIDE { 67 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 68 69 file_system_context_ = CreateFileSystemContextForTesting( 70 NULL, temp_dir_.path()); 71 72 file_system_context_->OpenFileSystem( 73 GURL(kURLOrigin), kFileSystemTypeTemporary, 74 OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, 75 base::Bind(&OnOpenFileSystem)); 76 base::MessageLoop::current()->RunUntilIdle(); 77 78 WriteFile(kTestFileName, kTestData, kTestDataSize, 79 &test_file_modification_time_); 80 } 81 82 virtual void TearDown() OVERRIDE { 83 base::MessageLoop::current()->RunUntilIdle(); 84 } 85 86 protected: 87 FileSystemFileStreamReader* CreateFileReader( 88 const std::string& file_name, 89 int64 initial_offset, 90 const base::Time& expected_modification_time) { 91 return new FileSystemFileStreamReader(file_system_context_.get(), 92 GetFileSystemURL(file_name), 93 initial_offset, 94 expected_modification_time); 95 } 96 97 base::Time test_file_modification_time() const { 98 return test_file_modification_time_; 99 } 100 101 void WriteFile(const std::string& file_name, 102 const char* buf, 103 int buf_size, 104 base::Time* modification_time) { 105 FileSystemFileUtil* file_util = file_system_context_->GetFileUtil( 106 kFileSystemTypeTemporary); 107 FileSystemURL url = GetFileSystemURL(file_name); 108 109 FileSystemOperationContext context(file_system_context_.get()); 110 context.set_allowed_bytes_growth(1024); 111 112 base::PlatformFile handle = base::kInvalidPlatformFileValue; 113 bool created = false; 114 ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen( 115 &context, 116 url, 117 base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, 118 &handle, 119 &created)); 120 EXPECT_TRUE(created); 121 ASSERT_NE(base::kInvalidPlatformFileValue, handle); 122 ASSERT_EQ(buf_size, 123 base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size)); 124 base::ClosePlatformFile(handle); 125 126 base::PlatformFileInfo file_info; 127 base::FilePath platform_path; 128 ASSERT_EQ(base::PLATFORM_FILE_OK, 129 file_util->GetFileInfo(&context, url, &file_info, 130 &platform_path)); 131 if (modification_time) 132 *modification_time = file_info.last_modified; 133 } 134 135 private: 136 static void OnOpenFileSystem(base::PlatformFileError result, 137 const std::string& name, 138 const GURL& root_url) { 139 ASSERT_EQ(base::PLATFORM_FILE_OK, result); 140 } 141 142 FileSystemURL GetFileSystemURL(const std::string& file_name) { 143 return file_system_context_->CreateCrackedFileSystemURL( 144 GURL(kURLOrigin), 145 kFileSystemTypeTemporary, 146 base::FilePath().AppendASCII(file_name)); 147 } 148 149 base::MessageLoop message_loop_; 150 base::ScopedTempDir temp_dir_; 151 scoped_refptr<FileSystemContext> file_system_context_; 152 base::Time test_file_modification_time_; 153 }; 154 155 TEST_F(FileSystemFileStreamReaderTest, NonExistent) { 156 const char kFileName[] = "nonexistent"; 157 scoped_ptr<FileSystemFileStreamReader> reader( 158 CreateFileReader(kFileName, 0, base::Time())); 159 int result = 0; 160 std::string data; 161 ReadFromReader(reader.get(), &data, 10, &result); 162 ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result); 163 ASSERT_EQ(0U, data.size()); 164 } 165 166 TEST_F(FileSystemFileStreamReaderTest, Empty) { 167 const char kFileName[] = "empty"; 168 WriteFile(kFileName, NULL, 0, NULL); 169 170 scoped_ptr<FileSystemFileStreamReader> reader( 171 CreateFileReader(kFileName, 0, base::Time())); 172 int result = 0; 173 std::string data; 174 ReadFromReader(reader.get(), &data, 10, &result); 175 ASSERT_EQ(net::OK, result); 176 ASSERT_EQ(0U, data.size()); 177 178 net::TestInt64CompletionCallback callback; 179 int64 length_result = reader->GetLength(callback.callback()); 180 if (length_result == net::ERR_IO_PENDING) 181 length_result = callback.WaitForResult(); 182 ASSERT_EQ(0, length_result); 183 } 184 185 TEST_F(FileSystemFileStreamReaderTest, GetLengthNormal) { 186 scoped_ptr<FileSystemFileStreamReader> reader( 187 CreateFileReader(kTestFileName, 0, test_file_modification_time())); 188 net::TestInt64CompletionCallback callback; 189 int64 result = reader->GetLength(callback.callback()); 190 if (result == net::ERR_IO_PENDING) 191 result = callback.WaitForResult(); 192 ASSERT_EQ(kTestDataSize, result); 193 } 194 195 TEST_F(FileSystemFileStreamReaderTest, GetLengthAfterModified) { 196 // Pass a fake expected modifictaion time so that the expectation fails. 197 base::Time fake_expected_modification_time = 198 test_file_modification_time() - base::TimeDelta::FromSeconds(10); 199 200 scoped_ptr<FileSystemFileStreamReader> reader( 201 CreateFileReader(kTestFileName, 0, fake_expected_modification_time)); 202 net::TestInt64CompletionCallback callback; 203 int64 result = reader->GetLength(callback.callback()); 204 if (result == net::ERR_IO_PENDING) 205 result = callback.WaitForResult(); 206 ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result); 207 208 // With NULL expected modification time this should work. 209 reader.reset(CreateFileReader(kTestFileName, 0, base::Time())); 210 result = reader->GetLength(callback.callback()); 211 if (result == net::ERR_IO_PENDING) 212 result = callback.WaitForResult(); 213 ASSERT_EQ(kTestDataSize, result); 214 } 215 216 TEST_F(FileSystemFileStreamReaderTest, GetLengthWithOffset) { 217 scoped_ptr<FileSystemFileStreamReader> reader( 218 CreateFileReader(kTestFileName, 3, base::Time())); 219 net::TestInt64CompletionCallback callback; 220 int64 result = reader->GetLength(callback.callback()); 221 if (result == net::ERR_IO_PENDING) 222 result = callback.WaitForResult(); 223 // Initial offset does not affect the result of GetLength. 224 ASSERT_EQ(kTestDataSize, result); 225 } 226 227 TEST_F(FileSystemFileStreamReaderTest, ReadNormal) { 228 scoped_ptr<FileSystemFileStreamReader> reader( 229 CreateFileReader(kTestFileName, 0, test_file_modification_time())); 230 int result = 0; 231 std::string data; 232 ReadFromReader(reader.get(), &data, kTestDataSize, &result); 233 ASSERT_EQ(net::OK, result); 234 ASSERT_EQ(kTestData, data); 235 } 236 237 TEST_F(FileSystemFileStreamReaderTest, ReadAfterModified) { 238 // Pass a fake expected modifictaion time so that the expectation fails. 239 base::Time fake_expected_modification_time = 240 test_file_modification_time() - base::TimeDelta::FromSeconds(10); 241 242 scoped_ptr<FileSystemFileStreamReader> reader( 243 CreateFileReader(kTestFileName, 0, fake_expected_modification_time)); 244 int result = 0; 245 std::string data; 246 ReadFromReader(reader.get(), &data, kTestDataSize, &result); 247 ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result); 248 ASSERT_EQ(0U, data.size()); 249 250 // With NULL expected modification time this should work. 251 data.clear(); 252 reader.reset(CreateFileReader(kTestFileName, 0, base::Time())); 253 ReadFromReader(reader.get(), &data, kTestDataSize, &result); 254 ASSERT_EQ(net::OK, result); 255 ASSERT_EQ(kTestData, data); 256 } 257 258 TEST_F(FileSystemFileStreamReaderTest, ReadWithOffset) { 259 scoped_ptr<FileSystemFileStreamReader> reader( 260 CreateFileReader(kTestFileName, 3, base::Time())); 261 int result = 0; 262 std::string data; 263 ReadFromReader(reader.get(), &data, kTestDataSize, &result); 264 ASSERT_EQ(net::OK, result); 265 ASSERT_EQ(&kTestData[3], data); 266 } 267 268 TEST_F(FileSystemFileStreamReaderTest, DeleteWithUnfinishedRead) { 269 scoped_ptr<FileSystemFileStreamReader> reader( 270 CreateFileReader(kTestFileName, 0, base::Time())); 271 272 net::TestCompletionCallback callback; 273 scoped_refptr<net::IOBufferWithSize> buf( 274 new net::IOBufferWithSize(kTestDataSize)); 275 int rv = reader->Read(buf.get(), buf->size(), base::Bind(&NeverCalled)); 276 ASSERT_TRUE(rv == net::ERR_IO_PENDING || rv >= 0); 277 278 // Delete immediately. 279 // Should not crash; nor should NeverCalled be callback. 280 reader.reset(); 281 } 282 283 } // namespace fileapi 284