text: // 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
  