|  | // Copyright 2015 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 <stddef.h> | 
|  | #include <stdint.h> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/files/file.h" | 
|  | #include "components/filesystem/files_test_base.h" | 
|  | #include "mojo/public/cpp/bindings/interface_request.h" | 
|  | #include "mojo/public/cpp/bindings/type_converter.h" | 
|  |  | 
|  | namespace filesystem { | 
|  | namespace { | 
|  |  | 
|  | using FileImplTest = FilesTestBase; | 
|  |  | 
|  | TEST_F(FileImplTest, CreateWriteCloseRenameOpenRead) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  | bool handled = false; | 
|  |  | 
|  | { | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write; | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('h')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('e')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  |  | 
|  | // Close it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Close((&error)); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | // Rename it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = directory->Rename("my_file", "your_file", &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | { | 
|  | // Open my_file again. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("your_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagOpen, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Read from it. | 
|  | base::Optional<std::vector<uint8_t>> bytes_read; | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Read(3, 1, mojom::Whence::FROM_BEGIN, &error, &bytes_read); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_TRUE(bytes_read.has_value()); | 
|  | ASSERT_EQ(3u, bytes_read.value().size()); | 
|  | EXPECT_EQ(static_cast<uint8_t>('e'), bytes_read.value()[0]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('l'), bytes_read.value()[1]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('l'), bytes_read.value()[2]); | 
|  | } | 
|  |  | 
|  | // TODO(vtl): Test read/write offset options. | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, CantWriteInReadMode) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | std::vector<uint8_t> bytes_to_write; | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('h')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('e')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  |  | 
|  | { | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  |  | 
|  | // Close it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Close((&error)); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Open my_file again, this time with read only mode. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagOpen, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Try to write in read mode; it should fail. | 
|  | error = mojom::FileError::OK; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  |  | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::FAILED, error); | 
|  | EXPECT_EQ(0u, num_bytes_written); | 
|  |  | 
|  | // Close it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Close((&error)); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, OpenInAppendMode) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | { | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write; | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('h')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('e')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  |  | 
|  | // Close it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Close(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Append to my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagAppend | mojom::kFlagOpen, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write; | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('g')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('d')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('b')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('y')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('e')); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  |  | 
|  | // Close it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Close((&error)); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Open my_file again. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagOpen, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Read from it. | 
|  | base::Optional<std::vector<uint8_t>> bytes_read; | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Read(12, 0, mojom::Whence::FROM_BEGIN, &error, &bytes_read); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_TRUE(bytes_read.has_value()); | 
|  | ASSERT_EQ(12u, bytes_read.value().size()); | 
|  | EXPECT_EQ(static_cast<uint8_t>('l'), bytes_read.value()[3]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('o'), bytes_read.value()[4]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('g'), bytes_read.value()[5]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('o'), bytes_read.value()[6]); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, OpenInTruncateMode) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | { | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write; | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('h')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('e')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  |  | 
|  | // Close it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Close(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Append to my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = directory->OpenFile( | 
|  | "my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagOpenTruncated, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write; | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('g')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('d')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('b')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('y')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('e')); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  |  | 
|  | // Close it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Close(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Open my_file again. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagOpen, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Read from it. | 
|  | base::Optional<std::vector<uint8_t>> bytes_read; | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Read(7, 0, mojom::Whence::FROM_BEGIN, &error, &bytes_read); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_TRUE(bytes_read.has_value()); | 
|  | ASSERT_EQ(7u, bytes_read.value().size()); | 
|  | EXPECT_EQ(static_cast<uint8_t>('g'), bytes_read.value()[0]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('o'), bytes_read.value()[1]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('o'), bytes_read.value()[2]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('d'), bytes_read.value()[3]); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Note: Ignore nanoseconds, since it may not always be supported. We expect at | 
|  | // least second-resolution support though. | 
|  | TEST_F(FileImplTest, StatTouch) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Stat it. | 
|  | error = mojom::FileError::FAILED; | 
|  | mojom::FileInformationPtr file_info; | 
|  | handled = file->Stat(&error, &file_info); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_FALSE(file_info.is_null()); | 
|  | EXPECT_EQ(mojom::FsFileType::REGULAR_FILE, file_info->type); | 
|  | EXPECT_EQ(0, file_info->size); | 
|  | EXPECT_GT(file_info->atime, 0);  // Expect that it's not 1970-01-01. | 
|  | EXPECT_GT(file_info->mtime, 0); | 
|  | double first_mtime = file_info->mtime; | 
|  |  | 
|  | // Touch only the atime. | 
|  | error = mojom::FileError::FAILED; | 
|  | mojom::TimespecOrNowPtr t(mojom::TimespecOrNow::New()); | 
|  | t->now = false; | 
|  | const int64_t kPartyTime1 = 1234567890;  // Party like it's 2009-02-13. | 
|  | t->seconds = kPartyTime1; | 
|  | handled = file->Touch(std::move(t), nullptr, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Stat again. | 
|  | error = mojom::FileError::FAILED; | 
|  | file_info.reset(); | 
|  | handled = file->Stat(&error, &file_info); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_FALSE(file_info.is_null()); | 
|  | EXPECT_EQ(kPartyTime1, file_info->atime); | 
|  | EXPECT_EQ(first_mtime, file_info->mtime); | 
|  |  | 
|  | // Touch only the mtime. | 
|  | t = mojom::TimespecOrNow::New(); | 
|  | t->now = false; | 
|  | const int64_t kPartyTime2 = 1425059525;  // No time like the present. | 
|  | t->seconds = kPartyTime2; | 
|  | handled = file->Touch(nullptr, std::move(t), &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Stat again. | 
|  | error = mojom::FileError::FAILED; | 
|  | file_info.reset(); | 
|  | handled = file->Stat(&error, &file_info); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_FALSE(file_info.is_null()); | 
|  | EXPECT_EQ(kPartyTime1, file_info->atime); | 
|  | EXPECT_EQ(kPartyTime2, file_info->mtime); | 
|  |  | 
|  | // TODO(vtl): Also test non-zero file size. | 
|  | // TODO(vtl): Also test Touch() "now" options. | 
|  | // TODO(vtl): Also test touching both atime and mtime. | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, TellSeek) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write(1000, '!'); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, &error, | 
|  | &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  | const int size = static_cast<int>(num_bytes_written); | 
|  |  | 
|  | // Tell. | 
|  | error = mojom::FileError::FAILED; | 
|  | int64_t position = -1; | 
|  | handled = file->Tell(&error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | // Should be at the end. | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(size, position); | 
|  |  | 
|  | // Seek back 100. | 
|  | error = mojom::FileError::FAILED; | 
|  | position = -1; | 
|  | handled = file->Seek(-100, mojom::Whence::FROM_CURRENT, &error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(size - 100, position); | 
|  |  | 
|  | // Tell. | 
|  | error = mojom::FileError::FAILED; | 
|  | position = -1; | 
|  | handled = file->Tell(&error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(size - 100, position); | 
|  |  | 
|  | // Seek to 123 from start. | 
|  | error = mojom::FileError::FAILED; | 
|  | position = -1; | 
|  | handled = file->Seek(123, mojom::Whence::FROM_BEGIN, &error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(123, position); | 
|  |  | 
|  | // Tell. | 
|  | error = mojom::FileError::FAILED; | 
|  | position = -1; | 
|  | handled = file->Tell(&error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(123, position); | 
|  |  | 
|  | // Seek to 123 back from end. | 
|  | error = mojom::FileError::FAILED; | 
|  | position = -1; | 
|  | handled = file->Seek(-123, mojom::Whence::FROM_END, &error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(size - 123, position); | 
|  |  | 
|  | // Tell. | 
|  | error = mojom::FileError::FAILED; | 
|  | position = -1; | 
|  | handled = file->Tell(&error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(size - 123, position); | 
|  |  | 
|  | // TODO(vtl): Check that seeking actually affects reading/writing. | 
|  | // TODO(vtl): Check that seeking can extend the file? | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, Dup) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | // Create my_file. | 
|  | mojom::FilePtr file1; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = directory->OpenFile( | 
|  | "my_file", MakeRequest(&file1), | 
|  | mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write; | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('h')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('e')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file1->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, &error, | 
|  | &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(bytes_to_write.size(), num_bytes_written); | 
|  | const int end_hello_pos = static_cast<int>(num_bytes_written); | 
|  |  | 
|  | // Dup it. | 
|  | mojom::FilePtr file2; | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file1->Dup(MakeRequest(&file2), &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // |file2| should have the same position. | 
|  | error = mojom::FileError::FAILED; | 
|  | int64_t position = -1; | 
|  | handled = file2->Tell(&error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(end_hello_pos, position); | 
|  |  | 
|  | // Write using |file2|. | 
|  | std::vector<uint8_t> more_bytes_to_write; | 
|  | more_bytes_to_write.push_back(static_cast<uint8_t>('w')); | 
|  | more_bytes_to_write.push_back(static_cast<uint8_t>('o')); | 
|  | more_bytes_to_write.push_back(static_cast<uint8_t>('r')); | 
|  | more_bytes_to_write.push_back(static_cast<uint8_t>('l')); | 
|  | more_bytes_to_write.push_back(static_cast<uint8_t>('d')); | 
|  | error = mojom::FileError::FAILED; | 
|  | num_bytes_written = 0; | 
|  | handled = file2->Write(more_bytes_to_write, 0, mojom::Whence::FROM_CURRENT, | 
|  | &error, &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(more_bytes_to_write.size(), num_bytes_written); | 
|  | const int end_world_pos = end_hello_pos + static_cast<int>(num_bytes_written); | 
|  |  | 
|  | // |file1| should have the same position. | 
|  | error = mojom::FileError::FAILED; | 
|  | position = -1; | 
|  | handled = file1->Tell(&error, &position); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(end_world_pos, position); | 
|  |  | 
|  | // Close |file1|. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file1->Close(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Read everything using |file2|. | 
|  | base::Optional<std::vector<uint8_t>> bytes_read; | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = | 
|  | file2->Read(1000, 0, mojom::Whence::FROM_BEGIN, &error, &bytes_read); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_TRUE(bytes_read.has_value()); | 
|  | ASSERT_EQ(static_cast<size_t>(end_world_pos), bytes_read.value().size()); | 
|  | // Just check the first and last bytes. | 
|  | EXPECT_EQ(static_cast<uint8_t>('h'), bytes_read.value()[0]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('d'), bytes_read.value()[end_world_pos - 1]); | 
|  |  | 
|  | // TODO(vtl): Test that |file2| has the same open options as |file1|. | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, Truncate) { | 
|  | const uint32_t kInitialSize = 1000; | 
|  | const uint32_t kTruncatedSize = 654; | 
|  |  | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file), | 
|  | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Write to it. | 
|  | std::vector<uint8_t> bytes_to_write(kInitialSize, '!'); | 
|  | error = mojom::FileError::FAILED; | 
|  | uint32_t num_bytes_written = 0; | 
|  | handled = file->Write(bytes_to_write, 0, mojom::Whence::FROM_CURRENT, &error, | 
|  | &num_bytes_written); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | EXPECT_EQ(kInitialSize, num_bytes_written); | 
|  |  | 
|  | // Stat it. | 
|  | error = mojom::FileError::FAILED; | 
|  | mojom::FileInformationPtr file_info; | 
|  | handled = file->Stat(&error, &file_info); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_FALSE(file_info.is_null()); | 
|  | EXPECT_EQ(kInitialSize, file_info->size); | 
|  |  | 
|  | // Truncate it. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Truncate(kTruncatedSize, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Stat again. | 
|  | error = mojom::FileError::FAILED; | 
|  | file_info.reset(); | 
|  | handled = file->Stat(&error, &file_info); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_FALSE(file_info.is_null()); | 
|  | EXPECT_EQ(kTruncatedSize, file_info->size); | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, AsHandle) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | { | 
|  | // Create my_file. | 
|  | mojom::FilePtr file1; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = directory->OpenFile( | 
|  | "my_file", MakeRequest(&file1), | 
|  | mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Fetch the file. | 
|  | error = mojom::FileError::FAILED; | 
|  | base::File raw_file; | 
|  | handled = file1->AsHandle(&error, &raw_file); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | ASSERT_TRUE(raw_file.IsValid()); | 
|  | EXPECT_EQ(5, raw_file.WriteAtCurrentPos("hello", 5)); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Reopen my_file. | 
|  | mojom::FilePtr file2; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = | 
|  | directory->OpenFile("my_file", MakeRequest(&file2), | 
|  | mojom::kFlagRead | mojom::kFlagOpen, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Verify that we wrote data raw on the file descriptor. | 
|  | base::Optional<std::vector<uint8_t>> bytes_read; | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file2->Read(5, 0, mojom::Whence::FROM_BEGIN, &error, &bytes_read); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | ASSERT_TRUE(bytes_read.has_value()); | 
|  | ASSERT_EQ(5u, bytes_read.value().size()); | 
|  | EXPECT_EQ(static_cast<uint8_t>('h'), bytes_read.value()[0]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('e'), bytes_read.value()[1]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('l'), bytes_read.value()[2]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('l'), bytes_read.value()[3]); | 
|  | EXPECT_EQ(static_cast<uint8_t>('o'), bytes_read.value()[4]); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, SimpleLockUnlock) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = directory->OpenFile( | 
|  | "my_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Lock the file. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Lock(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Unlock the file. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Unlock(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, CantDoubleLock) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = directory->OpenFile( | 
|  | "my_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Lock the file. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Lock(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Lock the file again. | 
|  | error = mojom::FileError::OK; | 
|  | handled = file->Lock(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::FAILED, error); | 
|  | } | 
|  |  | 
|  | TEST_F(FileImplTest, ClosingFileClearsLock) { | 
|  | mojom::DirectoryPtr directory; | 
|  | GetTemporaryRoot(&directory); | 
|  | mojom::FileError error; | 
|  |  | 
|  | { | 
|  | // Create my_file. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = directory->OpenFile( | 
|  | "my_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagOpenAlways, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // Lock the file. | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Lock(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Open the file again. | 
|  | mojom::FilePtr file; | 
|  | error = mojom::FileError::FAILED; | 
|  | bool handled = directory->OpenFile( | 
|  | "my_file", MakeRequest(&file), | 
|  | mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagOpenAlways, &error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  |  | 
|  | // The file shouldn't be locked (and we check by trying to lock it). | 
|  | error = mojom::FileError::FAILED; | 
|  | handled = file->Lock(&error); | 
|  | ASSERT_TRUE(handled); | 
|  | EXPECT_EQ(mojom::FileError::OK, error); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace filesystem |