Files
GDevelop/Core/tests/Serializer.cpp
2022-12-04 13:38:08 +01:00

231 lines
9.3 KiB
C++

/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering serialization to JSON.
*/
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "catch.hpp"
using namespace gd;
TEST_CASE("SerializerElement", "[common]") {
SECTION("Basics and copying") {
SerializerElement element;
element.AddChild("child1").SetStringValue("value123");
element.AddChild("child2").SetDoubleValue(45.6);
element.SetStringAttribute("attr1", "attr123");
SerializerElement copiedElement = element;
REQUIRE(element.GetChild("child1").GetStringValue() == "value123");
REQUIRE(element.GetChild("child2").GetDoubleValue() == 45.6);
REQUIRE(element.GetStringAttribute("attr1") == "attr123");
REQUIRE(copiedElement.GetChild("child1").GetStringValue() == "value123");
REQUIRE(copiedElement.GetChild("child2").GetDoubleValue() == 45.6);
REQUIRE(copiedElement.GetStringAttribute("attr1") == "attr123");
element.GetChild("child1").SetStringValue("value123 modified");
copiedElement.GetChild("child2").SetDoubleValue(45.678);
copiedElement.SetStringAttribute("attr1", "attr123 modified");
REQUIRE(element.GetChild("child1").GetStringValue() == "value123 modified");
REQUIRE(element.GetChild("child2").GetDoubleValue() == 45.6);
REQUIRE(element.GetStringAttribute("attr1") == "attr123");
REQUIRE(copiedElement.GetChild("child1").GetStringValue() == "value123");
REQUIRE(copiedElement.GetChild("child2").GetDoubleValue() == 45.678);
REQUIRE(copiedElement.GetStringAttribute("attr1") == "attr123 modified");
}
SECTION("Accessing already existing children, in objects") {
SerializerElement element;
element.AddChild("child1").SetStringValue("value123");
element.AddChild("child1").SetStringValue("value456");
element.AddChild("child2").SetDoubleValue(45.6);
REQUIRE(element.GetChild("child1").GetStringValue() == "value456");
REQUIRE(element.GetChild("child2").GetDoubleValue() == 45.6);
}
SECTION("Adding multiple named children, in arrays") {
SerializerElement element;
element.ConsiderAsArrayOf("namedElement");
element.AddChild("namedElement").SetStringValue("value123");
element.AddChild("namedElement").SetStringValue("value456");
element.AddChild("namedElement").SetDoubleValue(45.6);
REQUIRE(element.GetChild(0).GetStringValue() == "value123");
REQUIRE(element.GetChild(1).GetStringValue() == "value456");
REQUIRE(element.GetChild(2).GetDoubleValue() == 45.6);
}
SECTION("Adding multiple unnamed children, in arrays") {
SerializerElement element;
element.ConsiderAsArray();
element.AddChild("").SetStringValue("value123");
element.AddChild("").SetStringValue("value456");
element.AddChild("").SetDoubleValue(45.6);
REQUIRE(element.GetChild(0).GetStringValue() == "value123");
REQUIRE(element.GetChild(1).GetStringValue() == "value456");
REQUIRE(element.GetChild(2).GetDoubleValue() == 45.6);
}
SECTION("Multiline strings") {
SerializerElement element;
// A single line is saved as a string.
element.SetMultilineStringValue("test");
REQUIRE(element.GetMultilineStringValue() == "test");
REQUIRE(element.GetStringValue() == "test");
// A string can be read.
element.SetStringValue("test of\nsomething\nsaved as a string");
REQUIRE(element.GetMultilineStringValue() == "test of\nsomething\nsaved as a string");
// A multi lines string is saved as an array.
element.SetMultilineStringValue("test\nwith\nmultiple lines.");
REQUIRE(element.ConsideredAsArray() == true);
REQUIRE(element.GetChildrenCount() == 3);
REQUIRE(element.GetMultilineStringValue() == "test\nwith\nmultiple lines.");
element.SetMultilineStringValue("test\n\nwith\n\nmultiple lines.\n");
REQUIRE(element.ConsideredAsArray() == true);
REQUIRE(element.GetChildrenCount() == 6);
REQUIRE(element.GetMultilineStringValue() == "test\n\nwith\n\nmultiple lines.\n");
}
SECTION("(Deprecated) attributes") {
SerializerElement element;
element.AddChild("child1").SetStringValue("value123");
element.AddChild("child2").SetDoubleValue(45.6);
element.SetStringAttribute("attr1", "attr123");
// Setting a string attribute with the same name as a child
// will remove this child. This is because, even though in the future
// only child should be used (and only children are loaded from JSON),
// we still support attributes, so that
// setting one should take precedence over any children that was loaded.
element.SetStringAttribute("child1", "value456");
REQUIRE(element.HasChild("child1") == false);
REQUIRE(element.HasChild("child2") == true);
REQUIRE(element.GetStringAttribute("attr1") == "attr123");
REQUIRE(element.GetStringAttribute("child1") == "value456");
}
}
TEST_CASE("Serializer", "[common]") {
SECTION("JSON basics") {
gd::String originalJSON = "{\"ok\":true,\"hello\":\"world\"}";
SerializerElement element = Serializer::FromJSON(originalJSON);
REQUIRE(element.GetChild("ok").GetBoolValue() == true);
REQUIRE(element.GetChild("hello").GetStringValue() == "world");
gd::String json = Serializer::ToJSON(element);
REQUIRE(json == originalJSON);
}
SECTION("Quotes and special characters") {
gd::String originalJSON =
"{\"\\\"hello\\\"\":\" \\\"quote\\\" \",\"caret-prop\":"
"1,\"special-\\b\\f\\n\\r\\t\\\"\":\"\\b\\f\\n\\r\\t\"}";
SerializerElement element = Serializer::FromJSON(originalJSON);
REQUIRE(element.GetChild("caret-prop").GetBoolValue() == true);
REQUIRE(element.GetChild("\"hello\"").GetStringValue() ==
" \"quote\" ");
REQUIRE(element.GetChild("special-\b\f\n\r\t\"").GetStringValue() ==
"\b\f\n\r\t");
gd::String json = Serializer::ToJSON(element);
REQUIRE(json == originalJSON);
}
SECTION("UTF8 characters") {
gd::String originalJSON =
u8"{\"Ich heiße GDevelop\":\"Gut!\",\"Bonjour à tout le monde\":"
u8"1,\"Hello 官话 world\":\"官话\"}";
SerializerElement element = Serializer::FromJSON(originalJSON);
REQUIRE(
element.GetChild(u8"Bonjour à tout le monde").GetBoolValue() ==
true);
REQUIRE(element.GetChild(u8"Ich heiße GDevelop").GetStringValue() ==
"Gut!");
REQUIRE(element.GetChild(u8"Hello 官话 world").GetStringValue() ==
u8"官话");
gd::String json = Serializer::ToJSON(element);
REQUIRE(json == originalJSON);
}
SECTION("Idempotency of unserializing and serializing again") {
auto unserializeAndSerializeToJSON = [](const gd::String& originalJSON) {
SerializerElement element = Serializer::FromJSON(originalJSON);
return Serializer::ToJSON(element);
};
SECTION("Strings and numbers") {
gd::String test1 = "\"\"";
REQUIRE(unserializeAndSerializeToJSON(test1) == test1);
gd::String test2 = "123.455";
REQUIRE(unserializeAndSerializeToJSON(test2) == test2);
}
SECTION("Objects") {
gd::String test1 = "{}";
REQUIRE(unserializeAndSerializeToJSON(test1) == test1);
gd::String test2 = "{\"a\":1}";
REQUIRE(unserializeAndSerializeToJSON(test2) == test2);
gd::String test3 = "{\"a\":1,\"b\":{\"c\":2}}";
REQUIRE(unserializeAndSerializeToJSON(test3) == test3);
}
SECTION("Arrays") {
gd::String test1 = "[]";
REQUIRE(unserializeAndSerializeToJSON(test1) == test1);
gd::String test2 = "[1,2]";
REQUIRE(unserializeAndSerializeToJSON(test2) == test2);
}
SECTION("Mixed") {
gd::String test1 =
"{\"hello\":{\"world\":[{},[],3,\"4\"],\"world2\":[-1,\"-2\","
"{\"-3\":[-4]}]}}";
REQUIRE(unserializeAndSerializeToJSON(test1) == test1);
gd::String test2 =
"{\"hello\":{\"world\":[{},[],3,4],\"world2\":[-1,\"-2\","
"{\"-3\":[-4]}]}}";
REQUIRE(unserializeAndSerializeToJSON(test2) == test2);
}
}
SECTION("(Deprecated) attributes") {
gd::String originalJSON = "{\"ok\":true,\"hello\":\"world\"}";
SerializerElement element = Serializer::FromJSON(originalJSON);
REQUIRE(element.GetChild("ok").GetBoolValue() == true);
REQUIRE(element.GetChild("hello").GetStringValue() == "world");
// Attributes are deprecated, but can still be used to access
// children:
REQUIRE(element.GetBoolAttribute("ok") == true);
REQUIRE(element.GetStringAttribute("hello") == "world");
REQUIRE(element.GetStringAttribute("idontexists") == "");
// Add a new children and changes an attribute. Even though "hello"
// was loaded as a child, attributes are still supported so the child
// with this name should be removed.
element.AddChild("hello2").SetStringValue("world2");
element.SetStringAttribute("hello", "world1");
gd::String json = Serializer::ToJSON(element);
REQUIRE(json == "{\"hello\":\"world1\",\"ok\":true,\"hello2\":\"world2\"}");
}
}