| // 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/io2/llcpp/fidl.h> | 
 | #include <lib/async-loop/cpp/loop.h> | 
 | #include <lib/async-loop/default.h> | 
 | #include <lib/fidl-async/cpp/bind.h> | 
 | #include <lib/zxio/inception.h> | 
 | #include <lib/zxio/ops.h> | 
 | #include <lib/zxio/zxio.h> | 
 |  | 
 | #include <atomic> | 
 | #include <memory> | 
 |  | 
 | #include <zxtest/zxtest.h> | 
 |  | 
 | namespace { | 
 |  | 
 | namespace fio2 = ::llcpp::fuchsia::io2; | 
 |  | 
 | class TestServerBase : public fio2::Node::Interface { | 
 |  public: | 
 |   TestServerBase() = default; | 
 |   virtual ~TestServerBase() = default; | 
 |  | 
 |   void Reopen(fio2::ConnectionOptions options, ::zx::channel object_request, | 
 |               ReopenCompleter::Sync& completer) override { | 
 |     completer.Close(ZX_ERR_NOT_SUPPORTED); | 
 |   } | 
 |  | 
 |   // Exercised by |zxio_close|. | 
 |   void Close(CloseCompleter::Sync& completer) override { | 
 |     num_close_.fetch_add(1); | 
 |     completer.Close(ZX_OK); | 
 |   } | 
 |  | 
 |   void Describe(fio2::ConnectionInfoQuery query, DescribeCompleter::Sync& completer) override { | 
 |     completer.Close(ZX_ERR_NOT_SUPPORTED); | 
 |   } | 
 |  | 
 |   void GetToken(GetTokenCompleter::Sync& completer) override { | 
 |     completer.Close(ZX_ERR_NOT_SUPPORTED); | 
 |   } | 
 |  | 
 |   void GetAttributes(fio2::NodeAttributesQuery query, | 
 |                      GetAttributesCompleter::Sync& completer) override { | 
 |     completer.Close(ZX_ERR_NOT_SUPPORTED); | 
 |   } | 
 |  | 
 |   void UpdateAttributes(fio2::NodeAttributes attributes, | 
 |                         UpdateAttributesCompleter::Sync& completer) override { | 
 |     completer.Close(ZX_ERR_NOT_SUPPORTED); | 
 |   } | 
 |  | 
 |   void Sync(SyncCompleter::Sync& completer) override { completer.Close(ZX_ERR_NOT_SUPPORTED); } | 
 |  | 
 |   uint32_t num_close() const { return num_close_.load(); } | 
 |  | 
 |  private: | 
 |   std::atomic<uint32_t> num_close_ = 0; | 
 | }; | 
 |  | 
 | class RemoteV2 : public zxtest::Test { | 
 |  public: | 
 |   void SetUp() final { | 
 |     ASSERT_OK(zx::channel::create(0, &control_client_end_, &control_server_end_)); | 
 |     ASSERT_OK(zx::eventpair::create(0, &eventpair_to_client_, &eventpair_on_server_)); | 
 |     ASSERT_OK(zxio_remote_v2_init(&remote_, control_client_end_.release(), | 
 |                                   eventpair_to_client_.release())); | 
 |   } | 
 |  | 
 |   template <typename ServerImpl> | 
 |   ServerImpl* StartServer() { | 
 |     server_ = std::make_unique<ServerImpl>(); | 
 |     loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread); | 
 |     zx_status_t status; | 
 |     EXPECT_OK(status = loop_->StartThread("fake-filesystem")); | 
 |     if (status != ZX_OK) { | 
 |       return nullptr; | 
 |     } | 
 |     EXPECT_OK(fidl::BindSingleInFlightOnly(loop_->dispatcher(), std::move(control_server_end_), | 
 |                                            server_.get())); | 
 |     if (status != ZX_OK) { | 
 |       return nullptr; | 
 |     } | 
 |     return static_cast<ServerImpl*>(server_.get()); | 
 |   } | 
 |  | 
 |   void TearDown() final { | 
 |     ASSERT_EQ(0, server_->num_close()); | 
 |     ASSERT_OK(zxio_close(&remote_.io)); | 
 |     ASSERT_EQ(1, server_->num_close()); | 
 |   } | 
 |  | 
 |  protected: | 
 |   zxio_storage_t remote_; | 
 |   zx::channel control_client_end_; | 
 |   zx::channel control_server_end_; | 
 |   zx::eventpair eventpair_on_server_; | 
 |   zx::eventpair eventpair_to_client_; | 
 |   std::unique_ptr<TestServerBase> server_; | 
 |   std::unique_ptr<async::Loop> loop_; | 
 | }; | 
 |  | 
 | TEST_F(RemoteV2, GetAttributes) { | 
 |   constexpr uint64_t kContentSize = 42; | 
 |   constexpr uint64_t kId = 1; | 
 |   class TestServer : public TestServerBase { | 
 |    public: | 
 |     void GetAttributes(fio2::NodeAttributesQuery query, | 
 |                        GetAttributesCompleter::Sync& completer) override { | 
 |       EXPECT_EQ(query, fio2::NodeAttributesQuery::kMask); | 
 |       uint64_t content_size = kContentSize; | 
 |       uint64_t id = kId; | 
 |       fio2::NodeProtocols protocols = fio2::NodeProtocols::FILE; | 
 |       auto builder = fio2::NodeAttributes::UnownedBuilder() | 
 |                          .set_content_size(fidl::unowned_ptr(&content_size)) | 
 |                          .set_protocols(fidl::unowned_ptr(&protocols)) | 
 |                          .set_id(fidl::unowned_ptr(&id)); | 
 |       completer.ReplySuccess(builder.build()); | 
 |     } | 
 |   }; | 
 |   ASSERT_NO_FAILURES(StartServer<TestServer>()); | 
 |  | 
 |   zxio_node_attributes_t attr = {}; | 
 |   ASSERT_OK(zxio_attr_get(&remote_.io, &attr)); | 
 |  | 
 |   EXPECT_TRUE(attr.has.protocols); | 
 |   EXPECT_EQ(ZXIO_NODE_PROTOCOL_FILE, attr.protocols); | 
 |   EXPECT_TRUE(attr.has.content_size); | 
 |   EXPECT_EQ(kContentSize, attr.content_size); | 
 |   EXPECT_FALSE(attr.has.storage_size); | 
 |   EXPECT_FALSE(attr.has.abilities); | 
 |   EXPECT_FALSE(attr.has.creation_time); | 
 |   EXPECT_FALSE(attr.has.modification_time); | 
 |   EXPECT_TRUE(attr.has.id); | 
 |   EXPECT_EQ(kId, attr.id); | 
 |   EXPECT_FALSE(attr.has.link_count); | 
 | } | 
 |  | 
 | TEST_F(RemoteV2, GetAttributesError) { | 
 |   class TestServer : public TestServerBase { | 
 |    public: | 
 |     void GetAttributes(fio2::NodeAttributesQuery query, | 
 |                        GetAttributesCompleter::Sync& completer) override { | 
 |       completer.ReplyError(ZX_ERR_INVALID_ARGS); | 
 |     } | 
 |   }; | 
 |   ASSERT_NO_FAILURES(StartServer<TestServer>()); | 
 |  | 
 |   zxio_node_attributes_t attr = {}; | 
 |   EXPECT_EQ(ZX_ERR_INVALID_ARGS, zxio_attr_get(&remote_.io, &attr)); | 
 | } | 
 |  | 
 | TEST_F(RemoteV2, SetAttributes) { | 
 |   constexpr uint64_t kCreationTime = 123; | 
 |   class TestServer : public TestServerBase { | 
 |    public: | 
 |     void UpdateAttributes(fio2::NodeAttributes attributes, | 
 |                           UpdateAttributesCompleter::Sync& completer) override { | 
 |       EXPECT_TRUE(attributes.has_creation_time()); | 
 |       EXPECT_FALSE(attributes.has_protocols()); | 
 |       EXPECT_FALSE(attributes.has_abilities()); | 
 |       EXPECT_FALSE(attributes.has_modification_time()); | 
 |       EXPECT_FALSE(attributes.has_content_size()); | 
 |       EXPECT_FALSE(attributes.has_storage_size()); | 
 |       EXPECT_FALSE(attributes.has_link_count()); | 
 |  | 
 |       uint64_t creation_time = kCreationTime; | 
 |       EXPECT_EQ(creation_time, attributes.creation_time()); | 
 |       called_.store(true); | 
 |       completer.ReplySuccess(); | 
 |     } | 
 |  | 
 |     std::atomic<bool> called_ = false; | 
 |   }; | 
 |  | 
 |   TestServer* server; | 
 |   ASSERT_NO_FAILURES(server = StartServer<TestServer>()); | 
 |  | 
 |   zxio_node_attributes_t attr = {}; | 
 |   ZXIO_NODE_ATTR_SET(attr, creation_time, kCreationTime); | 
 |   ASSERT_OK(zxio_attr_set(&remote_.io, &attr)); | 
 |   EXPECT_TRUE(server->called_.load()); | 
 | } | 
 |  | 
 | TEST_F(RemoteV2, SetAttributesError) { | 
 |   class TestServer : public TestServerBase { | 
 |    public: | 
 |     void UpdateAttributes(fio2::NodeAttributes attributes, | 
 |                           UpdateAttributesCompleter::Sync& completer) override { | 
 |       completer.ReplyError(ZX_ERR_INVALID_ARGS); | 
 |     } | 
 |   }; | 
 |   ASSERT_NO_FAILURES(StartServer<TestServer>()); | 
 |  | 
 |   zxio_node_attributes_t attr = {}; | 
 |   EXPECT_EQ(ZX_ERR_INVALID_ARGS, zxio_attr_set(&remote_.io, &attr)); | 
 | } | 
 |  | 
 | TEST_F(RemoteV2, WaitTimeOut) { | 
 |   ASSERT_NO_FAILURES(StartServer<TestServerBase>()); | 
 |   zxio_signals_t observed = ZX_SIGNAL_NONE; | 
 |   ASSERT_STATUS(ZX_ERR_TIMED_OUT, | 
 |                 zxio_wait_one(&remote_.io, ZXIO_SIGNAL_ALL, ZX_TIME_INFINITE_PAST, &observed)); | 
 |   EXPECT_EQ(ZXIO_SIGNAL_NONE, observed); | 
 | } | 
 |  | 
 | TEST_F(RemoteV2, WaitForReadable) { | 
 |   ASSERT_NO_FAILURES(StartServer<TestServerBase>()); | 
 |   zxio_signals_t observed = ZX_SIGNAL_NONE; | 
 |   ASSERT_OK(eventpair_on_server_.signal_peer( | 
 |       ZX_SIGNAL_NONE, static_cast<zx_signals_t>(fio2::DeviceSignal::READABLE))); | 
 |   ASSERT_OK(zxio_wait_one(&remote_.io, ZXIO_SIGNAL_READABLE, ZX_TIME_INFINITE_PAST, &observed)); | 
 |   EXPECT_EQ(ZXIO_SIGNAL_READABLE, observed); | 
 | } | 
 |  | 
 | TEST_F(RemoteV2, WaitForWritable) { | 
 |   ASSERT_NO_FAILURES(StartServer<TestServerBase>()); | 
 |   zxio_signals_t observed = ZX_SIGNAL_NONE; | 
 |   ASSERT_OK(eventpair_on_server_.signal_peer( | 
 |       ZX_SIGNAL_NONE, static_cast<zx_signals_t>(fio2::DeviceSignal::WRITABLE))); | 
 |   ASSERT_OK(zxio_wait_one(&remote_.io, ZXIO_SIGNAL_WRITABLE, ZX_TIME_INFINITE_PAST, &observed)); | 
 |   EXPECT_EQ(ZXIO_SIGNAL_WRITABLE, observed); | 
 | } | 
 |  | 
 | }  // namespace |