blob: 2df89ef1eae1b28ca88767a77c82c9e2d69dd6c8 [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 <fuchsia/io/cpp/fidl.h>
#include <lib/async/dispatcher.h>
#include <lib/gtest/real_loop_fixture.h>
#include <lib/vfs/cpp/lazy_dir.h>
#include <lib/vfs/cpp/pseudo_file.h>
#include <lib/vfs/cpp/testing/dir_test_util.h>
#include <zircon/system/public/zircon/errors.h>
#include <memory>
namespace {
using vfs_tests::Dirent;
class TestLazyDir : public vfs::LazyDir {
public:
struct TestContent {
public:
TestContent(std::string name, std::unique_ptr<vfs::internal::Node> node)
: name_(std::move(name)), node_(std::move(node)) {}
std::string name_;
std::unique_ptr<vfs::internal::Node> node_;
};
TestLazyDir() : next_id_(GetStartingId()), loop_(&kAsyncLoopConfigNoAttachToThread) {
loop_.StartThread("vfs test thread");
}
void AddContent(TestContent content) { contents_.emplace(next_id_++, std::move(content)); }
void ClearContent() {
contents_.clear();
next_id_ = GetStartingId();
}
fuchsia::io::DirectorySyncPtr ServeForTest(int flags = fuchsia::io::OPEN_RIGHT_READABLE) {
fuchsia::io::DirectorySyncPtr ptr;
Serve(flags, ptr.NewRequest().TakeChannel(), loop_.dispatcher());
return ptr;
}
async_dispatcher_t* dispatcher() { return loop_.dispatcher(); }
protected:
void GetContents(LazyEntryVector* out_vector) const override {
out_vector->reserve(contents_.size());
for (const auto& content : contents_) {
out_vector->push_back({content.first, content.second.name_, fuchsia::io::MODE_TYPE_FILE});
}
}
zx_status_t GetFile(Node** out_node, uint64_t id, std::string name) const override {
auto search = contents_.find(id);
if (search != contents_.end()) {
*out_node = search->second.node_.get();
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
private:
uint64_t next_id_;
std::map<uint64_t, TestContent> contents_;
async::Loop loop_;
};
class LazyDirConnection : public vfs_tests::DirConnection {
protected:
vfs::internal::Directory* GetDirectoryNode() override { return &dir_; }
TestLazyDir::TestContent CreateTestFile(std::string name, std::string content) {
auto file = std::make_unique<vfs::PseudoFile>(
content.length(),
[content = std::move(content)](std::vector<uint8_t>* output, size_t max_file_size) {
output->resize(content.length());
std::copy(content.begin(), content.end(), output->begin());
return ZX_OK;
});
return TestLazyDir::TestContent(std::move(name), std::move(file));
}
TestLazyDir dir_;
};
TEST_F(LazyDirConnection, ReadDirEmpty) {
auto ptr = dir_.ServeForTest();
std::vector<Dirent> expected_dirents = {
Dirent::DirentForDot(),
};
AssertReadDirents(ptr, 1024, expected_dirents);
}
TEST_F(LazyDirConnection, ReadSimple) {
auto ptr = dir_.ServeForTest();
dir_.AddContent(CreateTestFile("file1", "file1"));
std::vector<Dirent> expected_dirents = {Dirent::DirentForDot(), Dirent::DirentForFile("file1")};
AssertReadDirents(ptr, 1024, expected_dirents);
}
TEST_F(LazyDirConnection, DynamicRead) {
auto ptr = dir_.ServeForTest();
dir_.AddContent(CreateTestFile("file1", "file1"));
dir_.AddContent(CreateTestFile("file2", "file2"));
std::vector<Dirent> expected_dirents = {Dirent::DirentForDot(), Dirent::DirentForFile("file1"),
Dirent::DirentForFile("file2")};
AssertReadDirents(ptr, 1024, expected_dirents);
dir_.ClearContent();
dir_.AddContent(CreateTestFile("file3", "file3"));
// should not get any dirent before we rewind.
expected_dirents = {};
AssertReadDirents(ptr, 1024, expected_dirents);
AssertRewind(ptr);
expected_dirents = {Dirent::DirentForDot(), Dirent::DirentForFile("file3")};
AssertReadDirents(ptr, 1024, expected_dirents);
}
TEST_F(LazyDirConnection, MultipleReads) {
auto ptr = dir_.ServeForTest();
dir_.AddContent(CreateTestFile("file1", "file1"));
dir_.AddContent(CreateTestFile("file2", "file2"));
dir_.AddContent(CreateTestFile("file3", "file3"));
dir_.AddContent(CreateTestFile("file4", "file4"));
std::vector<Dirent> expected_dirents = {Dirent::DirentForDot(), Dirent::DirentForFile("file1"),
Dirent::DirentForFile("file2")};
AssertReadDirents(ptr, 3 * sizeof(vdirent_t) + 15, expected_dirents);
expected_dirents = {Dirent::DirentForFile("file3"), Dirent::DirentForFile("file4")};
AssertReadDirents(ptr, 1024, expected_dirents);
expected_dirents = {};
AssertReadDirents(ptr, 1024, expected_dirents);
}
TEST_F(LazyDirConnection, LookupWorks) {
dir_.AddContent(CreateTestFile("file1", "file1"));
dir_.AddContent(CreateTestFile("file2", "file2"));
dir_.AddContent(CreateTestFile("file3", "file3"));
dir_.AddContent(CreateTestFile("file4", "file4"));
vfs::internal::Node* n;
ASSERT_EQ(ZX_OK, dir_.Lookup("file3", &n));
fuchsia::io::FileSyncPtr file_ptr;
n->Serve(fuchsia::io::OPEN_RIGHT_READABLE, file_ptr.NewRequest().TakeChannel(),
dir_.dispatcher());
zx_status_t status;
std::vector<uint8_t> data;
file_ptr->Read(20, &status, &data);
ASSERT_EQ(ZX_OK, status);
std::string str = "file3";
std::vector<uint8_t> expected_data(str.begin(), str.end());
ASSERT_EQ(expected_data, data);
}
TEST_F(LazyDirConnection, LookupReturnsNotFound) {
dir_.AddContent(CreateTestFile("file1", "file1"));
dir_.AddContent(CreateTestFile("file2", "file2"));
dir_.AddContent(CreateTestFile("file3", "file3"));
dir_.AddContent(CreateTestFile("file4", "file4"));
vfs::internal::Node* n;
ASSERT_EQ(ZX_ERR_NOT_FOUND, dir_.Lookup("file5", &n));
}
} // namespace