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 "base/files/file_util_proxy.h" 6 7 #include <map> 8 9 #include "base/bind.h" 10 #include "base/file_util.h" 11 #include "base/files/scoped_temp_dir.h" 12 #include "base/logging.h" 13 #include "base/memory/weak_ptr.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/platform_file.h" 16 #include "base/threading/thread.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 namespace base { 20 21 class FileUtilProxyTest : public testing::Test { 22 public: 23 FileUtilProxyTest() 24 : message_loop_(MessageLoop::TYPE_IO), 25 file_thread_("FileUtilProxyTestFileThread"), 26 error_(PLATFORM_FILE_OK), 27 created_(false), 28 file_(kInvalidPlatformFileValue), 29 bytes_written_(-1), 30 weak_factory_(this) {} 31 32 virtual void SetUp() OVERRIDE { 33 ASSERT_TRUE(dir_.CreateUniqueTempDir()); 34 ASSERT_TRUE(file_thread_.Start()); 35 } 36 37 virtual void TearDown() OVERRIDE { 38 if (file_ != kInvalidPlatformFileValue) 39 ClosePlatformFile(file_); 40 } 41 42 void DidFinish(PlatformFileError error) { 43 error_ = error; 44 MessageLoop::current()->QuitWhenIdle(); 45 } 46 47 void DidCreateOrOpen(PlatformFileError error, 48 PassPlatformFile file, 49 bool created) { 50 error_ = error; 51 file_ = file.ReleaseValue(); 52 created_ = created; 53 MessageLoop::current()->QuitWhenIdle(); 54 } 55 56 void DidCreateTemporary(PlatformFileError error, 57 PassPlatformFile file, 58 const FilePath& path) { 59 error_ = error; 60 file_ = file.ReleaseValue(); 61 path_ = path; 62 MessageLoop::current()->QuitWhenIdle(); 63 } 64 65 void DidGetFileInfo(PlatformFileError error, 66 const PlatformFileInfo& file_info) { 67 error_ = error; 68 file_info_ = file_info; 69 MessageLoop::current()->QuitWhenIdle(); 70 } 71 72 void DidRead(PlatformFileError error, 73 const char* data, 74 int bytes_read) { 75 error_ = error; 76 buffer_.resize(bytes_read); 77 memcpy(&buffer_[0], data, bytes_read); 78 MessageLoop::current()->QuitWhenIdle(); 79 } 80 81 void DidWrite(PlatformFileError error, 82 int bytes_written) { 83 error_ = error; 84 bytes_written_ = bytes_written; 85 MessageLoop::current()->QuitWhenIdle(); 86 } 87 88 protected: 89 PlatformFile GetTestPlatformFile(int flags) { 90 if (file_ != kInvalidPlatformFileValue) 91 return file_; 92 bool created; 93 PlatformFileError error; 94 file_ = CreatePlatformFile(test_path(), flags, &created, &error); 95 EXPECT_EQ(PLATFORM_FILE_OK, error); 96 EXPECT_NE(kInvalidPlatformFileValue, file_); 97 return file_; 98 } 99 100 TaskRunner* file_task_runner() const { 101 return file_thread_.message_loop_proxy().get(); 102 } 103 const FilePath& test_dir_path() const { return dir_.path(); } 104 const FilePath test_path() const { return dir_.path().AppendASCII("test"); } 105 106 MessageLoop message_loop_; 107 Thread file_thread_; 108 109 ScopedTempDir dir_; 110 PlatformFileError error_; 111 bool created_; 112 PlatformFile file_; 113 FilePath path_; 114 PlatformFileInfo file_info_; 115 std::vector<char> buffer_; 116 int bytes_written_; 117 WeakPtrFactory<FileUtilProxyTest> weak_factory_; 118 }; 119 120 TEST_F(FileUtilProxyTest, CreateOrOpen_Create) { 121 FileUtilProxy::CreateOrOpen( 122 file_task_runner(), 123 test_path(), 124 PLATFORM_FILE_CREATE | PLATFORM_FILE_READ, 125 Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); 126 MessageLoop::current()->Run(); 127 128 EXPECT_EQ(PLATFORM_FILE_OK, error_); 129 EXPECT_TRUE(created_); 130 EXPECT_NE(kInvalidPlatformFileValue, file_); 131 EXPECT_TRUE(PathExists(test_path())); 132 } 133 134 TEST_F(FileUtilProxyTest, CreateOrOpen_Open) { 135 // Creates a file. 136 file_util::WriteFile(test_path(), NULL, 0); 137 ASSERT_TRUE(PathExists(test_path())); 138 139 // Opens the created file. 140 FileUtilProxy::CreateOrOpen( 141 file_task_runner(), 142 test_path(), 143 PLATFORM_FILE_OPEN | PLATFORM_FILE_READ, 144 Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); 145 MessageLoop::current()->Run(); 146 147 EXPECT_EQ(PLATFORM_FILE_OK, error_); 148 EXPECT_FALSE(created_); 149 EXPECT_NE(kInvalidPlatformFileValue, file_); 150 } 151 152 TEST_F(FileUtilProxyTest, CreateOrOpen_OpenNonExistent) { 153 FileUtilProxy::CreateOrOpen( 154 file_task_runner(), 155 test_path(), 156 PLATFORM_FILE_OPEN | PLATFORM_FILE_READ, 157 Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); 158 MessageLoop::current()->Run(); 159 EXPECT_EQ(PLATFORM_FILE_ERROR_NOT_FOUND, error_); 160 EXPECT_FALSE(created_); 161 EXPECT_EQ(kInvalidPlatformFileValue, file_); 162 EXPECT_FALSE(PathExists(test_path())); 163 } 164 165 TEST_F(FileUtilProxyTest, Close) { 166 // Creates a file. 167 PlatformFile file = GetTestPlatformFile( 168 PLATFORM_FILE_CREATE | PLATFORM_FILE_WRITE); 169 170 #if defined(OS_WIN) 171 // This fails on Windows if the file is not closed. 172 EXPECT_FALSE(base::Move(test_path(), 173 test_dir_path().AppendASCII("new"))); 174 #endif 175 176 FileUtilProxy::Close( 177 file_task_runner(), 178 file, 179 Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); 180 MessageLoop::current()->Run(); 181 EXPECT_EQ(PLATFORM_FILE_OK, error_); 182 183 // Now it should pass on all platforms. 184 EXPECT_TRUE(base::Move(test_path(), test_dir_path().AppendASCII("new"))); 185 } 186 187 TEST_F(FileUtilProxyTest, CreateTemporary) { 188 FileUtilProxy::CreateTemporary( 189 file_task_runner(), 0 /* additional_file_flags */, 190 Bind(&FileUtilProxyTest::DidCreateTemporary, weak_factory_.GetWeakPtr())); 191 MessageLoop::current()->Run(); 192 EXPECT_EQ(PLATFORM_FILE_OK, error_); 193 EXPECT_TRUE(PathExists(path_)); 194 EXPECT_NE(kInvalidPlatformFileValue, file_); 195 196 // The file should be writable. 197 #if defined(OS_WIN) 198 HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 199 OVERLAPPED overlapped = {0}; 200 overlapped.hEvent = hEvent; 201 DWORD bytes_written; 202 if (!::WriteFile(file_, "test", 4, &bytes_written, &overlapped)) { 203 // Temporary file is created with ASYNC flag, so WriteFile may return 0 204 // with ERROR_IO_PENDING. 205 EXPECT_EQ(ERROR_IO_PENDING, GetLastError()); 206 GetOverlappedResult(file_, &overlapped, &bytes_written, TRUE); 207 } 208 EXPECT_EQ(4, bytes_written); 209 #else 210 // On POSIX ASYNC flag does not affect synchronous read/write behavior. 211 EXPECT_EQ(4, WritePlatformFile(file_, 0, "test", 4)); 212 #endif 213 EXPECT_TRUE(ClosePlatformFile(file_)); 214 file_ = kInvalidPlatformFileValue; 215 216 // Make sure the written data can be read from the returned path. 217 std::string data; 218 EXPECT_TRUE(file_util::ReadFileToString(path_, &data)); 219 EXPECT_EQ("test", data); 220 221 // Make sure we can & do delete the created file to prevent leaks on the bots. 222 EXPECT_TRUE(base::DeleteFile(path_, false)); 223 } 224 225 TEST_F(FileUtilProxyTest, GetFileInfo_File) { 226 // Setup. 227 ASSERT_EQ(4, file_util::WriteFile(test_path(), "test", 4)); 228 PlatformFileInfo expected_info; 229 file_util::GetFileInfo(test_path(), &expected_info); 230 231 // Run. 232 FileUtilProxy::GetFileInfo( 233 file_task_runner(), 234 test_path(), 235 Bind(&FileUtilProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr())); 236 MessageLoop::current()->Run(); 237 238 // Verify. 239 EXPECT_EQ(PLATFORM_FILE_OK, error_); 240 EXPECT_EQ(expected_info.size, file_info_.size); 241 EXPECT_EQ(expected_info.is_directory, file_info_.is_directory); 242 EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link); 243 EXPECT_EQ(expected_info.last_modified, file_info_.last_modified); 244 EXPECT_EQ(expected_info.last_accessed, file_info_.last_accessed); 245 EXPECT_EQ(expected_info.creation_time, file_info_.creation_time); 246 } 247 248 TEST_F(FileUtilProxyTest, GetFileInfo_Directory) { 249 // Setup. 250 ASSERT_TRUE(file_util::CreateDirectory(test_path())); 251 PlatformFileInfo expected_info; 252 file_util::GetFileInfo(test_path(), &expected_info); 253 254 // Run. 255 FileUtilProxy::GetFileInfo( 256 file_task_runner(), 257 test_path(), 258 Bind(&FileUtilProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr())); 259 MessageLoop::current()->Run(); 260 261 // Verify. 262 EXPECT_EQ(PLATFORM_FILE_OK, error_); 263 EXPECT_EQ(expected_info.size, file_info_.size); 264 EXPECT_EQ(expected_info.is_directory, file_info_.is_directory); 265 EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link); 266 EXPECT_EQ(expected_info.last_modified, file_info_.last_modified); 267 EXPECT_EQ(expected_info.last_accessed, file_info_.last_accessed); 268 EXPECT_EQ(expected_info.creation_time, file_info_.creation_time); 269 } 270 271 TEST_F(FileUtilProxyTest, Read) { 272 // Setup. 273 const char expected_data[] = "bleh"; 274 int expected_bytes = arraysize(expected_data); 275 ASSERT_EQ(expected_bytes, 276 file_util::WriteFile(test_path(), expected_data, expected_bytes)); 277 278 // Run. 279 FileUtilProxy::Read( 280 file_task_runner(), 281 GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_READ), 282 0, // offset 283 128, 284 Bind(&FileUtilProxyTest::DidRead, weak_factory_.GetWeakPtr())); 285 MessageLoop::current()->Run(); 286 287 // Verify. 288 EXPECT_EQ(PLATFORM_FILE_OK, error_); 289 EXPECT_EQ(expected_bytes, static_cast<int>(buffer_.size())); 290 for (size_t i = 0; i < buffer_.size(); ++i) { 291 EXPECT_EQ(expected_data[i], buffer_[i]); 292 } 293 } 294 295 TEST_F(FileUtilProxyTest, WriteAndFlush) { 296 const char data[] = "foo!"; 297 int data_bytes = ARRAYSIZE_UNSAFE(data); 298 PlatformFile file = GetTestPlatformFile( 299 PLATFORM_FILE_CREATE | PLATFORM_FILE_WRITE); 300 301 FileUtilProxy::Write( 302 file_task_runner(), 303 file, 304 0, // offset 305 data, 306 data_bytes, 307 Bind(&FileUtilProxyTest::DidWrite, weak_factory_.GetWeakPtr())); 308 MessageLoop::current()->Run(); 309 EXPECT_EQ(PLATFORM_FILE_OK, error_); 310 EXPECT_EQ(data_bytes, bytes_written_); 311 312 // Flush the written data. (So that the following read should always 313 // succeed. On some platforms it may work with or without this flush.) 314 FileUtilProxy::Flush( 315 file_task_runner(), 316 file, 317 Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); 318 MessageLoop::current()->Run(); 319 EXPECT_EQ(PLATFORM_FILE_OK, error_); 320 321 // Verify the written data. 322 char buffer[10]; 323 EXPECT_EQ(data_bytes, file_util::ReadFile(test_path(), buffer, data_bytes)); 324 for (int i = 0; i < data_bytes; ++i) { 325 EXPECT_EQ(data[i], buffer[i]); 326 } 327 } 328 329 TEST_F(FileUtilProxyTest, Touch) { 330 Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345); 331 Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765); 332 333 FileUtilProxy::Touch( 334 file_task_runner(), 335 GetTestPlatformFile(PLATFORM_FILE_CREATE | 336 PLATFORM_FILE_WRITE | 337 PLATFORM_FILE_WRITE_ATTRIBUTES), 338 last_accessed_time, 339 last_modified_time, 340 Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); 341 MessageLoop::current()->Run(); 342 EXPECT_EQ(PLATFORM_FILE_OK, error_); 343 344 PlatformFileInfo info; 345 file_util::GetFileInfo(test_path(), &info); 346 347 // The returned values may only have the seconds precision, so we cast 348 // the double values to int here. 349 EXPECT_EQ(static_cast<int>(last_modified_time.ToDoubleT()), 350 static_cast<int>(info.last_modified.ToDoubleT())); 351 EXPECT_EQ(static_cast<int>(last_accessed_time.ToDoubleT()), 352 static_cast<int>(info.last_accessed.ToDoubleT())); 353 } 354 355 TEST_F(FileUtilProxyTest, Truncate_Shrink) { 356 // Setup. 357 const char kTestData[] = "0123456789"; 358 ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10)); 359 PlatformFileInfo info; 360 file_util::GetFileInfo(test_path(), &info); 361 ASSERT_EQ(10, info.size); 362 363 // Run. 364 FileUtilProxy::Truncate( 365 file_task_runner(), 366 GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE), 367 7, 368 Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); 369 MessageLoop::current()->Run(); 370 371 // Verify. 372 file_util::GetFileInfo(test_path(), &info); 373 ASSERT_EQ(7, info.size); 374 375 char buffer[7]; 376 EXPECT_EQ(7, file_util::ReadFile(test_path(), buffer, 7)); 377 int i = 0; 378 for (; i < 7; ++i) 379 EXPECT_EQ(kTestData[i], buffer[i]); 380 } 381 382 TEST_F(FileUtilProxyTest, Truncate_Expand) { 383 // Setup. 384 const char kTestData[] = "9876543210"; 385 ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10)); 386 PlatformFileInfo info; 387 file_util::GetFileInfo(test_path(), &info); 388 ASSERT_EQ(10, info.size); 389 390 // Run. 391 FileUtilProxy::Truncate( 392 file_task_runner(), 393 GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE), 394 53, 395 Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); 396 MessageLoop::current()->Run(); 397 398 // Verify. 399 file_util::GetFileInfo(test_path(), &info); 400 ASSERT_EQ(53, info.size); 401 402 char buffer[53]; 403 EXPECT_EQ(53, file_util::ReadFile(test_path(), buffer, 53)); 404 int i = 0; 405 for (; i < 10; ++i) 406 EXPECT_EQ(kTestData[i], buffer[i]); 407 for (; i < 53; ++i) 408 EXPECT_EQ(0, buffer[i]); 409 } 410 411 } // namespace base 412