| // Copyright 2019 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 "src/lib/fidl_codec/message_decoder.h" |
| |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/fidl/cpp/test/frobinator_impl.h> |
| |
| #include <iostream> |
| #include <memory> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| #include <test/fidlcodec/examples/cpp/fidl.h> |
| |
| #include "src/lib/fidl_codec/fidl_codec_test.h" |
| #include "src/lib/fidl_codec/library_loader.h" |
| #include "src/lib/fidl_codec/library_loader_test_data.h" |
| #include "src/lib/fidl_codec/message_decoder.h" |
| #include "src/lib/fidl_codec/wire_object.h" |
| #include "src/lib/fidl_codec/wire_parser.h" |
| |
| using test::fidlcodec::examples::Echo; |
| using test::fidlcodec::examples::FidlCodecTestInterface; |
| |
| namespace fidl_codec { |
| |
| constexpr int kColumns = 80; |
| constexpr uint64_t kProcessKoid = 0x1234; |
| |
| class MessageDecoderTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| loader_ = GetLoader(); |
| ASSERT_NE(loader_, nullptr); |
| display_options_.pretty_print = true; |
| display_options_.columns = kColumns; |
| decoder_ = std::make_unique<MessageDecoderDispatcher>(loader_, display_options_); |
| } |
| |
| // Intercepts the caller's method call on a FIDL InterfacePtr and returns the bytes |
| // sent over the channel. |
| template <class T> |
| fidl::Message InvokeAndIntercept(std::function<void(fidl::InterfacePtr<T>&)> invoker) { |
| fidl::Message message = buffer_.CreateEmptyMessage(); |
| InterceptRequest<T>(message, invoker); |
| return message; |
| } |
| |
| // Simulates a server sending an epitaph and returns the bytes sent over the channel. |
| fidl::Message InvokeAndReceiveEpitaph(zx_status_t epitaph) { |
| fidl::Message message = buffer_.CreateEmptyMessage(); |
| // The protocol doesn't matter, no methods are actually called. |
| InterceptEpitaphResponse<FidlCodecTestInterface>(message, epitaph); |
| return message; |
| } |
| |
| // Asserts that the decoded and FIDL message matches the expected display output. |
| // `syscall_type` interprets the FIDL message as received or sent. |
| void AssertDecoded(const fidl::Message& message, SyscallFidlType syscall_type, |
| const char* expected) { |
| std::unique_ptr<zx_handle_info_t[]> handle_infos; |
| if (message.handles().size() > 0) { |
| handle_infos = std::make_unique<zx_handle_info_t[]>(message.handles().size()); |
| for (uint32_t i = 0; i < message.handles().size(); ++i) { |
| handle_infos[i].handle = message.handles().data()[i]; |
| handle_infos[i].type = ZX_OBJ_TYPE_NONE; |
| handle_infos[i].rights = 0; |
| } |
| } |
| |
| DecodedMessage decoded_message; |
| std::stringstream error_stream; |
| decoded_message.DecodeMessage(decoder(), process_koid(), ZX_HANDLE_INVALID, |
| message.bytes().data(), message.bytes().size(), nullptr, 0, |
| syscall_type, error_stream); |
| auto result = std::make_unique<fidl_codec::FidlMessageValue>( |
| &decoded_message, error_stream.str(), message.bytes().data(), message.bytes().size(), |
| nullptr, 0); |
| |
| std::stringstream output; |
| PrettyPrinter printer(output, decoder()->colors(), /*pretty_print=*/true, /*line_header=*/"", |
| /*max_line_size=*/kColumns, |
| /*header_on_every_line=*/false); |
| result->PrettyPrint(nullptr, printer); |
| ASSERT_EQ(output.str(), expected) << "expected = " << expected << " actual = " << output.str(); |
| } |
| |
| MessageDecoderDispatcher* decoder() const { return decoder_.get(); } |
| uint64_t process_koid() const { return process_koid_; } |
| |
| private: |
| fidl::MessageBuffer buffer_; |
| LibraryLoader* loader_; |
| std::unique_ptr<MessageDecoderDispatcher> decoder_; |
| DisplayOptions display_options_; |
| uint64_t process_koid_ = kProcessKoid; |
| }; |
| |
| #define TEST_DECODE_MESSAGE(_interface, _iface, _expected, ...) \ |
| do { \ |
| auto message = InvokeAndIntercept<_interface>( \ |
| [&](fidl::InterfacePtr<_interface>& ptr) { ptr->_iface(__VA_ARGS__); }); \ |
| AssertDecoded(message, SyscallFidlType::kOutputMessage, _expected); \ |
| } while (0) |
| |
| TEST_F(MessageDecoderTest, TestEmptyLaunched) { |
| decoder()->AddLaunchedProcess(process_koid()); |
| TEST_DECODE_MESSAGE(FidlCodecTestInterface, Empty, |
| "sent request test.fidlcodec.examples/FidlCodecTestInterface.Empty = {}\n"); |
| } |
| |
| TEST_F(MessageDecoderTest, TestStringLaunched) { |
| decoder()->AddLaunchedProcess(process_koid()); |
| TEST_DECODE_MESSAGE(FidlCodecTestInterface, String, |
| "sent request test.fidlcodec.examples/FidlCodecTestInterface.String = {\n" |
| " s: string = \"Hello World\"\n" |
| "}\n", |
| "Hello World"); |
| } |
| |
| TEST_F(MessageDecoderTest, TestStringAttached) { |
| TEST_DECODE_MESSAGE(FidlCodecTestInterface, String, |
| "sent request test.fidlcodec.examples/FidlCodecTestInterface.String = {\n" |
| " s: string = \"Hello World\"\n" |
| "}\n", |
| "Hello World"); |
| } |
| |
| TEST_F(MessageDecoderTest, TestEchoLaunched) { |
| decoder()->AddLaunchedProcess(process_koid()); |
| TEST_DECODE_MESSAGE(Echo, EchoString, |
| "sent request test.fidlcodec.examples/Echo.EchoString = {\n" |
| " value: string = \"Hello World\"\n" |
| "}\n", |
| "Hello World", [](const ::fidl::StringPtr&) {}); |
| } |
| |
| TEST_F(MessageDecoderTest, TestEchoAttached) { |
| TEST_DECODE_MESSAGE(Echo, EchoString, |
| "Can't determine request/response. it can be:\n" |
| " sent request test.fidlcodec.examples/Echo.EchoString = {\n" |
| " value: string = \"Hello World\"\n" |
| " }\n" |
| " sent response test.fidlcodec.examples/Echo.EchoString = {\n" |
| " response: string = \"Hello World\"\n" |
| " }\n", |
| "Hello World", [](const ::fidl::StringPtr&) {}); |
| } |
| |
| TEST_F(MessageDecoderTest, TestEpitaphReceived) { |
| auto message = InvokeAndReceiveEpitaph(ZX_ERR_UNAVAILABLE); |
| AssertDecoded(message, SyscallFidlType::kInputMessage, "received epitaph ZX_ERR_UNAVAILABLE\n"); |
| } |
| |
| TEST_F(MessageDecoderTest, TestUnknownEpitaphReceived) { |
| auto message = InvokeAndReceiveEpitaph(1990); |
| AssertDecoded(message, SyscallFidlType::kInputMessage, "received epitaph status=1990\n"); |
| } |
| |
| TEST_F(MessageDecoderTest, TestEpitaphSent) { |
| auto message = InvokeAndReceiveEpitaph(ZX_ERR_INTERNAL); |
| AssertDecoded(message, SyscallFidlType::kOutputMessage, "sent epitaph ZX_ERR_INTERNAL\n"); |
| } |
| |
| TEST_F(MessageDecoderTest, TestUnknownEpitaphSent) { |
| auto message = InvokeAndReceiveEpitaph(1990); |
| AssertDecoded(message, SyscallFidlType::kOutputMessage, "sent epitaph status=1990\n"); |
| } |
| |
| } // namespace fidl_codec |