blob: fcc6f35f0725bd42a07bc95c1ed9f50bdf4878a9 [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 "tools/fidlcat/lib/wire_parser.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "lib/fidl/cpp/test/frobinator_impl.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "test/fidlcat/examples/cpp/fidl.h"
#include "tools/fidlcat/lib/fidlcat_test.h"
#include "tools/fidlcat/lib/library_loader.h"
#include "tools/fidlcat/lib/library_loader_test_data.h"
#include "tools/fidlcat/lib/message_decoder.h"
#include "tools/fidlcat/lib/wire_object.h"
namespace fidlcat {
const Colors FakeColors(/*reset=*/"#rst#", /*red=*/"#red#", /*green=*/"#gre#",
/*blue=*/"#blu#", /*white_on_magenta=*/"#wom#");
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() {
std::vector<std::unique_ptr<std::istream>> library_files;
fidlcat_test::ExampleMap examples;
for (auto element : examples.map()) {
std::unique_ptr<std::istream> file = std::make_unique<std::istringstream>(
std::istringstream(element.second));
library_files.push_back(std::move(file));
}
LibraryReadError err;
return new LibraryLoader(library_files, &err);
}
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_;
};
TEST_F(WireParserTest, ParseSingleString) {
fidl::MessageBuffer buffer;
fidl::Message message = buffer.CreateEmptyMessage();
InterceptRequest<fidl::test::frobinator::Frobinator>(
message, [](fidl::InterfacePtr<fidl::test::frobinator::Frobinator>& ptr) {
ptr->Grob("one", [](fidl::StringPtr value) { FAIL(); });
});
fidl_message_header_t header = message.header();
const InterfaceMethod* method = loader_->GetByOrdinal(header.ordinal);
ASSERT_NE(method, nullptr);
ASSERT_EQ("Grob", method->name());
std::unique_ptr<fidlcat::Object> decoded_request;
fidlcat::DecodeRequest(method, message, &decoded_request);
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);
}
// 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::FidlcatTestInterface
// (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_BODY(_iface, _json_value, _pretty_print, ...) \
do { \
fidl::MessageBuffer buffer; \
fidl::Message message = buffer.CreateEmptyMessage(); \
using test::fidlcat::examples::FidlcatTestInterface; \
InterceptRequest<FidlcatTestInterface>( \
message, [&](fidl::InterfacePtr<FidlcatTestInterface>& ptr) { \
ptr->_iface(__VA_ARGS__); \
}); \
\
fidl_message_header_t header = message.header(); \
\
const InterfaceMethod* method = loader_->GetByOrdinal(header.ordinal); \
ASSERT_NE(method, nullptr); \
ASSERT_EQ(#_iface, method->name()); \
\
std::unique_ptr<fidlcat::Object> decoded_request; \
ASSERT_TRUE(fidlcat::DecodeRequest(method, message, &decoded_request)) \
<< "Could not decode message"; \
rapidjson::Document actual; \
if (decoded_request != nullptr) { \
decoded_request->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 (decoded_request != nullptr) { \
decoded_request->PrettyPrint(result, FakeColors, 0, 80, 80); \
} \
ASSERT_EQ(result.str(), _pretty_print) \
<< "expected = " << _pretty_print << " actual = " << result.str(); \
\
for (uint32_t actual = 0; actual < message.bytes().actual(); ++actual) { \
fidl::HandlePart handles(message.handles().data(), \
message.handles().capacity(), \
message.handles().actual()); \
fidl::BytePart bytes(message.bytes().data(), message.bytes().capacity(), \
actual); \
fidl::Message message_copy(std::move(bytes), std::move(handles)); \
MessageDecoder decoder(message_copy, /*output_errors=*/false); \
std::unique_ptr<Object> object = \
decoder.DecodeMessage(*method->request()); \
ASSERT_TRUE(decoder.HasError()) \
<< "expect decoding error for buffer size " << actual \
<< " instead of " << message.bytes().actual(); \
} \
\
for (uint32_t actual = 0; message.handles().actual() > actual; actual++) { \
fidl::HandlePart handles(message.handles().data(), \
message.handles().capacity(), actual); \
fidl::BytePart bytes(message.bytes().data(), message.bytes().capacity(), \
message.bytes().actual()); \
fidl::Message message_copy(std::move(bytes), std::move(handles)); \
MessageDecoder decoder(message_copy, /*output_errors=*/false); \
std::unique_ptr<Object> object = \
decoder.DecodeMessage(*method->request()); \
ASSERT_TRUE(decoder.HasError()) \
<< "expect decoding error for handle size " << actual \
<< " instead of " << message.handles().actual(); \
} \
} while (0)
// 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::FidlcatTestInterface
// (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__); \
}
// Scalar Tests
namespace {
template <class T>
std::string FieldToJson(std::string key, T value) {
return "\"" + key + "\":\"" + std::to_string(value) + "\"";
}
template <>
std::string FieldToJson(std::string key, bool value) {
return "\"" + key + "\":\"" + (value ? "true" : "false") + "\"";
}
template <>
std::string FieldToJson(std::string key, const char* value) {
return "\"" + key + "\":\"" + value + "\"";
}
template <>
std::string FieldToJson(std::string key, std::string value) {
return "\"" + key + "\":\"" + value + "\"";
}
template <class T>
std::string SingleToJson(std::string key, T value) {
return "{ " + FieldToJson(key, value) + " }";
}
template <class T>
std::string FieldToPretty(std::string key, std::string type, T value) {
return key + ": #gre#" + type + "#rst# = #blu#" + std::to_string(value) +
"#rst#";
}
template <>
std::string FieldToPretty(std::string key, std::string type, bool value) {
return key + ": #gre#" + type + "#rst# = #blu#" + (value ? "true" : "false") +
"#rst#";
}
template <>
std::string FieldToPretty(std::string key, std::string type,
const char* value) {
return key + ": #gre#" + type + "#rst# = #red#\"" + value + "\"#rst#";
}
template <>
std::string FieldToPretty(std::string key, std::string type,
std::string value) {
return key + ": #gre#" + type + "#rst# = #red#\"" + value + "\"#rst#";
}
std::string HandleToPretty(std::string key, zx_handle_t value) {
return key + ": #gre#handle#rst# = #red#" + std::to_string(value) + "#rst#";
}
template <class T>
std::string SingleToPretty(std::string key, std::string type, T value) {
return "{ " + FieldToPretty(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_SINGLE(String, String, s, string, "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, 0.25)
TEST_SINGLE(Float64, Float64, f64, float64, 9007199254740992.0)
TEST_DECODE_WIRE(TwoTuple, Complex, R"({"real":"1", "imaginary":"2"})",
"{ " + FieldToPretty("real", "int32", 1) + ", " +
FieldToPretty("imaginary", "int32", 2) + " }",
1, 2);
TEST_DECODE_WIRE(StringInt, StringInt, R"({"s":"groucho", "i32":"4"})",
"{ " + FieldToPretty("s", "string", "groucho") + ", " +
FieldToPretty("i32", "int32", 4) + " }",
"groucho", 4)
// Vector / Array Tests
namespace {
int32_t one_param[1] = {1};
int32_t two_params[2] = {1, 2};
template <class T, size_t N>
std::array<T, N> ToArray(T ts[N]) {
::std::array<T, N> ret;
std::copy_n(&ts[0], N, ret.begin());
return ret;
}
// Converts an array to a VectorPtr, so that it can be passed to a FIDL
// interface.
template <class T>
fidl::VectorPtr<T> ToVector(T ts[], size_t n) {
std::vector<T> vec;
for (size_t i = 0; i < n; i++) {
vec.push_back(ts[i]);
}
::fidl::VectorPtr<T> ret(vec);
return ret;
}
} // namespace
TEST_DECODE_WIRE(Array1, Array1, R"({"b_1":["1"]})",
"{ b_1: #gre#array<int32>#rst# = [ #blu#1#rst# ] }",
(ToArray<int32_t, 1>(one_param)));
TEST_DECODE_WIRE(
Array2, Array2, R"({"b_2":["1", "2"]})",
"{ b_2: #gre#array<int32>#rst# = [ #blu#1#rst#, #blu#2#rst# ] }",
(ToArray<int32_t, 2>(two_params)));
TEST_DECODE_WIRE(NullVector, Vector, R"({"v_1": null})",
"{ v_1: #gre#vector<int32>#rst# = #blu#null#rst# }", nullptr)
TEST_DECODE_WIRE(VectorOneElt, Vector, R"({"v_1":["1"]})",
"{ v_1: #gre#vector<int32>#rst# = [ #blu#1#rst# ] }",
(ToVector<int32_t>(one_param, 1)));
TEST_DECODE_WIRE(
VectorTwoElt, Vector, R"({"v_1":["1", "2"]})",
"{ v_1: #gre#vector<int32>#rst# = [ #blu#1#rst#, #blu#2#rst# ] }",
(ToVector<int32_t>(two_params, 2)));
namespace {
std::array<std::string, 2> TwoStringArrayFromVals(std::string v1,
std::string v2) {
std::array<std::string, 2> brother_array;
brother_array[0] = v1;
brother_array[1] = v2;
return brother_array;
}
} // namespace
TEST_DECODE_WIRE(
TwoStringArrayInt, TwoStringArrayInt,
R"({"arr":["harpo","chico"], "i32":"1"})",
R"({ arr: #gre#array<string>#rst# = [ #red#"harpo"#rst#, #red#"chico"#rst# ], )" +
FieldToPretty("i32", "int32", 1) + " }",
TwoStringArrayFromVals("harpo", "chico"), 1)
namespace {
fidl::VectorPtr<std::string> TwoStringVectorFromVals(std::string v1,
std::string v2) {
std::vector<std::string> brother_vector;
brother_vector.push_back(v1);
brother_vector.push_back(v2);
return fidl::VectorPtr(brother_vector);
}
} // namespace
TEST_DECODE_WIRE(
TwoStringVectorInt, TwoStringVectorInt,
R"({"vec":["harpo", "chico"], "i32":"1"})",
R"({ vec: #gre#vector<string>#rst# = [ #red#"harpo"#rst#, #red#"chico"#rst# ], )" +
FieldToPretty("i32", "int32", 1) + " }",
TwoStringVectorFromVals("harpo", "chico"), 1)
// 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 = 0.25;
pt.f64 = 9007199254740992.0;
}
std::string GetJson() {
std::ostringstream es;
es << R"({"p":{)" << FieldToJson("s", pt.s) << "," << FieldToJson("b", pt.b)
<< "," << FieldToJson("i8", pt.i8) << "," << FieldToJson("i16", pt.i16)
<< "," << FieldToJson("i32", pt.i32) << "," << FieldToJson("i64", pt.i64)
<< "," << FieldToJson("u8", pt.u8) << "," << FieldToJson("u16", pt.u16)
<< "," << FieldToJson("u32", pt.u32) << "," << FieldToJson("u64", pt.u64)
<< "," << FieldToJson("f32", pt.f32) << "," << FieldToJson("f64", pt.f64)
<< "}}";
return es.str();
}
std::string GetPretty() {
std::ostringstream es;
es << "{\n"
<< " p: #gre#test.fidlcat.examples/primitive_types#rst# = {\n"
<< " " << FieldToPretty("s", "string", pt.s) << "\n"
<< " " << FieldToPretty("b", "bool", pt.b) << "\n"
<< " " << FieldToPretty("i8", "int8", pt.i8) << "\n"
<< " " << FieldToPretty("i16", "int16", pt.i16) << "\n"
<< " " << FieldToPretty("i32", "int32", pt.i32) << "\n"
<< " " << FieldToPretty("i64", "int64", pt.i64) << "\n"
<< " " << FieldToPretty("u8", "uint8", pt.u8) << "\n"
<< " " << FieldToPretty("u16", "uint16", pt.u16) << "\n"
<< " " << FieldToPretty("u32", "uint32", pt.u32) << "\n"
<< " " << FieldToPretty("u64", "uint64", pt.u64) << "\n"
<< " " << FieldToPretty("f32", "float32", pt.f32) << "\n"
<< " " << FieldToPretty("f64", "float64", pt.f64) << "\n"
<< " }\n"
<< "}";
return es.str();
}
test::fidlcat::examples::primitive_types pt;
};
} // namespace
TEST_F(WireParserTest, ParseStruct) {
StructSupport sd;
TEST_DECODE_WIRE_BODY(Struct, sd.GetJson(), sd.GetPretty(), sd.pt);
}
TEST_DECODE_WIRE(
NullableStruct, NullableStruct, R"({"p":null})",
"{ p: #gre#test.fidlcat.examples/primitive_types#rst# = #blu#null#rst# }",
nullptr);
TEST_DECODE_WIRE(NullableStructAndInt, NullableStructAndInt,
R"({"p":null, "i":"1"})",
"{ p: #gre#test.fidlcat.examples/primitive_types#rst# = "
"#blu#null#rst#, i: #gre#int32#rst# = #blu#1#rst# }",
nullptr, 1);
namespace {
test::fidlcat::examples::two_string_struct TwoStringStructFromVals(
std::string v1, std::string v2) {
test::fidlcat::examples::two_string_struct tss;
tss.value1 = v1;
tss.value2 = v2;
return tss;
}
std::unique_ptr<test::fidlcat::examples::two_string_struct>
TwoStringStructFromValsPtr(std::string v1, std::string v2) {
std::unique_ptr<test::fidlcat::examples::two_string_struct> ptr(
new test::fidlcat::examples::two_string_struct());
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.fidlcat.examples/two_string_struct#rst# = {\n";
result += " " + FieldToPretty("value1", "string", s1) + "\n";
result += " " + FieldToPretty("value2", "string", s2) + "\n";
result += " }\n";
result += " " + FieldToPretty("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)
// TODO: Add the following struct tests:
// struct{uint8 f1; uint32 f2;}
// struct{struct{uint8 f1; uint32 f2;} inner; uint8 f3;}
// Union and XUnion tests
namespace {
using isu = test::fidlcat::examples::int_struct_union;
using xisu = test::fidlcat::examples::int_struct_xunion;
template <class T>
T GetIntUnion(int32_t i) {
T u;
u.set_variant_i(i);
return u;
}
template <class T>
T GetStructUnion(std::string v1, std::string v2) {
T u;
test::fidlcat::examples::two_string_struct 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(std::string v1, std::string v2) {
std::unique_ptr<T> ptr(new T());
test::fidlcat::examples::two_string_struct tss =
TwoStringStructFromVals(v1, v2);
ptr->set_variant_tss(tss);
return ptr;
}
std::string IntUnionIntPretty(std::string name, int u, int v) {
std::string result = "{\n";
result += " isu: #gre#test.fidlcat.examples/" + name + "#rst# = { " +
FieldToPretty("variant_i", "int32", u) + " }\n";
result += " " + FieldToPretty("i", "int32", v) + "\n";
result += "}";
return result;
}
std::string StructUnionIntPretty(std::string name, const char* u1,
const char* u2, int v) {
std::string result = "{\n";
result += " isu: #gre#test.fidlcat.examples/" + name + "#rst# = {\n";
result +=
" variant_tss: #gre#test.fidlcat.examples/two_string_struct#rst# = "
"{\n";
result += " " + FieldToPretty("value1", "string", u1) + "\n";
result += " " + FieldToPretty("value2", "string", u2) + "\n";
result += " }\n";
result += " }\n";
result += " " + FieldToPretty("i", "int32", v) + "\n";
result += "}";
return result;
}
std::string IntIntUnionPretty(std::string name, int v, int u) {
std::string result = "{\n";
result += " " + FieldToPretty("i", "int32", v) + "\n";
result += " isu: #gre#test.fidlcat.examples/" + name + "#rst# = { " +
FieldToPretty("variant_i", "int32", u) + " }\n";
result += "}";
return result;
}
std::string IntStructUnionPretty(std::string name, int v, const char* u1,
const char* u2) {
std::string result = "{\n";
result += " " + FieldToPretty("i", "int32", v) + "\n";
result += " isu: #gre#test.fidlcat.examples/" + name + "#rst# = {\n";
result +=
" variant_tss: #gre#test.fidlcat.examples/two_string_struct#rst# = "
"{\n";
result += " " + FieldToPretty("value1", "string", u1) + "\n";
result += " " + FieldToPretty("value2", "string", u2) + "\n";
result += " }\n";
result += " }\n";
result += "}";
return result;
}
} // namespace
TEST_DECODE_WIRE(UnionInt, Union, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("int_struct_union", 42, 1),
GetIntUnion<isu>(42), 1);
TEST_DECODE_WIRE(
UnionStruct, Union,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("int_struct_union", "harpo", "chico", 1),
GetStructUnion<isu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableUnionInt, NullableUnion,
R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("int_struct_union", 42, 1),
GetIntUnionPtr<isu>(42), 1);
TEST_DECODE_WIRE(
NullableUnionStruct, NullableUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("int_struct_union", "harpo", "chico", 1),
GetStructUnionPtr<isu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableUnionIntFirstInt, NullableUnionIntFirst,
R"({"i" : "1", "isu":{"variant_i":"42"}})",
IntIntUnionPretty("int_struct_union", 1, 42), 1,
GetIntUnionPtr<isu>(42));
TEST_DECODE_WIRE(
NullableUnionIntFirstStruct, NullableUnionIntFirst,
R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}})",
IntStructUnionPretty("int_struct_union", 1, "harpo", "chico"), 1,
GetStructUnionPtr<isu>("harpo", "chico"));
TEST_DECODE_WIRE(XUnionInt, XUnion, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("int_struct_xunion", 42, 1),
GetIntUnion<xisu>(42), 1);
TEST_DECODE_WIRE(
XUnionStruct, XUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("int_struct_xunion", "harpo", "chico", 1),
GetStructUnion<xisu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableXUnionInt, NullableXUnion,
R"({"isu":{"variant_i":"42"}, "i" : "1"})",
IntUnionIntPretty("int_struct_xunion", 42, 1),
GetIntUnionPtr<xisu>(42), 1);
TEST_DECODE_WIRE(
NullableXUnionStruct, NullableXUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
StructUnionIntPretty("int_struct_xunion", "harpo", "chico", 1),
GetStructUnionPtr<xisu>("harpo", "chico"), 1);
TEST_DECODE_WIRE(NullableXUnionIntFirstInt, NullableXUnionIntFirst,
R"({"i" : "1", "isu":{"variant_i":"42"}})",
IntIntUnionPretty("int_struct_xunion", 1, 42), 1,
GetIntUnionPtr<xisu>(42));
TEST_DECODE_WIRE(
NullableXUnionIntFirstStruct, NullableXUnionIntFirst,
R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}})",
IntStructUnionPretty("int_struct_xunion", 1, "harpo", "chico"), 1,
GetStructUnionPtr<xisu>("harpo", "chico"));
namespace {
using uuu = test::fidlcat::examples::u8_u16_union;
using uux = test::fidlcat::examples::u8_u16_xunion;
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(std::string name, const char* field,
const char* type, int u, int v) {
std::string result = "{\n";
result += " u: #gre#test.fidlcat.examples/" + name + "#rst# = { " +
FieldToPretty(field, type, u) + " }\n";
result += " " + FieldToPretty("i", "int32", v) + "\n";
result += "}";
return result;
}
} // namespace
TEST_DECODE_WIRE(ShortUnion8, ShortUnion,
R"({"u":{"variant_u8":"16"}, "i":"1"})",
ShortUnionPretty("u8_u16_union", "variant_u8", "uint8", 16, 1),
GetUInt8Union<uuu>(16), 1);
TEST_DECODE_WIRE(ShortUnion16, ShortUnion,
R"({"u":{"variant_u16":"1024"}, "i":"1"})",
ShortUnionPretty("u8_u16_union", "variant_u16", "uint16", 1024,
1),
GetUInt16Union<uuu>(1024), 1);
TEST_DECODE_WIRE(ShortXUnion8, ShortXUnion,
R"({"u":{"variant_u8":"16"}, "i":"1"})",
ShortUnionPretty("u8_u16_xunion", "variant_u8", "uint8", 16,
1),
GetUInt8Union<uux>(16), 1);
TEST_DECODE_WIRE(ShortXUnion16, ShortXUnion,
R"({"u":{"variant_u16":"1024"}, "i":"1"})",
ShortUnionPretty("u8_u16_xunion", "variant_u16", "uint16",
1024, 1),
GetUInt16Union<uux>(1024), 1);
// Enum Tests
TEST_DECODE_WIRE(
DefaultEnum, DefaultEnum, R"({"ev":"x"})",
"{ ev: #gre#test.fidlcat.examples/default_enum#rst# = #blu#x#rst# }",
test::fidlcat::examples::default_enum::x);
TEST_DECODE_WIRE(
I8Enum, I8Enum, R"({"ev":"x"})",
"{ ev: #gre#test.fidlcat.examples/i8_enum#rst# = #blu#x#rst# }",
test::fidlcat::examples::i8_enum::x);
TEST_DECODE_WIRE(
I16Enum, I16Enum, R"({"ev":"x"})",
"{ ev: #gre#test.fidlcat.examples/i16_enum#rst# = #blu#x#rst# }",
test::fidlcat::examples::i16_enum::x);
// Table Tests
test::fidlcat::examples::value_table 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::fidlcat::examples::value_table 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::fidlcat::examples::int_struct_union 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.fidlcat.examples/value_table#rst# = {}, ";
result += FieldToPretty("i", "int32", i);
result += " }";
return result;
}
std::string result = "{\n";
if (!value1.has_value() && !third_union_val.has_value()) {
result += " table: #gre#test.fidlcat.examples/value_table#rst# = { ";
result += FieldToPretty("first_int16", "int16", *first_int16) + " }\n";
} else {
result += " table: #gre#test.fidlcat.examples/value_table#rst# = {\n";
if (first_int16.has_value()) {
result +=
" " + FieldToPretty("first_int16", "int16", *first_int16) + "\n";
}
if (value1.has_value()) {
result +=
" second_struct: "
"#gre#test.fidlcat.examples/two_string_struct#rst# = {\n";
result += " " + FieldToPretty("value1", "string", *value1) + "\n";
result += " " + FieldToPretty("value2", "string", *value2) + "\n";
result += " }\n";
}
if (third_union_val.has_value()) {
result +=
" third_union: #gre#test.fidlcat.examples/int_struct_union#rst# = "
"{\n";
result += " " +
FieldToPretty("variant_i", "int32", *third_union_val) + "\n";
result += " }\n";
}
result += " }\n";
}
result += " " + FieldToPretty("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(DX-1476): 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_ = R"({"ch":")";
json_.append(std::to_string(out2_.get()));
json_.append(R"("})");
pretty_ = "{ " + HandleToPretty("ch", out2_.get()) + " }";
}
zx::channel handle() { return 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());
}
namespace {
class HandleStructSupport {
public:
HandleStructSupport() {
zx::channel::create(0, &out1_, &out2_);
zx::channel::create(0, &out3_, &out4_);
json_ = R"({"hs":{"h1":")";
json_.append(std::to_string(out1_.get()));
json_.append(R"(", "h2":")");
json_.append(std::to_string(out2_.get()));
json_.append(R"(", "h3":")");
json_.append(std::to_string(out3_.get()));
json_.append(R"("}})");
pretty_ =
"{\n hs: #gre#test.fidlcat.examples/handle_struct#rst# = {\n " +
HandleToPretty("h1", out1_.get()) + "\n " +
HandleToPretty("h2", out2_.get()) + "\n " +
HandleToPretty("h3", out3_.get()) + "\n }\n}";
}
test::fidlcat::examples::handle_struct handle_struct() {
test::fidlcat::examples::handle_struct 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(HandleStruct, support.GetJSON(), support.GetPretty(),
support.handle_struct());
}
// Corrupt data tests
TEST_F(WireParserTest, BadSchemaPrintHex) {
std::vector<std::unique_ptr<std::istream>> library_files;
// 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.fidlcat.examples/FidlcatTestInterface",
"location": {
"filename": "../../tools/fidlcat/lib/testdata/types.test.fidl",
"line": 11,
"column": 10
},
"methods": [
{
"ordinal": 1625951384,
"generated_ordinal": 1625951384,
"name": "Int32",
"location": {
"filename": "../../tools/fidlcat/lib/testdata/types.test.fidl",
"line": 16,
"column": 5
},
"has_request": true,
"maybe_request": [
{
"type": {
"kind": "primitive"
},
"name": "i32",
"location": {
"filename": "../../tools/fidlcat/lib/testdata/types.test.fidl",
"line": 16,
"column": 17
},
"size": 4,
"max_out_of_line": 0,
"alignment": 4,
"offset": 16,
"max_handles": 0
}
],
"maybe_request_size": 24,
"maybe_request_alignment": 8,
"has_response": false
}
]
}
],
"struct_declarations": [],
"table_declarations": [],
"union_declarations": [],
"xunion_declarations": []
})FIDL";
std::unique_ptr<std::istream> file =
std::make_unique<std::istringstream>(std::istringstream(bad_schema));
library_files.push_back(std::move(file));
LibraryReadError err;
LibraryLoader loader(library_files, &err);
ASSERT_TRUE(err.value == LibraryReadError::ErrorValue::kOk);
fidl::MessageBuffer buffer;
fidl::Message message = buffer.CreateEmptyMessage();
InterceptRequest<test::fidlcat::examples::FidlcatTestInterface>(
message,
[](fidl::InterfacePtr<test::fidlcat::examples::FidlcatTestInterface>&
ptr) { ptr->Int32(0xdeadbeef); });
fidl_message_header_t header = message.header();
const InterfaceMethod* method = loader.GetByOrdinal(header.ordinal);
// If this is null, you probably have to update the schema above.
ASSERT_NE(method, nullptr);
std::unique_ptr<fidlcat::Object> decoded_request;
fidlcat::DecodeRequest(method, message, &decoded_request);
rapidjson::Document actual;
if (decoded_request != nullptr) {
decoded_request->ExtractJson(actual.GetAllocator(), actual);
}
ASSERT_STREQ(actual["i32"].GetString(), "ef be ad de");
}
} // namespace fidlcat