mirror of
https://github.com/Heretek-AI/GDevelop-Heretek.git
synced 2026-10-12 02:02:42 +00:00
326 lines
14 KiB
C++
326 lines
14 KiB
C++
/*
|
|
* GDevelop Core
|
|
* Copyright 2008-2016 Florian Rival ([email protected]). All rights
|
|
* reserved. This project is released under the MIT License.
|
|
*/
|
|
/**
|
|
* @file Tests covering gd::ObjectsContainer, in particular the lookup of
|
|
* objects by name, which must stay correct whatever the way objects are
|
|
* added, removed, moved or renamed.
|
|
*/
|
|
#include "GDCore/Project/ObjectsContainer.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "DummyPlatform.h"
|
|
#include "GDCore/Extensions/Platform.h"
|
|
#include "GDCore/Project/Layout.h"
|
|
#include "GDCore/Project/Object.h"
|
|
#include "GDCore/Project/ObjectsContainersList.h"
|
|
#include "GDCore/Project/Project.h"
|
|
#include "GDCore/Serialization/SerializerElement.h"
|
|
#include "catch.hpp"
|
|
|
|
namespace {
|
|
gd::Object &InsertObject(gd::Project &project,
|
|
gd::ObjectsContainer &objectsContainer,
|
|
const gd::String &name) {
|
|
return objectsContainer.InsertNewObject(
|
|
project, "MyExtension::Sprite", name,
|
|
objectsContainer.GetObjectsCount());
|
|
}
|
|
} // namespace
|
|
|
|
TEST_CASE("ObjectsContainer", "[common]") {
|
|
gd::Platform platform;
|
|
gd::Project project;
|
|
SetupProjectWithDummyPlatform(project, platform);
|
|
gd::ObjectsContainer objectsContainer(gd::ObjectsContainer::Scene);
|
|
|
|
SECTION("Objects can be found by name once inserted") {
|
|
gd::Object &object1 = InsertObject(project, objectsContainer, "Object1");
|
|
gd::Object &object2 = InsertObject(project, objectsContainer, "Object2");
|
|
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object2"));
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object3"));
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object1);
|
|
REQUIRE(&objectsContainer.GetObject("Object2") == &object2);
|
|
|
|
// Insertion after a lookup is also found.
|
|
gd::Object &object3 = InsertObject(project, objectsContainer, "Object3");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object3"));
|
|
REQUIRE(&objectsContainer.GetObject("Object3") == &object3);
|
|
|
|
// Lookups on a const container work too.
|
|
const gd::ObjectsContainer &constObjectsContainer = objectsContainer;
|
|
REQUIRE(constObjectsContainer.HasObjectNamed("Object2"));
|
|
REQUIRE(&constObjectsContainer.GetObject("Object2") == &object2);
|
|
}
|
|
|
|
SECTION("Objects renamed directly with gd::Object::SetName are found") {
|
|
gd::Object &object1 = InsertObject(project, objectsContainer, "Object1");
|
|
gd::Object &object2 = InsertObject(project, objectsContainer, "Object2");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
object1.SetName("RenamedObject1");
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(objectsContainer.HasObjectNamed("RenamedObject1"));
|
|
REQUIRE(&objectsContainer.GetObject("RenamedObject1") == &object1);
|
|
|
|
// Swap names between two objects.
|
|
object2.SetName("Object1");
|
|
object1.SetName("Object2");
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object2);
|
|
REQUIRE(&objectsContainer.GetObject("Object2") == &object1);
|
|
REQUIRE(!objectsContainer.HasObjectNamed("RenamedObject1"));
|
|
}
|
|
|
|
SECTION("Objects renamed by unserialization are found") {
|
|
gd::Object &object = InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
gd::SerializerElement element;
|
|
object.SerializeTo(element);
|
|
element.SetAttribute("name", "RenamedObject1");
|
|
object.UnserializeFrom(project, element);
|
|
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(&objectsContainer.GetObject("RenamedObject1") == &object);
|
|
}
|
|
|
|
SECTION("Objects renamed by copy-assignment are found") {
|
|
gd::Object &object = InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
object = *project.CreateObject("MyExtension::Sprite", "RenamedObject1");
|
|
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(&objectsContainer.GetObject("RenamedObject1") == &object);
|
|
}
|
|
|
|
SECTION("Removed objects are not found anymore") {
|
|
InsertObject(project, objectsContainer, "Object1");
|
|
InsertObject(project, objectsContainer, "Object2");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
objectsContainer.RemoveObject("Object1");
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object2"));
|
|
|
|
// Re-adding an object with the same name gives the new object.
|
|
gd::Object &newObject1 = InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &newObject1);
|
|
|
|
objectsContainer.Clear();
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object2"));
|
|
}
|
|
|
|
SECTION("Objects inserted by copy or in a folder are found") {
|
|
gd::Object &object1 = InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
gd::Object &object2 = objectsContainer.InsertNewObjectInFolder(
|
|
project, "MyExtension::Sprite", "Object2",
|
|
objectsContainer.GetRootFolder(), 0);
|
|
REQUIRE(&objectsContainer.GetObject("Object2") == &object2);
|
|
|
|
std::unique_ptr<gd::Object> otherObject =
|
|
project.CreateObject("MyExtension::Sprite", "Object3");
|
|
gd::Object &object3 = objectsContainer.InsertObject(*otherObject, 0);
|
|
REQUIRE(&objectsContainer.GetObject("Object3") == &object3);
|
|
REQUIRE(&object3 != otherObject.get());
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object1);
|
|
}
|
|
|
|
SECTION("Moved objects are still found") {
|
|
gd::Object &object1 = InsertObject(project, objectsContainer, "Object1");
|
|
gd::Object &object2 = InsertObject(project, objectsContainer, "Object2");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
objectsContainer.MoveObject(0, 1);
|
|
REQUIRE(&objectsContainer.GetObject(0) == &object2);
|
|
REQUIRE(&objectsContainer.GetObject(1) == &object1);
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object1);
|
|
REQUIRE(&objectsContainer.GetObject("Object2") == &object2);
|
|
REQUIRE(objectsContainer.GetObjectPosition("Object1") == 1);
|
|
}
|
|
|
|
SECTION("Objects moved to another container are found in it only") {
|
|
gd::Object &object = InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
gd::ObjectsContainer otherObjectsContainer(gd::ObjectsContainer::Scene);
|
|
REQUIRE(!otherObjectsContainer.HasObjectNamed("Object1"));
|
|
|
|
objectsContainer.MoveObjectFolderOrObjectToAnotherContainerInFolder(
|
|
objectsContainer.GetRootFolder().GetObjectChild("Object1"),
|
|
otherObjectsContainer, otherObjectsContainer.GetRootFolder(), 0);
|
|
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(&otherObjectsContainer.GetObject("Object1") == &object);
|
|
}
|
|
|
|
SECTION("Copied containers have their own objects") {
|
|
gd::Object &object = InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
gd::ObjectsContainer copiedObjectsContainer(objectsContainer);
|
|
REQUIRE(copiedObjectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(&copiedObjectsContainer.GetObject("Object1") != &object);
|
|
|
|
gd::ObjectsContainer assignedObjectsContainer(gd::ObjectsContainer::Global);
|
|
InsertObject(project, assignedObjectsContainer, "Object2");
|
|
REQUIRE(assignedObjectsContainer.HasObjectNamed("Object2"));
|
|
assignedObjectsContainer = objectsContainer;
|
|
REQUIRE(!assignedObjectsContainer.HasObjectNamed("Object2"));
|
|
REQUIRE(assignedObjectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(&assignedObjectsContainer.GetObject("Object1") != &object);
|
|
|
|
// Renaming in a copy does not affect the original.
|
|
copiedObjectsContainer.GetObject("Object1").SetName("RenamedObject1");
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object);
|
|
REQUIRE(!objectsContainer.HasObjectNamed("RenamedObject1"));
|
|
REQUIRE(copiedObjectsContainer.HasObjectNamed("RenamedObject1"));
|
|
}
|
|
|
|
SECTION("Unserialized objects are found") {
|
|
InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object1"));
|
|
|
|
gd::SerializerElement element;
|
|
objectsContainer.SerializeObjectsTo(element);
|
|
element.GetChild(0).SetAttribute("name", "Object2");
|
|
|
|
objectsContainer.UnserializeObjectsFrom(project, element);
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(objectsContainer.HasObjectNamed("Object2"));
|
|
REQUIRE(&objectsContainer.GetObject("Object2") ==
|
|
&objectsContainer.GetObject(0));
|
|
}
|
|
|
|
SECTION("Empty container and empty names") {
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Object1"));
|
|
REQUIRE(!objectsContainer.HasObjectNamed(""));
|
|
|
|
InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(!objectsContainer.HasObjectNamed(""));
|
|
|
|
gd::Object &unnamedObject = InsertObject(project, objectsContainer, "");
|
|
REQUIRE(&objectsContainer.GetObject("") == &unnamedObject);
|
|
}
|
|
|
|
SECTION("Names with non-ASCII characters") {
|
|
gd::Object &object = InsertObject(project, objectsContainer, "Objet_é_日本");
|
|
REQUIRE(objectsContainer.HasObjectNamed("Objet_é_日本"));
|
|
REQUIRE(&objectsContainer.GetObject("Objet_é_日本") == &object);
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Objet_e_日本"));
|
|
|
|
object.SetName("Objet_è_日本");
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Objet_é_日本"));
|
|
REQUIRE(&objectsContainer.GetObject("Objet_è_日本") == &object);
|
|
}
|
|
|
|
SECTION("References stay valid while other objects are changed") {
|
|
gd::Object &object1 = InsertObject(project, objectsContainer, "Object1");
|
|
const gd::Object *found = &objectsContainer.GetObject("Object1");
|
|
REQUIRE(found == &object1);
|
|
|
|
// Enough insertions to force reallocations of the underlying vector.
|
|
for (int i = 0; i < 100; ++i) {
|
|
InsertObject(project, objectsContainer, "Other" + gd::String::From(i));
|
|
}
|
|
objectsContainer.RemoveObject("Other3");
|
|
objectsContainer.MoveObject(0, 50);
|
|
objectsContainer.GetObject("Other4").SetName("RenamedOther4");
|
|
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == found);
|
|
REQUIRE(&objectsContainer.GetObject("Other99") ==
|
|
&objectsContainer.GetObject(objectsContainer.GetObjectsCount() - 1));
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Other3"));
|
|
REQUIRE(!objectsContainer.HasObjectNamed("Other4"));
|
|
REQUIRE(objectsContainer.HasObjectNamed("RenamedOther4"));
|
|
}
|
|
|
|
SECTION("Duplicated object renamed afterwards (as done by the IDE)") {
|
|
gd::Object &object1 = InsertObject(project, objectsContainer, "Object1");
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object1);
|
|
|
|
// The IDE inserts a copy (which has the same name), then renames it.
|
|
gd::Object &duplicatedObject =
|
|
objectsContainer.InsertObject(objectsContainer.GetObject("Object1"), 0);
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &duplicatedObject);
|
|
|
|
duplicatedObject.SetName("Object1Copy");
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object1);
|
|
REQUIRE(&objectsContainer.GetObject("Object1Copy") == &duplicatedObject);
|
|
}
|
|
|
|
SECTION("A copied container outlives the original") {
|
|
std::unique_ptr<gd::ObjectsContainer> copiedObjectsContainer;
|
|
gd::ObjectsContainer assignedObjectsContainer(gd::ObjectsContainer::Global);
|
|
InsertObject(project, assignedObjectsContainer, "OldObject");
|
|
REQUIRE(assignedObjectsContainer.HasObjectNamed("OldObject"));
|
|
{
|
|
gd::ObjectsContainer originalObjectsContainer(
|
|
gd::ObjectsContainer::Scene);
|
|
InsertObject(project, originalObjectsContainer, "Object1");
|
|
// Build the index of the original before copying it.
|
|
REQUIRE(originalObjectsContainer.HasObjectNamed("Object1"));
|
|
|
|
copiedObjectsContainer.reset(
|
|
new gd::ObjectsContainer(originalObjectsContainer));
|
|
assignedObjectsContainer = originalObjectsContainer;
|
|
}
|
|
|
|
REQUIRE(copiedObjectsContainer->HasObjectNamed("Object1"));
|
|
REQUIRE(&copiedObjectsContainer->GetObject("Object1") ==
|
|
&copiedObjectsContainer->GetObject(0));
|
|
REQUIRE(!assignedObjectsContainer.HasObjectNamed("OldObject"));
|
|
REQUIRE(&assignedObjectsContainer.GetObject("Object1") ==
|
|
&assignedObjectsContainer.GetObject(0));
|
|
}
|
|
|
|
SECTION("Lookups through an ObjectsContainersList follow renames") {
|
|
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
|
|
gd::Object &globalObject =
|
|
InsertObject(project, project.GetObjects(), "GlobalObject");
|
|
gd::Object &sceneObject =
|
|
InsertObject(project, layout.GetObjects(), "SceneObject");
|
|
auto objectsContainersList = gd::ObjectsContainersList::
|
|
MakeNewObjectsContainersListForProjectAndLayout(project, layout);
|
|
// The variables container identifies which object was found.
|
|
auto findVariables = [&](const gd::String &name) {
|
|
return objectsContainersList.GetObjectOrGroupVariablesContainer(name);
|
|
};
|
|
|
|
REQUIRE(findVariables("GlobalObject") == &globalObject.GetVariables());
|
|
REQUIRE(findVariables("SceneObject") == &sceneObject.GetVariables());
|
|
REQUIRE(findVariables("UnknownObject") == nullptr);
|
|
REQUIRE(!objectsContainersList.HasObjectNamed("UnknownObject"));
|
|
|
|
sceneObject.SetName("RenamedSceneObject");
|
|
globalObject.SetName("SceneObject");
|
|
REQUIRE(!objectsContainersList.HasObjectNamed("GlobalObject"));
|
|
REQUIRE(findVariables("RenamedSceneObject") == &sceneObject.GetVariables());
|
|
REQUIRE(findVariables("SceneObject") == &globalObject.GetVariables());
|
|
|
|
layout.GetObjects().RemoveObject("RenamedSceneObject");
|
|
REQUIRE(!objectsContainersList.HasObjectNamed("RenamedSceneObject"));
|
|
REQUIRE(findVariables("RenamedSceneObject") == nullptr);
|
|
}
|
|
|
|
SECTION("The first object wins when two objects have the same name") {
|
|
gd::Object &object1 = InsertObject(project, objectsContainer, "Object1");
|
|
gd::Object &object2 = InsertObject(project, objectsContainer, "Object2");
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object1);
|
|
|
|
object2.SetName("Object1");
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object1);
|
|
|
|
objectsContainer.MoveObject(1, 0);
|
|
REQUIRE(&objectsContainer.GetObject("Object1") == &object2);
|
|
}
|
|
}
|