| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/files/file_proxy.h" | 
 |  | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/files/file.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/files/scoped_temp_dir.h" | 
 | #include "base/macros.h" | 
 | #include "base/memory/weak_ptr.h" | 
 | #include "base/message_loop/message_loop.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/threading/thread.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "build/build_config.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | class FileProxyTest : public testing::Test { | 
 |  public: | 
 |   FileProxyTest() | 
 |       : file_thread_("FileProxyTestFileThread"), | 
 |         error_(File::FILE_OK), | 
 |         bytes_written_(-1), | 
 |         weak_factory_(this) {} | 
 |  | 
 |   void SetUp() override { | 
 |     ASSERT_TRUE(dir_.CreateUniqueTempDir()); | 
 |     ASSERT_TRUE(file_thread_.Start()); | 
 |   } | 
 |  | 
 |   void DidFinish(File::Error error) { | 
 |     error_ = error; | 
 |     RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 |  | 
 |   void DidCreateOrOpen(File::Error error) { | 
 |     error_ = error; | 
 |     RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 |  | 
 |   void DidCreateTemporary(File::Error error, | 
 |                           const FilePath& path) { | 
 |     error_ = error; | 
 |     path_ = path; | 
 |     RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 |  | 
 |   void DidGetFileInfo(File::Error error, | 
 |                       const File::Info& file_info) { | 
 |     error_ = error; | 
 |     file_info_ = file_info; | 
 |     RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 |  | 
 |   void DidRead(File::Error error, | 
 |                const char* data, | 
 |                int bytes_read) { | 
 |     error_ = error; | 
 |     buffer_.resize(bytes_read); | 
 |     memcpy(&buffer_[0], data, bytes_read); | 
 |     RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 |  | 
 |   void DidWrite(File::Error error, | 
 |                 int bytes_written) { | 
 |     error_ = error; | 
 |     bytes_written_ = bytes_written; | 
 |     RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 |  | 
 |  protected: | 
 |   void CreateProxy(uint32_t flags, FileProxy* proxy) { | 
 |     proxy->CreateOrOpen( | 
 |         TestPath(), flags, | 
 |         BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |     RunLoop().Run(); | 
 |     EXPECT_TRUE(proxy->IsValid()); | 
 |   } | 
 |  | 
 |   TaskRunner* file_task_runner() const { | 
 |     return file_thread_.task_runner().get(); | 
 |   } | 
 |   const FilePath& TestDirPath() const { return dir_.GetPath(); } | 
 |   const FilePath TestPath() const { return dir_.GetPath().AppendASCII("test"); } | 
 |  | 
 |   ScopedTempDir dir_; | 
 |   MessageLoopForIO message_loop_; | 
 |   Thread file_thread_; | 
 |  | 
 |   File::Error error_; | 
 |   FilePath path_; | 
 |   File::Info file_info_; | 
 |   std::vector<char> buffer_; | 
 |   int bytes_written_; | 
 |   WeakPtrFactory<FileProxyTest> weak_factory_; | 
 | }; | 
 |  | 
 | TEST_F(FileProxyTest, CreateOrOpen_Create) { | 
 |   FileProxy proxy(file_task_runner()); | 
 |   proxy.CreateOrOpen( | 
 |       TestPath(), File::FLAG_CREATE | File::FLAG_READ, | 
 |       BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |   EXPECT_TRUE(proxy.IsValid()); | 
 |   EXPECT_TRUE(proxy.created()); | 
 |   EXPECT_TRUE(PathExists(TestPath())); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, CreateOrOpen_Open) { | 
 |   // Creates a file. | 
 |   base::WriteFile(TestPath(), nullptr, 0); | 
 |   ASSERT_TRUE(PathExists(TestPath())); | 
 |  | 
 |   // Opens the created file. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   proxy.CreateOrOpen( | 
 |       TestPath(), File::FLAG_OPEN | File::FLAG_READ, | 
 |       BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |   EXPECT_TRUE(proxy.IsValid()); | 
 |   EXPECT_FALSE(proxy.created()); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, CreateOrOpen_OpenNonExistent) { | 
 |   FileProxy proxy(file_task_runner()); | 
 |   proxy.CreateOrOpen( | 
 |       TestPath(), File::FLAG_OPEN | File::FLAG_READ, | 
 |       BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |   EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, error_); | 
 |   EXPECT_FALSE(proxy.IsValid()); | 
 |   EXPECT_FALSE(proxy.created()); | 
 |   EXPECT_FALSE(PathExists(TestPath())); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) { | 
 |   bool prev = ThreadRestrictions::SetIOAllowed(false); | 
 |   { | 
 |     FileProxy proxy(file_task_runner()); | 
 |     proxy.CreateOrOpen( | 
 |         TestPath(), File::FLAG_CREATE | File::FLAG_READ, | 
 |         BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |   } | 
 |   RunLoop().Run(); | 
 |   ThreadRestrictions::SetIOAllowed(prev); | 
 |  | 
 |   EXPECT_TRUE(PathExists(TestPath())); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, Close) { | 
 |   // Creates a file. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy); | 
 |  | 
 | #if defined(OS_WIN) | 
 |   // This fails on Windows if the file is not closed. | 
 |   EXPECT_FALSE(base::Move(TestPath(), TestDirPath().AppendASCII("new"))); | 
 | #endif | 
 |  | 
 |   proxy.Close(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |   EXPECT_FALSE(proxy.IsValid()); | 
 |  | 
 |   // Now it should pass on all platforms. | 
 |   EXPECT_TRUE(base::Move(TestPath(), TestDirPath().AppendASCII("new"))); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, CreateTemporary) { | 
 |   { | 
 |     FileProxy proxy(file_task_runner()); | 
 |     proxy.CreateTemporary(0 /* additional_file_flags */, | 
 |                           BindOnce(&FileProxyTest::DidCreateTemporary, | 
 |                                    weak_factory_.GetWeakPtr())); | 
 |     RunLoop().Run(); | 
 |  | 
 |     EXPECT_TRUE(proxy.IsValid()); | 
 |     EXPECT_EQ(File::FILE_OK, error_); | 
 |     EXPECT_TRUE(PathExists(path_)); | 
 |  | 
 |     // The file should be writable. | 
 |     proxy.Write(0, "test", 4, | 
 |                 BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr())); | 
 |     RunLoop().Run(); | 
 |     EXPECT_EQ(File::FILE_OK, error_); | 
 |     EXPECT_EQ(4, bytes_written_); | 
 |   } | 
 |  | 
 |   // Make sure the written data can be read from the returned path. | 
 |   std::string data; | 
 |   EXPECT_TRUE(ReadFileToString(path_, &data)); | 
 |   EXPECT_EQ("test", data); | 
 |  | 
 |   // Make sure we can & do delete the created file to prevent leaks on the bots. | 
 |   EXPECT_TRUE(base::DeleteFile(path_, false)); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, SetAndTake) { | 
 |   File file(TestPath(), File::FLAG_CREATE | File::FLAG_READ); | 
 |   ASSERT_TRUE(file.IsValid()); | 
 |   FileProxy proxy(file_task_runner()); | 
 |   EXPECT_FALSE(proxy.IsValid()); | 
 |   proxy.SetFile(std::move(file)); | 
 |   EXPECT_TRUE(proxy.IsValid()); | 
 |   EXPECT_FALSE(file.IsValid()); | 
 |  | 
 |   file = proxy.TakeFile(); | 
 |   EXPECT_FALSE(proxy.IsValid()); | 
 |   EXPECT_TRUE(file.IsValid()); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, DuplicateFile) { | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy); | 
 |   ASSERT_TRUE(proxy.IsValid()); | 
 |  | 
 |   base::File duplicate = proxy.DuplicateFile(); | 
 |   EXPECT_TRUE(proxy.IsValid()); | 
 |   EXPECT_TRUE(duplicate.IsValid()); | 
 |  | 
 |   FileProxy invalid_proxy(file_task_runner()); | 
 |   ASSERT_FALSE(invalid_proxy.IsValid()); | 
 |  | 
 |   base::File invalid_duplicate = invalid_proxy.DuplicateFile(); | 
 |   EXPECT_FALSE(invalid_proxy.IsValid()); | 
 |   EXPECT_FALSE(invalid_duplicate.IsValid()); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, GetInfo) { | 
 |   // Setup. | 
 |   ASSERT_EQ(4, base::WriteFile(TestPath(), "test", 4)); | 
 |   File::Info expected_info; | 
 |   GetFileInfo(TestPath(), &expected_info); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); | 
 |   proxy.GetInfo( | 
 |       BindOnce(&FileProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   // Verify. | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |   EXPECT_EQ(expected_info.size, file_info_.size); | 
 |   EXPECT_EQ(expected_info.is_directory, file_info_.is_directory); | 
 |   EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link); | 
 |   EXPECT_EQ(expected_info.last_modified, file_info_.last_modified); | 
 |   EXPECT_EQ(expected_info.creation_time, file_info_.creation_time); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, Read) { | 
 |   // Setup. | 
 |   const char expected_data[] = "bleh"; | 
 |   int expected_bytes = arraysize(expected_data); | 
 |   ASSERT_EQ(expected_bytes, | 
 |             base::WriteFile(TestPath(), expected_data, expected_bytes)); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); | 
 |  | 
 |   proxy.Read(0, 128, | 
 |              BindOnce(&FileProxyTest::DidRead, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   // Verify. | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |   EXPECT_EQ(expected_bytes, static_cast<int>(buffer_.size())); | 
 |   for (size_t i = 0; i < buffer_.size(); ++i) { | 
 |     EXPECT_EQ(expected_data[i], buffer_[i]); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, WriteAndFlush) { | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy); | 
 |  | 
 |   const char data[] = "foo!"; | 
 |   int data_bytes = arraysize(data); | 
 |   proxy.Write(0, data, data_bytes, | 
 |               BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |   EXPECT_EQ(data_bytes, bytes_written_); | 
 |  | 
 |   // Flush the written data.  (So that the following read should always | 
 |   // succeed.  On some platforms it may work with or without this flush.) | 
 |   proxy.Flush(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |  | 
 |   // Verify the written data. | 
 |   char buffer[10]; | 
 |   EXPECT_EQ(data_bytes, base::ReadFile(TestPath(), buffer, data_bytes)); | 
 |   for (int i = 0; i < data_bytes; ++i) { | 
 |     EXPECT_EQ(data[i], buffer[i]); | 
 |   } | 
 | } | 
 |  | 
 | #if defined(OS_ANDROID) | 
 | // Flaky on Android, see http://crbug.com/489602 | 
 | #define MAYBE_SetTimes DISABLED_SetTimes | 
 | #else | 
 | #define MAYBE_SetTimes SetTimes | 
 | #endif | 
 | TEST_F(FileProxyTest, MAYBE_SetTimes) { | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy( | 
 |       File::FLAG_CREATE | File::FLAG_WRITE | File::FLAG_WRITE_ATTRIBUTES, | 
 |       &proxy); | 
 |  | 
 |   Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345); | 
 |   Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765); | 
 |  | 
 |   proxy.SetTimes( | 
 |       last_accessed_time, last_modified_time, | 
 |       BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |  | 
 |   File::Info info; | 
 |   GetFileInfo(TestPath(), &info); | 
 |  | 
 |   // The returned values may only have the seconds precision, so we cast | 
 |   // the double values to int here. | 
 |   EXPECT_EQ(static_cast<int>(last_modified_time.ToDoubleT()), | 
 |             static_cast<int>(info.last_modified.ToDoubleT())); | 
 |   EXPECT_EQ(static_cast<int>(last_accessed_time.ToDoubleT()), | 
 |             static_cast<int>(info.last_accessed.ToDoubleT())); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, SetLength_Shrink) { | 
 |   // Setup. | 
 |   const char kTestData[] = "0123456789"; | 
 |   ASSERT_EQ(10, base::WriteFile(TestPath(), kTestData, 10)); | 
 |   File::Info info; | 
 |   GetFileInfo(TestPath(), &info); | 
 |   ASSERT_EQ(10, info.size); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); | 
 |   proxy.SetLength( | 
 |       7, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   // Verify. | 
 |   GetFileInfo(TestPath(), &info); | 
 |   ASSERT_EQ(7, info.size); | 
 |  | 
 |   char buffer[7]; | 
 |   EXPECT_EQ(7, base::ReadFile(TestPath(), buffer, 7)); | 
 |   int i = 0; | 
 |   for (; i < 7; ++i) | 
 |     EXPECT_EQ(kTestData[i], buffer[i]); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, SetLength_Expand) { | 
 |   // Setup. | 
 |   const char kTestData[] = "9876543210"; | 
 |   ASSERT_EQ(10, base::WriteFile(TestPath(), kTestData, 10)); | 
 |   File::Info info; | 
 |   GetFileInfo(TestPath(), &info); | 
 |   ASSERT_EQ(10, info.size); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); | 
 |   proxy.SetLength( | 
 |       53, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   // Verify. | 
 |   GetFileInfo(TestPath(), &info); | 
 |   ASSERT_EQ(53, info.size); | 
 |  | 
 |   char buffer[53]; | 
 |   EXPECT_EQ(53, base::ReadFile(TestPath(), buffer, 53)); | 
 |   int i = 0; | 
 |   for (; i < 10; ++i) | 
 |     EXPECT_EQ(kTestData[i], buffer[i]); | 
 |   for (; i < 53; ++i) | 
 |     EXPECT_EQ(0, buffer[i]); | 
 | } | 
 |  | 
 | }  // namespace base |