| // 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 <lib/async-loop/cpp/loop.h> |
| #include <fs/tracked-remote-dir.h> |
| |
| #include <unittest/unittest.h> |
| |
| #include <utility> |
| |
| namespace { |
| |
| // A Remote Directory which shuts down a dispatch loop when it is destroyed. |
| // This may be utilized to synchronize destruction of the remote directory |
| // with a test's dispatch loop. |
| class TestRemoteDir final : public fs::TrackedRemoteDir { |
| public: |
| TestRemoteDir(zx::channel remote, async::Loop* loop) |
| : TrackedRemoteDir(std::move(remote)), loop_(loop) {} |
| ~TestRemoteDir() { |
| loop_->Shutdown(); |
| } |
| private: |
| async::Loop* loop_; |
| }; |
| |
| bool TestAddingTrackedDirectory() { |
| BEGIN_TEST; |
| |
| async::Loop loop(&kAsyncLoopConfigNoAttachToThread); |
| zx::channel server, client; |
| ASSERT_EQ(ZX_OK, zx::channel::create(0u, &server, &client)); |
| |
| fbl::String name = "remote-directory"; |
| auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir()); |
| |
| // 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); |
| |
| // "name" should not yet exist within the directory. |
| fbl::RefPtr<fs::Vnode> node; |
| EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, name)); |
| |
| // Add a remote directory, observe that it can be looked up. |
| auto remote = fbl::AdoptRef<TestRemoteDir>(new TestRemoteDir(std::move(client), &loop)); |
| EXPECT_EQ(ZX_OK, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name)); |
| remote.reset(); |
| EXPECT_EQ(ZX_OK, dir->Lookup(&node, name)); |
| node.reset(); |
| |
| // Forcing the remote connection to become "peer closed" causes the entry to be removed. |
| server.reset(); |
| EXPECT_EQ(ZX_ERR_BAD_STATE, loop.Run()); |
| EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, name)); |
| |
| END_TEST; |
| } |
| |
| bool TestAddingTrackedDirectoryMultiple() { |
| BEGIN_TEST; |
| |
| async::Loop loop(&kAsyncLoopConfigNoAttachToThread); |
| zx::channel server, client; |
| ASSERT_EQ(ZX_OK, zx::channel::create(0u, &server, &client)); |
| |
| fbl::String name = "remote-directory"; |
| auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir()); |
| |
| auto remote = fbl::AdoptRef<TestRemoteDir>(new TestRemoteDir(std::move(client), &loop)); |
| EXPECT_EQ(ZX_OK, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name)); |
| |
| // Observe that we cannot track the remote object multiple times. |
| EXPECT_EQ(ZX_ERR_BAD_STATE, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name)); |
| |
| // Observe that we cannot track the remote directory in a different |
| // container. |
| auto dir2 = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir()); |
| EXPECT_EQ(ZX_ERR_BAD_STATE, remote->AddAsTrackedEntry(loop.dispatcher(), dir2.get(), name)); |
| |
| remote.reset(); |
| |
| // Forcing the remote connection to become "peer closed" causes the entry to be removed. |
| server.reset(); |
| EXPECT_EQ(ZX_ERR_BAD_STATE, loop.Run()); |
| fbl::RefPtr<fs::Vnode> node; |
| EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, name)); |
| |
| END_TEST; |
| } |
| |
| bool TestTrackAddingDifferentVnode() { |
| BEGIN_TEST; |
| |
| async::Loop loop(&kAsyncLoopConfigNoAttachToThread); |
| zx::channel server, client; |
| ASSERT_EQ(ZX_OK, zx::channel::create(0u, &server, &client)); |
| |
| auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir()); |
| |
| auto remote = fbl::AdoptRef<TestRemoteDir>(new TestRemoteDir(std::move(client), &loop)); |
| auto not_remote = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir()); |
| |
| // Test a subtle behavior: |
| // - Add |remote| to |dir|, begin tracking the remote handle. |
| // - Remove |remote| from |dir| (while still tracking). |
| // - Add a different Vnode to |dir| with the same name. |
| // - Close the remote connection for the still-tracked, but already-removed |
| // vnode. |
| // |
| // This tests that when |remote| is closed, we don't accidentally remove the |
| // "wrong" Vnode from the containing pseudodirectory. |
| fbl::String name = "remote-directory"; |
| EXPECT_EQ(ZX_OK, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name)); |
| EXPECT_EQ(ZX_OK, dir->RemoveEntry(name)); |
| EXPECT_EQ(ZX_OK, dir->AddEntry(name, not_remote)); |
| remote.reset(); |
| server.reset(); |
| |
| EXPECT_EQ(ZX_ERR_BAD_STATE, loop.Run()); |
| fbl::RefPtr<fs::Vnode> node; |
| |
| // The underlying entry should NOT have been removed. |
| EXPECT_EQ(ZX_OK, dir->Lookup(&node, name)); |
| |
| END_TEST; |
| } |
| |
| } // namespace |
| |
| BEGIN_TEST_CASE(tracked_remote_dir_tests) |
| RUN_TEST(TestAddingTrackedDirectory) |
| RUN_TEST(TestAddingTrackedDirectoryMultiple) |
| RUN_TEST(TestTrackAddingDifferentVnode) |
| END_TEST_CASE(tracked_remote_dir_tests) |