// 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 a; object o; string s; optional o1; optional 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()); ASSERT_TRUE(obj.at("two").is()); ASSERT_TRUE(obj.at("three").is()); ASSERT_TRUE(obj.at("four").is()); ASSERT_EQ(ref_obj.at("one").get(), obj.at("one").get()); ASSERT_EQ(ref_obj.at("two").get(), obj.at("two").get()); ASSERT_EQ(ref_obj.at("three").get(), obj.at("three").get()); ASSERT_EQ(ref_obj.at("four").get(), obj.at("four").get()); NESTED_TEST_FAILED = false; } template 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(), decoded.o["one"].get()); ASSERT_EQ(encoded.o["two"].get(), decoded.o["two"].get()); 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()); decoded_embed_obj = decoded["embed_obj"].get(); _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()); decoded_embed_obj = decoded["embed_struct"].get(); ASSERT_TRUE(decoded_embed_obj.at("b").is()); ASSERT_TRUE(decoded_embed_obj.at("i").is()); ASSERT_EQ(encoded_embed_struct.b, decoded_embed_obj["b"].get()); ASSERT_EQ(encoded_embed_struct.i, decoded_embed_obj["i"].get()); } TEST_F(JSONSerializer, SerializeDeserializeEmbeddedIntArray) { dap::object encoded; dap::object decoded; // array nested inside object dap::array encoded_embed_arr = {1, 2, 3, 4}; dap::array 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>()); decoded_embed_arr = decoded["embed_arr"].get>(); 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()); ASSERT_EQ(encoded_embed_arr[i], decoded_embed_arr[i].get()); } } TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObjectArray) { dap::object encoded; dap::object decoded; dap::array encoded_embed_arr = {GetSimpleObject(), GetSimpleObject()}; dap::array 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>()); decoded_embed_arr = decoded["embed_arr"].get>(); 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()); _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_arr[i].get())); } } 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 decoded_empty_obj = decoded["empty_obj"].get(); 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()); }