|  | // 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 <stdint.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <set> | 
|  |  | 
|  | #include "base/files/file.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/files/scoped_temp_dir.h" | 
|  | #include "base/macros.h" | 
|  | #include "storage/browser/fileapi/native_file_util.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using storage::FileSystemFileUtil; | 
|  | using storage::FileSystemOperation; | 
|  | using storage::NativeFileUtil; | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | class NativeFileUtilTest : public testing::Test { | 
|  | public: | 
|  | NativeFileUtilTest() {} | 
|  |  | 
|  | void SetUp() override { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); } | 
|  |  | 
|  | protected: | 
|  | base::FilePath Path() { | 
|  | return data_dir_.path(); | 
|  | } | 
|  |  | 
|  | base::FilePath Path(const char* file_name) { | 
|  | return data_dir_.path().AppendASCII(file_name); | 
|  | } | 
|  |  | 
|  | bool FileExists(const base::FilePath& path) { | 
|  | return base::PathExists(path) && | 
|  | !base::DirectoryExists(path); | 
|  | } | 
|  |  | 
|  | int64_t GetSize(const base::FilePath& path) { | 
|  | base::File::Info info; | 
|  | base::GetFileInfo(path, &info); | 
|  | return info.size; | 
|  | } | 
|  |  | 
|  | private: | 
|  | base::ScopedTempDir data_dir_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(NativeFileUtilTest); | 
|  | }; | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, CreateCloseAndDeleteFile) { | 
|  | base::FilePath file_name = Path("test_file"); | 
|  | int flags = base::File::FLAG_WRITE | base::File::FLAG_ASYNC; | 
|  | base::File file = | 
|  | NativeFileUtil::CreateOrOpen(file_name, base::File::FLAG_CREATE | flags); | 
|  | ASSERT_TRUE(file.IsValid()); | 
|  | ASSERT_TRUE(file.created()); | 
|  |  | 
|  | EXPECT_TRUE(base::PathExists(file_name)); | 
|  | EXPECT_TRUE(NativeFileUtil::PathExists(file_name)); | 
|  | EXPECT_EQ(0, GetSize(file_name)); | 
|  | file.Close(); | 
|  |  | 
|  | file = NativeFileUtil::CreateOrOpen(file_name, base::File::FLAG_OPEN | flags); | 
|  | ASSERT_TRUE(file.IsValid()); | 
|  | ASSERT_FALSE(file.created()); | 
|  | file.Close(); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::DeleteFile(file_name)); | 
|  | EXPECT_FALSE(base::PathExists(file_name)); | 
|  | EXPECT_FALSE(NativeFileUtil::PathExists(file_name)); | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, EnsureFileExists) { | 
|  | base::FilePath file_name = Path("foobar"); | 
|  | bool created = false; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(file_name, &created)); | 
|  | ASSERT_TRUE(created); | 
|  |  | 
|  | EXPECT_TRUE(FileExists(file_name)); | 
|  | EXPECT_EQ(0, GetSize(file_name)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(file_name, &created)); | 
|  | EXPECT_FALSE(created); | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, CreateAndDeleteDirectory) { | 
|  | base::FilePath dir_name = Path("test_dir"); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CreateDirectory(dir_name, | 
|  | false /* exclusive */, | 
|  | false /* recursive */)); | 
|  |  | 
|  | EXPECT_TRUE(NativeFileUtil::DirectoryExists(dir_name)); | 
|  | EXPECT_TRUE(base::DirectoryExists(dir_name)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_ERROR_EXISTS, | 
|  | NativeFileUtil::CreateDirectory(dir_name, | 
|  | true /* exclusive */, | 
|  | false /* recursive */)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::DeleteDirectory(dir_name)); | 
|  | EXPECT_FALSE(base::DirectoryExists(dir_name)); | 
|  | EXPECT_FALSE(NativeFileUtil::DirectoryExists(dir_name)); | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, TouchFileAndGetFileInfo) { | 
|  | base::FilePath file_name = Path("test_file"); | 
|  | base::File::Info native_info; | 
|  | ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND, | 
|  | NativeFileUtil::GetFileInfo(file_name, &native_info)); | 
|  |  | 
|  | bool created = false; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(file_name, &created)); | 
|  | ASSERT_TRUE(created); | 
|  |  | 
|  | base::File::Info info; | 
|  | ASSERT_TRUE(base::GetFileInfo(file_name, &info)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::GetFileInfo(file_name, &native_info)); | 
|  | ASSERT_EQ(info.size, native_info.size); | 
|  | ASSERT_EQ(info.is_directory, native_info.is_directory); | 
|  | ASSERT_EQ(info.is_symbolic_link, native_info.is_symbolic_link); | 
|  | ASSERT_EQ(info.last_modified, native_info.last_modified); | 
|  | ASSERT_EQ(info.last_accessed, native_info.last_accessed); | 
|  | ASSERT_EQ(info.creation_time, native_info.creation_time); | 
|  |  | 
|  | const base::Time new_accessed = | 
|  | info.last_accessed + base::TimeDelta::FromHours(10); | 
|  | const base::Time new_modified = | 
|  | info.last_modified + base::TimeDelta::FromHours(5); | 
|  |  | 
|  | EXPECT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::Touch(file_name, | 
|  | new_accessed, new_modified)); | 
|  |  | 
|  | ASSERT_TRUE(base::GetFileInfo(file_name, &info)); | 
|  | EXPECT_EQ(new_accessed, info.last_accessed); | 
|  | EXPECT_EQ(new_modified, info.last_modified); | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, CreateFileEnumerator) { | 
|  | base::FilePath path_1 = Path("dir1"); | 
|  | base::FilePath path_2 = Path("file1"); | 
|  | base::FilePath path_11 = Path("dir1").AppendASCII("file11"); | 
|  | base::FilePath path_12 = Path("dir1").AppendASCII("dir12"); | 
|  | base::FilePath path_121 = | 
|  | Path("dir1").AppendASCII("dir12").AppendASCII("file121"); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CreateDirectory(path_1, false, false)); | 
|  | bool created = false; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(path_2, &created)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(path_11, &created)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CreateDirectory(path_12, false, false)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(path_121, &created)); | 
|  |  | 
|  | { | 
|  | std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator = | 
|  | NativeFileUtil::CreateFileEnumerator(Path(), false); | 
|  | std::set<base::FilePath> set; | 
|  | set.insert(path_1); | 
|  | set.insert(path_2); | 
|  | for (base::FilePath path = enumerator->Next(); !path.empty(); | 
|  | path = enumerator->Next()) | 
|  | EXPECT_EQ(1U, set.erase(path)); | 
|  | EXPECT_TRUE(set.empty()); | 
|  | } | 
|  |  | 
|  | { | 
|  | std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator = | 
|  | NativeFileUtil::CreateFileEnumerator(Path(), true); | 
|  | std::set<base::FilePath> set; | 
|  | set.insert(path_1); | 
|  | set.insert(path_2); | 
|  | set.insert(path_11); | 
|  | set.insert(path_12); | 
|  | set.insert(path_121); | 
|  | for (base::FilePath path = enumerator->Next(); !path.empty(); | 
|  | path = enumerator->Next()) | 
|  | EXPECT_EQ(1U, set.erase(path)); | 
|  | EXPECT_TRUE(set.empty()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, Truncate) { | 
|  | base::FilePath file_name = Path("truncated"); | 
|  | bool created = false; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(file_name, &created)); | 
|  | ASSERT_TRUE(created); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::Truncate(file_name, 1020)); | 
|  |  | 
|  | EXPECT_TRUE(FileExists(file_name)); | 
|  | EXPECT_EQ(1020, GetSize(file_name)); | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, CopyFile) { | 
|  | base::FilePath from_file = Path("fromfile"); | 
|  | base::FilePath to_file1 = Path("tofile1"); | 
|  | base::FilePath to_file2 = Path("tofile2"); | 
|  | const NativeFileUtil::CopyOrMoveMode nosync = NativeFileUtil::COPY_NOSYNC; | 
|  | const NativeFileUtil::CopyOrMoveMode sync = NativeFileUtil::COPY_SYNC; | 
|  | bool created = false; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(from_file, &created)); | 
|  | ASSERT_TRUE(created); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::Truncate(from_file, 1020)); | 
|  |  | 
|  | EXPECT_TRUE(FileExists(from_file)); | 
|  | EXPECT_EQ(1020, GetSize(from_file)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_file1, FileSystemOperation::OPTION_NONE, nosync)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_file2, FileSystemOperation::OPTION_NONE, sync)); | 
|  |  | 
|  | EXPECT_TRUE(FileExists(from_file)); | 
|  | EXPECT_EQ(1020, GetSize(from_file)); | 
|  | EXPECT_TRUE(FileExists(to_file1)); | 
|  | EXPECT_EQ(1020, GetSize(to_file1)); | 
|  | EXPECT_TRUE(FileExists(to_file2)); | 
|  | EXPECT_EQ(1020, GetSize(to_file2)); | 
|  |  | 
|  | base::FilePath dir = Path("dir"); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CreateDirectory(dir, false, false)); | 
|  | ASSERT_TRUE(base::DirectoryExists(dir)); | 
|  | base::FilePath to_dir_file = dir.AppendASCII("file"); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_dir_file, | 
|  | FileSystemOperation::OPTION_NONE, nosync)); | 
|  | EXPECT_TRUE(FileExists(to_dir_file)); | 
|  | EXPECT_EQ(1020, GetSize(to_dir_file)); | 
|  |  | 
|  | // Following tests are error checking. | 
|  | // Source doesn't exist. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | Path("nonexists"), Path("file"), | 
|  | FileSystemOperation::OPTION_NONE, nosync)); | 
|  |  | 
|  | // Source is not a file. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | dir, Path("file"), FileSystemOperation::OPTION_NONE, nosync)); | 
|  | // Destination is not a file. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, dir, FileSystemOperation::OPTION_NONE, nosync)); | 
|  | // Destination's parent doesn't exist. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, Path("nodir").AppendASCII("file"), | 
|  | FileSystemOperation::OPTION_NONE, nosync)); | 
|  | // Destination's parent is a file. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, Path("tofile1").AppendASCII("file"), | 
|  | FileSystemOperation::OPTION_NONE, nosync)); | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, MoveFile) { | 
|  | base::FilePath from_file = Path("fromfile"); | 
|  | base::FilePath to_file = Path("tofile"); | 
|  | const NativeFileUtil::CopyOrMoveMode move = NativeFileUtil::MOVE; | 
|  | bool created = false; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(from_file, &created)); | 
|  | ASSERT_TRUE(created); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::Truncate(from_file, 1020)); | 
|  |  | 
|  | EXPECT_TRUE(FileExists(from_file)); | 
|  | EXPECT_EQ(1020, GetSize(from_file)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_file, FileSystemOperation::OPTION_NONE, move)); | 
|  |  | 
|  | EXPECT_FALSE(FileExists(from_file)); | 
|  | EXPECT_TRUE(FileExists(to_file)); | 
|  | EXPECT_EQ(1020, GetSize(to_file)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(from_file, &created)); | 
|  | ASSERT_TRUE(FileExists(from_file)); | 
|  | ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::Truncate(from_file, 1020)); | 
|  |  | 
|  | base::FilePath dir = Path("dir"); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CreateDirectory(dir, false, false)); | 
|  | ASSERT_TRUE(base::DirectoryExists(dir)); | 
|  | base::FilePath to_dir_file = dir.AppendASCII("file"); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_dir_file, | 
|  | FileSystemOperation::OPTION_NONE, move)); | 
|  | EXPECT_FALSE(FileExists(from_file)); | 
|  | EXPECT_TRUE(FileExists(to_dir_file)); | 
|  | EXPECT_EQ(1020, GetSize(to_dir_file)); | 
|  |  | 
|  | // Following is error checking. | 
|  | // Source doesn't exist. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | Path("nonexists"), Path("file"), | 
|  | FileSystemOperation::OPTION_NONE, move)); | 
|  |  | 
|  | // Source is not a file. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | dir, Path("file"), FileSystemOperation::OPTION_NONE, move)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(from_file, &created)); | 
|  | ASSERT_TRUE(FileExists(from_file)); | 
|  | // Destination is not a file. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, dir, FileSystemOperation::OPTION_NONE, move)); | 
|  |  | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(from_file, &created)); | 
|  | ASSERT_TRUE(FileExists(from_file)); | 
|  | // Destination's parent doesn't exist. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, Path("nodir").AppendASCII("file"), | 
|  | FileSystemOperation::OPTION_NONE, move)); | 
|  | // Destination's parent is a file. | 
|  | EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, Path("tofile1").AppendASCII("file"), | 
|  | FileSystemOperation::OPTION_NONE, move)); | 
|  | } | 
|  |  | 
|  | TEST_F(NativeFileUtilTest, PreserveLastModified) { | 
|  | base::FilePath from_file = Path("fromfile"); | 
|  | base::FilePath to_file1 = Path("tofile1"); | 
|  | base::FilePath to_file2 = Path("tofile2"); | 
|  | base::FilePath to_file3 = Path("tofile3"); | 
|  | bool created = false; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::EnsureFileExists(from_file, &created)); | 
|  | ASSERT_TRUE(created); | 
|  | EXPECT_TRUE(FileExists(from_file)); | 
|  |  | 
|  | base::File::Info file_info1; | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::GetFileInfo(from_file, &file_info1)); | 
|  |  | 
|  | // Test for copy (nosync). | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_file1, | 
|  | FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, | 
|  | NativeFileUtil::COPY_NOSYNC)); | 
|  |  | 
|  | base::File::Info file_info2; | 
|  | ASSERT_TRUE(FileExists(to_file1)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::GetFileInfo(to_file1, &file_info2)); | 
|  | EXPECT_EQ(file_info1.last_modified, file_info2.last_modified); | 
|  |  | 
|  | // Test for copy (sync). | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_file2, | 
|  | FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, | 
|  | NativeFileUtil::COPY_SYNC)); | 
|  |  | 
|  | ASSERT_TRUE(FileExists(to_file2)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::GetFileInfo(to_file1, &file_info2)); | 
|  | EXPECT_EQ(file_info1.last_modified, file_info2.last_modified); | 
|  |  | 
|  | // Test for move. | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::CopyOrMoveFile( | 
|  | from_file, to_file3, | 
|  | FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, | 
|  | NativeFileUtil::MOVE)); | 
|  |  | 
|  | ASSERT_TRUE(FileExists(to_file3)); | 
|  | ASSERT_EQ(base::File::FILE_OK, | 
|  | NativeFileUtil::GetFileInfo(to_file2, &file_info2)); | 
|  | EXPECT_EQ(file_info1.last_modified, file_info2.last_modified); | 
|  | } | 
|  |  | 
|  | }  // namespace content |