| // Copyright 2017 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 <fs/pseudo-dir.h> |
| |
| #include <fs/pseudo-file.h> |
| #include <unittest/unittest.h> |
| |
| namespace { |
| |
| class DirentChecker { |
| public: |
| DirentChecker(const void* buffer, size_t length) |
| : current_(reinterpret_cast<const uint8_t*>(buffer)), remaining_(length) {} |
| |
| bool ExpectEnd() { |
| BEGIN_HELPER; |
| |
| EXPECT_EQ(0u, remaining_); |
| |
| END_HELPER; |
| } |
| |
| bool ExpectEntry(const char* name, uint32_t vtype) { |
| BEGIN_HELPER; |
| |
| ASSERT_NE(0u, remaining_); |
| auto entry = reinterpret_cast<const vdirent_t*>(current_); |
| size_t entry_size = entry->size + sizeof(vdirent_t); |
| ASSERT_GE(remaining_, entry_size); |
| current_ += entry_size; |
| remaining_ -= entry_size; |
| EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(name), |
| reinterpret_cast<const uint8_t*>(entry->name), strlen(name), "name"); |
| EXPECT_EQ(VTYPE_TO_DTYPE(vtype), entry->type); |
| |
| END_HELPER; |
| } |
| |
| private: |
| const uint8_t* current_; |
| size_t remaining_; |
| }; |
| |
| bool TestPseudoDir() { |
| BEGIN_TEST; |
| |
| auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir()); |
| auto subdir = fbl::AdoptRef<fs::Vnode>(new fs::PseudoDir()); |
| auto file1 = fbl::AdoptRef<fs::Vnode>(new fs::UnbufferedPseudoFile()); |
| auto file2 = fbl::AdoptRef<fs::Vnode>(new fs::UnbufferedPseudoFile()); |
| |
| // add entries |
| EXPECT_EQ(ZX_OK, dir->AddEntry("subdir", subdir)); |
| EXPECT_EQ(ZX_OK, dir->AddEntry("file1", file1)); |
| EXPECT_EQ(ZX_OK, dir->AddEntry("file2", file2)); |
| EXPECT_EQ(ZX_OK, dir->AddEntry("file2b", file2)); |
| |
| // try to add duplicates |
| EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, dir->AddEntry("subdir", subdir)); |
| EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, dir->AddEntry("file1", subdir)); |
| |
| // remove entries |
| EXPECT_EQ(ZX_OK, dir->RemoveEntry("file2")); |
| EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->RemoveEntry("file2")); |
| |
| // open as directory |
| fbl::RefPtr<fs::Vnode> redirect; |
| EXPECT_EQ(ZX_OK, dir->ValidateFlags(ZX_FS_FLAG_DIRECTORY)); |
| EXPECT_EQ(ZX_OK, dir->Open(ZX_FS_FLAG_DIRECTORY, &redirect)); |
| EXPECT_NULL(redirect); |
| |
| // get attributes |
| vnattr_t attr; |
| EXPECT_EQ(ZX_OK, dir->Getattr(&attr)); |
| EXPECT_EQ(V_TYPE_DIR | V_IRUSR, attr.mode); |
| EXPECT_EQ(1, attr.nlink); |
| |
| // lookup entries |
| fbl::RefPtr<fs::Vnode> node; |
| EXPECT_EQ(ZX_OK, dir->Lookup(&node, "subdir")); |
| EXPECT_EQ(subdir.get(), node.get()); |
| EXPECT_EQ(ZX_OK, dir->Lookup(&node, "file1")); |
| EXPECT_EQ(file1.get(), node.get()); |
| EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, "file2")); |
| EXPECT_EQ(ZX_OK, dir->Lookup(&node, "file2b")); |
| EXPECT_EQ(file2.get(), node.get()); |
| |
| // readdir |
| { |
| fs::vdircookie_t cookie = {}; |
| uint8_t buffer[4096]; |
| size_t length; |
| EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); |
| DirentChecker dc(buffer, length); |
| EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); |
| EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR)); |
| EXPECT_TRUE(dc.ExpectEntry("file1", V_TYPE_FILE)); |
| EXPECT_TRUE(dc.ExpectEntry("file2b", V_TYPE_FILE)); |
| EXPECT_TRUE(dc.ExpectEnd()); |
| } |
| |
| // readdir with small buffer |
| { |
| fs::vdircookie_t cookie = {}; |
| uint8_t buffer[2*sizeof(vdirent) + 13]; |
| size_t length; |
| EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); |
| DirentChecker dc(buffer, length); |
| EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); |
| EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR)); |
| EXPECT_TRUE(dc.ExpectEnd()); |
| |
| // readdir again |
| EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); |
| DirentChecker dc1(buffer, length); |
| EXPECT_TRUE(dc1.ExpectEntry("file1", V_TYPE_FILE)); |
| EXPECT_TRUE(dc1.ExpectEntry("file2b", V_TYPE_FILE)); |
| EXPECT_TRUE(dc1.ExpectEnd()); |
| } |
| |
| // test removed entries do not appear in readdir or lookup |
| dir->RemoveEntry("file1"); |
| { |
| fs::vdircookie_t cookie = {}; |
| uint8_t buffer[4096]; |
| size_t length; |
| EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); |
| DirentChecker dc(buffer, length); |
| EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); |
| EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR)); |
| EXPECT_TRUE(dc.ExpectEntry("file2b", V_TYPE_FILE)); |
| EXPECT_TRUE(dc.ExpectEnd()); |
| } |
| EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, "file1")); |
| |
| // remove all entries |
| dir->RemoveAllEntries(); |
| |
| // readdir again |
| { |
| fs::vdircookie_t cookie = {}; |
| uint8_t buffer[4096]; |
| size_t length; |
| EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); |
| DirentChecker dc(buffer, length); |
| EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); |
| EXPECT_TRUE(dc.ExpectEnd()); |
| } |
| |
| // FIXME(ZX-1186): Can't unittest watch/notify (hard to isolate right now). |
| |
| END_TEST; |
| } |
| |
| } // namespace |
| |
| BEGIN_TEST_CASE(pseudo_dir_tests) |
| RUN_TEST(TestPseudoDir) |
| END_TEST_CASE(pseudo_dir_tests) |