| // Copyright 2018 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/fidl/cpp/internal/proxy_controller.h" | |
| #include <lib/fidl/cpp/message_buffer.h> | |
| #include <lib/fidl/cpp/message_builder.h> | |
| #include <lib/fit/defer.h> | |
| #include <lib/zx/channel.h> | |
| #include <gtest/gtest.h> | |
| #include "lib/fidl/cpp/string.h" | |
| #include "lib/fidl/cpp/test/async_loop_for_test.h" | |
| #include "lib/fidl/cpp/test/fidl_types.h" | |
| #include "lib/fidl/txn_header.h" | |
| namespace fidl { | |
| namespace internal { | |
| namespace { | |
| TEST(ProxyController, Trivial) { ProxyController controller; } | |
| TEST(ProxyController, Send) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller; | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| Encoder encoder(5u); | |
| StringPtr string("hello!"); | |
| fidl::Encode(&encoder, &string, encoder.Alloc(sizeof(fidl_string_t))); | |
| EXPECT_EQ(ZX_OK, controller.Send(&unbounded_nonnullable_string_message_type, encoder.GetMessage(), | |
| nullptr)); | |
| MessageBuffer buffer; | |
| Message message = buffer.CreateEmptyMessage(); | |
| EXPECT_EQ(ZX_OK, message.Read(h2.get(), 0)); | |
| EXPECT_EQ(0u, message.txid()); | |
| EXPECT_EQ(5u, message.ordinal()); | |
| fidl_string_t* view = message.GetPayloadAs<fidl_string_t>(); | |
| EXPECT_EQ(6u, view->size); | |
| } | |
| TEST(ProxyController, Callback) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller; | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| Encoder encoder(3u); | |
| StringPtr string("hello!"); | |
| fidl::Encode(&encoder, &string, encoder.Alloc(sizeof(fidl_string_t))); | |
| int callback_count = 0; | |
| auto handler = std::make_unique<SingleUseMessageHandler>( | |
| [&callback_count](Message&& message) { | |
| ++callback_count; | |
| EXPECT_EQ(42u, message.ordinal()); | |
| return ZX_OK; | |
| }, | |
| &zero_arg_message_type); | |
| EXPECT_EQ(ZX_OK, controller.Send(&unbounded_nonnullable_string_message_type, encoder.GetMessage(), | |
| std::move(handler))); | |
| EXPECT_EQ(0, callback_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(0, callback_count); | |
| MessageBuffer buffer; | |
| Message message = buffer.CreateEmptyMessage(); | |
| EXPECT_EQ(ZX_OK, message.Read(h2.get(), 0)); | |
| EXPECT_NE(0u, message.txid()); | |
| EXPECT_EQ(3u, message.ordinal()); | |
| zx_txid_t txid = message.txid(); | |
| fidl_message_header_t header = {}; | |
| fidl_init_txn_header(&header, txid, 42u); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &header, sizeof(fidl_message_header_t), nullptr, 0)); | |
| EXPECT_EQ(0, callback_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(1, callback_count); | |
| } | |
| TEST(ProxyController, BadSend) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller; | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| Encoder encoder(3u); | |
| // Bad message format. | |
| int error_count = 0; | |
| controller.reader().set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, status); | |
| ++error_count; | |
| }); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, controller.Send(&unbounded_nonnullable_string_message_type, | |
| encoder.GetMessage(), nullptr)); | |
| EXPECT_EQ(0, error_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(ZX_OK, controller.reader().Unbind().replace(ZX_RIGHT_WAIT, &h1)); | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| encoder.Reset(35u); | |
| StringPtr string("hello!"); | |
| fidl::Encode(&encoder, &string, encoder.Alloc(sizeof(fidl_string_t))); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(ZX_ERR_ACCESS_DENIED, controller.Send(&unbounded_nonnullable_string_message_type, | |
| encoder.GetMessage(), nullptr)); | |
| EXPECT_EQ(0, error_count); | |
| } | |
| TEST(ProxyController, BadReply) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller; | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| zx_status_t expected_error = ZX_ERR_NOT_SUPPORTED; | |
| int error_count = 0; | |
| controller.reader().set_error_handler([&expected_error, &error_count](zx_status_t status) { | |
| EXPECT_EQ(expected_error, status); | |
| ++error_count; | |
| }); | |
| fidl_message_header_t header = {}; | |
| fidl_init_txn_header(&header, 0, 42u); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &header, sizeof(fidl_message_header_t), nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(1, error_count); | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| header.txid = 42u; | |
| expected_error = ZX_ERR_NOT_FOUND; | |
| EXPECT_EQ(ZX_OK, h2.write(0, &header, sizeof(fidl_message_header_t), nullptr, 0)); | |
| EXPECT_EQ(1, error_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(2, error_count); | |
| } | |
| TEST(ProxyController, ShortReply) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller; | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| int expected_error = ZX_ERR_NOT_SUPPORTED; | |
| int error_count = 0; | |
| controller.reader().set_error_handler([&expected_error, &error_count](zx_status_t status) { | |
| EXPECT_EQ(expected_error, status); | |
| ++error_count; | |
| }); | |
| fidl_message_header_t header = {}; | |
| fidl_init_txn_header(&header, 0, 42u); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &header, sizeof(fidl_message_header_t), nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(1, error_count); | |
| expected_error = ZX_ERR_INVALID_ARGS; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| header.txid = 42u; | |
| EXPECT_EQ(ZX_OK, h2.write(0, "a", 1, nullptr, 0)); | |
| EXPECT_EQ(1, error_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(2, error_count); | |
| } | |
| TEST(ProxyController, Move) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller1; | |
| EXPECT_EQ(ZX_OK, controller1.reader().Bind(std::move(h1))); | |
| Encoder encoder(3u); | |
| StringPtr string("hello!"); | |
| fidl::Encode(&encoder, &string, encoder.Alloc(sizeof(fidl_string_t))); | |
| int callback_count = 0; | |
| auto handler = std::make_unique<SingleUseMessageHandler>( | |
| [&callback_count](Message&& message) { | |
| ++callback_count; | |
| EXPECT_EQ(42u, message.ordinal()); | |
| return ZX_OK; | |
| }, | |
| &zero_arg_message_type); | |
| EXPECT_EQ(ZX_OK, controller1.Send(&unbounded_nonnullable_string_message_type, | |
| encoder.GetMessage(), std::move(handler))); | |
| EXPECT_EQ(0, callback_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(0, callback_count); | |
| MessageBuffer buffer; | |
| Message message = buffer.CreateEmptyMessage(); | |
| EXPECT_EQ(ZX_OK, message.Read(h2.get(), 0)); | |
| EXPECT_NE(0u, message.txid()); | |
| EXPECT_EQ(3u, message.ordinal()); | |
| ProxyController controller2 = std::move(controller1); | |
| EXPECT_FALSE(controller1.reader().is_bound()); | |
| EXPECT_TRUE(controller2.reader().is_bound()); | |
| zx_txid_t txid = message.txid(); | |
| fidl_message_header_t header = {}; | |
| fidl_init_txn_header(&header, txid, 42u); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &header, sizeof(fidl_message_header_t), nullptr, 0)); | |
| EXPECT_EQ(0, callback_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(1, callback_count); | |
| } | |
| TEST(ProxyController, Reset) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller; | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| Encoder encoder(3u); | |
| StringPtr string("hello!"); | |
| fidl::Encode(&encoder, &string, encoder.Alloc(sizeof(fidl_string_t))); | |
| int callback_count = 0; | |
| auto handler = std::make_unique<SingleUseMessageHandler>( | |
| [&callback_count](Message&& message) { | |
| ++callback_count; | |
| EXPECT_EQ(42u, message.ordinal()); | |
| return ZX_OK; | |
| }, | |
| &zero_arg_message_type); | |
| EXPECT_EQ(ZX_OK, controller.Send(&unbounded_nonnullable_string_message_type, encoder.GetMessage(), | |
| std::move(handler))); | |
| EXPECT_EQ(0, callback_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(0, callback_count); | |
| MessageBuffer buffer; | |
| Message message = buffer.CreateEmptyMessage(); | |
| EXPECT_EQ(ZX_OK, message.Read(h2.get(), 0)); | |
| EXPECT_NE(0u, message.txid()); | |
| EXPECT_EQ(3u, message.ordinal()); | |
| controller.Reset(); | |
| EXPECT_FALSE(controller.reader().is_bound()); | |
| zx_txid_t txid = message.txid(); | |
| fidl_message_header_t header = {}; | |
| fidl_init_txn_header(&header, txid, 42u); | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, h2.write(0, &header, sizeof(fidl_message_header_t), nullptr, 0)); | |
| EXPECT_EQ(0, callback_count); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(0, callback_count); | |
| } | |
| TEST(ProxyController, ReentrantDestructor) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| ProxyController controller; | |
| EXPECT_EQ(ZX_OK, controller.reader().Bind(std::move(h1))); | |
| Encoder encoder(3u); | |
| StringPtr string("hello!"); | |
| fidl::Encode(&encoder, &string, encoder.Alloc(sizeof(fidl_string_t))); | |
| int destructor_count = 0; | |
| auto defer = fit::defer([&destructor_count, &controller]() { | |
| ++destructor_count; | |
| EXPECT_EQ(destructor_count, 1); | |
| Encoder encoder(3u); | |
| StringPtr string("world!"); | |
| fidl::Encode(&encoder, &string, encoder.Alloc(sizeof(fidl_string_t))); | |
| auto callback_handler = std::make_unique<SingleUseMessageHandler>( | |
| [](Message&& message) { return ZX_OK; }, &zero_arg_message_type); | |
| zx_status_t status = controller.Send(&unbounded_nonnullable_string_message_type, | |
| encoder.GetMessage(), std::move(callback_handler)); | |
| EXPECT_EQ(ZX_ERR_BAD_HANDLE, status); | |
| controller.Reset(); | |
| }); | |
| auto handler = std::make_unique<SingleUseMessageHandler>( | |
| [defer = std::move(defer)](Message&& message) { return ZX_OK; }, &zero_arg_message_type); | |
| EXPECT_EQ(ZX_OK, controller.Send(&unbounded_nonnullable_string_message_type, encoder.GetMessage(), | |
| std::move(handler))); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(destructor_count, 0); | |
| controller.Reset(); | |
| EXPECT_EQ(destructor_count, 1); | |
| EXPECT_FALSE(controller.reader().is_bound()); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(destructor_count, 1); | |
| } | |
| } // namespace | |
| } // namespace internal | |
| } // namespace fidl |