Path: blob/master/tests/scene/test_instance_placeholder.cpp
45991 views
/**************************************************************************/1/* test_instance_placeholder.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tests/test_macros.h"3132TEST_FORCE_LINK(test_instance_placeholder)3334#include "core/io/dir_access.h"35#include "core/io/resource_loader.h"36#include "core/io/resource_saver.h"37#include "core/object/class_db.h"38#include "scene/main/instance_placeholder.h"39#include "scene/main/scene_tree.h"40#include "scene/main/window.h"41#include "scene/resources/packed_scene.h"42#include "tests/test_utils.h"4344namespace TestInstancePlaceholder {4546class _TestInstancePlaceholderNode : public Node {47GDCLASS(_TestInstancePlaceholderNode, Node);4849protected:50static void _bind_methods() {51ClassDB::bind_method(D_METHOD("set_int_property", "int_property"), &_TestInstancePlaceholderNode::set_int_property);52ClassDB::bind_method(D_METHOD("get_int_property"), &_TestInstancePlaceholderNode::get_int_property);5354ADD_PROPERTY(PropertyInfo(Variant::INT, "int_property"), "set_int_property", "get_int_property");5556ClassDB::bind_method(D_METHOD("set_reference_property", "reference_property"), &_TestInstancePlaceholderNode::set_reference_property);57ClassDB::bind_method(D_METHOD("get_reference_property"), &_TestInstancePlaceholderNode::get_reference_property);5859ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "reference_property", PROPERTY_HINT_NODE_TYPE), "set_reference_property", "get_reference_property");6061ClassDB::bind_method(D_METHOD("set_reference_array_property", "reference_array_property"), &_TestInstancePlaceholderNode::set_reference_array_property);62ClassDB::bind_method(D_METHOD("get_reference_array_property"), &_TestInstancePlaceholderNode::get_reference_array_property);6364// The hint string value "24/34:Node" is determined from existing PackedScenes with typed Array properties.65ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "reference_array_property", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_reference_array_property", "get_reference_array_property");66}6768public:69int int_property = 0;7071void set_int_property(int p_int) {72int_property = p_int;73}7475int get_int_property() const {76return int_property;77}7879Variant reference_property;8081void set_reference_property(const Variant &p_node) {82reference_property = p_node;83}8485Variant get_reference_property() const {86return reference_property;87}8889Array reference_array_property;9091void set_reference_array_property(const Array &p_array) {92reference_array_property = p_array;93}9495Array get_reference_array_property() const {96return reference_array_property;97}9899_TestInstancePlaceholderNode() {100reference_array_property.set_typed(Variant::OBJECT, "Node", Variant());101}102};103104TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with no overrides") {105GDREGISTER_CLASS(_TestInstancePlaceholderNode);106107SUBCASE("with non-node values") {108InstancePlaceholder *ip = memnew(InstancePlaceholder);109ip->set_name("TestScene");110Node *root = memnew(Node);111SceneTree::get_singleton()->get_root()->add_child(root);112113root->add_child(ip);114// Create a scene to instance.115_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);116scene->set_int_property(12);117118// Pack the scene.119Ref<PackedScene> packed_scene = memnew(PackedScene);120const Error err = packed_scene->pack(scene);121REQUIRE(err == OK);122123// Instantiate the scene.124_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));125REQUIRE(created != nullptr);126CHECK(created->get_name() == "TestScene");127CHECK(created->get_int_property() == 12);128129root->queue_free();130memdelete(scene);131}132133SUBCASE("with node value") {134InstancePlaceholder *ip = memnew(InstancePlaceholder);135ip->set_name("TestScene");136Node *root = memnew(Node);137SceneTree::get_singleton()->get_root()->add_child(root);138139root->add_child(ip);140// Create a scene to instance.141_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);142Node *referenced = memnew(Node);143scene->add_child(referenced);144referenced->set_owner(scene);145scene->set_reference_property(referenced);146// Pack the scene.147Ref<PackedScene> packed_scene = memnew(PackedScene);148const Error err = packed_scene->pack(scene);149REQUIRE(err == OK);150151// Instantiate the scene.152_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));153REQUIRE(created != nullptr);154CHECK(created->get_name() == "TestScene");155CHECK(created->get_child_count() == 1);156CHECK(created->get_reference_property().identity_compare(created->get_child(0, false)));157CHECK_FALSE(created->get_reference_property().identity_compare(referenced));158159root->queue_free();160memdelete(scene);161}162163SUBCASE("with node-array value") {164InstancePlaceholder *ip = memnew(InstancePlaceholder);165ip->set_name("TestScene");166Node *root = memnew(Node);167SceneTree::get_singleton()->get_root()->add_child(root);168169root->add_child(ip);170// Create a scene to instance.171_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);172Node *referenced1 = memnew(Node);173Node *referenced2 = memnew(Node);174scene->add_child(referenced1);175scene->add_child(referenced2);176referenced1->set_owner(scene);177referenced2->set_owner(scene);178Array node_array;179node_array.set_typed(Variant::OBJECT, "Node", Variant());180node_array.push_back(referenced1);181node_array.push_back(referenced2);182scene->set_reference_array_property(node_array);183// Pack the scene.184Ref<PackedScene> packed_scene = memnew(PackedScene);185const Error err = packed_scene->pack(scene);186REQUIRE(err == OK);187188// Instantiate the scene.189_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));190REQUIRE(created != nullptr);191CHECK(created->get_name() == "TestScene");192CHECK(created->get_child_count() == 2);193Array created_array = created->get_reference_array_property();194REQUIRE(created_array.size() == node_array.size());195REQUIRE(created_array.size() == created->get_child_count());196197// Iterate over all nodes, since the ordering is not guaranteed.198for (int i = 0; i < node_array.size(); i++) {199bool node_found = false;200for (int j = 0; j < created->get_child_count(); j++) {201if (created_array[i].identity_compare(created->get_child(j, true))) {202node_found = true;203}204}205CHECK(node_found);206}207root->queue_free();208memdelete(scene);209}210}211212TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with overrides") {213GDREGISTER_CLASS(_TestInstancePlaceholderNode);214215SUBCASE("with non-node values") {216InstancePlaceholder *ip = memnew(InstancePlaceholder);217Node *root = memnew(Node);218SceneTree::get_singleton()->get_root()->add_child(root);219220root->add_child(ip);221ip->set_name("TestScene");222ip->set("int_property", 45);223// Create a scene to pack.224_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);225scene->set_int_property(12);226227// Pack the scene.228Ref<PackedScene> packed_scene = memnew(PackedScene);229packed_scene->pack(scene);230231// Instantiate the scene.232_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));233REQUIRE(created != nullptr);234CHECK(created->get_int_property() == 45);235236root->queue_free();237memdelete(scene);238}239240SUBCASE("with node values") {241InstancePlaceholder *ip = memnew(InstancePlaceholder);242ip->set_name("TestScene");243Node *root = memnew(Node);244Node *overriding = memnew(Node);245SceneTree::get_singleton()->get_root()->add_child(root);246247root->add_child(ip);248root->add_child(overriding);249ip->set("reference_property", overriding);250// Create a scene to instance.251_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);252Node *referenced = memnew(Node);253scene->add_child(referenced);254referenced->set_owner(scene);255scene->set_reference_property(referenced);256// Pack the scene.257Ref<PackedScene> packed_scene = memnew(PackedScene);258const Error err = packed_scene->pack(scene);259REQUIRE(err == OK);260261// Instantiate the scene.262_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));263REQUIRE(created != nullptr);264CHECK(created->get_name() == "TestScene");265CHECK(created->get_child_count() == 1);266CHECK(created->get_reference_property().identity_compare(overriding));267CHECK_FALSE(created->get_reference_property().identity_compare(referenced));268269root->queue_free();270memdelete(scene);271}272273SUBCASE("with node-array value") {274InstancePlaceholder *ip = memnew(InstancePlaceholder);275ip->set_name("TestScene");276Node *root = memnew(Node);277SceneTree::get_singleton()->get_root()->add_child(root);278279Node *override1 = memnew(Node);280Node *override2 = memnew(Node);281Node *override3 = memnew(Node);282root->add_child(ip);283root->add_child(override1);284root->add_child(override2);285root->add_child(override3);286287Array override_node_array;288override_node_array.set_typed(Variant::OBJECT, "Node", Variant());289override_node_array.push_back(override1);290override_node_array.push_back(override2);291override_node_array.push_back(override3);292293ip->set("reference_array_property", override_node_array);294295// Create a scene to instance.296_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);297Node *referenced1 = memnew(Node);298Node *referenced2 = memnew(Node);299300scene->add_child(referenced1);301scene->add_child(referenced2);302303referenced1->set_owner(scene);304referenced2->set_owner(scene);305Array referenced_array;306referenced_array.set_typed(Variant::OBJECT, "Node", Variant());307referenced_array.push_back(referenced1);308referenced_array.push_back(referenced2);309310scene->set_reference_array_property(referenced_array);311// Pack the scene.312Ref<PackedScene> packed_scene = memnew(PackedScene);313const Error err = packed_scene->pack(scene);314REQUIRE(err == OK);315316// Instantiate the scene.317_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));318REQUIRE(created != nullptr);319CHECK(created->get_name() == "TestScene");320CHECK(created->get_child_count() == 2);321Array created_array = created->get_reference_array_property();322REQUIRE_FALSE(created_array.size() == referenced_array.size());323REQUIRE(created_array.size() == override_node_array.size());324REQUIRE_FALSE(created_array.size() == created->get_child_count());325326// Iterate over all nodes, since the ordering is not guaranteed.327for (int i = 0; i < override_node_array.size(); i++) {328bool node_found = false;329for (int j = 0; j < created_array.size(); j++) {330if (override_node_array[i].identity_compare(created_array[j])) {331node_found = true;332}333}334CHECK(node_found);335}336root->queue_free();337memdelete(scene);338}339}340341#ifdef TOOLS_ENABLED342TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with no overrides") {343GDREGISTER_CLASS(_TestInstancePlaceholderNode);344345// Create the internal scene.346_TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);347internal->set_name("InternalNode");348Node *referenced = memnew(Node);349referenced->set_name("OriginalReference");350internal->add_child(referenced);351referenced->set_owner(internal);352internal->set_reference_property(referenced);353354// Pack the internal scene.355Ref<PackedScene> internal_scene = memnew(PackedScene);356Error err = internal_scene->pack(internal);357REQUIRE(err == OK);358359const String internal_path = TestUtils::get_temp_path("instance_placeholder_test_internal.tscn");360err = ResourceSaver::save(internal_scene, internal_path);361REQUIRE(err == OK);362363Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);364REQUIRE(err == OK);365366// Create the main scene.367Node *root = memnew(Node);368root->set_name("MainNode");369Node *overriding = memnew(Node);370overriding->set_name("OverridingReference");371372_TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));373internal_created->set_scene_instance_load_placeholder(true);374root->add_child(internal_created);375internal_created->set_owner(root);376377root->add_child(overriding);378overriding->set_owner(root);379// Here we introduce an error, we override the property with an internal node to the instance placeholder.380// The InstancePlaceholder is now forced to properly resolve the Node.381internal_created->set("reference_property", NodePath("OriginalReference"));382383// Pack the main scene.384Ref<PackedScene> main_scene = memnew(PackedScene);385err = main_scene->pack(root);386REQUIRE(err == OK);387388const String main_path = TestUtils::get_temp_path("instance_placeholder_test_main.tscn");389err = ResourceSaver::save(main_scene, main_path);390REQUIRE(err == OK);391392// // Instantiate the scene.393Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);394REQUIRE(err == OK);395396Node *instanced_main_node = main_scene_loaded->instantiate();397REQUIRE(instanced_main_node != nullptr);398SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);399CHECK(instanced_main_node->get_name() == "MainNode");400REQUIRE(instanced_main_node->get_child_count() == 2);401InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));402REQUIRE(instanced_placeholder != nullptr);403404_TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));405REQUIRE(final_node != nullptr);406REQUIRE(final_node->get_child_count() == 1);407REQUIRE(final_node->get_reference_property().identity_compare(final_node->get_child(0, true)));408409instanced_main_node->queue_free();410memdelete(overriding);411memdelete(root);412memdelete(internal);413DirAccess::remove_file_or_error(internal_path);414DirAccess::remove_file_or_error(main_path);415}416417TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with overrides") {418GDREGISTER_CLASS(_TestInstancePlaceholderNode);419420// Create the internal scene.421_TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);422internal->set_name("InternalNode");423Node *referenced = memnew(Node);424referenced->set_name("OriginalReference");425internal->add_child(referenced);426referenced->set_owner(internal);427internal->set_reference_property(referenced);428429Node *array_ref1 = memnew(Node);430array_ref1->set_name("ArrayRef1");431internal->add_child(array_ref1);432array_ref1->set_owner(internal);433Node *array_ref2 = memnew(Node);434array_ref2->set_name("ArrayRef2");435internal->add_child(array_ref2);436array_ref2->set_owner(internal);437Array referenced_array;438referenced_array.set_typed(Variant::OBJECT, "Node", Variant());439referenced_array.push_back(array_ref1);440referenced_array.push_back(array_ref2);441internal->set_reference_array_property(referenced_array);442443// Pack the internal scene.444Ref<PackedScene> internal_scene = memnew(PackedScene);445Error err = internal_scene->pack(internal);446REQUIRE(err == OK);447448const String internal_path = TestUtils::get_temp_path("instance_placeholder_test_internal_override.tscn");449err = ResourceSaver::save(internal_scene, internal_path);450REQUIRE(err == OK);451452Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);453REQUIRE(err == OK);454455// Create the main scene.456Node *root = memnew(Node);457root->set_name("MainNode");458Node *overriding = memnew(Node);459overriding->set_name("OverridingReference");460Node *array_ext = memnew(Node);461array_ext->set_name("ExternalArrayMember");462463_TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));464internal_created->set_scene_instance_load_placeholder(true);465root->add_child(internal_created);466internal_created->set_owner(root);467468root->add_child(overriding);469overriding->set_owner(root);470root->add_child(array_ext);471array_ext->set_owner(root);472// Here we introduce an error, we override the property with an internal node to the instance placeholder.473// The InstancePlaceholder is now forced to properly resolve the Node.474internal_created->set_reference_property(overriding);475Array internal_array = internal_created->get_reference_array_property();476Array override_array;477override_array.set_typed(Variant::OBJECT, "Node", Variant());478for (int i = 0; i < internal_array.size(); i++) {479override_array.push_back(internal_array[i]);480}481override_array.push_back(array_ext);482internal_created->set_reference_array_property(override_array);483484// Pack the main scene.485Ref<PackedScene> main_scene = memnew(PackedScene);486err = main_scene->pack(root);487REQUIRE(err == OK);488489const String main_path = TestUtils::get_temp_path("instance_placeholder_test_main_override.tscn");490err = ResourceSaver::save(main_scene, main_path);491REQUIRE(err == OK);492493// // Instantiate the scene.494Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);495REQUIRE(err == OK);496497Node *instanced_main_node = main_scene_loaded->instantiate();498REQUIRE(instanced_main_node != nullptr);499SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);500CHECK(instanced_main_node->get_name() == "MainNode");501REQUIRE(instanced_main_node->get_child_count() == 3);502InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));503REQUIRE(instanced_placeholder != nullptr);504505_TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));506REQUIRE(final_node != nullptr);507REQUIRE(final_node->get_child_count() == 3);508REQUIRE(final_node->get_reference_property().identity_compare(instanced_main_node->get_child(1, true)));509Array final_array = final_node->get_reference_array_property();510REQUIRE(final_array.size() == 3);511Array wanted_node_array = {512instanced_main_node->get_child(2, true), // ExternalArrayMember513final_node->get_child(1, true), // ArrayRef1514final_node->get_child(2, true) // ArrayRef2515};516517// Iterate over all nodes, since the ordering is not guaranteed.518for (int i = 0; i < wanted_node_array.size(); i++) {519bool node_found = false;520for (int j = 0; j < final_array.size(); j++) {521if (wanted_node_array[i].identity_compare(final_array[j])) {522node_found = true;523}524}525CHECK(node_found);526}527528instanced_main_node->queue_free();529memdelete(array_ext);530memdelete(overriding);531memdelete(root);532memdelete(internal);533DirAccess::remove_file_or_error(internal_path);534DirAccess::remove_file_or_error(main_path);535}536537#endif // TOOLS_ENABLED538539} // namespace TestInstancePlaceholder540541542