| // 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_TRUE(value.has_value()); | |
| 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_FALSE(binding.has_error_handler()); | |
| 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_TRUE(binding.has_error_handler()); | |
| 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_FALSE(binding->has_error_handler()); | |
| 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_TRUE(binding->has_error_handler()); | |
| 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()); | |
| }); | |
| EXPECT_TRUE(binding.has_error_handler()); | |
| 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; | |
| }); | |
| EXPECT_TRUE(binding.has_error_handler()); | |
| 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; }); | |
| EXPECT_TRUE(binding.has_error_handler()); | |
| 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 |