| // 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/message_reader.h" | |
| #include <lib/zx/channel.h> | |
| #include <zircon/errors.h> | |
| #include <zircon/fidl.h> | |
| #include <fidl/test/misc/cpp/fidl.h> | |
| #include <gtest/gtest.h> | |
| #include "lib/fidl/cpp/binding.h" | |
| #include "lib/fidl/cpp/test/async_loop_for_test.h" | |
| #include "lib/fidl/txn_header.h" | |
| namespace fidl { | |
| namespace internal { | |
| namespace { | |
| class CopyingMessageHandler : public MessageHandler { | |
| public: | |
| int message_count_ = 0; | |
| int channel_gone_count_ = 0; | |
| std::vector<uint8_t> bytes_; | |
| std::vector<zx_handle_t> handles_; | |
| zx_status_t OnMessage(Message message) override { | |
| ++message_count_; | |
| auto& bytes = message.bytes(); | |
| bytes_ = std::vector<uint8_t>(bytes.data(), bytes.data() + bytes.actual()); | |
| auto& handles = message.handles(); | |
| handles_ = std::vector<zx_handle_t>(handles.data(), handles.data() + handles.actual()); | |
| return ZX_OK; | |
| } | |
| void OnChannelGone() override { ++channel_gone_count_; } | |
| }; | |
| class StatusMessageHandler : public MessageHandler { | |
| public: | |
| zx_status_t status = ZX_OK; | |
| zx_status_t OnMessage(Message message) override { return status; } | |
| }; | |
| class CallbackMessageHandler : public MessageHandler { | |
| public: | |
| fit::function<zx_status_t(Message)> callback; | |
| zx_status_t OnMessage(Message message) override { return callback(std::move(message)); } | |
| }; | |
| class DestructionCounter { | |
| public: | |
| DestructionCounter(int* counter) : counter_(counter) {} | |
| DestructionCounter(DestructionCounter&& other) : counter_(other.counter_) { | |
| other.counter_ = nullptr; | |
| } | |
| DestructionCounter(const DestructionCounter&) = delete; | |
| DestructionCounter& operator=(const DestructionCounter&) = delete; | |
| DestructionCounter& operator=(DestructionCounter&& other) = delete; | |
| ~DestructionCounter() { | |
| if (counter_) | |
| ++(*counter_); | |
| } | |
| private: | |
| int* counter_ = nullptr; | |
| }; | |
| class EchoServer : public fidl::test::misc::Echo { | |
| public: | |
| explicit EchoServer(fidl::InterfaceRequest<fidl::test::misc::Echo> request) | |
| : binding_(this, std::move(request)) {} | |
| void EchoString(fidl::StringPtr value, EchoStringCallback callback) override { callback(value); } | |
| void Close() { binding_.Close(10); } | |
| private: | |
| fidl::Binding<fidl::test::misc::Echo> binding_; | |
| }; | |
| template <size_t N> | |
| struct StringMessage { | |
| FIDL_ALIGNDECL | |
| fidl_message_header_t hdr; | |
| std::array<char, N> data; | |
| }; | |
| constexpr size_t kMessageDataStart = 16; | |
| template <size_t N> | |
| StringMessage<N> CreateFidlMessage(const char (&data)[N]) { | |
| StringMessage<N> msg; | |
| fidl_init_txn_header(&msg.hdr, 0, 0); | |
| memcpy(&msg.data, data, N); | |
| return msg; | |
| } | |
| const auto test_msg0 = CreateFidlMessage("hello"); | |
| const uint32_t test_msg0_size = 21; | |
| const auto test_msg1 = CreateFidlMessage(", world"); | |
| const uint32_t test_msg1_size = 23; | |
| const auto test_msg2 = CreateFidlMessage("!"); | |
| const uint32_t test_msg2_size = 17; | |
| TEST(MessageReader, Trivial) { MessageReader reader; } | |
| TEST(MessageReader, Bind) { | |
| MessageReader reader; | |
| EXPECT_EQ(ZX_OK, reader.Bind(zx::channel())); | |
| EXPECT_FALSE(reader.is_bound()); | |
| EXPECT_EQ(ZX_HANDLE_INVALID, reader.Unbind().get()); | |
| } | |
| TEST(MessageReader, Control) { | |
| CopyingMessageHandler handler; | |
| MessageReader reader(&handler); | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| EXPECT_FALSE(reader.is_bound()); | |
| zx_handle_t saved = h1.get(); | |
| reader.Bind(std::move(h1)); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(saved, reader.channel().get()); | |
| EXPECT_EQ(0, handler.channel_gone_count_); | |
| zx::channel h3 = reader.Unbind(); | |
| EXPECT_EQ(saved, h3.get()); | |
| EXPECT_EQ(1, handler.channel_gone_count_); | |
| reader.Bind(std::move(h3)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(0, handler.message_count_); | |
| EXPECT_EQ(ZX_OK, reader.WaitAndDispatchOneMessageUntil(zx::time::infinite())); | |
| EXPECT_EQ(1, handler.message_count_); | |
| EXPECT_EQ(test_msg0_size, handler.bytes_.size()); | |
| EXPECT_EQ('h', handler.bytes_[kMessageDataStart]); | |
| EXPECT_EQ(0u, handler.handles_.size()); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg1, test_msg1_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(2, handler.message_count_); | |
| EXPECT_EQ(test_msg1_size, handler.bytes_.size()); | |
| EXPECT_EQ(',', handler.bytes_[kMessageDataStart]); | |
| EXPECT_EQ(0u, handler.handles_.size()); | |
| int error_count = 0; | |
| reader.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, status); | |
| ++error_count; | |
| }); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| h2.reset(); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(1, handler.channel_gone_count_); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(1, error_count); | |
| EXPECT_EQ(2, handler.channel_gone_count_); | |
| EXPECT_FALSE(reader.is_bound()); | |
| } | |
| TEST(MessageReader, HandlerError) { | |
| StatusMessageHandler handler; | |
| handler.status = ZX_ERR_INTERNAL; | |
| MessageReader reader; | |
| reader.set_message_handler(&handler); | |
| int error_count = 0; | |
| reader.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_INTERNAL, status); | |
| ++error_count; | |
| }); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| reader.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(1, error_count); | |
| EXPECT_FALSE(reader.is_bound()); | |
| } | |
| TEST(MessageReader, HandlerErrorWithoutErrorHandler) { | |
| StatusMessageHandler handler; | |
| handler.status = ZX_ERR_INTERNAL; | |
| MessageReader reader; | |
| reader.set_message_handler(&handler); | |
| EXPECT_FALSE(reader.has_error_handler()); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| reader.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_FALSE(reader.is_bound()); | |
| } | |
| TEST(MessageReader, BindTwice) { | |
| MessageReader reader; | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2, j1, j2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &j1, &j2)); | |
| zx_handle_t saved = j1.get(); | |
| reader.Bind(std::move(h1)); | |
| reader.Bind(std::move(j1)); | |
| zx_signals_t pending = ZX_SIGNAL_NONE; | |
| EXPECT_EQ(ZX_OK, h2.wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time(), &pending)); | |
| EXPECT_TRUE(pending & ZX_CHANNEL_PEER_CLOSED); | |
| EXPECT_EQ(saved, reader.channel().get()); | |
| } | |
| TEST(MessageReader, WaitAndDispatchOneMessageUntilErrors) { | |
| MessageReader reader; | |
| int error_count = 0; | |
| reader.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, status); | |
| ++error_count; | |
| }); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(ZX_ERR_BAD_STATE, reader.WaitAndDispatchOneMessageUntil(zx::time())); | |
| EXPECT_EQ(0, error_count); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| h1.replace(ZX_RIGHT_NONE, &h1); | |
| EXPECT_EQ(ZX_ERR_ACCESS_DENIED, reader.Bind(std::move(h1))); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_FALSE(reader.is_bound()); | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| EXPECT_EQ(ZX_OK, reader.Bind(std::move(h1))); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(ZX_ERR_TIMED_OUT, reader.WaitAndDispatchOneMessageUntil(zx::time())); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_TRUE(reader.is_bound()); | |
| reader.Bind(std::move(h2)); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, reader.WaitAndDispatchOneMessageUntil(zx::time())); | |
| EXPECT_EQ(1, error_count); | |
| EXPECT_FALSE(reader.is_bound()); | |
| } | |
| TEST(MessageReader, UnbindDuringHandler) { | |
| MessageReader reader; | |
| zx::channel stash; | |
| CallbackMessageHandler handler; | |
| handler.callback = [&reader, &stash](Message message) { | |
| stash = reader.Unbind(); | |
| return ZX_OK; | |
| }; | |
| reader.set_message_handler(&handler); | |
| int error_count = 0; | |
| reader.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, status); | |
| ++error_count; | |
| }); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| reader.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_FALSE(stash.is_valid()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_FALSE(reader.is_bound()); | |
| EXPECT_TRUE(stash.is_valid()); | |
| // The handler unbound the channel, which means the reader is no longer | |
| // listening to the channel. | |
| CopyingMessageHandler logger; | |
| reader.set_message_handler(&logger); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_FALSE(reader.is_bound()); | |
| EXPECT_EQ(0, logger.message_count_); | |
| reader.set_message_handler(nullptr); | |
| } | |
| TEST(MessageReader, ShouldWaitFromRead) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| CallbackMessageHandler handler; | |
| MessageReader reader(&handler); | |
| int message_count = 0; | |
| handler.callback = [&message_count, &reader](Message message) { | |
| ++message_count; | |
| uint32_t actual_bytes, actual_handles; | |
| EXPECT_EQ(ZX_ERR_BUFFER_TOO_SMALL, | |
| reader.channel().read(ZX_CHANNEL_READ_MAY_DISCARD, nullptr, nullptr, 0, 0, | |
| &actual_bytes, &actual_handles)); | |
| return ZX_OK; | |
| }; | |
| int error_count = 0; | |
| reader.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_CANCELED, status); | |
| ++error_count; | |
| }); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| fidl::test::AsyncLoopForTest loop; | |
| reader.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg1, test_msg1_size, nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(0, message_count); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(1, message_count); | |
| EXPECT_TRUE(reader.is_bound()); | |
| // The reader should still be listening to the channel. | |
| CopyingMessageHandler logger; | |
| reader.set_message_handler(&logger); | |
| auto msg = CreateFidlMessage("again!"); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &msg, 22, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(1, logger.message_count_); | |
| EXPECT_EQ(22u, logger.bytes_.size()); | |
| EXPECT_EQ('a', logger.bytes_[kMessageDataStart]); | |
| reader.set_message_handler(nullptr); | |
| } | |
| TEST(MessageReader, ShouldWaitFromReadWithUnbind) { | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| CallbackMessageHandler handler; | |
| MessageReader reader(&handler); | |
| int message_count = 0; | |
| handler.callback = [&message_count, &reader](Message message) { | |
| ++message_count; | |
| uint32_t actual_bytes, actual_handles; | |
| EXPECT_EQ(ZX_ERR_BUFFER_TOO_SMALL, | |
| reader.channel().read(ZX_CHANNEL_READ_MAY_DISCARD, nullptr, nullptr, 0, 0, | |
| &actual_bytes, &actual_handles)); | |
| reader.Unbind(); | |
| return ZX_OK; | |
| }; | |
| int error_count = 0; | |
| reader.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_INVALID_ARGS, status); | |
| ++error_count; | |
| }); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| fidl::test::AsyncLoopForTest loop; | |
| reader.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg1, test_msg1_size, nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(0, message_count); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(1, message_count); | |
| EXPECT_FALSE(reader.is_bound()); | |
| // The handler unbound the channel, so the reader should not be listening to | |
| // the channel anymore. | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| reader.set_message_handler(nullptr); | |
| } | |
| TEST(MessageReader, NoHandler) { | |
| MessageReader reader; | |
| int error_count = 0; | |
| reader.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_CANCELED, status); | |
| ++error_count; | |
| }); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| reader.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_TRUE(reader.is_bound()); | |
| } | |
| TEST(MessageReader, Reset) { | |
| MessageReader reader; | |
| int destruction_count = 0; | |
| DestructionCounter counter(&destruction_count); | |
| reader.set_error_handler([counter = std::move(counter)](zx_status_t status) {}); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| fidl::test::AsyncLoopForTest loop; | |
| reader.Bind(std::move(h1)); | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(0, destruction_count); | |
| reader.Reset(); | |
| EXPECT_FALSE(reader.is_bound()); | |
| EXPECT_EQ(1, destruction_count); | |
| } | |
| TEST(MessageReader, TakeChannelAndErrorHandlerFrom) { | |
| StatusMessageHandler handler1; | |
| handler1.status = ZX_OK; | |
| MessageReader reader1; | |
| reader1.set_message_handler(&handler1); | |
| int error_count = 0; | |
| reader1.set_error_handler([&error_count](zx_status_t status) { | |
| EXPECT_EQ(ZX_ERR_INTERNAL, status); | |
| ++error_count; | |
| }); | |
| EXPECT_TRUE(reader1.has_error_handler()); | |
| StatusMessageHandler handler2; | |
| handler2.status = ZX_ERR_INTERNAL; | |
| MessageReader reader2; | |
| reader2.set_message_handler(&handler2); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| reader1.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, reader2.TakeChannelAndErrorHandlerFrom(&reader1)); | |
| EXPECT_FALSE(reader1.is_bound()); | |
| EXPECT_TRUE(reader2.is_bound()); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(0, error_count); | |
| EXPECT_FALSE(reader1.is_bound()); | |
| EXPECT_TRUE(reader2.is_bound()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_EQ(1, error_count); | |
| EXPECT_FALSE(reader1.is_bound()); | |
| EXPECT_FALSE(reader2.is_bound()); | |
| } | |
| TEST(MessageReader, ReentrantDestruction) { | |
| std::unique_ptr<MessageReader> reader = std::make_unique<MessageReader>(); | |
| int read_count = 0; | |
| CallbackMessageHandler handler; | |
| handler.callback = [&reader, &read_count](Message message) { | |
| ++read_count; | |
| reader.reset(); | |
| return ZX_OK; | |
| }; | |
| reader->set_message_handler(&handler); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| reader->Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg1, test_msg1_size, nullptr, 0)); | |
| EXPECT_TRUE(reader->is_bound()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_FALSE(reader); | |
| // The handler destroyed the reader, which means the reader should have read | |
| // only one of the messages and its endpoint should be closed. | |
| EXPECT_EQ(1, read_count); | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, h2.write(0, "!", 1, nullptr, 0)); | |
| } | |
| TEST(MessageReader, DoubleReentrantDestruction) { | |
| std::unique_ptr<MessageReader> reader = std::make_unique<MessageReader>(); | |
| int read_count = 0; | |
| CallbackMessageHandler handler; | |
| handler.callback = [&reader, &read_count](Message message) { | |
| ++read_count; | |
| if (read_count == 1) { | |
| reader->WaitAndDispatchOneMessageUntil(zx::time::infinite()); | |
| } else { | |
| reader.reset(); | |
| } | |
| return ZX_OK; | |
| }; | |
| reader->set_message_handler(&handler); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| reader->Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg1, test_msg1_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg2, test_msg2_size, nullptr, 0)); | |
| EXPECT_TRUE(reader->is_bound()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_FALSE(reader); | |
| // The handler destroyed the reader in a nested callstack, which means the | |
| // reader should have read two of the messages and its endpoint should be | |
| // closed. | |
| EXPECT_EQ(2, read_count); | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, h2.write(0, "\n", 1, nullptr, 0)); | |
| } | |
| TEST(MessageReader, DoubleReentrantUnbind) { | |
| MessageReader reader; | |
| int read_count = 0; | |
| CallbackMessageHandler handler; | |
| handler.callback = [&reader, &read_count](Message message) { | |
| ++read_count; | |
| if (read_count == 1) { | |
| reader.WaitAndDispatchOneMessageUntil(zx::time::infinite()); | |
| } else { | |
| reader.Unbind(); | |
| } | |
| return ZX_OK; | |
| }; | |
| reader.set_message_handler(&handler); | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| reader.Bind(std::move(h1)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg0, test_msg0_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg1, test_msg1_size, nullptr, 0)); | |
| EXPECT_EQ(ZX_OK, h2.write(0, &test_msg2, test_msg2_size, nullptr, 0)); | |
| ; | |
| EXPECT_TRUE(reader.is_bound()); | |
| EXPECT_EQ(ZX_OK, loop.RunUntilIdle()); | |
| EXPECT_FALSE(reader.is_bound()); | |
| // The handler unbound the reader in a nested callstack, which means the | |
| // reader should have read two of the messages and its endpoint should be | |
| // closed. | |
| EXPECT_EQ(2, read_count); | |
| EXPECT_EQ(ZX_ERR_PEER_CLOSED, h2.write(0, "\n", 1, nullptr, 0)); | |
| } | |
| TEST(MessageReader, ReentrantErrorHandler) { | |
| fidl::test::AsyncLoopForTest loop; | |
| fidl::test::misc::EchoPtr echo_ptr; | |
| EchoServer server(echo_ptr.NewRequest()); | |
| echo_ptr.set_error_handler([](zx_status_t status) {}); | |
| auto* echo_ptr_ptr = echo_ptr.get(); | |
| echo_ptr_ptr->EchoString("Some string", | |
| [echo_ptr = std::move(echo_ptr)](fidl::StringPtr echoed_value) {}); | |
| server.Close(); | |
| loop.RunUntilIdle(); | |
| } | |
| TEST(MessageReader, Close) { | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, h2; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2)); | |
| MessageReader client, server; | |
| client.Bind(std::move(h1)); | |
| EXPECT_TRUE(client.is_bound()); | |
| server.Bind(std::move(h2)); | |
| EXPECT_TRUE(server.is_bound()); | |
| zx_status_t error = 0; | |
| client.set_error_handler([&error](zx_status_t remote_error) { error = remote_error; }); | |
| constexpr zx_status_t kSysError = 0xabDECADE; | |
| EXPECT_EQ(ZX_OK, server.Close(kSysError)); | |
| EXPECT_FALSE(server.is_bound()); | |
| // should only be able to call Close successfully once | |
| EXPECT_EQ(ZX_ERR_BAD_STATE, server.Close(kSysError)); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(kSysError, error); | |
| EXPECT_FALSE(client.is_bound()); | |
| } | |
| TEST(MessageReader, MagicNumberCheck) { | |
| fidl::test::AsyncLoopForTest loop; | |
| zx::channel h1, writer; | |
| EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &writer)); | |
| MessageReader reader; | |
| reader.Bind(std::move(h1)); | |
| EXPECT_TRUE(reader.is_bound()); | |
| zx_status_t error = 0; | |
| reader.set_error_handler([&error](zx_status_t remote_error) { error = remote_error; }); | |
| EXPECT_TRUE(reader.has_error_handler()); | |
| fidl_message_header_t header; | |
| header.magic_number = 0xFF; | |
| EXPECT_EQ(ZX_OK, writer.write(0, &header, sizeof(header), nullptr, 0)); | |
| loop.RunUntilIdle(); | |
| EXPECT_EQ(error, ZX_ERR_PROTOCOL_NOT_SUPPORTED); | |
| } | |
| } // namespace | |
| } // namespace internal | |
| } // namespace fidl |