[vfs][file] Implement seek and add tests.
Also fixed Close().
TEST=fx run-test vfs_cpp_tests
Change-Id: Ice636c0f1e79d29c2f80f6547cb9c82f1ca0deb2
diff --git a/public/lib/vfs/cpp/connection.cc b/public/lib/vfs/cpp/connection.cc
index 995d9b7..341b0d5 100644
--- a/public/lib/vfs/cpp/connection.cc
+++ b/public/lib/vfs/cpp/connection.cc
@@ -20,7 +20,8 @@
}
void Connection::Close(Node* vn, fuchsia::io::Node::CloseCallback callback) {
- callback(vn->Close(this));
+ callback(ZX_OK);
+ vn->Close(this);
// |this| is destroyed at this point.
}
diff --git a/public/lib/vfs/cpp/file.cc b/public/lib/vfs/cpp/file.cc
index b06319c..1d56a67 100644
--- a/public/lib/vfs/cpp/file.cc
+++ b/public/lib/vfs/cpp/file.cc
@@ -39,6 +39,8 @@
return ZX_OK;
}
+size_t File::GetCapacity() { return std::numeric_limits<size_t>::max(); }
+
bool File::IsDirectory() const { return false; }
} // namespace vfs
diff --git a/public/lib/vfs/cpp/file.h b/public/lib/vfs/cpp/file.h
index 5ac694c..7e4d5df 100644
--- a/public/lib/vfs/cpp/file.h
+++ b/public/lib/vfs/cpp/file.h
@@ -50,6 +50,17 @@
// Override that describes this object as a file.
void Describe(fuchsia::io::NodeInfo* out_info) override;
+ // Returns current file length.
+ //
+ // All implementations should implement this.
+ virtual uint64_t GetLength() = 0;
+
+ // Returns file capacity.
+ //
+ // Seek() uses this to return ZX_ERR_OUT_OF_RANGE if new seek is more than
+ // this value.
+ virtual size_t GetCapacity();
+
protected:
zx_status_t CreateConnection(
uint32_t flags, std::unique_ptr<Connection>* connection) override;
diff --git a/public/lib/vfs/cpp/file_unittest.cc b/public/lib/vfs/cpp/file_unittest.cc
index aa498a7..7a26c5a 100644
--- a/public/lib/vfs/cpp/file_unittest.cc
+++ b/public/lib/vfs/cpp/file_unittest.cc
@@ -46,6 +46,10 @@
return ZX_OK;
}
+ uint64_t GetLength() override { return buffer_->size(); }
+
+ size_t GetCapacity() override { return buffer_->size(); }
+
private:
std::vector<uint8_t>* buffer_;
};
diff --git a/public/lib/vfs/cpp/internal/file_connection.cc b/public/lib/vfs/cpp/internal/file_connection.cc
index c0b79de..1fbfa50 100644
--- a/public/lib/vfs/cpp/internal/file_connection.cc
+++ b/public/lib/vfs/cpp/internal/file_connection.cc
@@ -109,11 +109,32 @@
callback(status, actual);
}
-void FileConnection::Seek(int64_t offset, fuchsia::io::SeekOrigin start,
+void FileConnection::Seek(int64_t new_offset, fuchsia::io::SeekOrigin seek,
SeekCallback callback) {
- // TODO: Check flags.
- // TODO: Implement seek.
- callback(ZX_OK, 0u);
+ int64_t cur_len = vn_->GetLength();
+ size_t capacity = vn_->GetCapacity();
+ uint64_t calculated_offset = 0u;
+ switch (seek) {
+ case fuchsia::io::SeekOrigin::START:
+ calculated_offset = new_offset;
+ break;
+ case fuchsia::io::SeekOrigin::CURRENT:
+ calculated_offset = offset() + new_offset;
+ break;
+ case fuchsia::io::SeekOrigin::END:
+ calculated_offset = cur_len + new_offset;
+ break;
+ default:
+ callback(ZX_ERR_INVALID_ARGS, 0u);
+ return;
+ }
+
+ if (static_cast<size_t>(calculated_offset) > capacity) {
+ callback(ZX_ERR_OUT_OF_RANGE, offset());
+ return;
+ }
+ set_offset(calculated_offset);
+ callback(ZX_OK, offset());
}
void FileConnection::Truncate(uint64_t length, TruncateCallback callback) {
diff --git a/public/lib/vfs/cpp/node.cc b/public/lib/vfs/cpp/node.cc
index 35593c7..88f5f51 100644
--- a/public/lib/vfs/cpp/node.cc
+++ b/public/lib/vfs/cpp/node.cc
@@ -24,9 +24,13 @@
Node::~Node() = default;
-zx_status_t Node::Close(Connection* connection) {
- RemoveConnection(connection);
- return ZX_OK;
+std::unique_ptr<Connection> Node::Close(Connection* connection) {
+ auto connection_iterator = std::find_if(
+ connections_.begin(), connections_.end(),
+ [connection](const auto& entry) { return entry.get() == connection; });
+ auto ret = std::move(*connection_iterator);
+ connections_.erase(connection_iterator);
+ return ret;
}
zx_status_t Node::Sync() { return ZX_ERR_NOT_SUPPORTED; }
@@ -104,12 +108,6 @@
request.write(0, &msg, sizeof(msg), nullptr, 0);
}
-void Node::RemoveConnection(Connection* connection) {
- connections_.erase(std::find_if(
- connections_.begin(), connections_.end(),
- [connection](const auto& entry) { return entry.get() == connection; }));
-}
-
void Node::AddConnection(std::unique_ptr<Connection> connection) {
connections_.push_back(std::move(connection));
}
diff --git a/public/lib/vfs/cpp/node.h b/public/lib/vfs/cpp/node.h
index 8839a4f..8bd8649 100644
--- a/public/lib/vfs/cpp/node.h
+++ b/public/lib/vfs/cpp/node.h
@@ -31,9 +31,9 @@
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
- // Notifies |Node| that it should remove |connection| from its list as it is
- // getting closed.
- virtual zx_status_t Close(Connection* connection);
+ // Notifies |Node| that it should remove and return
+ // |connection| from its list as it is getting closed.
+ virtual std::unique_ptr<Connection> Close(Connection* connection);
// Implementation of |fuchsia.io.Node/Describe|.
//
@@ -87,11 +87,6 @@
void SendOnOpenEventOnError(uint32_t flags, zx::channel request,
zx_status_t status);
- // Destroys the given connection.
- //
- // The underlying channel is closed.
- void RemoveConnection(Connection* connection);
-
// Store given connection.
void AddConnection(std::unique_ptr<Connection> connection);
diff --git a/public/lib/vfs/cpp/pseudo_file.cc b/public/lib/vfs/cpp/pseudo_file.cc
index 2ca2981..e3c80b1 100644
--- a/public/lib/vfs/cpp/pseudo_file.cc
+++ b/public/lib/vfs/cpp/pseudo_file.cc
@@ -50,6 +50,20 @@
return fuchsia::io::OPEN_FLAG_APPEND;
}
+uint64_t BufferedPseudoFile::GetLength() {
+ // this should never be called
+ ZX_DEBUG_ASSERT(false);
+
+ return 0u;
+}
+
+size_t BufferedPseudoFile::GetCapacity() {
+ // this should never be called
+ ZX_DEBUG_ASSERT(false);
+
+ return buffer_capacity_;
+}
+
BufferedPseudoFile::Content::Content(BufferedPseudoFile* file, uint32_t flags,
std::vector<uint8_t> content)
: Connection(flags),
@@ -119,6 +133,12 @@
return ZX_OK;
}
+uint64_t BufferedPseudoFile::Content::GetLength() { return buffer_.size(); }
+
+size_t BufferedPseudoFile::Content::GetCapacity() {
+ return file_->buffer_capacity_;
+}
+
void BufferedPseudoFile::Content::SetInputLength(size_t length) {
ZX_DEBUG_ASSERT(length <= file_->buffer_capacity_);
@@ -141,8 +161,9 @@
return status;
}
-zx_status_t BufferedPseudoFile::Content::Close(Connection* connection) {
- Node::Close(connection);
+std::unique_ptr<Connection> BufferedPseudoFile::Content::Close(
+ Connection* connection) {
+ File::Close(connection);
return file_->Close(this);
}
diff --git a/public/lib/vfs/cpp/pseudo_file.h b/public/lib/vfs/cpp/pseudo_file.h
index 1c14133..875c761 100644
--- a/public/lib/vfs/cpp/pseudo_file.h
+++ b/public/lib/vfs/cpp/pseudo_file.h
@@ -67,6 +67,10 @@
uint64_t* out_actual) override;
zx_status_t Truncate(uint64_t length) override;
+ uint64_t GetLength() override;
+
+ size_t GetCapacity() override;
+
// Connection implementation:
zx_status_t Bind(zx::channel request,
async_dispatcher_t* dispatcher) override;
@@ -74,7 +78,7 @@
void SendOnOpenEvent(zx_status_t status) override;
// Node implementation
- zx_status_t Close(Connection* connection) override;
+ std::unique_ptr<Connection> Close(Connection* connection) override;
protected:
uint32_t GetAdditionalAllowedFlags() const override;
@@ -93,6 +97,11 @@
bool dirty_ = false;
};
+ // |File| implementations:
+ uint64_t GetLength() override;
+
+ size_t GetCapacity() override;
+
ReadHandler const read_handler_;
WriteHandler const write_handler_;
const size_t buffer_capacity_;
diff --git a/public/lib/vfs/cpp/pseudo_file_unittest.cc b/public/lib/vfs/cpp/pseudo_file_unittest.cc
index 3fed558..c90d5bc 100644
--- a/public/lib/vfs/cpp/pseudo_file_unittest.cc
+++ b/public/lib/vfs/cpp/pseudo_file_unittest.cc
@@ -25,17 +25,25 @@
vfs::BufferedPseudoFile* file() { return file_.get(); };
static FileWrapper CreateReadWriteFile(std::string initial_str,
- size_t capacity) {
- return FileWrapper(true, initial_str, capacity);
+ size_t capacity,
+ bool start_loop = true) {
+ return FileWrapper(true, initial_str, capacity, start_loop);
}
- static FileWrapper CreateReadOnlyFile(std::string initial_str) {
- return FileWrapper(false, initial_str, initial_str.length());
+ 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)
- : buffer_(std::move(initial_str)) {
+ 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) {
output->resize(buffer_.length());
std::copy(buffer_.begin(), buffer_.end(), output->begin());
@@ -53,10 +61,14 @@
file_ = std::make_unique<vfs::BufferedPseudoFile>(
std::move(readFn), std::move(writeFn), capacity);
+ if (start_loop) {
+ loop_.StartThread("vfs test thread");
+ }
}
std::unique_ptr<vfs::BufferedPseudoFile> file_;
std::string buffer_;
+ async::Loop loop_;
};
class BufferedPseudoFileTest : public gtest::RealLoopFixture {
@@ -76,7 +88,7 @@
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 caleld only once
+ EXPECT_FALSE(on_open_called); // should be called only once
on_open_called = true;
EXPECT_EQ(expected_status, status);
if (expected_status == ZX_OK) {
@@ -91,6 +103,107 @@
}
}
+ fuchsia::io::FileSyncPtr OpenReadWrite(vfs::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::Node* node,
+ async_dispatcher_t* dispatcher) {
+ return OpenFile(node, fuchsia::io::OPEN_RIGHT_READABLE, dispatcher);
+ }
+
+ fuchsia::io::FileSyncPtr OpenFile(vfs::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) {
+ ASSERT_TRUE(RunLoopWithTimeoutOrUntil(
+ [&]() { return file_wrapper.buffer() == expected_str; }, zx::sec(1)))
+ << file_wrapper.buffer();
+ }
+
int OpenAsFD(vfs::Node* node, async_dispatcher_t* dispatcher) {
zx::channel local, remote;
EXPECT_EQ(ZX_OK, zx::channel::create(0, &local, &remote));
@@ -106,7 +219,7 @@
};
TEST_F(BufferedPseudoFileTest, ServeOnInValidFlagsForReadWriteFile) {
- auto file_wrapper = FileWrapper::CreateReadWriteFile("test_str", 100);
+ auto file_wrapper = FileWrapper::CreateReadWriteFile("test_str", 100, false);
{
SCOPED_TRACE("OPEN_FLAG_DIRECTORY");
AssertOpen(file_wrapper.file(), dispatcher(),
@@ -154,7 +267,7 @@
}
TEST_F(BufferedPseudoFileTest, ServeOnValidFlagsForReadWriteFile) {
- auto file_wrapper = FileWrapper::CreateReadWriteFile("test_str", 100);
+ 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};
@@ -165,7 +278,7 @@
}
TEST_F(BufferedPseudoFileTest, ServeOnValidFlagsForReadOnlyFile) {
- auto file_wrapper = FileWrapper::CreateReadOnlyFile("test_str");
+ 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) {
@@ -176,10 +289,8 @@
TEST_F(BufferedPseudoFileTest, Simple) {
auto file_wrapper = FileWrapper::CreateReadWriteFile("test_str", 100);
- async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
- loop.StartThread("vfs test thread");
- int fd = OpenAsFD(file_wrapper.file(), loop.dispatcher());
+ int fd = OpenAsFD(file_wrapper.file(), file_wrapper.dispatcher());
ASSERT_LE(0, fd);
char buffer[1024];
@@ -192,12 +303,244 @@
EXPECT_STREQ("abcd_", buffer);
ASSERT_GE(0, close(fd));
- loop.RunUntilIdle();
+ file_wrapper.loop().RunUntilIdle();
- ASSERT_TRUE(RunLoopWithTimeoutOrUntil(
- [&file_wrapper]() { return file_wrapper.buffer() == "abcd_str"; },
- zx::sec(1)))
- << file_wrapper.buffer();
+ AssertFileWrapperState(file_wrapper, "abcd_str");
+}
+
+TEST_F(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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 + "\0\0\0\0\0is";
+
+ 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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(BufferedPseudoFileTest, 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);
}
} // namespace