| // 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); | |
| if (!result->unknown_direction()) { | |
| // When the direction is known, only one of request/response must be set. | |
| ASSERT_TRUE((result->decoded_request() == nullptr) || | |
| (result->decoded_response() == nullptr)); | |
| } | |
| 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 |