blob: 4dc992cca15271911721553da8e8c7d2e9bf98db [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "lib/vfs/cpp/pseudo_file.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/limits.h>
#include <unistd.h>
#include <zircon/errors.h>
#include <zircon/processargs.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "fuchsia/io/cpp/fidl.h"
#include "lib/gtest/real_loop_fixture.h"
namespace {
class FileWrapper {
public:
const std::string& buffer() { return buffer_; };
vfs::PseudoFile* file() { return file_.get(); };
static FileWrapper CreateReadWriteFile(std::string initial_str,
size_t capacity,
bool start_loop = true) {
return FileWrapper(true, initial_str, capacity, start_loop);
}
static FileWrapper CreateReadOnlyFile(std::string initial_str,
bool start_loop = true) {
return FileWrapper(false, initial_str, initial_str.length(), start_loop);
}
async_dispatcher_t* dispatcher() { return loop_.dispatcher(); }
async::Loop& loop() { return loop_; }
private:
FileWrapper(bool write_allowed, std::string initial_str, size_t capacity,
bool start_loop)
: buffer_(std::move(initial_str)),
loop_(&kAsyncLoopConfigNoAttachToThread) {
auto readFn = [this](std::vector<uint8_t>* output, size_t max_file_size) {
output->resize(buffer_.length());
std::copy(buffer_.begin(), buffer_.end(), output->begin());
return ZX_OK;
};
vfs::PseudoFile::WriteHandler writeFn;
if (write_allowed) {
writeFn = [this](std::vector<uint8_t> input) {
std::string str(input.size(), 0);
std::copy(input.begin(), input.begin() + input.size(), str.begin());
buffer_ = std::move(str);
return ZX_OK;
};
}
file_ = std::make_unique<vfs::PseudoFile>(capacity, std::move(readFn),
std::move(writeFn));
if (start_loop) {
loop_.StartThread("vfs test thread");
}
}
std::unique_ptr<vfs::PseudoFile> file_;
std::string buffer_;
async::Loop loop_;
};
class PseudoFileTest : public gtest::RealLoopFixture {
protected:
void AssertOpen(vfs::internal::Node* node, async_dispatcher_t* dispatcher,
uint32_t flags, zx_status_t expected_status,
bool test_on_open_event = true) {
fuchsia::io::NodePtr node_ptr;
if (test_on_open_event) {
flags |= fuchsia::io::OPEN_FLAG_DESCRIBE;
}
EXPECT_EQ(
expected_status,
node->Serve(flags, node_ptr.NewRequest().TakeChannel(), dispatcher));
if (test_on_open_event) {
bool on_open_called = false;
node_ptr.events().OnOpen =
[&](zx_status_t status, std::unique_ptr<fuchsia::io::NodeInfo> info) {
EXPECT_FALSE(on_open_called); // should be called only once
on_open_called = true;
EXPECT_EQ(expected_status, status);
if (expected_status == ZX_OK) {
ASSERT_NE(info.get(), nullptr);
EXPECT_TRUE(info->is_file());
} else {
EXPECT_EQ(info.get(), nullptr);
}
};
RunLoopUntil([&]() { return on_open_called; });
}
}
fuchsia::io::FileSyncPtr OpenReadWrite(vfs::internal::Node* node,
async_dispatcher_t* dispatcher) {
return OpenFile(
node,
fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE,
dispatcher);
}
fuchsia::io::FileSyncPtr OpenRead(vfs::internal::Node* node,
async_dispatcher_t* dispatcher) {
return OpenFile(node, fuchsia::io::OPEN_RIGHT_READABLE, dispatcher);
}
fuchsia::io::FileSyncPtr OpenFile(vfs::internal::Node* node, uint32_t flags,
async_dispatcher_t* dispatcher) {
fuchsia::io::FileSyncPtr ptr;
node->Serve(flags, ptr.NewRequest().TakeChannel(), dispatcher);
return ptr;
}
void AssertWriteAt(fuchsia::io::FileSyncPtr& file, const std::string& str,
int offset, zx_status_t expected_status = ZX_OK,
int expected_actual = -1) {
zx_status_t status;
uint64_t actual;
std::vector<uint8_t> buffer;
buffer.resize(str.length());
std::copy(str.begin(), str.end(), buffer.begin());
file->WriteAt(buffer, offset, &status, &actual);
ASSERT_EQ(expected_status, status);
ASSERT_EQ(expected_actual == -1 ? str.length() : expected_actual, actual);
}
void AssertWrite(fuchsia::io::FileSyncPtr& file, const std::string& str,
zx_status_t expected_status = ZX_OK,
int expected_actual = -1) {
zx_status_t status;
uint64_t actual;
std::vector<uint8_t> buffer;
buffer.resize(str.length());
std::copy(str.begin(), str.end(), buffer.begin());
file->Write(buffer, &status, &actual);
ASSERT_EQ(expected_status, status);
ASSERT_EQ(expected_actual == -1 ? str.length() : expected_actual, actual);
}
void AssertReadAt(fuchsia::io::FileSyncPtr& file, int offset, int count,
const std::string& expected_str,
zx_status_t expected_status = ZX_OK) {
zx_status_t status;
std::vector<uint8_t> buffer;
file->ReadAt(count, offset, &status, &buffer);
ASSERT_EQ(expected_status, status);
std::string str(buffer.size(), 0);
std::copy(buffer.begin(), buffer.end(), str.begin());
ASSERT_EQ(expected_str, str);
}
void AssertRead(fuchsia::io::FileSyncPtr& file, int count,
const std::string& expected_str,
zx_status_t expected_status = ZX_OK) {
zx_status_t status;
std::vector<uint8_t> buffer;
file->Read(count, &status, &buffer);
ASSERT_EQ(expected_status, status);
std::string str(buffer.size(), 0);
std::copy(buffer.begin(), buffer.end(), str.begin());
ASSERT_EQ(expected_str, str);
}
void AssertTruncate(fuchsia::io::FileSyncPtr& file, int count,
zx_status_t expected_status = ZX_OK) {
zx_status_t status;
file->Truncate(count, &status);
ASSERT_EQ(expected_status, status);
}
void AssertSeek(fuchsia::io::FileSyncPtr& file, int64_t offest,
fuchsia::io::SeekOrigin seek, uint64_t expected_offset,
zx_status_t expected_status = ZX_OK) {
zx_status_t status;
uint64_t new_offset;
file->Seek(offest, seek, &status, &new_offset);
ASSERT_EQ(expected_status, status);
ASSERT_EQ(expected_offset, new_offset);
}
void CloseFile(fuchsia::io::FileSyncPtr& file,
zx_status_t expected_status = ZX_OK) {
zx_status_t status = 1;
file->Close(&status);
EXPECT_EQ(expected_status, status);
}
void AssertFileWrapperState(FileWrapper& file_wrapper,
const std::string& expected_str) {
RunLoopUntil([&]() { return file_wrapper.buffer() == expected_str; });
}
int OpenAsFD(vfs::internal::Node* node, async_dispatcher_t* dispatcher) {
zx::channel local, remote;
EXPECT_EQ(ZX_OK, zx::channel::create(0, &local, &remote));
EXPECT_EQ(ZX_OK, node->Serve(fuchsia::io::OPEN_RIGHT_READABLE |
fuchsia::io::OPEN_RIGHT_WRITABLE,
std::move(remote), dispatcher));
int fd = -1;
EXPECT_EQ(ZX_OK, fdio_fd_create(local.release(), &fd));
return fd;
}
};
TEST_F(PseudoFileTest, ServeOnInValidFlagsForReadWriteFile) {
auto file_wrapper = FileWrapper::CreateReadWriteFile("test_str", 100, false);
{
SCOPED_TRACE("OPEN_FLAG_DIRECTORY");
AssertOpen(file_wrapper.file(), dispatcher(),
fuchsia::io::OPEN_FLAG_DIRECTORY, ZX_ERR_NOT_DIR);
}
uint32_t not_allowed_flags[] = {
fuchsia::io::OPEN_RIGHT_ADMIN, fuchsia::io::OPEN_FLAG_CREATE,
fuchsia::io::OPEN_FLAG_CREATE_IF_ABSENT, fuchsia::io::OPEN_FLAG_NO_REMOTE,
fuchsia::io::OPEN_FLAG_APPEND};
for (auto not_allowed_flag : not_allowed_flags) {
SCOPED_TRACE(std::to_string(not_allowed_flag));
AssertOpen(file_wrapper.file(), dispatcher(), not_allowed_flag,
ZX_ERR_NOT_SUPPORTED);
}
}
TEST_F(PseudoFileTest, ServeOnInValidFlagsForReadOnlyFile) {
auto file_wrapper = FileWrapper::CreateReadOnlyFile("test_str");
{
SCOPED_TRACE("OPEN_FLAG_DIRECTORY");
AssertOpen(file_wrapper.file(), dispatcher(),
fuchsia::io::OPEN_FLAG_DIRECTORY, ZX_ERR_NOT_DIR);
}
uint32_t not_allowed_flags[] = {fuchsia::io::OPEN_RIGHT_ADMIN,
fuchsia::io::OPEN_FLAG_CREATE,
fuchsia::io::OPEN_FLAG_CREATE_IF_ABSENT,
fuchsia::io::OPEN_FLAG_NO_REMOTE,
fuchsia::io::OPEN_RIGHT_WRITABLE,
fuchsia::io::OPEN_FLAG_TRUNCATE,
fuchsia::io::OPEN_FLAG_APPEND};
for (auto not_allowed_flag : not_allowed_flags) {
SCOPED_TRACE(std::to_string(not_allowed_flag));
AssertOpen(file_wrapper.file(), dispatcher(), not_allowed_flag,
ZX_ERR_NOT_SUPPORTED);
}
}
TEST_F(PseudoFileTest, ServeOnValidFlagsForReadWriteFile) {
auto file_wrapper = FileWrapper::CreateReadWriteFile("test_str", 100, false);
uint32_t allowed_flags[] = {
fuchsia::io::OPEN_RIGHT_READABLE, fuchsia::io::OPEN_RIGHT_WRITABLE,
fuchsia::io::OPEN_FLAG_NODE_REFERENCE, fuchsia::io::OPEN_FLAG_TRUNCATE};
for (auto allowed_flag : allowed_flags) {
SCOPED_TRACE(std::to_string(allowed_flag));
AssertOpen(file_wrapper.file(), dispatcher(), allowed_flag, ZX_OK);
}
}
TEST_F(PseudoFileTest, ServeOnValidFlagsForReadOnlyFile) {
auto file_wrapper = FileWrapper::CreateReadOnlyFile("test_str", false);
uint32_t allowed_flags[] = {fuchsia::io::OPEN_RIGHT_READABLE,
fuchsia::io::OPEN_FLAG_NODE_REFERENCE};
for (auto allowed_flag : allowed_flags) {
SCOPED_TRACE(std::to_string(allowed_flag));
AssertOpen(file_wrapper.file(), dispatcher(), allowed_flag, ZX_OK);
}
}
TEST_F(PseudoFileTest, Simple) {
auto file_wrapper = FileWrapper::CreateReadWriteFile("test_str", 100);
int fd = OpenAsFD(file_wrapper.file(), file_wrapper.dispatcher());
ASSERT_LE(0, fd);
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
ASSERT_EQ(5, pread(fd, buffer, 5, 0));
EXPECT_STREQ("test_", buffer);
ASSERT_EQ(4, write(fd, "abcd", 4));
ASSERT_EQ(5, pread(fd, buffer, 5, 0));
EXPECT_STREQ("abcd_", buffer);
ASSERT_GE(0, close(fd));
file_wrapper.loop().RunUntilIdle();
AssertFileWrapperState(file_wrapper, "abcd_str");
}
TEST_F(PseudoFileTest, WriteAt) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertWriteAt(file, "was", 5);
const std::string updated_str = "this wasa test string";
// confirm by reading
AssertRead(file, str.length(), updated_str);
// make sure file was not updated before conenction was closed.
ASSERT_EQ(file_wrapper.buffer(), str);
CloseFile(file);
AssertFileWrapperState(file_wrapper, updated_str);
}
TEST_F(PseudoFileTest, MultipleWriteAt) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertWriteAt(file, "was", 5);
AssertWriteAt(file, "tests", 10);
const std::string updated_str = "this wasa testsstring";
// confirm by reading
AssertRead(file, str.length(), updated_str);
// make sure file was not updated before conenction was closed.
ASSERT_EQ(file_wrapper.buffer(), str);
CloseFile(file);
AssertFileWrapperState(file_wrapper, updated_str);
}
TEST_F(PseudoFileTest, ReadAt) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertReadAt(file, 5, 10, str.substr(5, 10));
// try one more
AssertReadAt(file, 15, 5, str.substr(15, 5));
}
TEST_F(PseudoFileTest, Read) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertRead(file, 10, str.substr(0, 10));
// offset should have moved
AssertRead(file, 10, str.substr(10, 10));
}
TEST_F(PseudoFileTest, Write) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertWrite(file, "It");
// offset should have moved
AssertWrite(file, " is");
const std::string updated_str = "It isis a test string";
AssertReadAt(file, 0, 100, updated_str);
// make sure file was not updated before conenction was closed.
ASSERT_EQ(file_wrapper.buffer(), str);
CloseFile(file);
// make sure file was updated
AssertFileWrapperState(file_wrapper, updated_str);
}
TEST_F(PseudoFileTest, Truncate) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertTruncate(file, 10);
AssertRead(file, 100, str.substr(0, 10));
// make sure file was not updated before conenction was closed.
ASSERT_EQ(file_wrapper.buffer(), str);
CloseFile(file);
// make sure file was updated
AssertFileWrapperState(file_wrapper, str.substr(0, 10));
}
TEST_F(PseudoFileTest, SeekFromStart) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadOnlyFile(str);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertSeek(file, 5, fuchsia::io::SeekOrigin::START, 5);
AssertRead(file, 100, str.substr(5));
}
TEST_F(PseudoFileTest, SeekFromCurent) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadOnlyFile(str);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertSeek(file, 5, fuchsia::io::SeekOrigin::START, 5);
AssertSeek(file, 10, fuchsia::io::SeekOrigin::CURRENT, 15);
AssertRead(file, 100, str.substr(15));
}
TEST_F(PseudoFileTest, SeekFromEnd) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadOnlyFile(str);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertSeek(file, -2, fuchsia::io::SeekOrigin::END, str.length() - 2);
AssertRead(file, 100, str.substr(str.length() - 2));
}
TEST_F(PseudoFileTest, SeekFromEndWith0Offset) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadOnlyFile(str);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertSeek(file, 0, fuchsia::io::SeekOrigin::END, str.length());
AssertRead(file, 100, "");
}
TEST_F(PseudoFileTest, SeekFailsIfOffsetMoreThanCapacity) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadOnlyFile(str);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertSeek(file, 1, fuchsia::io::SeekOrigin::END, 0, ZX_ERR_OUT_OF_RANGE);
// make sure offset didnot change
AssertRead(file, 100, str);
}
TEST_F(PseudoFileTest, WriteafterEndOfFile) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertSeek(file, 5, fuchsia::io::SeekOrigin::END, str.length() + 5);
AssertWrite(file, "is");
auto updated_str = str;
updated_str.append(5, 0).append("is");
AssertReadAt(file, 0, 100, updated_str);
// make sure file was not updated before conenction was closed.
ASSERT_EQ(file_wrapper.buffer(), str);
CloseFile(file);
// make sure file was updated
AssertFileWrapperState(file_wrapper, updated_str);
}
TEST_F(PseudoFileTest, WriteFailsForReadOnly) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertWrite(file, "is", ZX_ERR_ACCESS_DENIED, 0);
CloseFile(file);
// make sure file was not updated
AssertFileWrapperState(file_wrapper, str);
}
TEST_F(PseudoFileTest, WriteAtFailsForReadOnly) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertWriteAt(file, "is", 0, ZX_ERR_ACCESS_DENIED, 0);
CloseFile(file);
// make sure file was not updated
AssertFileWrapperState(file_wrapper, str);
}
TEST_F(PseudoFileTest, TruncateFailsForReadOnly) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenRead(file_wrapper.file(), file_wrapper.dispatcher());
AssertTruncate(file, 10, ZX_ERR_ACCESS_DENIED);
CloseFile(file);
// make sure file was not updated
AssertFileWrapperState(file_wrapper, str);
}
TEST_F(PseudoFileTest, ReadAtFailsForWriteOnly) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenFile(file_wrapper.file(), fuchsia::io::OPEN_RIGHT_WRITABLE,
file_wrapper.dispatcher());
AssertReadAt(file, 0, 10, "", ZX_ERR_ACCESS_DENIED);
}
TEST_F(PseudoFileTest, ReadFailsForWriteOnly) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenFile(file_wrapper.file(), fuchsia::io::OPEN_RIGHT_WRITABLE,
file_wrapper.dispatcher());
AssertRead(file, 10, "", ZX_ERR_ACCESS_DENIED);
}
TEST_F(PseudoFileTest, CapacityisSameAsFileContentSize) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, str.length());
auto file = OpenFile(file_wrapper.file(), fuchsia::io::OPEN_RIGHT_READABLE,
file_wrapper.dispatcher());
AssertRead(file, str.length(), str);
}
TEST_F(PseudoFileTest, OpenFailsForOverFlowingFile) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, str.length() - 1);
AssertOpen(file_wrapper.file(), file_wrapper.dispatcher(),
fuchsia::io::OPEN_RIGHT_READABLE, ZX_ERR_FILE_BIG);
}
TEST_F(PseudoFileTest, CantReadNodeReferenceFile) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file =
OpenFile(file_wrapper.file(), fuchsia::io::OPEN_FLAG_NODE_REFERENCE,
file_wrapper.dispatcher());
// make sure node reference was opened
zx_status_t status;
fuchsia::io::NodeAttributes attr;
ASSERT_EQ(ZX_OK, file->GetAttr(&status, &attr));
ASSERT_EQ(ZX_OK, status);
ASSERT_NE(0u, attr.mode | fuchsia::io::MODE_TYPE_FILE);
std::vector<uint8_t> buffer;
ASSERT_EQ(ZX_ERR_PEER_CLOSED, file->Read(100, &status, &buffer));
}
TEST_F(PseudoFileTest, CanCloneFileConnectionAndReadAndWrite) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
fuchsia::io::FileSyncPtr cloned_file;
file->Clone(fuchsia::io::CLONE_FLAG_SAME_RIGHTS,
fidl::InterfaceRequest<fuchsia::io::Node>(
cloned_file.NewRequest().TakeChannel()));
CloseFile(file);
AssertWrite(cloned_file, "It");
const std::string updated_str = "Itis is a test string";
AssertReadAt(cloned_file, 0, 100, updated_str);
// make sure file was not updated before connection was closed.
ASSERT_EQ(file_wrapper.buffer(), str);
CloseFile(cloned_file);
// make sure file was updated
AssertFileWrapperState(file_wrapper, updated_str);
}
TEST_F(PseudoFileTest, NodeReferenceIsClonedAsNodeReference) {
const std::string str = "this is a test string";
auto file_wrapper = FileWrapper::CreateReadWriteFile(str, 100);
auto file =
OpenFile(file_wrapper.file(), fuchsia::io::OPEN_FLAG_NODE_REFERENCE,
file_wrapper.dispatcher());
fuchsia::io::FileSyncPtr cloned_file;
file->Clone(fuchsia::io::CLONE_FLAG_SAME_RIGHTS,
fidl::InterfaceRequest<fuchsia::io::Node>(
cloned_file.NewRequest().TakeChannel()));
CloseFile(file);
// make sure node reference was opened
zx_status_t status;
fuchsia::io::NodeAttributes attr;
ASSERT_EQ(ZX_OK, cloned_file->GetAttr(&status, &attr));
ASSERT_EQ(ZX_OK, status);
ASSERT_NE(0u, attr.mode | fuchsia::io::MODE_TYPE_FILE);
std::vector<uint8_t> buffer;
ASSERT_EQ(ZX_ERR_PEER_CLOSED, cloned_file->Read(100, &status, &buffer));
}
class FileWrapperWithFailingWriteFn {
public:
vfs::PseudoFile* file() { return file_.get(); };
static FileWrapperWithFailingWriteFn Create(std::string initial_str,
size_t capacity,
zx_status_t write_error) {
return FileWrapperWithFailingWriteFn(initial_str, capacity, write_error);
}
async_dispatcher_t* dispatcher() { return loop_.dispatcher(); }
async::Loop& loop() { return loop_; }
private:
FileWrapperWithFailingWriteFn(std::string initial_str, size_t capacity,
zx_status_t write_error)
: buffer_(std::move(initial_str)),
loop_(&kAsyncLoopConfigNoAttachToThread) {
auto readFn = [this](std::vector<uint8_t>* output, size_t max_file_size) {
output->resize(buffer_.length());
std::copy(buffer_.begin(), buffer_.end(), output->begin());
return ZX_OK;
};
vfs::PseudoFile::WriteHandler writeFn;
writeFn = [this, write_error](std::vector<uint8_t> input) {
std::string str(input.size(), 0);
std::copy(input.begin(), input.begin() + input.size(), str.begin());
buffer_ = std::move(str);
return write_error;
};
file_ = std::make_unique<vfs::PseudoFile>(capacity, std::move(readFn),
std::move(writeFn));
loop_.StartThread("vfs test thread");
}
std::unique_ptr<vfs::PseudoFile> file_;
std::string buffer_;
async::Loop loop_;
};
TEST_F(PseudoFileTest, CloseReturnsWriteError) {
const std::string str = "this is a test string";
auto file_wrapper =
FileWrapperWithFailingWriteFn::Create(str, 100, ZX_ERR_IO);
auto file = OpenReadWrite(file_wrapper.file(), file_wrapper.dispatcher());
AssertWrite(file, "It");
CloseFile(file, ZX_ERR_IO);
}
} // namespace