| // 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/binding.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "gtest/gtest.h" |
| #include "lib/fidl/cpp/interface_ptr.h" |
| #include "lib/fidl/cpp/test/async_loop_for_test.h" |
| #include "lib/fidl/cpp/test/frobinator_impl.h" |
| |
| namespace fidl { |
| namespace { |
| |
| TEST(Binding, Trivial) { |
| Binding<fidl::test::frobinator::Frobinator> binding(nullptr); |
| } |
| |
| TEST(Binding, Control) { |
| fidl::test::AsyncLoopForTest loop; |
| |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl); |
| |
| fidl::test::frobinator::FrobinatorPtr ptr; |
| EXPECT_EQ(nullptr, binding.dispatcher()); |
| EXPECT_EQ(ZX_OK, binding.Bind(ptr.NewRequest())); |
| EXPECT_EQ(loop.dispatcher(), binding.dispatcher()); |
| |
| ptr->Frob("hello"); |
| |
| EXPECT_TRUE(impl.frobs.empty()); |
| EXPECT_TRUE(impl.grobs.empty()); |
| |
| EXPECT_EQ(ZX_OK, binding.WaitForMessage()); |
| |
| EXPECT_EQ(1u, impl.frobs.size()); |
| EXPECT_EQ("hello", impl.frobs[0]); |
| EXPECT_TRUE(impl.grobs.empty()); |
| |
| impl.frobs.clear(); |
| |
| std::vector<std::string> responses; |
| ptr->Grob("world", [&responses](StringPtr value) { |
| EXPECT_FALSE(value.is_null()); |
| responses.push_back(std::move(*value)); |
| }); |
| |
| EXPECT_TRUE(impl.frobs.empty()); |
| EXPECT_TRUE(impl.grobs.empty()); |
| EXPECT_TRUE(responses.empty()); |
| |
| loop.RunUntilIdle(); |
| |
| EXPECT_TRUE(impl.frobs.empty()); |
| EXPECT_EQ(1u, impl.grobs.size()); |
| EXPECT_EQ("world", impl.grobs[0]); |
| EXPECT_EQ(1u, responses.size()); |
| EXPECT_EQ("response", responses[0]); |
| } |
| |
| TEST(Binding, Bind) { |
| fidl::test::AsyncLoopForTest loop; |
| |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl); |
| |
| EXPECT_FALSE(binding.is_bound()); |
| EXPECT_EQ(&impl, binding.impl()); |
| |
| auto handle = binding.NewBinding(); |
| EXPECT_TRUE(handle.is_valid()); |
| EXPECT_TRUE(binding.is_bound()); |
| EXPECT_EQ(&impl, binding.impl()); |
| |
| auto request = binding.Unbind(); |
| EXPECT_TRUE(request.is_valid()); |
| EXPECT_FALSE(binding.is_bound()); |
| EXPECT_EQ(&impl, binding.impl()); |
| |
| EXPECT_EQ(ZX_OK, binding.Bind(std::move(request))); |
| EXPECT_FALSE(request.is_valid()); |
| EXPECT_TRUE(binding.is_bound()); |
| EXPECT_EQ(&impl, binding.impl()); |
| } |
| |
| TEST(Binding, ConstructBound) { |
| fidl::test::AsyncLoopForTest loop; |
| |
| InterfaceHandle<fidl::test::frobinator::Frobinator> handle; |
| |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl, |
| handle.NewRequest()); |
| EXPECT_TRUE(handle.is_valid()); |
| EXPECT_TRUE(binding.is_bound()); |
| EXPECT_EQ(&impl, binding.impl()); |
| } |
| |
| TEST(Binding, ErrorHandler) { |
| fidl::test::AsyncLoopForTest loop; |
| |
| InterfaceHandle<fidl::test::frobinator::Frobinator> handle; |
| |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl, |
| handle.NewRequest()); |
| EXPECT_TRUE(handle.is_valid()); |
| EXPECT_TRUE(binding.is_bound()); |
| EXPECT_EQ(&impl, binding.impl()); |
| |
| int error_count = 0; |
| binding.set_error_handler([&error_count, &binding](zx_status_t status) { |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, status); |
| ++error_count; |
| EXPECT_FALSE(binding.is_bound()); |
| }); |
| |
| EXPECT_EQ(ZX_OK, handle.channel().write(0, "a", 1, nullptr, 0)); |
| EXPECT_EQ(0, error_count); |
| |
| loop.RunUntilIdle(); |
| |
| EXPECT_EQ(1, error_count); |
| } |
| |
| TEST(Binding, DestructDuringErrorHandler) { |
| fidl::test::AsyncLoopForTest loop; |
| |
| InterfaceHandle<fidl::test::frobinator::Frobinator> handle; |
| |
| test::FrobinatorImpl impl; |
| auto binding = |
| std::make_unique<Binding<fidl::test::frobinator::Frobinator>>(&impl); |
| binding->Bind(handle.NewRequest()); |
| EXPECT_TRUE(handle.is_valid()); |
| EXPECT_TRUE(binding->is_bound()); |
| EXPECT_EQ(&impl, binding->impl()); |
| |
| int error_count = 0; |
| binding->set_error_handler([&error_count, &binding](zx_status_t status) { |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, status); |
| ++error_count; |
| EXPECT_FALSE(binding->is_bound()); |
| binding.reset(); |
| }); |
| |
| EXPECT_EQ(ZX_OK, handle.channel().write(0, "a", 1, nullptr, 0)); |
| EXPECT_EQ(0, error_count); |
| |
| loop.RunUntilIdle(); |
| |
| EXPECT_EQ(1, error_count); |
| } |
| |
| TEST(Binding, PeerClosedTriggersErrorHandler) { |
| fidl::test::AsyncLoopForTest loop; |
| InterfaceHandle<fidl::test::frobinator::Frobinator> handle; |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl, |
| handle.NewRequest()); |
| |
| int error_count = 0; |
| binding.set_error_handler([&error_count, &binding](zx_status_t status) { |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, status); |
| ++error_count; |
| EXPECT_FALSE(binding.is_bound()); |
| }); |
| |
| handle = nullptr; |
| EXPECT_EQ(0, error_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(1, error_count); |
| } |
| |
| TEST(Binding, UnbindDoesNotTriggerErrorHandler) { |
| fidl::test::AsyncLoopForTest loop; |
| InterfaceHandle<fidl::test::frobinator::Frobinator> handle; |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl, |
| handle.NewRequest()); |
| |
| int error_count = 0; |
| binding.set_error_handler([&error_count](zx_status_t status) { |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, status); |
| ++error_count; |
| }); |
| |
| binding.Unbind(); |
| EXPECT_EQ(0, error_count); |
| loop.RunUntilIdle(); |
| EXPECT_EQ(0, error_count); |
| } |
| |
| TEST(Binding, EpitaphReceivedWhenBindingClosed) { |
| fidl::test::AsyncLoopForTest loop; |
| |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl); |
| |
| constexpr zx_status_t kSysError = 0xabDECADE; |
| |
| fidl::test::frobinator::FrobinatorPtr ptr; |
| EXPECT_EQ(ZX_OK, binding.Bind(ptr.NewRequest())); |
| |
| zx_status_t error = 0; |
| ptr.set_error_handler( |
| [&error](zx_status_t remote_error) { error = remote_error; }); |
| |
| EXPECT_EQ(ZX_OK, binding.Close(kSysError)); |
| |
| // Check that you can only call Close once... |
| EXPECT_EQ(ZX_ERR_BAD_STATE, binding.Close(kSysError)); |
| |
| loop.RunUntilIdle(); |
| |
| EXPECT_EQ(kSysError, error); |
| |
| ptr->Frob("This should break"); |
| EXPECT_EQ(ZX_ERR_BAD_STATE, binding.WaitForMessage()); |
| } |
| |
| TEST(Binding, ErrorHandlerCalledWhenInterfacePtrClosed) { |
| fidl::test::AsyncLoopForTest loop; |
| |
| test::FrobinatorImpl impl; |
| Binding<fidl::test::frobinator::Frobinator> binding(&impl); |
| |
| fidl::test::frobinator::FrobinatorPtr ptr; |
| EXPECT_EQ(ZX_OK, binding.Bind(ptr.NewRequest())); |
| |
| zx_status_t error = 0; |
| binding.set_error_handler( |
| [&error](zx_status_t remote_error) { error = remote_error; }); |
| |
| ptr.Unbind(); |
| |
| loop.RunUntilIdle(); |
| |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, error); |
| |
| ptr->Frob("This should break"); |
| EXPECT_EQ(ZX_ERR_BAD_STATE, binding.WaitForMessage()); |
| } |
| |
| } // namespace |
| } // namespace fidl |