// 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 #include #include #include #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 bytes_to_write; bytes_to_write.push_back(static_cast('h')); bytes_to_write.push_back(static_cast('e')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('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> 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('e'), bytes_read.value()[0]); EXPECT_EQ(static_cast('l'), bytes_read.value()[1]); EXPECT_EQ(static_cast('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 bytes_to_write; bytes_to_write.push_back(static_cast('h')); bytes_to_write.push_back(static_cast('e')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('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 bytes_to_write; bytes_to_write.push_back(static_cast('h')); bytes_to_write.push_back(static_cast('e')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('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 bytes_to_write; bytes_to_write.push_back(static_cast('g')); bytes_to_write.push_back(static_cast('o')); bytes_to_write.push_back(static_cast('o')); bytes_to_write.push_back(static_cast('d')); bytes_to_write.push_back(static_cast('b')); bytes_to_write.push_back(static_cast('y')); bytes_to_write.push_back(static_cast('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> 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('l'), bytes_read.value()[3]); EXPECT_EQ(static_cast('o'), bytes_read.value()[4]); EXPECT_EQ(static_cast('g'), bytes_read.value()[5]); EXPECT_EQ(static_cast('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 bytes_to_write; bytes_to_write.push_back(static_cast('h')); bytes_to_write.push_back(static_cast('e')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('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 bytes_to_write; bytes_to_write.push_back(static_cast('g')); bytes_to_write.push_back(static_cast('o')); bytes_to_write.push_back(static_cast('o')); bytes_to_write.push_back(static_cast('d')); bytes_to_write.push_back(static_cast('b')); bytes_to_write.push_back(static_cast('y')); bytes_to_write.push_back(static_cast('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> 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('g'), bytes_read.value()[0]); EXPECT_EQ(static_cast('o'), bytes_read.value()[1]); EXPECT_EQ(static_cast('o'), bytes_read.value()[2]); EXPECT_EQ(static_cast('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 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(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 bytes_to_write; bytes_to_write.push_back(static_cast('h')); bytes_to_write.push_back(static_cast('e')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('l')); bytes_to_write.push_back(static_cast('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(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 more_bytes_to_write; more_bytes_to_write.push_back(static_cast('w')); more_bytes_to_write.push_back(static_cast('o')); more_bytes_to_write.push_back(static_cast('r')); more_bytes_to_write.push_back(static_cast('l')); more_bytes_to_write.push_back(static_cast('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(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> 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(end_world_pos), bytes_read.value().size()); // Just check the first and last bytes. EXPECT_EQ(static_cast('h'), bytes_read.value()[0]); EXPECT_EQ(static_cast('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 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> 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('h'), bytes_read.value()[0]); EXPECT_EQ(static_cast('e'), bytes_read.value()[1]); EXPECT_EQ(static_cast('l'), bytes_read.value()[2]); EXPECT_EQ(static_cast('l'), bytes_read.value()[3]); EXPECT_EQ(static_cast('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