| // 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/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; | 
 |     MessageLoop::current()->QuitWhenIdle(); | 
 |   } | 
 |  | 
 |   void DidCreateOrOpen(File::Error error) { | 
 |     error_ = error; | 
 |     MessageLoop::current()->QuitWhenIdle(); | 
 |   } | 
 |  | 
 |   void DidCreateTemporary(File::Error error, | 
 |                           const FilePath& path) { | 
 |     error_ = error; | 
 |     path_ = path; | 
 |     MessageLoop::current()->QuitWhenIdle(); | 
 |   } | 
 |  | 
 |   void DidGetFileInfo(File::Error error, | 
 |                       const File::Info& file_info) { | 
 |     error_ = error; | 
 |     file_info_ = file_info; | 
 |     MessageLoop::current()->QuitWhenIdle(); | 
 |   } | 
 |  | 
 |   void DidRead(File::Error error, | 
 |                const char* data, | 
 |                int bytes_read) { | 
 |     error_ = error; | 
 |     buffer_.resize(bytes_read); | 
 |     memcpy(&buffer_[0], data, bytes_read); | 
 |     MessageLoop::current()->QuitWhenIdle(); | 
 |   } | 
 |  | 
 |   void DidWrite(File::Error error, | 
 |                 int bytes_written) { | 
 |     error_ = error; | 
 |     bytes_written_ = bytes_written; | 
 |     MessageLoop::current()->QuitWhenIdle(); | 
 |   } | 
 |  | 
 |  protected: | 
 |   void CreateProxy(uint32_t flags, FileProxy* proxy) { | 
 |     proxy->CreateOrOpen( | 
 |         test_path(), flags, | 
 |         Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |     RunLoop().Run(); | 
 |     EXPECT_TRUE(proxy->IsValid()); | 
 |   } | 
 |  | 
 |   TaskRunner* file_task_runner() const { | 
 |     return file_thread_.task_runner().get(); | 
 |   } | 
 |   const FilePath& test_dir_path() const { return dir_.path(); } | 
 |   const FilePath test_path() const { return dir_.path().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( | 
 |       test_path(), | 
 |       File::FLAG_CREATE | File::FLAG_READ, | 
 |       Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |   EXPECT_TRUE(proxy.IsValid()); | 
 |   EXPECT_TRUE(proxy.created()); | 
 |   EXPECT_TRUE(PathExists(test_path())); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, CreateOrOpen_Open) { | 
 |   // Creates a file. | 
 |   base::WriteFile(test_path(), NULL, 0); | 
 |   ASSERT_TRUE(PathExists(test_path())); | 
 |  | 
 |   // Opens the created file. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   proxy.CreateOrOpen( | 
 |       test_path(), | 
 |       File::FLAG_OPEN | File::FLAG_READ, | 
 |       Bind(&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( | 
 |       test_path(), | 
 |       File::FLAG_OPEN | File::FLAG_READ, | 
 |       Bind(&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(test_path())); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) { | 
 |   bool prev = ThreadRestrictions::SetIOAllowed(false); | 
 |   { | 
 |     FileProxy proxy(file_task_runner()); | 
 |     proxy.CreateOrOpen( | 
 |         test_path(), | 
 |         File::FLAG_CREATE | File::FLAG_READ, | 
 |         Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); | 
 |   } | 
 |   RunLoop().Run(); | 
 |   ThreadRestrictions::SetIOAllowed(prev); | 
 |  | 
 |   EXPECT_TRUE(PathExists(test_path())); | 
 | } | 
 |  | 
 | 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(test_path(), test_dir_path().AppendASCII("new"))); | 
 | #endif | 
 |  | 
 |   proxy.Close(Bind(&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(test_path(), test_dir_path().AppendASCII("new"))); | 
 | } | 
 |  | 
 | TEST_F(FileProxyTest, CreateTemporary) { | 
 |   { | 
 |     FileProxy proxy(file_task_runner()); | 
 |     proxy.CreateTemporary( | 
 |         0 /* additional_file_flags */, | 
 |         Bind(&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, | 
 |                 Bind(&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(test_path(), 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(test_path(), "test", 4)); | 
 |   File::Info expected_info; | 
 |   GetFileInfo(test_path(), &expected_info); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); | 
 |   proxy.GetInfo( | 
 |       Bind(&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(test_path(), expected_data, expected_bytes)); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); | 
 |  | 
 |   proxy.Read(0, 128, Bind(&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, | 
 |               Bind(&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(Bind(&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(test_path(), 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, | 
 |                  Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |   EXPECT_EQ(File::FILE_OK, error_); | 
 |  | 
 |   File::Info info; | 
 |   GetFileInfo(test_path(), &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(test_path(), kTestData, 10)); | 
 |   File::Info info; | 
 |   GetFileInfo(test_path(), &info); | 
 |   ASSERT_EQ(10, info.size); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); | 
 |   proxy.SetLength(7, | 
 |                   Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   // Verify. | 
 |   GetFileInfo(test_path(), &info); | 
 |   ASSERT_EQ(7, info.size); | 
 |  | 
 |   char buffer[7]; | 
 |   EXPECT_EQ(7, base::ReadFile(test_path(), 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(test_path(), kTestData, 10)); | 
 |   File::Info info; | 
 |   GetFileInfo(test_path(), &info); | 
 |   ASSERT_EQ(10, info.size); | 
 |  | 
 |   // Run. | 
 |   FileProxy proxy(file_task_runner()); | 
 |   CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); | 
 |   proxy.SetLength(53, | 
 |                   Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); | 
 |   RunLoop().Run(); | 
 |  | 
 |   // Verify. | 
 |   GetFileInfo(test_path(), &info); | 
 |   ASSERT_EQ(53, info.size); | 
 |  | 
 |   char buffer[53]; | 
 |   EXPECT_EQ(53, base::ReadFile(test_path(), 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 |