| // 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/message_buffer.h> |
| #include <lib/fidl/cpp/message_builder.h> |
| #include <lib/zx/channel.h> |
| |
| #include "gtest/gtest.h" |
| #include "lib/fidl/cpp/internal/proxy_controller.h" |
| #include "lib/fidl/cpp/internal/stub.h" |
| #include "lib/fidl/cpp/internal/stub_controller.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" |
| |
| namespace fidl { |
| namespace internal { |
| namespace { |
| |
| class CallbackStub : public Stub { |
| public: |
| fit::function<zx_status_t(Message, PendingResponse)> callback; |
| |
| zx_status_t Dispatch_(Message message, PendingResponse response) override { |
| return callback(std::move(message), std::move(response)); |
| } |
| }; |
| |
| class CallbackMessageHandler : public MessageHandler { |
| public: |
| fit::function<zx_status_t(Message)> callback; |
| |
| zx_status_t OnMessage(Message message) override { |
| return callback(std::move(message)); |
| } |
| }; |
| |
| TEST(StubController, Trivial) { StubController controller; } |
| |
| TEST(StubController, NoResponse) { |
| zx::channel h1, h2; |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); |
| |
| fidl::test::AsyncLoopForTest loop; |
| |
| StubController stub_ctrl; |
| EXPECT_EQ(ZX_OK, stub_ctrl.reader().Bind(std::move(h1))); |
| |
| ProxyController proxy_ctrl; |
| EXPECT_EQ(ZX_OK, proxy_ctrl.reader().Bind(std::move(h2))); |
| |
| CallbackStub stub; |
| |
| int callback_count = 0; |
| stub.callback = [&callback_count](Message message, PendingResponse response) { |
| ++callback_count; |
| EXPECT_EQ(5u, message.ordinal()); |
| EXPECT_FALSE(response.needs_response()); |
| EXPECT_EQ( |
| ZX_ERR_BAD_STATE, |
| response.Send(&unbounded_nonnullable_string_message_type, Message())); |
| return ZX_OK; |
| }; |
| |
| stub_ctrl.set_stub(&stub); |
| |
| Encoder encoder(5u); |
| StringPtr string("hello!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| |
| EXPECT_EQ(ZX_OK, proxy_ctrl.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage(), nullptr)); |
| EXPECT_EQ(0, callback_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(1, callback_count); |
| } |
| |
| TEST(StubController, Response) { |
| zx::channel h1, h2; |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); |
| |
| fidl::test::AsyncLoopForTest loop; |
| |
| StubController stub_ctrl; |
| EXPECT_EQ(ZX_OK, stub_ctrl.reader().Bind(std::move(h1))); |
| |
| ProxyController proxy_ctrl; |
| EXPECT_EQ(ZX_OK, proxy_ctrl.reader().Bind(std::move(h2))); |
| |
| CallbackStub stub; |
| |
| int callback_count = 0; |
| stub.callback = [&callback_count](Message message, PendingResponse response) { |
| ++callback_count; |
| EXPECT_EQ(5u, message.ordinal()); |
| EXPECT_TRUE(response.needs_response()); |
| Encoder encoder(42u); |
| StringPtr string("welcome!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| EXPECT_EQ(ZX_OK, response.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage())); |
| return ZX_OK; |
| }; |
| |
| stub_ctrl.set_stub(&stub); |
| |
| Encoder encoder(5u); |
| StringPtr string("hello!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| |
| int response_count = 0; |
| auto handler = std::make_unique<CallbackMessageHandler>(); |
| handler->callback = [&response_count](Message message) { |
| ++response_count; |
| EXPECT_EQ(42u, message.ordinal()); |
| return ZX_OK; |
| }; |
| |
| EXPECT_EQ(ZX_OK, proxy_ctrl.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage(), std::move(handler))); |
| EXPECT_EQ(0, callback_count); |
| EXPECT_EQ(0, response_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(1, callback_count); |
| EXPECT_EQ(1, response_count); |
| } |
| |
| TEST(StubController, ResponseAfterUnbind) { |
| zx::channel h1, h2; |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); |
| |
| fidl::test::AsyncLoopForTest loop; |
| |
| StubController stub_ctrl; |
| EXPECT_EQ(ZX_OK, stub_ctrl.reader().Bind(std::move(h1))); |
| |
| ProxyController proxy_ctrl; |
| EXPECT_EQ(ZX_OK, proxy_ctrl.reader().Bind(std::move(h2))); |
| |
| CallbackStub stub; |
| |
| int callback_count = 0; |
| stub.callback = [&callback_count, &stub_ctrl](Message message, |
| PendingResponse response) { |
| ++callback_count; |
| |
| stub_ctrl.reader().Unbind(); |
| |
| EXPECT_EQ(5u, message.ordinal()); |
| EXPECT_TRUE(response.needs_response()); |
| Encoder encoder(42u); |
| StringPtr string("welcome!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| EXPECT_EQ(ZX_ERR_BAD_STATE, |
| response.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage())); |
| return ZX_OK; |
| }; |
| |
| stub_ctrl.set_stub(&stub); |
| |
| Encoder encoder(5u); |
| StringPtr string("hello!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| |
| int response_count = 0; |
| auto handler = std::make_unique<CallbackMessageHandler>(); |
| handler->callback = [&response_count](Message message) { |
| ++response_count; |
| return ZX_OK; |
| }; |
| |
| EXPECT_EQ(ZX_OK, proxy_ctrl.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage(), std::move(handler))); |
| EXPECT_EQ(0, callback_count); |
| EXPECT_EQ(0, response_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(1, callback_count); |
| EXPECT_EQ(0, response_count); |
| } |
| |
| TEST(StubController, ResponseAfterDestroy) { |
| zx::channel h1, h2; |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); |
| |
| fidl::test::AsyncLoopForTest loop; |
| |
| auto stub_ctrl = std::make_unique<StubController>(); |
| EXPECT_EQ(ZX_OK, stub_ctrl->reader().Bind(std::move(h1))); |
| |
| ProxyController proxy_ctrl; |
| EXPECT_EQ(ZX_OK, proxy_ctrl.reader().Bind(std::move(h2))); |
| |
| CallbackStub stub; |
| |
| int callback_count = 0; |
| stub.callback = [&callback_count, &stub_ctrl](Message message, |
| PendingResponse response) { |
| ++callback_count; |
| |
| stub_ctrl.reset(); |
| |
| EXPECT_EQ(5u, message.ordinal()); |
| EXPECT_TRUE(response.needs_response()); |
| Encoder encoder(42u); |
| StringPtr string("welcome!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| EXPECT_EQ(ZX_ERR_BAD_STATE, |
| response.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage())); |
| return ZX_OK; |
| }; |
| |
| stub_ctrl->set_stub(&stub); |
| |
| Encoder encoder(5u); |
| StringPtr string("hello!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| |
| int response_count = 0; |
| auto handler = std::make_unique<CallbackMessageHandler>(); |
| handler->callback = [&response_count](Message message) { |
| ++response_count; |
| return ZX_OK; |
| }; |
| |
| EXPECT_EQ(ZX_OK, proxy_ctrl.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage(), std::move(handler))); |
| EXPECT_EQ(0, callback_count); |
| EXPECT_EQ(0, response_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(1, callback_count); |
| EXPECT_EQ(0, response_count); |
| } |
| |
| TEST(StubController, BadResponse) { |
| zx::channel h1, h2; |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); |
| |
| fidl::test::AsyncLoopForTest loop; |
| |
| StubController stub_ctrl; |
| EXPECT_EQ(ZX_OK, stub_ctrl.reader().Bind(std::move(h1))); |
| |
| int error_count = 0; |
| stub_ctrl.reader().set_error_handler( |
| [&error_count](zx_status_t status) { |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, status); |
| ++error_count; |
| }); |
| |
| ProxyController proxy_ctrl; |
| EXPECT_EQ(ZX_OK, proxy_ctrl.reader().Bind(std::move(h2))); |
| |
| CallbackStub stub; |
| |
| int callback_count = 0; |
| stub.callback = [&callback_count](Message message, PendingResponse response) { |
| ++callback_count; |
| EXPECT_EQ(5u, message.ordinal()); |
| EXPECT_TRUE(response.needs_response()); |
| Encoder encoder(42u); |
| // Bad message format. |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, |
| response.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage())); |
| return ZX_OK; |
| }; |
| |
| stub_ctrl.set_stub(&stub); |
| |
| Encoder encoder(5u); |
| StringPtr string("hello!"); |
| string.Encode(&encoder, encoder.Alloc(sizeof(fidl_string_t))); |
| |
| int response_count = 0; |
| auto handler = std::make_unique<CallbackMessageHandler>(); |
| handler->callback = [&response_count](Message message) { |
| ++response_count; |
| return ZX_OK; |
| }; |
| |
| EXPECT_EQ(ZX_OK, proxy_ctrl.Send(&unbounded_nonnullable_string_message_type, |
| encoder.GetMessage(), std::move(handler))); |
| EXPECT_EQ(0, callback_count); |
| EXPECT_EQ(0, response_count); |
| EXPECT_EQ(0, error_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(1, callback_count); |
| EXPECT_EQ(0, response_count); |
| EXPECT_EQ(0, error_count); |
| } |
| |
| TEST(StubController, BadMessage) { |
| zx::channel h1, h2; |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); |
| |
| fidl::test::AsyncLoopForTest loop; |
| |
| StubController stub_ctrl; |
| EXPECT_EQ(ZX_OK, stub_ctrl.reader().Bind(std::move(h1))); |
| |
| int error_count = 0; |
| stub_ctrl.reader().set_error_handler( |
| [&error_count](zx_status_t status) { |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, status); |
| ++error_count; |
| }); |
| |
| CallbackStub stub; |
| |
| int callback_count = 0; |
| stub.callback = [&callback_count](Message message, PendingResponse response) { |
| ++callback_count; |
| return ZX_OK; |
| }; |
| |
| stub_ctrl.set_stub(&stub); |
| |
| EXPECT_EQ(ZX_OK, h2.write(0, "a", 1, nullptr, 0)); |
| |
| EXPECT_EQ(0, callback_count); |
| EXPECT_EQ(0, error_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(0, callback_count); |
| EXPECT_EQ(1, error_count); |
| } |
| |
| } // namespace |
| } // namespace internal |
| } // namespace fidl |