blob: 9fdbcbb36a361e5848413d84b6c6b2de5ddbb14c [file] [log] [blame]
// 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/wire_parser.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 <zircon/rights.h>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <test/fidlcodec/examples/cpp/fidl.h>
#include "src/lib/fidl_codec/encoder.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/logger.h"
#include "src/lib/fidl_codec/message_decoder.h"
#include "src/lib/fidl_codec/wire_object.h"
namespace fidl_codec {
constexpr uint32_t kUninitialized = 0xdeadbeef;
constexpr float kFloatValue = 0.25;
constexpr double kDoubleValue = 9007199254740992.0;
constexpr int kUint32Precision = 8;
const Colors FakeColors(/*new_reset=*/"#rst#", /*new_red=*/"#red#", /*new_green=*/"#gre#",
/*new_blue=*/"#blu#", /*new_white_on_magenta=*/"#wom#",
/*new_yellow_background=*/"#yeb#");
AsyncLoopForTest::AsyncLoopForTest() : impl_(std::make_unique<AsyncLoopForTestImpl>()) {}
AsyncLoopForTest::~AsyncLoopForTest() = default;
zx_status_t AsyncLoopForTest::RunUntilIdle() { return impl_->loop()->RunUntilIdle(); }
zx_status_t AsyncLoopForTest::Run() { return impl_->loop()->Run(); }
async_dispatcher_t* AsyncLoopForTest::dispatcher() { return impl_->loop()->dispatcher(); }
LibraryLoader* InitLoader() {
LibraryReadError err;
auto loader = new LibraryLoader();
fidl_codec_test::FidlcodecExamples examples;
for (const auto& element : examples.map()) {
loader->AddContent(element.second, &err);
}
return loader;
}
LibraryLoader* GetLoader() {
static LibraryLoader* loader = InitLoader();
return loader;
}
class WireParserTest : public ::testing::Test {
protected:
void SetUp() override {
loader_ = GetLoader();
ASSERT_NE(loader_, nullptr);
}
LibraryLoader* loader() const { return loader_; }
private:
LibraryLoader* loader_;
};
TEST_F(WireParserTest, ParseSingleString) {
fidl::IncomingMessageBuffer buffer;
fidl::HLCPPIncomingMessage message = buffer.CreateEmptyIncomingMessage();
InterceptRequest<fidl::test::frobinator::Frobinator>(
message, [](fidl::InterfacePtr<fidl::test::frobinator::Frobinator>& ptr) {
ptr->Grob("one", [](const fidl::StringPtr& /*value*/) { FAIL(); });
});
fidl_message_header_t header = message.header();
const std::vector<const InterfaceMethod*>* methods = loader()->GetByOrdinal(header.ordinal);
ASSERT_NE(methods, nullptr);
ASSERT_TRUE(!methods->empty());
const InterfaceMethod* method = (*methods)[0];
ASSERT_NE(method, nullptr);
ASSERT_EQ("Grob", method->name());
zx_handle_disposition_t* handle_dispositions = nullptr;
if (message.handles().size() > 0) {
handle_dispositions = new zx_handle_disposition_t[message.handles().size()];
for (uint32_t i = 0; i < message.handles().size(); ++i) {
handle_dispositions[i].operation = fidl_codec::kNoHandleDisposition;
handle_dispositions[i].handle = message.handles().data()[i].handle;
handle_dispositions[i].type = ZX_OBJ_TYPE_NONE;
handle_dispositions[i].rights = 0;
handle_dispositions[i].result = ZX_OK;
}
}
std::unique_ptr<fidl_codec::StructValue> decoded_request;
std::stringstream error_stream;
fidl_codec::DecodeRequest(method, message.bytes().data(), message.bytes().size(),
handle_dispositions, message.handles().size(), &decoded_request,
error_stream);
rapidjson::Document actual;
if (decoded_request != nullptr) {
decoded_request->ExtractJson(actual.GetAllocator(), actual);
}
rapidjson::Document expected;
expected.Parse(R"JSON({"value":"one"})JSON");
ASSERT_EQ(expected, actual);
delete[] handle_dispositions;
}
// This is a general-purpose macro for calling InterceptRequest and checking its
// results. It can be generalized to a wide variety of types (and is, below).
// It checks for successful parsing, as well as failure when parsing truncated
// values.
// |_iface| is the interface method name on examples::FidlCodecTestInterface
// (TODO: generalize which interface to use)
// |_json_value| is the expected JSON representation of the message.
// |_pretty_print| is the expected pretty print of the message.
// The remaining parameters are the parameters to |_iface| to generate the
// message.
// If patched_offset is not -1, we patch the encoded buffer with patched_value.
// This is useful when we want to test that we can decode junk data.
// If num_bytes is not -1, instead of decoding the full buffer, we only decode num_bytes of buffer.
// This is helpful when we want to test display of incorect data.
#define TEST_DECODE_WIRE_BODY_COMMON(_iface, patched_offset, patched_value, _json_value, \
_pretty_print, num_bytes, ...) \
do { \
fidl::IncomingMessageBuffer buffer; \
fidl::HLCPPIncomingMessage message = buffer.CreateEmptyIncomingMessage(); \
using test::fidlcodec::examples::FidlCodecTestInterface; \
InterceptRequest<FidlCodecTestInterface>( \
message, \
[&](fidl::InterfacePtr<FidlCodecTestInterface>& ptr) { ptr->_iface(__VA_ARGS__); }); \
\
fidl_message_header_t header = message.header(); \
\
const std::vector<const InterfaceMethod*>* methods = loader()->GetByOrdinal(header.ordinal); \
ASSERT_NE(methods, nullptr); \
ASSERT_TRUE(!methods->empty()); \
const InterfaceMethod* method = (*methods)[0]; \
ASSERT_NE(method, nullptr); \
ASSERT_EQ(#_iface, method->name()); \
\
zx_handle_disposition_t* handle_dispositions = nullptr; \
if (message.handles().size() > 0) { \
handle_dispositions = new zx_handle_disposition_t[message.handles().size()]; \
for (uint32_t i = 0; i < message.handles().size(); ++i) { \
handle_dispositions[i].operation = fidl_codec::kNoHandleDisposition; \
/* Do not keep the returned value (it can be random). Instead use a well known value */ \
handle_dispositions[i].handle = 0x100 + i; \
handle_dispositions[i].type = ZX_OBJ_TYPE_CHANNEL; \
handle_dispositions[i].rights = ZX_DEFAULT_CHANNEL_RIGHTS; \
handle_dispositions[i].result = ZX_OK; \
} \
} \
if (patched_offset != -1) { \
*(reinterpret_cast<uint64_t*>(message.bytes().data() + patched_offset)) = patched_value; \
} \
\
std::stringstream error_stream; \
MessageDecoder decoder(message.bytes().data(), \
(num_bytes == -1) ? message.bytes().size() : num_bytes, \
handle_dispositions, message.handles().size(), error_stream); \
std::unique_ptr<StructValue> object = decoder.DecodeMessage(*method->request()); \
if ((num_bytes == -1) && (patched_offset == -1)) { \
std::cerr << error_stream.str(); \
ASSERT_FALSE(decoder.HasError()) << "Could not decode message"; \
} \
rapidjson::Document actual; \
if (object != nullptr) { \
object->ExtractJson(actual.GetAllocator(), actual); \
} \
rapidjson::StringBuffer actual_string; \
rapidjson::Writer<rapidjson::StringBuffer> actual_w(actual_string); \
actual.Accept(actual_w); \
\
rapidjson::Document expected; \
std::string expected_source = _json_value; \
expected.Parse(expected_source.c_str()); \
rapidjson::StringBuffer expected_string; \
rapidjson::Writer<rapidjson::StringBuffer> expected_w(expected_string); \
expected.Accept(expected_w); \
\
ASSERT_EQ(expected, actual) << "expected = " << expected_string.GetString() << " (" \
<< expected_source << ")" \
<< " and actual = " << actual_string.GetString(); \
\
std::stringstream result; \
if (object != nullptr) { \
PrettyPrinter printer(result, FakeColors, false, "", 80, /*header_on_every_line=*/false); \
object->PrettyPrint(nullptr, printer); \
} \
ASSERT_EQ(result.str(), _pretty_print) \
<< "expected = " << _pretty_print << " actual = " << result.str(); \
\
for (uint32_t actual = 0; actual < message.bytes().actual(); ++actual) { \
std::stringstream error_stream; \
MessageDecoder decoder(message.bytes().data(), actual, handle_dispositions, \
message.handles().size(), error_stream); \
std::unique_ptr<StructValue> object = decoder.DecodeMessage(*method->request()); \
ASSERT_TRUE(decoder.HasError()) << "expect decoder error for buffer size " << actual \
<< " instead of " << message.bytes().actual(); \
} \
\
for (uint32_t actual = 0; message.handles().actual() > actual; actual++) { \
std::stringstream error_stream; \
MessageDecoder decoder(message.bytes().data(), message.bytes().size(), handle_dispositions, \
actual, error_stream); \
std::unique_ptr<StructValue> object = decoder.DecodeMessage(*method->request()); \
ASSERT_TRUE(decoder.HasError()) << "expect decoder error for handle size " << actual \
<< " instead of " << message.handles().actual(); \
} \
\
if ((num_bytes == -1) && (patched_offset == -1)) { \
auto encode_result = Encoder::EncodeMessage(header.txid, header.ordinal, header.flags, \
header.magic_number, *object.get()); \
ASSERT_THAT(encode_result.bytes, ::testing::ElementsAreArray(message.bytes())); \
ASSERT_EQ(message.handles().size(), encode_result.handles.size()); \
\
for (uint32_t i = 0; i < message.handles().size(); ++i) { \
EXPECT_EQ(0x100 + i, encode_result.handles[i].handle); \
} \
} \
\
delete[] handle_dispositions; \
} while (0)
#define TEST_DECODE_WIRE_BODY(_iface, _json_value, _pretty_print, ...) \
TEST_DECODE_WIRE_BODY_COMMON(_iface, -1, 0, _json_value, _pretty_print, -1, __VA_ARGS__)
#define TEST_DECODE_WIRE_BODY_BAD(_iface, _json_value, _pretty_print, num_bytes, ...) \
TEST_DECODE_WIRE_BODY_COMMON(_iface, -1, 0, _json_value, _pretty_print, num_bytes, __VA_ARGS__)
// This is a convenience wrapper for calling TEST_DECODE_WIRE_BODY that simply
// executes the code in a test.
// |_testname| is the name of the test (prepended by Parse in the output)
// |_iface| is the interface method name on examples::FidlCodecTestInterface
// (TODO: generalize which interface to use)
// |_json_value| is the expected JSON representation of the message.
// |_pretty_print| is the expected pretty print of the message.
// The remaining parameters are the parameters to |_iface| to generate the
// message.
#define TEST_DECODE_WIRE(_testname, _iface, _json_value, _pretty_print, ...) \
TEST_F(WireParserTest, Parse##_testname) { \
TEST_DECODE_WIRE_BODY(_iface, _json_value, _pretty_print, __VA_ARGS__); \
}
#define TEST_DECODE_WIRE_PATCHED(_testname, _iface, patched_offset, patched_value, _json_value, \
_pretty_print, ...) \
TEST_F(WireParserTest, Parse##_testname) { \
TEST_DECODE_WIRE_BODY_COMMON(_iface, patched_offset, patched_value, _json_value, \
_pretty_print, -1, __VA_ARGS__); \
}
// Scalar Tests
namespace {
template <class T>
std::string ValueToJson(const std::string& key, T value) {
return "\"" + key + "\":\"" + std::to_string(value) + "\"";
}
template <>
std::string ValueToJson(const std::string& key, bool value) {
return "\"" + key + "\":\"" + (value ? "true" : "false") + "\"";
}
template <>
std::string ValueToJson(const std::string& key, const char* value) {
return "\"" + key + "\":\"" + value + "\"";
}
template <>
std::string ValueToJson(const std::string& key, std::string value) {
return "\"" + key + "\":\"" + value + "\"";
}
template <class T>
std::string SingleToJson(const std::string& key, T value) {
return "{ " + ValueToJson(key, value) + " }";
}
template <class T>
std::string ValueToPretty(const std::string& key, const std::string& type, T value) {
return key + ": #gre#" + type + "#rst# = #blu#" + std::to_string(value) + "#rst#";
}
template <>
std::string ValueToPretty(const std::string& key, const std::string& type, bool value) {
return key + ": #gre#" + type + "#rst# = #blu#" + (value ? "true" : "false") + "#rst#";
}
template <>
std::string ValueToPretty(const std::string& key, const std::string& type, const char* value) {
return key + ": #gre#" + type + "#rst# = #red#\"" + value + "\"#rst#";
}
template <>
std::string ValueToPretty(const std::string& key, const std::string& type, std::string value) {
return key + ": #gre#" + type + "#rst# = #red#\"" + value + "\"#rst#";
}
std::string HandleToJson(const std::string& key, uint32_t value) {
std::stringstream ss;
ss << std::hex << std::setfill('0') << std::setw(kUint32Precision) << value << std::dec
<< std::setw(0);
return "\"" + key + "\":\"Channel:" + ss.str() +
"(ZX_RIGHT_TRANSFER | ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_SIGNAL | " +
"ZX_RIGHT_SIGNAL_PEER | ZX_RIGHT_WAIT | ZX_RIGHT_INSPECT)\"";
}
std::string HandleToPretty(const std::string& key, uint32_t value) {
std::stringstream ss;
ss << std::hex << std::setfill('0') << std::setw(kUint32Precision) << value << std::dec
<< std::setw(0);
return key + ": #gre#handle#rst# = #red#Channel:" + ss.str() + "#rst#(#blu#" +
"ZX_RIGHT_TRANSFER | ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_SIGNAL | " +
"ZX_RIGHT_SIGNAL_PEER | ZX_RIGHT_WAIT | ZX_RIGHT_INSPECT" + "#rst#)";
}
template <class T>
std::string SingleToPretty(const std::string& key, const std::string& type, T value) {
return "{ " + ValueToPretty(key, type, value) + " }";
}
} // namespace
#define TEST_SINGLE(_testname, _iface, _key, _type, _value) \
TEST_DECODE_WIRE(_testname, _iface, SingleToJson(#_key, _value), \
SingleToPretty(#_key, #_type, _value), _value)
TEST_DECODE_WIRE(Empty, Empty, "{}", "{}")
TEST_SINGLE(String, String, s, string, "Hello World!")
TEST_DECODE_WIRE_PATCHED(StringBadSize, String, 16, 100, "{\"s\":\"(invalid)\"}",
"{ s: #gre#string#rst# = #red#invalid#rst# }", "Hello World!")
TEST_DECODE_WIRE_PATCHED(StringHugeSize, String, 16, ULLONG_MAX, "{\"s\":\"(invalid)\"}",
"{ s: #gre#string#rst# = #red#invalid#rst# }", "Hello World!")
TEST_SINGLE(BoolTrue, Bool, b, bool, true)
TEST_SINGLE(BoolFalse, Bool, b, bool, false)
TEST_SINGLE(Int8Min, Int8, i8, int8, std::numeric_limits<int8_t>::min())
TEST_SINGLE(Int16Min, Int16, i16, int16, std::numeric_limits<int16_t>::min())
TEST_SINGLE(Int32Min, Int32, i32, int32, std::numeric_limits<int32_t>::min())
TEST_SINGLE(Int64Min, Int64, i64, int64, std::numeric_limits<int64_t>::min())
TEST_SINGLE(Int8Max, Int8, i8, int8, std::numeric_limits<int8_t>::max())
TEST_SINGLE(Int16Max, Int16, i16, int16, std::numeric_limits<int16_t>::max())
TEST_SINGLE(Int32Max, Int32, i32, int32, std::numeric_limits<int32_t>::max())
TEST_SINGLE(Int64Max, Int64, i64, int64, std::numeric_limits<int64_t>::max())
TEST_SINGLE(Uint8Min, Uint8, ui8, uint8, std::numeric_limits<uint8_t>::min())
TEST_SINGLE(Uint16Min, Uint16, ui16, uint16, std::numeric_limits<uint16_t>::min())
TEST_SINGLE(Uint32Min, Uint32, ui32, uint32, std::numeric_limits<uint32_t>::min())
TEST_SINGLE(Uint64Min, Uint64, ui64, uint64, std::numeric_limits<uint64_t>::min())
TEST_SINGLE(Uint8Max, Uint8, ui8, uint8, std::numeric_limits<uint8_t>::max())
TEST_SINGLE(Uint16Max, Uint16, ui16, uint16, std::numeric_limits<uint16_t>::max())
TEST_SINGLE(Uint32Max, Uint32, ui32, uint32, std::numeric_limits<uint32_t>::max())
TEST_SINGLE(Uint64Max, Uint64, ui64, uint64, std::numeric_limits<uint64_t>::max())
TEST_SINGLE(Float32, Float32, f32, float32, kFloatValue)
TEST_SINGLE(Float64, Float64, f64, float64, kDoubleValue)
TEST_DECODE_WIRE(TwoTuple, Complex, R"({"real":"1", "imaginary":"2"})",
"{ " + ValueToPretty("real", "int32", 1) + ", " +
ValueToPretty("imaginary", "int32", 2) + " }",
1, 2);
TEST_DECODE_WIRE(StringInt, StringInt, R"({"s":"groucho", "i32":"4"})",
"{ " + ValueToPretty("s", "string", "groucho") + ", " +
ValueToPretty("i32", "int32", 4) + " }",
"groucho", 4)
// Vector / Array Tests
namespace {
std::array<int32_t, 1> one_param_array = {1};
std::array<int32_t, 2> two_param_array = {1, 2};
std::vector<int32_t> one_param_vector = {1};
std::vector<int32_t> two_param_vector = {1, 2};
} // namespace
TEST_DECODE_WIRE(Array1, Array1, R"({"b_1":["1"]})",
"{ b_1: array<#gre#int32#rst#> = [ #blu#1#rst# ] }", one_param_array);
TEST_DECODE_WIRE(Array2, Array2, R"({"b_2":["1", "2"]})",
"{ b_2: array<#gre#int32#rst#> = [ #blu#1#rst#, #blu#2#rst# ] }", two_param_array);
TEST_DECODE_WIRE(NullVector, Vector, R"({"v_1": null})",
"{ v_1: vector<#gre#int32#rst#> = #red#null#rst# }", nullptr)
TEST_DECODE_WIRE(VectorOneElt, Vector, R"({"v_1":["1"]})",
"{ v_1: vector<#gre#int32#rst#> = [ #blu#1#rst# ] }", one_param_vector);
TEST_DECODE_WIRE(VectorTwoElt, Vector, R"({"v_1":["1", "2"]})",
"{ v_1: vector<#gre#int32#rst#> = [ #blu#1#rst#, #blu#2#rst# ] }",
two_param_vector);
std::array<std::string, 2> TwoStringArrayFromVals(const std::string& v1, const std::string& v2) {
std::array<std::string, 2> brother_array;
brother_array[0] = v1;
brother_array[1] = v2;
return brother_array;
}
TEST_DECODE_WIRE(TwoStringArrayInt, TwoStringArrayInt, R"({"arr":["harpo","chico"], "i32":"1"})",
R"({ arr: array<#gre#string#rst#> = [ #red#"harpo"#rst#, #red#"chico"#rst# ], )" +
ValueToPretty("i32", "int32", 1) + " }",
TwoStringArrayFromVals("harpo", "chico"), 1)
namespace {
std::vector<std::string> TwoStringVectorFromVals(const std::string& v1, const std::string& v2) {
return std::vector<std::string>({v1, v2});
}
std::vector<uint8_t> VectorUint8() {
const int kItemCount = 40;
std::vector<std::uint8_t> result;
for (int i = 0; i <= kItemCount; ++i) {
result.push_back(i);
}
return result;
}
std::vector<uint8_t> VectorUint8(const char* text) {
std::vector<std::uint8_t> result;
while (*text != 0) {
result.push_back(*text++);
}
return result;
}
std::vector<uint32_t> VectorUint32() {
const int kItemCount = 25;
std::vector<std::uint32_t> result;
for (int i = 0; i <= kItemCount; ++i) {
const int kShift = 16;
result.push_back(i + ((i & 1) << kShift));
}
return result;
}
} // namespace
TEST_DECODE_WIRE(TwoStringVectorInt, TwoStringVectorInt, R"({"vec":["harpo", "chico"], "i32":"1"})",
R"({ vec: vector<#gre#string#rst#> = [ #red#"harpo"#rst#, #red#"chico"#rst# ], )" +
ValueToPretty("i32", "int32", 1) + " }",
TwoStringVectorFromVals("harpo", "chico"), 1)
TEST_DECODE_WIRE(TwoStringVectors, TwoStringVectors,
R"({"v_1":["harpo","chico"],"v_2":["groucho","zeppo"]})",
"{\n v_1: vector<#gre#string#rst#> = "
R"([ #red#"harpo"#rst#, #red#"chico"#rst# ])"
"\n v_2: vector<#gre#string#rst#> = "
R"([ #red#"groucho"#rst#, #red#"zeppo"#rst# ])"
"\n}",
TwoStringVectorFromVals("harpo", "chico"),
TwoStringVectorFromVals("groucho", "zeppo"))
TEST_DECODE_WIRE(
VectorUint8, VectorUint8,
R"({"v":["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40"]})",
"{\n"
" v: vector<#gre#uint8#rst#> = [\n"
" #blu#0#rst#, #blu#1#rst#, #blu#2#rst#, #blu#3#rst#, #blu#4#rst#, #blu#5#rst#, "
"#blu#6#rst#, #blu#7#rst#, #blu#8#rst#, #blu#9#rst#, #blu#10#rst#, #blu#11#rst#, #blu#12#rst#, "
"#blu#13#rst#, #blu#14#rst#, #blu#15#rst#, #blu#16#rst#, #blu#17#rst#, #blu#18#rst#, "
"#blu#19#rst#, #blu#20#rst#\n"
" #blu#21#rst#, #blu#22#rst#, #blu#23#rst#, #blu#24#rst#, #blu#25#rst#, #blu#26#rst#, "
"#blu#27#rst#, #blu#28#rst#, #blu#29#rst#, #blu#30#rst#, #blu#31#rst#, #blu#32#rst#, "
"#blu#33#rst#, #blu#34#rst#, #blu#35#rst#, #blu#36#rst#, #blu#37#rst#, #blu#38#rst#, "
"#blu#39#rst#\n"
" #blu#40#rst#\n"
" ]\n"
"}",
VectorUint8())
TEST_DECODE_WIRE(
VectorUint8String, VectorUint8,
R"({"v":["72","101","108","108","111","32","116","101","115","116","105","110","103","32","119","111","114","108","100","33"]})",
"{ v: vector<#gre#uint8#rst#> = #red#\"Hello testing world!\"#rst# }",
VectorUint8("Hello testing world!"))
TEST_DECODE_WIRE(
VectorUint8MultilineString, VectorUint8,
R"({"v":["72","101","108","108","111","32","116","101","115","116","105","110","103","32","119","111","114","108","100","33","10","72","111","119","32","97","114","101","32","121","111","117","32","116","111","100","97","121", "63","10","73","39","109","32","116","101","115","116","105","110","103","32","102","105","100","108","95","99","111","100","101","99","46"]})",
"{\n"
" v: vector<#gre#uint8#rst#> = [\n"
" #red#Hello testing world!\n"
" How are you today?\n"
" I'm testing fidl_codec.#rst#\n"
" ]\n"
"}",
VectorUint8("Hello testing world!\nHow are you today?\nI'm testing fidl_codec."))
TEST_DECODE_WIRE(
VectorUint32, VectorUint32,
R"({"v":["0","65537","2","65539","4","65541","6","65543","8","65545","10","65547","12","65549","14","65551","16","65553","18","65555","20","65557","22","65559","24","65561"]})",
"{\n"
" v: vector<#gre#uint32#rst#> = [\n"
" #blu#0#rst#, #blu#65537#rst#, #blu#2#rst#, #blu#65539#rst#, #blu#4#rst#, #blu#65541#rst#, "
"#blu#6#rst#, #blu#65543#rst#, #blu#8#rst#, #blu#65545#rst#, #blu#10#rst#, #blu#65547#rst#, "
"#blu#12#rst#, #blu#65549#rst#, #blu#14#rst#\n"
" #blu#65551#rst#, #blu#16#rst#, #blu#65553#rst#, #blu#18#rst#, #blu#65555#rst#, "
"#blu#20#rst#, #blu#65557#rst#, #blu#22#rst#, #blu#65559#rst#, #blu#24#rst#, #blu#65561#rst#\n"
" ]\n"
"}",
VectorUint32())
TEST_DECODE_WIRE_PATCHED(
VectorUint32BadSize, VectorUint32, 16, 100000,
R"({"v":["0","65537","2","65539","4","65541","6","65543","8","65545","10","65547","12","65549","14","65551","16","65553","18","65555","20","65557","22","65559","24","65561"]})",
"{\n"
" v: vector<#gre#uint32#rst#> = [\n"
" #blu#0#rst#, #blu#65537#rst#, #blu#2#rst#, #blu#65539#rst#, #blu#4#rst#, #blu#65541#rst#, "
"#blu#6#rst#, #blu#65543#rst#, #blu#8#rst#, #blu#65545#rst#, #blu#10#rst#, #blu#65547#rst#, "
"#blu#12#rst#, #blu#65549#rst#, #blu#14#rst#\n"
" #blu#65551#rst#, #blu#16#rst#, #blu#65553#rst#, #blu#18#rst#, #blu#65555#rst#, "
"#blu#20#rst#, #blu#65557#rst#, #blu#22#rst#, #blu#65559#rst#, #blu#24#rst#, #blu#65561#rst#\n"
" ]\n"
"}",
VectorUint32())
// Struct Tests
namespace {
class StructSupport {
public:
StructSupport() {
pt.s = "Hello";
pt.b = true;
pt.i8 = std::numeric_limits<int8_t>::min();
pt.i16 = std::numeric_limits<int16_t>::min();
pt.i32 = std::numeric_limits<int32_t>::min();
pt.i64 = std::numeric_limits<int64_t>::min();
pt.u8 = std::numeric_limits<uint8_t>::max();
pt.u16 = std::numeric_limits<uint16_t>::max();
pt.u32 = std::numeric_limits<uint32_t>::max();
pt.u64 = std::numeric_limits<uint64_t>::max();
pt.f32 = kFloatValue;
pt.f64 = kDoubleValue;
}
std::string GetJson() {
std::ostringstream es;
es << R"({"p":{)" << ValueToJson("s", pt.s) << "," << ValueToJson("b", pt.b) << ","
<< ValueToJson("i8", pt.i8) << "," << ValueToJson("i16", pt.i16) << ","
<< ValueToJson("i32", pt.i32) << "," << ValueToJson("i64", pt.i64) << ","
<< ValueToJson("u8", pt.u8) << "," << ValueToJson("u16", pt.u16) << ","
<< ValueToJson("u32", pt.u32) << "," << ValueToJson("u64", pt.u64) << ","
<< ValueToJson("f32", pt.f32) << "," << ValueToJson("f64", pt.f64) << "}}";
return es.str();
}
std::string GetPretty() {
std::ostringstream es;
es << "{\n"
<< " p: #gre#test.fidlcodec.examples/PrimitiveTypes#rst# = {\n"
<< " " << ValueToPretty("s", "string", pt.s) << "\n"
<< " " << ValueToPretty("b", "bool", pt.b) << "\n"
<< " " << ValueToPretty("i8", "int8", pt.i8) << "\n"
<< " " << ValueToPretty("i16", "int16", pt.i16) << "\n"
<< " " << ValueToPretty("i32", "int32", pt.i32) << "\n"
<< " " << ValueToPretty("i64", "int64", pt.i64) << "\n"
<< " " << ValueToPretty("u8", "uint8", pt.u8) << "\n"
<< " " << ValueToPretty("u16", "uint16", pt.u16) << "\n"
<< " " << ValueToPretty("u32", "uint32", pt.u32) << "\n"
<< " " << ValueToPretty("u64", "uint64", pt.u64) << "\n"
<< " " << ValueToPretty("f32", "float32", pt.f32) << "\n"
<< " " << ValueToPretty("f64", "float64", pt.f64) << "\n"
<< " }\n"
<< "}";
return es.str();
}
test::fidlcodec::examples::PrimitiveTypes pt;
};
} // namespace
TEST_F(WireParserTest, ParseStruct) {
StructSupport sd;
TEST_DECODE_WIRE_BODY(Struct, sd.GetJson(), sd.GetPretty(), sd.pt);
}
TEST_F(WireParserTest, BadBoolStruct) {
test::fidlcodec::examples::BoolStructType s;
TEST_DECODE_WIRE_BODY_BAD(BoolStruct, "{\"s\":{\"b\":\"(invalid)\"}}",
"{ s: #gre#test.fidlcodec.examples/BoolStructType#rst# = "
"{ b: #gre#bool#rst# = #red#invalid#rst# } }",
16, s);
}
TEST_DECODE_WIRE(NullableStruct, NullableStruct, R"({"p":null})",
"{ p: #gre#test.fidlcodec.examples/PrimitiveTypes#rst# = #red#null#rst# }",
nullptr);
TEST_DECODE_WIRE(NullableStructAndInt, NullableStructAndInt, R"({"p":null, "i":"1"})",
"{ p: #gre#test.fidlcodec.examples/PrimitiveTypes#rst# = "
"#red#null#rst#, i: #gre#int32#rst# = #blu#1#rst# }",
nullptr, 1);
namespace {
std::array<std::unique_ptr<test::fidlcodec::examples::TwoStringStruct>, 3> GetArrayNullableStruct(
const std::string& v1, const std::string& v2, const std::string& v3, const std::string& v4) {
std::array<std::unique_ptr<test::fidlcodec::examples::TwoStringStruct>, 3> a;
a[0] = std::make_unique<test::fidlcodec::examples::TwoStringStruct>();
a[0]->value1 = v1;
a[0]->value2 = v2;
a[1] = nullptr;
a[2] = std::make_unique<test::fidlcodec::examples::TwoStringStruct>();
a[2]->value1 = v3;
a[2]->value2 = v4;
return a;
}
} // namespace
TEST_DECODE_WIRE(
ArrayNullableStruct, ArrayNullableStruct,
R"({"a":[{"value1":"harpo","value2":"chico"},null,{"value1":"groucho","value2":"zeppo"}]})",
"{\n"
" a: array<#gre#test.fidlcodec.examples/TwoStringStruct#rst#> = [\n"
" { value1: #gre#string#rst# = #red#\"harpo\"#rst#, "
"value2: #gre#string#rst# = #red#\"chico\"#rst# }, #red#null#rst#\n"
" { value1: #gre#string#rst# = #red#\"groucho\"#rst#, "
"value2: #gre#string#rst# = #red#\"zeppo\"#rst# }\n"
" ]\n"
"}",
GetArrayNullableStruct("harpo", "chico", "groucho", "zeppo"))
namespace {
test::fidlcodec::examples::SmallStruct SmallStructFromVals(uint8_t a, uint8_t b, uint8_t c) {
test::fidlcodec::examples::SmallStruct ss;
ss.a = a;
ss.b = b;
ss.c = c;
return ss;
}
} // namespace
TEST_DECODE_WIRE(SmallStruct, SmallStructAfterByte,
R"({"u":"1","s1":{"a":"2","b":"3","c":"4"},"s2":{"a":"5","b":"6","c":"7"}})",
"{\n"
" u: #gre#uint8#rst# = #blu#1#rst#\n"
" s1: #gre#test.fidlcodec.examples/SmallStruct#rst# = {\n"
" a: #gre#uint8#rst# = #blu#2#rst#\n"
" b: #gre#uint8#rst# = #blu#3#rst#\n"
" c: #gre#uint8#rst# = #blu#4#rst#\n"
" }\n"
" s2: #gre#test.fidlcodec.examples/SmallStruct#rst# = {\n"
" a: #gre#uint8#rst# = #blu#5#rst#\n"
" b: #gre#uint8#rst# = #blu#6#rst#\n"
" c: #gre#uint8#rst# = #blu#7#rst#\n"
" }\n"
"}",
1, SmallStructFromVals(2, 3, 4), SmallStructFromVals(5, 6, 7));
namespace {
test::fidlcodec::examples::TwoStringStruct TwoStringStructFromVals(const std::string& v1,
const std::string& v2) {
test::fidlcodec::examples::TwoStringStruct tss;
tss.value1 = v1;
tss.value2 = v2;
return tss;
}
std::unique_ptr<test::fidlcodec::examples::TwoStringStruct> TwoStringStructFromValsPtr(
const std::string& v1, const std::string& v2) {
std::unique_ptr<test::fidlcodec::examples::TwoStringStruct> ptr(
new test::fidlcodec::examples::TwoStringStruct());
ptr->value1 = v1;
ptr->value2 = v2;
return ptr;
}
std::string TwoStringStructIntPretty(const char* s1, const char* s2, int v) {
std::string result = "{\n s: #gre#test.fidlcodec.examples/TwoStringStruct#rst# = {\n";
result += " " + ValueToPretty("value1", "string", s1) + "\n";
result += " " + ValueToPretty("value2", "string", s2) + "\n";
result += " }\n";
result += " " + ValueToPretty("i32", "int32", v) + "\n";
result += "}";
return result;
}
} // namespace
TEST_DECODE_WIRE(TwoStringStructInt, TwoStringStructInt,
R"({"s":{"value1":"harpo", "value2":"chico"}, "i32":"1"})",
TwoStringStructIntPretty("harpo", "chico", 1),
TwoStringStructFromVals("harpo", "chico"), 1)
TEST_DECODE_WIRE(TwoStringNullableStructInt, TwoStringNullableStructInt,
R"({"s":{"value1":"harpo", "value2":"chico"}, "i32":"1"})",
TwoStringStructIntPretty("harpo", "chico", 1),
TwoStringStructFromValsPtr("harpo", "chico"), 1)
TEST_DECODE_WIRE(VectorStruct, VectorStruct,
R"({"v":[{"a":"1","b":"2","c":"3"},{"a":"2","b":"4","c":"6"},)"
R"({"a":"3","b":"6","c":"9"}]})",
"{\n"
" v: vector<#gre#test.fidlcodec.examples/SmallStruct#rst#> = [\n"
" { a: #gre#uint8#rst# = #blu#1#rst#, b: #gre#uint8#rst# = #blu#2#rst#, c: "
"#gre#uint8#rst# = #blu#3#rst# }\n"
" { a: #gre#uint8#rst# = #blu#2#rst#, b: #gre#uint8#rst# = #blu#4#rst#, c: "
"#gre#uint8#rst# = #blu#6#rst# }\n"
" { a: #gre#uint8#rst# = #blu#3#rst#, b: #gre#uint8#rst# = #blu#6#rst#, c: "
"#gre#uint8#rst# = #blu#9#rst# }\n"
" ]\n"
"}",
std::vector{SmallStructFromVals(1, 2, 3), SmallStructFromVals(2, 4, 6),
SmallStructFromVals(3, 6, 9)})
TEST_DECODE_WIRE(ArrayStruct, ArrayStruct,
R"({"a":[{"a":"1","b":"2","c":"3"},{"a":"2","b":"4","c":"6"},)"
R"({"a":"3","b":"6","c":"9"}]})",
"{\n"
" a: array<#gre#test.fidlcodec.examples/SmallStruct#rst#> = [\n"
" { a: #gre#uint8#rst# = #blu#1#rst#, b: #gre#uint8#rst# = #blu#2#rst#, c: "
"#gre#uint8#rst# = #blu#3#rst# }\n"
" { a: #gre#uint8#rst# = #blu#2#rst#, b: #gre#uint8#rst# = #blu#4#rst#, c: "
"#gre#uint8#rst# = #blu#6#rst# }\n"
" { a: #gre#uint8#rst# = #blu#3#rst#, b: #gre#uint8#rst# = #blu#6#rst#, c: "
"#gre#uint8#rst# = #blu#9#rst# }\n"
" ]\n"
"}",
std::array{SmallStructFromVals(1, 2, 3), SmallStructFromVals(2, 4, 6),
SmallStructFromVals(3, 6, 9)})
namespace {
test::fidlcodec::examples::SmallUnevenStruct SmallUnevenStructFromVals(uint8_t a, uint8_t b) {
test::fidlcodec::examples::SmallUnevenStruct ss;
ss.a = a;
ss.b = b;
return ss;
}
} // namespace
TEST_DECODE_WIRE(VectorStruct2, VectorStruct2,
R"({"v":[{"a":"1","b":"2"},{"a":"2","b":"4"},{"a":"3","b":"6"}]})",
"{\n"
" v: vector<#gre#test.fidlcodec.examples/SmallUnevenStruct#rst#> = [\n"
" { a: #gre#uint32#rst# = #blu#1#rst#, b: #gre#uint8#rst# = #blu#2#rst# },"
" { a: #gre#uint32#rst# = #blu#2#rst#, b: #gre#uint8#rst# = #blu#4#rst# }\n"
" { a: #gre#uint32#rst# = #blu#3#rst#, b: #gre#uint8#rst# = #blu#6#rst# }\n"
" ]\n"
"}",
std::vector{SmallUnevenStructFromVals(1, 2), SmallUnevenStructFromVals(2, 4),
SmallUnevenStructFromVals(3, 6)})
TEST_DECODE_WIRE(ArrayStruct2, ArrayStruct2,
R"({"a":[{"a":"1","b":"2"},{"a":"2","b":"4"},{"a":"3","b":"6"}]})",
"{\n"
" a: array<#gre#test.fidlcodec.examples/SmallUnevenStruct#rst#> = [\n"
" { a: #gre#uint32#rst# = #blu#1#rst#, b: #gre#uint8#rst# = #blu#2#rst# },"
" { a: #gre#uint32#rst# = #blu#2#rst#, b: #gre#uint8#rst# = #blu#4#rst# }\n"
" { a: #gre#uint32#rst# = #blu#3#rst#, b: #gre#uint8#rst# = #blu#6#rst# }\n"
" ]\n"
"}",
std::array{SmallUnevenStructFromVals(1, 2), SmallUnevenStructFromVals(2, 4),
SmallUnevenStructFromVals(3, 6)})
// Union and XUnion tests
namespace {
using isu = test::fidlcodec::examples::IntStructUnion;
using xisu = test::fidlcodec::examples::IntStructXunion;
using u8u16struct = test::fidlcodec::examples::U8U16UnionStructType;
template <class T>
T GetIntUnion(int32_t i) {
T u;
u.set_variant_i(i);
return u;
}
template <class T>
T GetStructUnion(const std::string& v1, const std::string& v2) {
T u;
test::fidlcodec::examples::TwoStringStruct tss = TwoStringStructFromVals(v1, v2);
u.set_variant_tss(tss);
return u;
}
template <class T>
std::unique_ptr<T> GetIntUnionPtr(int32_t i) {
std::unique_ptr<T> ptr(new T());
ptr->set_variant_i(i);
return ptr;
}
template <class T>
std::unique_ptr<T> GetStructUnionPtr(const std::string& v1, const std::string& v2) {
std::unique_ptr<T> ptr(new T());
test::fidlcodec::examples::TwoStringStruct tss = TwoStringStructFromVals(v1, v2);
ptr->set_variant_tss(tss);
return ptr;
}
u8u16struct GetU8U16UnionStruct(int8_t i) {
u8u16struct s;
s.u.set_variant_u8(i);
return s;
}
std::string IntUnionIntPretty(const std::string& name, int u, int v) {
std::string result = "{\n";
result += " isu: #gre#test.fidlcodec.examples/" + name + "#rst# = { " +
ValueToPretty("variant_i", "int32", u) + " }\n";
result += " " + ValueToPretty("i", "int32", v) + "\n";
result += "}";
return result;
}
std::string StructUnionIntPretty(const std::string& name, const char* u1, const char* u2, int v) {
std::string result = "{\n";
result += " isu: #gre#test.fidlcodec.examples/" + name + "#rst# = {\n";
result +=
" variant_tss: #gre#test.fidlcodec.examples/TwoStringStruct#rst# = "
"{\n";
result += " " + ValueToPretty("value1", "string", u1) + "\n";
result += " " + ValueToPretty("value2", "string", u2) + "\n";
result += " }\n";
result += " }\n";
result += " " + ValueToPretty("i", "int32", v) + "\n";
result += "}";
return result;
}
std::string IntIntUnionPretty(const std::string& name, int v, int u) {
std::string result = "{\n";
result += " " + ValueToPretty("i", "int32", v) + "\n";
result += " isu: #gre#test.fidlcodec.examples/" + name + "#rst# = { " +
ValueToPretty("variant_i", "int32", u) + " }\n";
result += "}";
return result;
}
std::string IntStructUnionPretty(const std::string& name, int v, const char* u1, const char* u2) {
std::string result = "{\n";
result += " " + ValueToPretty("i", "int32", v) + "\n";
result += " isu: #gre#test.fidlcodec.examples/" + name + "#rst# = {\n";
result +=
" variant_tss: #gre#test.fidlcodec.examples/TwoStringStruct#rst# = "
"{\n";
result += " " + ValueToPretty("value1", "string", u1) + "\n";
result += " " + ValueToPretty("value2", "string", u2) + "\n";
result += " }\n";
result += " }\n";
result += "}";
return result;
}
test::fidlcodec::examples::DataElement GetDataElement(int32_t i32, uint8_t u8) {
test::fidlcodec::examples::DataElement result;
std::vector<std::unique_ptr<test::fidlcodec::examples::DataElement>> alternatives;
auto item_1 = std::make_unique<test::fidlcodec::examples::DataElement>();
item_1->set_int32(i32);
alternatives.emplace_back(std::move(item_1));
auto item_2 = std::make_unique<test::fidlcodec::examples::DataElement>();
item_2->set_uint8(u8);
alternatives.emplace_back(std::move(item_2));
result.set_alternatives(std::move(alternatives));
return result;
}
} // namespace
TEST_DECODE_WIRE(UnionInt, Union, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("IntStructUnion", 42, 1), GetIntUnion<isu>(42), 1);
TEST_DECODE_WIRE(UnionStruct, Union,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("IntStructUnion", "harpo", "chico", 1),
GetStructUnion<isu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableUnionInt, NullableUnion, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("IntStructUnion", 42, 1), GetIntUnionPtr<isu>(42), 1);
TEST_DECODE_WIRE(NullableUnionStruct, NullableUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("IntStructUnion", "harpo", "chico", 1),
GetStructUnionPtr<isu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableUnionIntFirstInt, NullableUnionIntFirst,
R"({"i" : "1", "isu":{"variant_i":"42"}})",
IntIntUnionPretty("IntStructUnion", 1, 42), 1, GetIntUnionPtr<isu>(42));
TEST_DECODE_WIRE(NullableUnionIntFirstStruct, NullableUnionIntFirst,
R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}})",
IntStructUnionPretty("IntStructUnion", 1, "harpo", "chico"), 1,
GetStructUnionPtr<isu>("harpo", "chico"));
TEST_DECODE_WIRE(XUnionInt, XUnion, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("IntStructXunion", 42, 1), GetIntUnion<xisu>(42), 1);
TEST_DECODE_WIRE(XUnionStruct, XUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("IntStructXunion", "harpo", "chico", 1),
GetStructUnion<xisu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableXUnionInt, NullableXUnion, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("IntStructXunion", 42, 1), GetIntUnionPtr<xisu>(42), 1);
TEST_DECODE_WIRE(NullableXUnionStruct, NullableXUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("IntStructXunion", "harpo", "chico", 1),
GetStructUnionPtr<xisu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableXUnionIntFirstInt, NullableXUnionIntFirst,
R"({"i" : "1", "isu":{"variant_i":"42"}})",
IntIntUnionPretty("IntStructXunion", 1, 42), 1, GetIntUnionPtr<xisu>(42));
TEST_DECODE_WIRE(NullableXUnionIntFirstStruct, NullableXUnionIntFirst,
R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}})",
IntStructUnionPretty("IntStructXunion", 1, "harpo", "chico"), 1,
GetStructUnionPtr<xisu>("harpo", "chico"));
TEST_DECODE_WIRE(
RecursiveUnion, RecursiveUnion, R"({"e":{"alternatives":[{"int32":"-10"},{"uint8":"200"}]}})",
"{\n"
" e: #gre#test.fidlcodec.examples/DataElement#rst# = {\n"
" alternatives: vector<#gre#test.fidlcodec.examples/DataElement#rst#> = [\n"
" { int32: #gre#int32#rst# = #blu#-10#rst# }, { uint8: #gre#uint8#rst# = #blu#200#rst# }\n"
" ]\n"
" }\n"
"}",
GetDataElement(-10, 200));
namespace {
std::array<std::unique_ptr<test::fidlcodec::examples::IntStructUnion>, 3> GetArrayNullableUnion(
int32_t i, const std::string& v1, const std::string& v2) {
std::array<std::unique_ptr<test::fidlcodec::examples::IntStructUnion>, 3> a;
a[0] = std::make_unique<test::fidlcodec::examples::IntStructUnion>();
a[0]->set_variant_i(i);
test::fidlcodec::examples::TwoStringStruct tss = TwoStringStructFromVals(v1, v2);
a[2] = std::make_unique<test::fidlcodec::examples::IntStructUnion>();
a[2]->set_variant_tss(tss);
return a;
}
} // namespace
TEST_DECODE_WIRE(
ArrayNullableUnion, ArrayNullableUnion,
R"({"a":[{"variant_i":"1234"},null,{"variant_tss":{"value1":"harpo","value2":"chico"}}]})",
"{\n"
" a: array<#gre#test.fidlcodec.examples/IntStructUnion#rst#> = [\n"
" { variant_i: #gre#int32#rst# = #blu#1234#rst# }, #red#null#rst#\n"
" {\n"
" variant_tss: #gre#test.fidlcodec.examples/TwoStringStruct#rst# = {\n"
" value1: #gre#string#rst# = #red#\"harpo\"#rst#\n"
" value2: #gre#string#rst# = #red#\"chico\"#rst#\n"
" }\n"
" }\n"
" ]\n"
"}",
GetArrayNullableUnion(1234, "harpo", "chico"))
TEST_F(WireParserTest, BadU8U16UnionStruct) {
TEST_DECODE_WIRE_BODY_COMMON(U8U16UnionStruct, -1, 0,
"{\"s\":{\"u\":{\"variant_u8\":\"(invalid)\"}}}",
"{\n"
" s: #gre#test.fidlcodec.examples/U8U16UnionStructType#rst# = {\n"
" u: #gre#test.fidlcodec.examples/U8U16Union#rst# = "
"{ variant_u8: #gre#uint8#rst# = #red#invalid#rst# }\n"
" }\n"
"}",
24, GetU8U16UnionStruct(12));
}
namespace {
using uuu = test::fidlcodec::examples::U8U16Union;
using uuur = test::fidlcodec::examples::U8U16UnionReserved;
using uux = test::fidlcodec::examples::U8U16Xunion;
template <class T>
T GetUInt8Union(uint8_t i) {
T u;
u.set_variant_u8(i);
return u;
}
template <class T>
T GetUInt16Union(uint16_t i) {
T u;
u.set_variant_u16(i);
return u;
}
std::string ShortUnionPretty(const std::string& name, const char* field, const char* type, int u,
int v) {
std::string result = "{\n";
result += " u: #gre#test.fidlcodec.examples/" + name + "#rst# = { " +
ValueToPretty(field, type, u) + " }\n";
result += " " + ValueToPretty("i", "int32", v) + "\n";
result += "}";
return result;
}
} // namespace
TEST_DECODE_WIRE(ShortUnion8, ShortUnion, R"({"u":{"variant_u8":"16"}, "i":"1"})",
ShortUnionPretty("U8U16Union", "variant_u8", "uint8", 16, 1),
GetUInt8Union<uuu>(16), 1);
TEST_DECODE_WIRE(ShortUnion16, ShortUnion, R"({"u":{"variant_u16":"1024"}, "i":"1"})",
ShortUnionPretty("U8U16Union", "variant_u16", "uint16", 1024, 1),
GetUInt16Union<uuu>(1024), 1);
TEST_DECODE_WIRE(ShortUnionReserved8, ShortUnionReserved, R"({"u":{"variant_u8":"16"}, "i":"1"})",
ShortUnionPretty("U8U16UnionReserved", "variant_u8", "uint8", 16, 1),
GetUInt8Union<uuur>(16), 1);
TEST_DECODE_WIRE(ShortUnionReserved16, ShortUnionReserved,
R"({"u":{"variant_u16":"1024"}, "i":"1"})",
ShortUnionPretty("U8U16UnionReserved", "variant_u16", "uint16", 1024, 1),
GetUInt16Union<uuur>(1024), 1);
TEST_DECODE_WIRE(ShortXUnion8, ShortXUnion, R"({"u":{"variant_u8":"16"}, "i":"1"})",
ShortUnionPretty("U8U16Xunion", "variant_u8", "uint8", 16, 1),
GetUInt8Union<uux>(16), 1);
TEST_DECODE_WIRE(ShortXUnion16, ShortXUnion, R"({"u":{"variant_u16":"1024"}, "i":"1"})",
ShortUnionPretty("U8U16Xunion", "variant_u16", "uint16", 1024, 1),
GetUInt16Union<uux>(1024), 1);
// Enum Tests
TEST_DECODE_WIRE(DefaultEnum, DefaultEnumMessage, R"({"ev":"X"})",
"{ ev: #gre#test.fidlcodec.examples/DefaultEnum#rst# = #blu#X#rst# }",
test::fidlcodec::examples::DefaultEnum::X);
TEST_DECODE_WIRE(I8Enum, I8EnumMessage, R"({"ev":"X"})",
"{ ev: #gre#test.fidlcodec.examples/I8Enum#rst# = #blu#X#rst# }",
test::fidlcodec::examples::I8Enum::X);
TEST_DECODE_WIRE(I16Enum, I16EnumMessage, R"({"ev":"X"})",
"{ ev: #gre#test.fidlcodec.examples/I16Enum#rst# = #blu#X#rst# }",
test::fidlcodec::examples::I16Enum::X);
TEST_DECODE_WIRE(I32Enum, I32EnumMessage, R"({"ev":"X"})",
"{ ev: #gre#test.fidlcodec.examples/I32Enum#rst# = #blu#X#rst# }",
test::fidlcodec::examples::I32Enum::X);
TEST_DECODE_WIRE(I64Enum, I64EnumMessage, R"({"ev":"X"})",
"{ ev: #gre#test.fidlcodec.examples/I64Enum#rst# = #blu#X#rst# }",
test::fidlcodec::examples::I64Enum::X);
// Bits Tests
TEST_DECODE_WIRE(DefaultBits, DefaultBitsMessage, R"({"v":"A|C"})",
"{ v: #gre#test.fidlcodec.examples/DefaultBits#rst# = #blu#A|C#rst# }",
static_cast<test::fidlcodec::examples::DefaultBits>(
static_cast<uint8_t>(test::fidlcodec::examples::DefaultBits::A) |
static_cast<uint8_t>(test::fidlcodec::examples::DefaultBits::C)));
TEST_DECODE_WIRE(I8Bits, I8BitsMessage, R"({"v":"A|D"})",
"{ v: #gre#test.fidlcodec.examples/I8Bits#rst# = #blu#A|D#rst# }",
static_cast<test::fidlcodec::examples::I8Bits>(
static_cast<uint8_t>(test::fidlcodec::examples::I8Bits::A) |
static_cast<uint8_t>(test::fidlcodec::examples::I8Bits::D)));
TEST_DECODE_WIRE(I16Bits, I16BitsMessage, R"({"v":"B|C"})",
"{ v: #gre#test.fidlcodec.examples/I16Bits#rst# = #blu#B|C#rst# }",
static_cast<test::fidlcodec::examples::I16Bits>(
static_cast<uint16_t>(test::fidlcodec::examples::I16Bits::B) |
static_cast<uint16_t>(test::fidlcodec::examples::I16Bits::C)));
TEST_DECODE_WIRE(I32Bits, I32BitsMessage, R"({"v":"B|D"})",
"{ v: #gre#test.fidlcodec.examples/I32Bits#rst# = #blu#B|D#rst# }",
static_cast<test::fidlcodec::examples::I32Bits>(
static_cast<uint32_t>(test::fidlcodec::examples::I32Bits::B) |
static_cast<uint32_t>(test::fidlcodec::examples::I32Bits::D)));
TEST_DECODE_WIRE(I64Bits, I64BitsMessage, R"({"v":"C|D"})",
"{ v: #gre#test.fidlcodec.examples/I64Bits#rst# = #blu#C|D#rst# }",
static_cast<test::fidlcodec::examples::I64Bits>(
static_cast<uint64_t>(test::fidlcodec::examples::I64Bits::C) |
static_cast<uint64_t>(test::fidlcodec::examples::I64Bits::D)));
TEST_DECODE_WIRE(EmptyDefaultBits, DefaultBitsMessage, R"({"v":"<none>"})",
"{ v: #gre#test.fidlcodec.examples/DefaultBits#rst# = #blu#<none>#rst# }",
static_cast<test::fidlcodec::examples::DefaultBits>(0));
// Table Tests
test::fidlcodec::examples::ValueTable GetTable(std::optional<int16_t> first_int16,
std::optional<std::string> value1,
std::optional<std::string> value2,
std::optional<int32_t> third_union_val) {
test::fidlcodec::examples::ValueTable table;
if (first_int16.has_value()) {
table.set_first_int16(*first_int16);
}
if (value1.has_value()) {
table.set_second_struct(TwoStringStructFromVals(*value1, *value2));
}
if (third_union_val.has_value()) {
test::fidlcodec::examples::IntStructUnion u;
u.set_variant_i(*third_union_val);
table.set_third_union(std::move(u));
}
return table;
}
std::string TablePretty(std::optional<int16_t> first_int16, std::optional<std::string> value1,
std::optional<std::string> value2, std::optional<int32_t> third_union_val,
int i) {
if (!first_int16.has_value() && !value1.has_value() && !third_union_val.has_value()) {
std::string result = "{ table: #gre#test.fidlcodec.examples/ValueTable#rst# = {}, ";
result += ValueToPretty("i", "int32", i);
result += " }";
return result;
}
std::string result = "{\n";
if (!value1.has_value() && !third_union_val.has_value()) {
result += " table: #gre#test.fidlcodec.examples/ValueTable#rst# = { ";
result += ValueToPretty("first_int16", "int16", *first_int16) + " }\n";
} else {
result += " table: #gre#test.fidlcodec.examples/ValueTable#rst# = {\n";
if (first_int16.has_value()) {
result += " " + ValueToPretty("first_int16", "int16", *first_int16) + "\n";
}
if (value1.has_value()) {
result +=
" second_struct: "
"#gre#test.fidlcodec.examples/TwoStringStruct#rst# = {\n";
result += " " + ValueToPretty("value1", "string", *value1) + "\n";
result += " " + ValueToPretty("value2", "string", *value2) + "\n";
result += " }\n";
}
if (third_union_val.has_value()) {
result += " third_union: #gre#test.fidlcodec.examples/IntStructUnion#rst# = {\n";
result += " " + ValueToPretty("variant_i", "int32", *third_union_val) + "\n";
result += " }\n";
}
result += " }\n";
}
result += " " + ValueToPretty("i", "int32", i) + "\n";
result += "}";
return result;
}
TEST_DECODE_WIRE(Table0, Table, R"({"table":{}, "i":"2"})", TablePretty({}, {}, {}, {}, 2),
GetTable({}, {}, {}, {}), 2)
TEST_DECODE_WIRE(Table1, Table,
R"({"table":{
"third_union":{"variant_i":"42"}
},
"i":"2"})",
TablePretty({}, {}, {}, 42, 2), GetTable({}, {}, {}, 42), 2)
TEST_DECODE_WIRE(Table2, Table,
R"({"table":{
"second_struct":{"value1":"harpo", "value2":"groucho"}
},
"i":"2"})",
TablePretty({}, "harpo", "groucho", {}, 2), GetTable({}, "harpo", "groucho", {}),
2)
TEST_DECODE_WIRE(Table3, Table,
R"({"table":{
"second_struct":{
"value1":"harpo", "value2":"groucho"},
"third_union":{"variant_i":"42"}},
"i":"2"})",
TablePretty({}, "harpo", "groucho", 42, 2), GetTable({}, "harpo", "groucho", 42),
2)
TEST_DECODE_WIRE(Table4, Table, R"({"table":{
"first_int16":"1"
},
"i":"2"})",
TablePretty(1, {}, {}, {}, 2), GetTable(1, {}, {}, {}), 2)
TEST_DECODE_WIRE(Table5, Table,
R"({"table":{
"first_int16":"1",
"third_union":{"variant_i":"42"}
},
"i":"2"})",
TablePretty(1, {}, {}, 42, 2), GetTable(1, {}, {}, 42), 2)
TEST_DECODE_WIRE(Table6, Table,
R"({"table":{
"first_int16":"1",
"second_struct":{
"value1":"harpo", "value2":"groucho"}
},
"i":"2"})",
TablePretty(1, "harpo", "groucho", {}, 2), GetTable(1, "harpo", "groucho", {}), 2)
TEST_DECODE_WIRE(Table7, Table,
R"({"table":{
"first_int16":"1",
"second_struct":{
"value1":"harpo", "value2":"groucho"},
"third_union":{"variant_i":"42"}
},
"i":"2"})",
TablePretty(1, "harpo", "groucho", 42, 2), GetTable(1, "harpo", "groucho", 42), 2)
// TODO(fxbug.dev/6274): Add a test that exercises what happens when we encounter an
// unknown type in a table.
// Handle Tests
namespace {
class HandleSupport {
public:
HandleSupport() {
zx::channel::create(0, &out1_, &out2_);
json_ = "{" + HandleToJson("ch", 0x100) + "}";
pretty_ = "{ " + HandleToPretty("ch", 0x100) + " }";
}
zx::channel handle() { return std::move(out2_); }
template <typename Interface>
fidl::InterfaceHandle<Interface> interface() {
return fidl::InterfaceHandle<Interface>(std::move(out2_));
}
std::string GetJSON() { return json_; }
std::string GetPretty() { return pretty_; }
private:
zx::channel out1_;
zx::channel out2_;
std::string json_;
std::string pretty_;
};
} // namespace
TEST_F(WireParserTest, ParseHandle) {
HandleSupport support;
TEST_DECODE_WIRE_BODY(Handle, support.GetJSON(), support.GetPretty(), support.handle());
}
TEST_F(WireParserTest, ParseNullableHandle) {
HandleSupport support;
TEST_DECODE_WIRE_BODY(NullableHandle, support.GetJSON(), support.GetPretty(), support.handle());
}
TEST_F(WireParserTest, ParseProtocol) {
HandleSupport support;
TEST_DECODE_WIRE_BODY(Protocol, support.GetJSON(), support.GetPretty(),
support.interface<test::fidlcodec::examples::ParamProtocol>());
}
namespace {
class HandleStructSupport {
public:
HandleStructSupport() {
zx::channel::create(0, &out1_, &out2_);
zx::channel::create(0, &out3_, &out4_);
json_ = "{\"hs\":{" + HandleToJson("h1", 0x100) + "," + HandleToJson("h2", 0x101) + "," +
HandleToJson("h3", 0x102) + "}}";
pretty_ = "{\n hs: #gre#test.fidlcodec.examples/HandleStruct#rst# = {\n " +
HandleToPretty("h1", 0x100) + "\n " + HandleToPretty("h2", 0x101) + "\n " +
HandleToPretty("h3", 0x102) + "\n }\n}";
}
test::fidlcodec::examples::HandleStruct handle_struct() {
test::fidlcodec::examples::HandleStruct hs;
hs.h1 = std::move(out1_);
hs.h2 = std::move(out2_);
hs.h3 = std::move(out3_);
return hs;
}
std::string GetJSON() { return json_; }
std::string GetPretty() { return pretty_; }
private:
zx::channel out1_;
zx::channel out2_;
zx::channel out3_;
zx::channel out4_;
std::string json_;
std::string pretty_;
};
} // namespace
TEST_F(WireParserTest, ParseHandleStruct) {
HandleStructSupport support;
TEST_DECODE_WIRE_BODY(HandleStructMessage, support.GetJSON(), support.GetPretty(),
support.handle_struct());
}
namespace {
class HandleTableSupport {
public:
HandleTableSupport() {
zx::channel::create(0, &out1_, &out2_);
json_ = "{\"t\":{" + HandleToJson("h1", 0x100) + ",\"s1\":{\"sh1\":\"00000000\"," +
HandleToJson("sh2", 0x101) + "}}}";
pretty_ =
"{\n"
" t: #gre#test.fidlcodec.examples/HandleTable#rst# = {\n"
" " +
HandleToPretty("h1", 0x100) +
"\n"
" s1: #gre#test.fidlcodec.examples/OptHandleStruct#rst# = {\n"
" sh1: #gre#handle#rst# = #red#00000000#rst#\n"
" " +
HandleToPretty("sh2", 0x101) +
"\n"
" }\n"
" }\n"
"}";
}
test::fidlcodec::examples::HandleTable handle_table() {
test::fidlcodec::examples::HandleTable t;
t.set_h1(std::move(out1_));
test::fidlcodec::examples::OptHandleStruct s;
s.sh2 = std::move(out2_);
t.set_s1(std::move(s));
return t;
}
std::string GetJSON() { return json_; }
std::string GetPretty() { return pretty_; }
private:
zx::channel out1_;
zx::channel out2_;
std::string json_;
std::string pretty_;
};
} // namespace
TEST_F(WireParserTest, ParseHandleTable) {
HandleTableSupport support;
TEST_DECODE_WIRE_BODY(HandleTableMessage, support.GetJSON(), support.GetPretty(),
support.handle_table());
}
namespace {
class TraversalOrderSupport {
public:
TraversalOrderSupport() {
zx::channel::create(0, &sh1_, &sh2_);
zx::channel::create(0, &h1_, &h2_);
json_ = "{\"t\":{\"s\":{" + HandleToJson("sh1", 0x100) + "," + HandleToJson("sh2", 0x101) +
"}," + HandleToJson("h1", 0x102) + "," + HandleToJson("h2", 0x103) + "}}";
pretty_ =
"{\n t: #gre#test.fidlcodec.examples/TraversalOrder#rst# = {\n "
"s: #gre#test.fidlcodec.examples/OptHandleStruct#rst# = {\n " +
HandleToPretty("sh1", 0x100) + "\n " + HandleToPretty("sh2", 0x101) + "\n }\n " +
HandleToPretty("h1", 0x102) + "\n " + HandleToPretty("h2", 0x103) + "\n }\n}";
}
test::fidlcodec::examples::TraversalOrder TraversalOrder() {
test::fidlcodec::examples::TraversalOrder t;
auto s = std::make_unique<test::fidlcodec::examples::OptHandleStruct>();
s->sh1 = std::move(sh1_);
s->sh2 = std::move(sh2_);
t.s = std::move(s);
t.h1 = std::move(h1_);
t.h2 = std::move(h2_);
return t;
}
std::string GetJSON() { return json_; }
std::string GetPretty() { return pretty_; }
private:
zx::channel sh1_;
zx::channel sh2_;
zx::channel h1_;
zx::channel h2_;
std::string json_;
std::string pretty_;
};
} // namespace
TEST_F(WireParserTest, ParseTraversalOrder) {
TraversalOrderSupport support;
TEST_DECODE_WIRE_BODY(TraversalOrderMessage, support.GetJSON(), support.GetPretty(),
support.TraversalOrder());
}
namespace {
class TraversalMainSupport {
public:
TraversalMainSupport() {
zx::channel::create(0, &out1_, &out2_);
json_ = R"JSON({"v":[{"x":"10","y":{"a":"20",)JSON" + HandleToJson("b", 0x100) +
R"JSON(}},{"x":"30","y":{"a":"40",)JSON" + HandleToJson("b", 0x101) +
R"JSON(}}],"s":{"a":"50","b":"00000000"}})JSON";
pretty_ =
"{\n"
" v: vector<#gre#test.fidlcodec.examples/TraversalMain#rst#> = [\n"
" {\n"
" x: #gre#uint32#rst# = #blu#10#rst#\n"
" y: #gre#test.fidlcodec.examples/TraversalStruct#rst# = {\n"
" a: #gre#uint32#rst# = #blu#20#rst#\n"
" " +
HandleToPretty("b", 0x100) +
"\n"
" }\n"
" }\n"
" {\n"
" x: #gre#uint32#rst# = #blu#30#rst#\n"
" y: #gre#test.fidlcodec.examples/TraversalStruct#rst# = {\n"
" a: #gre#uint32#rst# = #blu#40#rst#\n"
" " +
HandleToPretty("b", 0x101) +
"\n"
" }\n"
" }\n"
" ]\n"
" s: #gre#test.fidlcodec.examples/TraversalStruct#rst# = { "
"a: #gre#uint32#rst# = #blu#50#rst#, "
"b: #gre#handle#rst# = #red#00000000#rst# }\n"
"}";
}
std::vector<std::unique_ptr<test::fidlcodec::examples::TraversalMain>> GetV() {
std::vector<std::unique_ptr<test::fidlcodec::examples::TraversalMain>> result;
auto object1 = std::make_unique<test::fidlcodec::examples::TraversalMain>();
object1->x = 10;
auto object2 = std::make_unique<test::fidlcodec::examples::TraversalStruct>();
object2->a = 20;
object2->b = std::move(out1_);
object1->y = std::move(object2);
result.emplace_back(std::move(object1));
auto object3 = std::make_unique<test::fidlcodec::examples::TraversalMain>();
object3->x = 30;
auto object4 = std::make_unique<test::fidlcodec::examples::TraversalStruct>();
object4->a = 40;
object4->b = std::move(out2_);
object3->y = std::move(object4);
result.emplace_back(std::move(object3));
return result;
}
std::unique_ptr<test::fidlcodec::examples::TraversalStruct> GetS() {
auto result = std::make_unique<test::fidlcodec::examples::TraversalStruct>();
result->a = 50;
return result;
}
std::string GetJSON() { return json_; }
std::string GetPretty() { return pretty_; }
private:
zx::channel out1_;
zx::channel out2_;
std::string json_;
std::string pretty_;
};
} // namespace
TEST_F(WireParserTest, ParseTraversalMain) {
TraversalMainSupport support_v1;
TEST_DECODE_WIRE_BODY(TraversalMainMessage, support_v1.GetJSON(), support_v1.GetPretty(),
support_v1.GetV(), support_v1.GetS());
}
// Corrupt data tests
TEST_F(WireParserTest, BadSchemaPrintHex) {
std::ostringstream log_msg;
fidl_codec::logger::LogCapturer capturer(&log_msg);
// i32 in the schema is missing "subtype": "int32"
std::string bad_schema = R"FIDL({
"version": "0.0.1",
"name": "fidl.examples.types",
"library_dependencies": [],
"bits_declarations": [],
"const_declarations": [],
"enum_declarations": [],
"interface_declarations": [
{
"name": "test.fidlcodec.examples/FidlCodecTestInterface",
"location": {
"filename": "../../src/lib/fidl_codec/testdata/types.test.fidl",
"line": 11,
"column": 10
},
"methods": [
{
"ordinal": 1593056155789170713,
"name": "Int32",
"location": {
"filename": "../../src/lib/fidl_codec/testdata/types.test.fidl",
"line": 16,
"column": 5
},
"has_request": true,
"maybe_request": [
{
"type": {
"kind": "primitive"
},
"name": "i32",
"location": {
"filename": "../../src/lib/fidl_codec/testdata/types.test.fidl",
"line": 16,
"column": 17
},
"size": 4,
"max_out_of_line": 0,
"alignment": 4,
"offset": 16,
"max_handles": 0,
"field_shape_old": {
"offset": 16,
"padding": 0
},
"field_shape_v1": {
"offset": 16,
"padding": 0
}
}
],
"maybe_request_size": 24,
"maybe_request_alignment": 8,
"maybe_request_type_shape_old": {
"inline_size": 24,
"alignment": 8,
"depth": 0,
"max_handles": 0,
"max_out_of_line": 0,
"has_padding": true,
"has_flexible_envelope": false
},
"maybe_request_type_shape_v1": {
"inline_size": 24,
"alignment": 8,
"depth": 0,
"max_handles": 0,
"max_out_of_line": 0,
"has_padding": true,
"has_flexible_envelope": false
},
"has_response": false,
"is_composed": false
}
]
}
],
"struct_declarations": [],
"table_declarations": [],
"union_declarations": [],
"xunion_declarations": []
})FIDL";
LibraryReadError err;
LibraryLoader loader;
loader.AddContent(bad_schema, &err);
ASSERT_TRUE(err.value == LibraryReadError::ErrorValue::kOk);
fidl::IncomingMessageBuffer buffer;
fidl::HLCPPIncomingMessage message = buffer.CreateEmptyIncomingMessage();
InterceptRequest<test::fidlcodec::examples::FidlCodecTestInterface>(
message, [](fidl::InterfacePtr<test::fidlcodec::examples::FidlCodecTestInterface>& ptr) {
ptr->Int32(kUninitialized);
});
fidl_message_header_t header = message.header();
zx_handle_disposition_t* handle_dispositions = nullptr;
if (message.handles().size() > 0) {
handle_dispositions = new zx_handle_disposition_t[message.handles().size()];
for (uint32_t i = 0; i < message.handles().size(); ++i) {
handle_dispositions[i].operation = fidl_codec::kNoHandleDisposition;
handle_dispositions[i].handle = message.handles().data()[i].handle;
handle_dispositions[i].type = ZX_OBJ_TYPE_NONE;
handle_dispositions[i].rights = 0;
handle_dispositions[i].result = ZX_OK;
}
}
const std::vector<const InterfaceMethod*>* methods = loader.GetByOrdinal(header.ordinal);
ASSERT_NE(methods, nullptr);
ASSERT_TRUE(!methods->empty());
const InterfaceMethod* method = (*methods)[0];
// If this is null, you probably have to update the schema above.
ASSERT_NE(method, nullptr);
std::unique_ptr<fidl_codec::StructValue> decoded_request;
std::stringstream error_stream;
fidl_codec::DecodeRequest(method, message.bytes().data(), message.bytes().size(),
handle_dispositions, message.handles().size(), &decoded_request,
error_stream);
rapidjson::Document actual;
if (decoded_request != nullptr) {
decoded_request->ExtractJson(actual.GetAllocator(), actual);
}
// Checks that an invalid type generates an invalid value.
ASSERT_STREQ(actual["i32"].GetString(), "(invalid)");
delete[] handle_dispositions;
ASSERT_STREQ(log_msg.str().c_str(), "Invalid type");
}
// Checks that MessageDecoder::DecodeValue doesn't core dump with a null type.
TEST_F(WireParserTest, DecodeNullTypeValue) {
std::stringstream error_stream;
fidl_message_header_t header;
MessageDecoder decoder(reinterpret_cast<uint8_t*>(&header), sizeof(header), nullptr, 0,
error_stream);
decoder.DecodeValue(nullptr);
}
} // namespace fidl_codec