| // Copyright 2019 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "json_serializer.h" |
| |
| #include "dap/typeinfo.h" |
| #include "dap/typeof.h" |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace dap { |
| |
| struct JSONInnerTestObject { |
| integer i; |
| }; |
| |
| DAP_STRUCT_TYPEINFO(JSONInnerTestObject, |
| "json-inner-test-object", |
| DAP_FIELD(i, "i")); |
| |
| struct JSONTestObject { |
| boolean b; |
| integer i; |
| number n; |
| array<integer> a; |
| object o; |
| string s; |
| optional<integer> o1; |
| optional<integer> o2; |
| JSONInnerTestObject inner; |
| }; |
| |
| DAP_STRUCT_TYPEINFO(JSONTestObject, |
| "json-test-object", |
| DAP_FIELD(b, "b"), |
| DAP_FIELD(i, "i"), |
| DAP_FIELD(n, "n"), |
| DAP_FIELD(a, "a"), |
| DAP_FIELD(o, "o"), |
| DAP_FIELD(s, "s"), |
| DAP_FIELD(o1, "o1"), |
| DAP_FIELD(o2, "o2"), |
| DAP_FIELD(inner, "inner")); |
| |
| struct JSONObjectNoFields {}; |
| |
| DAP_STRUCT_TYPEINFO(JSONObjectNoFields, "json-object-no-fields"); |
| |
| struct SimpleJSONTestObject { |
| boolean b; |
| integer i; |
| }; |
| DAP_STRUCT_TYPEINFO(SimpleJSONTestObject, |
| "simple-json-test-object", |
| DAP_FIELD(b, "b"), |
| DAP_FIELD(i, "i")); |
| |
| } // namespace dap |
| |
| class JSONSerializer : public testing::Test { |
| protected: |
| static dap::object GetSimpleObject() { |
| return dap::object({{"one", dap::integer(1)}, |
| {"two", dap::number(2)}, |
| {"three", dap::string("three")}, |
| {"four", dap::boolean(true)}}); |
| } |
| void TEST_SIMPLE_OBJECT(const dap::object& obj) { |
| NESTED_TEST_FAILED = true; |
| auto ref_obj = GetSimpleObject(); |
| ASSERT_EQ(obj.size(), ref_obj.size()); |
| ASSERT_TRUE(obj.at("one").is<dap::integer>()); |
| ASSERT_TRUE(obj.at("two").is<dap::number>()); |
| ASSERT_TRUE(obj.at("three").is<dap::string>()); |
| ASSERT_TRUE(obj.at("four").is<dap::boolean>()); |
| |
| ASSERT_EQ(ref_obj.at("one").get<dap::integer>(), |
| obj.at("one").get<dap::integer>()); |
| ASSERT_EQ(ref_obj.at("two").get<dap::number>(), |
| obj.at("two").get<dap::number>()); |
| ASSERT_EQ(ref_obj.at("three").get<dap::string>(), |
| obj.at("three").get<dap::string>()); |
| ASSERT_EQ(ref_obj.at("four").get<dap::boolean>(), |
| obj.at("four").get<dap::boolean>()); |
| NESTED_TEST_FAILED = false; |
| } |
| template <typename T> |
| void TEST_SERIALIZING_DESERIALIZING(const T& encoded, T& decoded) { |
| NESTED_TEST_FAILED = true; |
| dap::json::Serializer s; |
| ASSERT_TRUE(s.serialize(encoded)); |
| dap::json::Deserializer d(s.dump()); |
| ASSERT_TRUE(d.deserialize(&decoded)); |
| NESTED_TEST_FAILED = false; |
| } |
| bool NESTED_TEST_FAILED = false; |
| #define _ASSERT_PASS(NESTED_TEST) \ |
| NESTED_TEST; \ |
| ASSERT_FALSE(NESTED_TEST_FAILED); |
| }; |
| |
| TEST_F(JSONSerializer, SerializeDeserialize) { |
| dap::JSONTestObject encoded; |
| encoded.b = true; |
| encoded.i = 32; |
| encoded.n = 123.456; |
| encoded.a = {2, 4, 6, 8, 0x100000000, -2, -4, -6, -8, -0x100000000}; |
| encoded.o["one"] = dap::integer(1); |
| encoded.o["two"] = dap::number(2); |
| encoded.s = "hello world"; |
| encoded.o2 = 42; |
| encoded.inner.i = 70; |
| |
| dap::json::Serializer s; |
| ASSERT_TRUE(s.serialize(encoded)); |
| |
| dap::JSONTestObject decoded; |
| dap::json::Deserializer d(s.dump()); |
| ASSERT_TRUE(d.deserialize(&decoded)); |
| |
| ASSERT_EQ(encoded.b, decoded.b); |
| ASSERT_EQ(encoded.i, decoded.i); |
| ASSERT_EQ(encoded.n, decoded.n); |
| ASSERT_EQ(encoded.a, decoded.a); |
| ASSERT_EQ(encoded.o["one"].get<dap::integer>(), |
| decoded.o["one"].get<dap::integer>()); |
| ASSERT_EQ(encoded.o["two"].get<dap::number>(), |
| decoded.o["two"].get<dap::number>()); |
| ASSERT_EQ(encoded.s, decoded.s); |
| ASSERT_EQ(encoded.o2, decoded.o2); |
| ASSERT_EQ(encoded.inner.i, decoded.inner.i); |
| } |
| |
| TEST_F(JSONSerializer, SerializeObjectNoFields) { |
| dap::JSONObjectNoFields obj; |
| dap::json::Serializer s; |
| ASSERT_TRUE(s.serialize(obj)); |
| ASSERT_EQ(s.dump(), "{}"); |
| } |
| |
| TEST_F(JSONSerializer, SerializeDeserializeObject) { |
| dap::object encoded = GetSimpleObject(); |
| dap::object decoded; |
| _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); |
| _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded)); |
| } |
| |
| TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObject) { |
| dap::object encoded; |
| dap::object decoded; |
| // object nested inside object |
| dap::object encoded_embed_obj = GetSimpleObject(); |
| dap::object decoded_embed_obj; |
| encoded["embed_obj"] = encoded_embed_obj; |
| _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); |
| ASSERT_TRUE(decoded["embed_obj"].is<dap::object>()); |
| decoded_embed_obj = decoded["embed_obj"].get<dap::object>(); |
| _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_obj)); |
| } |
| |
| TEST_F(JSONSerializer, SerializeDeserializeEmbeddedStruct) { |
| dap::object encoded; |
| dap::object decoded; |
| // object nested inside object |
| dap::SimpleJSONTestObject encoded_embed_struct; |
| encoded_embed_struct.b = true; |
| encoded_embed_struct.i = 50; |
| encoded["embed_struct"] = encoded_embed_struct; |
| |
| dap::object decoded_embed_obj; |
| _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); |
| ASSERT_TRUE(decoded["embed_struct"].is<dap::object>()); |
| decoded_embed_obj = decoded["embed_struct"].get<dap::object>(); |
| ASSERT_TRUE(decoded_embed_obj.at("b").is<dap::boolean>()); |
| ASSERT_TRUE(decoded_embed_obj.at("i").is<dap::integer>()); |
| |
| ASSERT_EQ(encoded_embed_struct.b, decoded_embed_obj["b"].get<dap::boolean>()); |
| ASSERT_EQ(encoded_embed_struct.i, decoded_embed_obj["i"].get<dap::integer>()); |
| } |
| |
| TEST_F(JSONSerializer, SerializeDeserializeEmbeddedIntArray) { |
| dap::object encoded; |
| dap::object decoded; |
| // array nested inside object |
| dap::array<dap::integer> encoded_embed_arr = {1, 2, 3, 4}; |
| dap::array<dap::any> decoded_embed_arr; |
| |
| encoded["embed_arr"] = encoded_embed_arr; |
| |
| _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); |
| // TODO: Deserializing array should infer basic member types |
| ASSERT_TRUE(decoded["embed_arr"].is<dap::array<dap::any>>()); |
| decoded_embed_arr = decoded["embed_arr"].get<dap::array<dap::any>>(); |
| ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size()); |
| for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) { |
| ASSERT_TRUE(decoded_embed_arr[i].is<dap::integer>()); |
| ASSERT_EQ(encoded_embed_arr[i], decoded_embed_arr[i].get<dap::integer>()); |
| } |
| } |
| |
| TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObjectArray) { |
| dap::object encoded; |
| dap::object decoded; |
| |
| dap::array<dap::object> encoded_embed_arr = {GetSimpleObject(), |
| GetSimpleObject()}; |
| dap::array<dap::any> decoded_embed_arr; |
| |
| encoded["embed_arr"] = encoded_embed_arr; |
| |
| _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); |
| // TODO: Deserializing array should infer basic member types |
| ASSERT_TRUE(decoded["embed_arr"].is<dap::array<dap::any>>()); |
| decoded_embed_arr = decoded["embed_arr"].get<dap::array<dap::any>>(); |
| ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size()); |
| for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) { |
| ASSERT_TRUE(decoded_embed_arr[i].is<dap::object>()); |
| _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_arr[i].get<dap::object>())); |
| } |
| } |
| |
| TEST_F(JSONSerializer, DeserializeSerializeEmptyObject) { |
| auto empty_obj = "{}"; |
| dap::object decoded; |
| dap::json::Deserializer d(empty_obj); |
| ASSERT_TRUE(d.deserialize(&decoded)); |
| dap::json::Serializer s; |
| ASSERT_TRUE(s.serialize(decoded)); |
| ASSERT_EQ(s.dump(), empty_obj); |
| } |
| |
| TEST_F(JSONSerializer, SerializeDeserializeEmbeddedEmptyObject) { |
| dap::object encoded_empty_obj; |
| dap::object encoded = {{"empty_obj", encoded_empty_obj}}; |
| dap::object decoded; |
| |
| _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); |
| ASSERT_TRUE(decoded["empty_obj"].is<dap::object>()); |
| dap::object decoded_empty_obj = decoded["empty_obj"].get<dap::object>(); |
| ASSERT_EQ(encoded_empty_obj.size(), decoded_empty_obj.size()); |
| } |
| |
| TEST_F(JSONSerializer, SerializeDeserializeObjectWithNulledField) { |
| auto thing = dap::any(dap::null()); |
| dap::object encoded; |
| encoded["nulled_field"] = dap::null(); |
| dap::json::Serializer s; |
| ASSERT_TRUE(s.serialize(encoded)); |
| dap::object decoded; |
| auto dump = s.dump(); |
| dap::json::Deserializer d(dump); |
| ASSERT_TRUE(d.deserialize(&decoded)); |
| ASSERT_TRUE(encoded["nulled_field"].is<dap::null>()); |
| } |