blob: aec3dcc3e84e7d7d112a2d47f6b7c60b1f7f3bbe [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 "wire_parser.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <iostream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "lib/fidl/cpp/test/frobinator_impl.h"
#include "library_loader.h"
#include "message_decoder.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "test/fidlcat/examples/cpp/fidl.h"
#include "tools/fidlcat/lib/library_loader_test_data.h"
#include "wire_object.h"
#include "wire_parser.h"
namespace fidlcat {
// Stolen from //sdk/lib/fidl/cpp/test/async_loop_for_test.{h,cc}; cc
// is not public
class AsyncLoopForTestImpl;
class AsyncLoopForTest {
public:
// The AsyncLoopForTest constructor should also call
// async_set_default_dispatcher() with the chosen dispatcher implementation.
AsyncLoopForTest();
~AsyncLoopForTest();
// This call matches the behavior of async_loop_run_until_idle().
zx_status_t RunUntilIdle();
// This call matches the behavior of async_loop_run().
zx_status_t Run();
// Returns the underlying async_t.
async_dispatcher_t* dispatcher();
private:
std::unique_ptr<AsyncLoopForTestImpl> impl_;
};
class AsyncLoopForTestImpl {
public:
AsyncLoopForTestImpl() : loop_(&kAsyncLoopConfigAttachToThread) {}
~AsyncLoopForTestImpl() = default;
async::Loop* loop() { return &loop_; }
private:
async::Loop loop_;
};
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_;
};
// The tests in this file work the following way:
// 1) Create a channel.
// 2) Bind an interface pointer to the client side of that channel.
// 3) Listen at the other end of the channel for the message.
// 4) Convert the message to JSON using the JSON message converter, and check
// that the results look as expected.
// This binds |invoke| to one end of a channel, invokes it, and drops the wire
// format bits it picks up off the other end into |message|.
template <class T>
void InterceptRequest(fidl::Message& message,
std::function<void(fidl::InterfacePtr<T>&)> invoke) {
AsyncLoopForTest loop;
zx::channel h1, h2;
EXPECT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2));
fidl::InterfacePtr<T> ptr;
int error_count = 0;
ptr.set_error_handler([&error_count](zx_status_t status) {
EXPECT_EQ(ZX_ERR_INVALID_ARGS, status);
++error_count;
});
EXPECT_EQ(ZX_OK, ptr.Bind(std::move(h1)));
invoke(ptr);
loop.RunUntilIdle();
EXPECT_EQ(ZX_OK, message.Read(h2.get(), 0));
}
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;
ASSERT_TRUE(loader_->GetByOrdinal(header.ordinal, &method));
ASSERT_EQ("Grob", method->name());
rapidjson::Document actual;
fidlcat::RequestToJSON(method, message, 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::this_is_an_interface
// (TODO: generalize which interface to use)
// _json_value is a JSON representation of the value in the previous parameter.
// The remaining parameters are the parameters to _iface to generate the
// _json_value.
#define TEST_WIRE_TO_JSON_BODY(_iface, _json_value, ...) \
do { \
fidl::MessageBuffer buffer; \
fidl::Message message = buffer.CreateEmptyMessage(); \
using test::fidlcat::examples::this_is_an_interface; \
InterceptRequest<this_is_an_interface>( \
message, [&](fidl::InterfacePtr<this_is_an_interface>& ptr) { \
ptr->_iface(__VA_ARGS__); \
}); \
\
fidl_message_header_t header = message.header(); \
\
const InterfaceMethod* method; \
ASSERT_TRUE(loader_->GetByOrdinal(header.ordinal, &method)); \
ASSERT_EQ(#_iface, method->name()); \
\
rapidjson::Document actual; \
ASSERT_TRUE(fidlcat::RequestToJSON(method, message, actual)) \
<< "Could not convert message to json"; \
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(); \
\
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_WIRE_TO_JSON_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::this_is_an_interface
// (TODO: generalize which interface to use)
// _json_value is a JSON representation of the value in the previous parameter.
// The remaining parameters are the parameters to _iface to generate the
// _json_value.
#define TEST_WIRE_TO_JSON(_testname, _iface, _json_value, ...) \
TEST_F(WireParserTest, Parse##_testname) { \
TEST_WIRE_TO_JSON_BODY(_iface, _json_value, __VA_ARGS__); \
}
namespace {
std::string RawPair(std::string key, std::string value) {
std::string result = "{\"";
result.append(key);
result.append("\":");
result.append(value);
result.append("}");
return result;
}
} // namespace
// Scalar Tests
namespace {
template <class T>
std::string SingleToJson(std::string key, T value) {
return RawPair(key, "\"" + std::to_string(value) + "\"");
}
} // namespace
#define TEST_SINGLE(_iface, _key, _value) \
TEST_WIRE_TO_JSON(_iface, _iface, SingleToJson(#_key, _value), _value)
TEST_SINGLE(Float32, f32, 0.25)
TEST_SINGLE(Float64, f64, 9007199254740992.0)
TEST_SINGLE(Int8, i8, std::numeric_limits<int8_t>::min())
TEST_SINGLE(Int16, i16, std::numeric_limits<int16_t>::min())
TEST_SINGLE(Int32, i32, std::numeric_limits<int32_t>::min())
TEST_SINGLE(Int64, i64, std::numeric_limits<int64_t>::min())
TEST_SINGLE(Uint8, i8, std::numeric_limits<uint8_t>::max())
TEST_SINGLE(Uint16, i16, std::numeric_limits<uint16_t>::max())
TEST_SINGLE(Uint32, i32, std::numeric_limits<uint32_t>::max())
TEST_SINGLE(Uint64, i64, std::numeric_limits<uint64_t>::max())
TEST_WIRE_TO_JSON(SingleBool, Bool, R"({"b":"true"})", true)
TEST_WIRE_TO_JSON(TwoTuple, Complex, R"({"real":"1", "imaginary":"2"})", 1, 2);
TEST_WIRE_TO_JSON(StringInt, StringInt, R"({"s":"groucho", "i32":"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_WIRE_TO_JSON(Array1, Array1, R"({"b_1":["1"]})",
(ToArray<int32_t, 1>(one_param)));
TEST_WIRE_TO_JSON(Array2, Array2, R"({"b_2":["1", "2"]})",
(ToArray<int32_t, 2>(two_params)));
TEST_WIRE_TO_JSON(VectorOneElt, Vector, R"({"v_1":["1"]})",
(ToVector<int32_t>(one_param, 1)));
std::string NullPair(std::string key, void* v) { return RawPair(key, "null"); }
TEST_WIRE_TO_JSON(NullVector, Vector, R"({"v_1": null})", nullptr)
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_WIRE_TO_JSON(TwoStringArrayInt, TwoStringArrayInt,
R"({"arr":["harpo","chico"], "i32":"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_WIRE_TO_JSON(TwoStringVectorInt, TwoStringVectorInt,
R"({"vec":["harpo", "chico"], "i32":"1"})",
TwoStringVectorFromVals("harpo", "chico"), 1)
// Struct Tests
namespace {
class StructSupport {
public:
StructSupport() {
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;
}
// TODO: It might be nice to have a more readable strategy for generating
// JSON, e.g.,:
// return GenerateObject([&]() {
// GenerateObjectMember("b", "true");
// GenerateObjectMember("i8", "-128");
// ...
// });
// Ideally, we'd also harmonize StructSupport and HandleStructSupport.
std::string GetJSON() {
std::ostringstream es;
es << R"JSON({"p":{)JSON"
<< R"JSON("b":")JSON" << (pt.b ? "true" : "false") << R"JSON(",)JSON"
<< R"JSON("i8":")JSON" << std::to_string(pt.i8)
<< R"JSON(", "i16":")JSON" << std::to_string(pt.i16)
<< R"JSON(", "i32":")JSON" << std::to_string(pt.i32)
<< R"JSON(", "i64":")JSON" << std::to_string(pt.i64)
<< R"JSON(", "u8":")JSON" << std::to_string(pt.u8)
<< R"JSON(", "u16":")JSON" << std::to_string(pt.u16)
<< R"JSON(", "u32":")JSON" << std::to_string(pt.u32)
<< R"JSON(", "u64":")JSON" << std::to_string(pt.u64)
<< R"JSON(", "f32":")JSON" << std::to_string(pt.f32)
<< R"JSON(", "f64":")JSON" << std::to_string(pt.f64) << "\"}}";
return es.str();
}
test::fidlcat::examples::primitive_types pt;
};
} // namespace
TEST_F(WireParserTest, ParseStruct) {
StructSupport sd;
TEST_WIRE_TO_JSON_BODY(Struct, sd.GetJSON(), sd.pt);
}
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;
}
} // namespace
TEST_WIRE_TO_JSON(TwoStringStructInt, TwoStringStructInt,
R"({"s":{"value1":"harpo", "value2":"chico"}, "i32":"1"})",
TwoStringStructFromVals("harpo", "chico"), 1)
namespace {
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;
}
} // namespace
TEST_WIRE_TO_JSON(TwoStringNullableStructInt, TwoStringNullableStructInt,
R"({"s":{"value1":"harpo", "value2":"chico"}, "i32":"1"})",
TwoStringStructFromValsPtr("harpo", "chico"), 1)
TEST_WIRE_TO_JSON(NullableStruct, NullableStruct, R"({"p":null})", nullptr);
TEST_WIRE_TO_JSON(NullableStructAndInt, NullableStructAndInt,
R"({"p":null, "i":"1"})", nullptr, 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;
}
} // namespace
TEST_WIRE_TO_JSON(UnionInt, Union, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
GetIntUnion<isu>(42), 1);
TEST_WIRE_TO_JSON(
UnionStruct, Union,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
GetStructUnion<isu>("harpo", "chico"), 1);
TEST_WIRE_TO_JSON(NullableUnionInt, NullableUnion,
R"({"isu":{"variant_i":"42"}, "i" : "1"})",
GetIntUnionPtr<isu>(42), 1);
TEST_WIRE_TO_JSON(
NullableUnionStruct, NullableUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
GetStructUnionPtr<isu>("harpo", "chico"), 1);
TEST_WIRE_TO_JSON(NullableUnionIntFirstInt, NullableUnionIntFirst,
R"({"i" : "1", "isu":{"variant_i":"42"}})", 1,
GetIntUnionPtr<isu>(42));
TEST_WIRE_TO_JSON(
NullableUnionIntFirstStruct, NullableUnionIntFirst,
R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}})",
1, GetStructUnionPtr<isu>("harpo", "chico"));
TEST_WIRE_TO_JSON(XUnionInt, XUnion, R"({"isu":{"variant_i":"42"}, "i" : "1"})",
GetIntUnion<xisu>(42), 1);
TEST_WIRE_TO_JSON(
XUnionStruct, XUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
GetStructUnion<xisu>("harpo", "chico"), 1);
TEST_WIRE_TO_JSON(NullableXUnionInt, NullableXUnion,
R"({"isu":{"variant_i":"42"}, "i" : "1"})",
GetIntUnionPtr<xisu>(42), 1);
TEST_WIRE_TO_JSON(
NullableXUnionStruct, NullableXUnion,
R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})",
GetStructUnionPtr<xisu>("harpo", "chico"), 1);
TEST_WIRE_TO_JSON(NullableXUnionIntFirstInt, NullableXUnionIntFirst,
R"({"i" : "1", "isu":{"variant_i":"42"}})", 1,
GetIntUnionPtr<xisu>(42));
TEST_WIRE_TO_JSON(
NullableXUnionIntFirstStruct, NullableXUnionIntFirst,
R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"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;
}
} // namespace
TEST_WIRE_TO_JSON(ShortUnion8, ShortUnion,
R"({"u":{"variant_u8":"16"}, "i":"1"})",
GetUInt8Union<uuu>(16), 1);
TEST_WIRE_TO_JSON(ShortUnion16, ShortUnion,
R"({"u":{"variant_u16":"1024"}, "i":"1"})",
GetUInt16Union<uuu>(1024), 1);
TEST_WIRE_TO_JSON(ShortXUnion8, ShortXUnion,
R"({"u":{"variant_u8":"16"}, "i":"1"})",
GetUInt8Union<uux>(16), 1);
TEST_WIRE_TO_JSON(ShortXUnion16, ShortXUnion,
R"({"u":{"variant_u16":"1024"}, "i":"1"})",
GetUInt16Union<uux>(1024), 1);
// Enum Tests
TEST_WIRE_TO_JSON(DefaultEnum, DefaultEnum, R"({"ev":"x"})",
test::fidlcat::examples::default_enum::x);
TEST_WIRE_TO_JSON(I8Enum, I8Enum, R"({"ev":"x"})",
test::fidlcat::examples::i8_enum::x);
TEST_WIRE_TO_JSON(I16Enum, I16Enum, R"({"ev":"x"})",
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;
}
TEST_WIRE_TO_JSON(Table0, Table, R"({"table":{}, "i":"2"})",
GetTable({}, {}, {}, {}), 2)
TEST_WIRE_TO_JSON(Table1, Table,
R"({"table":{
"third_union":{"variant_i":"42"}
},
"i":"2"})",
GetTable({}, {}, {}, 42), 2)
TEST_WIRE_TO_JSON(Table2, Table,
R"({"table":{
"second_struct":{"value1":"harpo", "value2":"groucho"}
},
"i":"2"})",
GetTable({}, "harpo", "groucho", {}), 2)
TEST_WIRE_TO_JSON(Table3, Table,
R"({"table":{
"second_struct":{
"value1":"harpo", "value2":"groucho"},
"third_union":{"variant_i":"42"}},
"i":"2"})",
GetTable({}, "harpo", "groucho", 42), 2)
TEST_WIRE_TO_JSON(Table4, Table, R"({"table":{
"first_int16":"1"
},
"i":"2"})",
GetTable(1, {}, {}, {}), 2)
TEST_WIRE_TO_JSON(Table5, Table,
R"({"table":{
"first_int16":"1",
"third_union":{"variant_i":"42"}
},
"i":"2"})",
GetTable(1, {}, {}, 42), 2)
TEST_WIRE_TO_JSON(Table6, Table,
R"({"table":{
"first_int16":"1",
"second_struct":{
"value1":"harpo", "value2":"groucho"}
},
"i":"2"})",
GetTable(1, "harpo", "groucho", {}), 2)
TEST_WIRE_TO_JSON(Table7, Table,
R"({"table":{
"first_int16":"1",
"second_struct":{
"value1":"harpo", "value2":"groucho"},
"third_union":{"variant_i":"42"}
},
"i":"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"("})");
}
zx::channel handle() { return std::move(out2_); }
std::string GetJSON() { return json_; }
private:
zx::channel out1_;
zx::channel out2_;
std::string json_;
};
} // namespace
TEST_F(WireParserTest, ParseHandle) {
HandleSupport support;
TEST_WIRE_TO_JSON_BODY(Handle, support.GetJSON(), support.handle());
}
TEST_F(WireParserTest, ParseNullableHandle) {
HandleSupport support;
TEST_WIRE_TO_JSON_BODY(NullableHandle, support.GetJSON(), 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"("}})");
}
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_; }
private:
zx::channel out1_;
zx::channel out2_;
zx::channel out3_;
zx::channel out4_;
std::string json_;
};
} // namespace
TEST_F(WireParserTest, ParseHandleStruct) {
HandleStructSupport support;
TEST_WIRE_TO_JSON_BODY(HandleStruct, support.GetJSON(),
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/this_is_an_interface",
"location": {
"filename": "../../tools/fidlcat/lib/testdata/types.test.fidl",
"line": 7,
"column": 9
},
"methods": [
{
"ordinal": 912304001,
"generated_ordinal": 912304001,
"name": "Int32",
"location": {
"filename": "../../tools/fidlcat/lib/testdata/types.test.fidl",
"line": 12,
"column": 4
},
"has_request": true,
"maybe_request": [
{
"type": {
"kind": "primitive"
},
"name": "i32",
"location": {
"filename": "../../tools/fidlcat/lib/testdata/types.test.fidl",
"line": 12,
"column": 16
},
"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::this_is_an_interface>(
message,
[](fidl::InterfacePtr<test::fidlcat::examples::this_is_an_interface>&
ptr) { ptr->Int32(0xdeadbeef); });
fidl_message_header_t header = message.header();
const InterfaceMethod* method;
// If this is false, you probably have to update the schema above.
ASSERT_TRUE(loader.GetByOrdinal(header.ordinal, &method));
rapidjson::Document actual;
fidlcat::RequestToJSON(method, message, actual);
ASSERT_STREQ(actual["i32"].GetString(), "ef be ad de");
}
} // namespace fidlcat