| // 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/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_(&kAsyncLoopConfigNoAttachToCurrentThread) { |
| 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 |