Path: blob/master/tests/scene/test_instance_placeholder.h
10277 views
/**************************************************************************/1/* test_instance_placeholder.h */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#pragma once3132#include "scene/main/instance_placeholder.h"33#include "scene/resources/packed_scene.h"3435#include "tests/test_macros.h"3637class _TestInstancePlaceholderNode : public Node {38GDCLASS(_TestInstancePlaceholderNode, Node);3940protected:41static void _bind_methods() {42ClassDB::bind_method(D_METHOD("set_int_property", "int_property"), &_TestInstancePlaceholderNode::set_int_property);43ClassDB::bind_method(D_METHOD("get_int_property"), &_TestInstancePlaceholderNode::get_int_property);4445ADD_PROPERTY(PropertyInfo(Variant::INT, "int_property"), "set_int_property", "get_int_property");4647ClassDB::bind_method(D_METHOD("set_reference_property", "reference_property"), &_TestInstancePlaceholderNode::set_reference_property);48ClassDB::bind_method(D_METHOD("get_reference_property"), &_TestInstancePlaceholderNode::get_reference_property);4950ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "reference_property", PROPERTY_HINT_NODE_TYPE), "set_reference_property", "get_reference_property");5152ClassDB::bind_method(D_METHOD("set_reference_array_property", "reference_array_property"), &_TestInstancePlaceholderNode::set_reference_array_property);53ClassDB::bind_method(D_METHOD("get_reference_array_property"), &_TestInstancePlaceholderNode::get_reference_array_property);5455// The hint string value "24/34:Node" is determined from existing PackedScenes with typed Array properties.56ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "reference_array_property", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_reference_array_property", "get_reference_array_property");57}5859public:60int int_property = 0;6162void set_int_property(int p_int) {63int_property = p_int;64}6566int get_int_property() const {67return int_property;68}6970Variant reference_property;7172void set_reference_property(const Variant &p_node) {73reference_property = p_node;74}7576Variant get_reference_property() const {77return reference_property;78}7980Array reference_array_property;8182void set_reference_array_property(const Array &p_array) {83reference_array_property = p_array;84}8586Array get_reference_array_property() const {87return reference_array_property;88}8990_TestInstancePlaceholderNode() {91reference_array_property.set_typed(Variant::OBJECT, "Node", Variant());92}93};9495namespace TestInstancePlaceholder {9697TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with no overrides") {98GDREGISTER_CLASS(_TestInstancePlaceholderNode);99100SUBCASE("with non-node values") {101InstancePlaceholder *ip = memnew(InstancePlaceholder);102ip->set_name("TestScene");103Node *root = memnew(Node);104SceneTree::get_singleton()->get_root()->add_child(root);105106root->add_child(ip);107// Create a scene to instance.108_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);109scene->set_int_property(12);110111// Pack the scene.112PackedScene *packed_scene = memnew(PackedScene);113const Error err = packed_scene->pack(scene);114REQUIRE(err == OK);115116// Instantiate the scene.117_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));118REQUIRE(created != nullptr);119CHECK(created->get_name() == "TestScene");120CHECK(created->get_int_property() == 12);121122root->queue_free();123memdelete(scene);124}125126SUBCASE("with node value") {127InstancePlaceholder *ip = memnew(InstancePlaceholder);128ip->set_name("TestScene");129Node *root = memnew(Node);130SceneTree::get_singleton()->get_root()->add_child(root);131132root->add_child(ip);133// Create a scene to instance.134_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);135Node *referenced = memnew(Node);136scene->add_child(referenced);137referenced->set_owner(scene);138scene->set_reference_property(referenced);139// Pack the scene.140PackedScene *packed_scene = memnew(PackedScene);141const Error err = packed_scene->pack(scene);142REQUIRE(err == OK);143144// Instantiate the scene.145_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));146REQUIRE(created != nullptr);147CHECK(created->get_name() == "TestScene");148CHECK(created->get_child_count() == 1);149CHECK(created->get_reference_property().identity_compare(created->get_child(0, false)));150CHECK_FALSE(created->get_reference_property().identity_compare(referenced));151152root->queue_free();153memdelete(scene);154}155156SUBCASE("with node-array value") {157InstancePlaceholder *ip = memnew(InstancePlaceholder);158ip->set_name("TestScene");159Node *root = memnew(Node);160SceneTree::get_singleton()->get_root()->add_child(root);161162root->add_child(ip);163// Create a scene to instance.164_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);165Node *referenced1 = memnew(Node);166Node *referenced2 = memnew(Node);167scene->add_child(referenced1);168scene->add_child(referenced2);169referenced1->set_owner(scene);170referenced2->set_owner(scene);171Array node_array;172node_array.set_typed(Variant::OBJECT, "Node", Variant());173node_array.push_back(referenced1);174node_array.push_back(referenced2);175scene->set_reference_array_property(node_array);176// Pack the scene.177PackedScene *packed_scene = memnew(PackedScene);178const Error err = packed_scene->pack(scene);179REQUIRE(err == OK);180181// Instantiate the scene.182_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));183REQUIRE(created != nullptr);184CHECK(created->get_name() == "TestScene");185CHECK(created->get_child_count() == 2);186Array created_array = created->get_reference_array_property();187REQUIRE(created_array.size() == node_array.size());188REQUIRE(created_array.size() == created->get_child_count());189190// Iterate over all nodes, since the ordering is not guaranteed.191for (int i = 0; i < node_array.size(); i++) {192bool node_found = false;193for (int j = 0; j < created->get_child_count(); j++) {194if (created_array[i].identity_compare(created->get_child(j, true))) {195node_found = true;196}197}198CHECK(node_found);199}200root->queue_free();201memdelete(scene);202}203}204205TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with overrides") {206GDREGISTER_CLASS(_TestInstancePlaceholderNode);207208SUBCASE("with non-node values") {209InstancePlaceholder *ip = memnew(InstancePlaceholder);210Node *root = memnew(Node);211SceneTree::get_singleton()->get_root()->add_child(root);212213root->add_child(ip);214ip->set_name("TestScene");215ip->set("int_property", 45);216// Create a scene to pack.217_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);218scene->set_int_property(12);219220// Pack the scene.221PackedScene *packed_scene = memnew(PackedScene);222packed_scene->pack(scene);223224// Instantiate the scene.225_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));226REQUIRE(created != nullptr);227CHECK(created->get_int_property() == 45);228229root->queue_free();230memdelete(scene);231}232233SUBCASE("with node values") {234InstancePlaceholder *ip = memnew(InstancePlaceholder);235ip->set_name("TestScene");236Node *root = memnew(Node);237Node *overriding = memnew(Node);238SceneTree::get_singleton()->get_root()->add_child(root);239240root->add_child(ip);241root->add_child(overriding);242ip->set("reference_property", overriding);243// Create a scene to instance.244_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);245Node *referenced = memnew(Node);246scene->add_child(referenced);247referenced->set_owner(scene);248scene->set_reference_property(referenced);249// Pack the scene.250PackedScene *packed_scene = memnew(PackedScene);251const Error err = packed_scene->pack(scene);252REQUIRE(err == OK);253254// Instantiate the scene.255_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));256REQUIRE(created != nullptr);257CHECK(created->get_name() == "TestScene");258CHECK(created->get_child_count() == 1);259CHECK(created->get_reference_property().identity_compare(overriding));260CHECK_FALSE(created->get_reference_property().identity_compare(referenced));261262root->queue_free();263memdelete(scene);264}265266SUBCASE("with node-array value") {267InstancePlaceholder *ip = memnew(InstancePlaceholder);268ip->set_name("TestScene");269Node *root = memnew(Node);270SceneTree::get_singleton()->get_root()->add_child(root);271272Node *override1 = memnew(Node);273Node *override2 = memnew(Node);274Node *override3 = memnew(Node);275root->add_child(ip);276root->add_child(override1);277root->add_child(override2);278root->add_child(override3);279280Array override_node_array;281override_node_array.set_typed(Variant::OBJECT, "Node", Variant());282override_node_array.push_back(override1);283override_node_array.push_back(override2);284override_node_array.push_back(override3);285286ip->set("reference_array_property", override_node_array);287288// Create a scene to instance.289_TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);290Node *referenced1 = memnew(Node);291Node *referenced2 = memnew(Node);292293scene->add_child(referenced1);294scene->add_child(referenced2);295296referenced1->set_owner(scene);297referenced2->set_owner(scene);298Array referenced_array;299referenced_array.set_typed(Variant::OBJECT, "Node", Variant());300referenced_array.push_back(referenced1);301referenced_array.push_back(referenced2);302303scene->set_reference_array_property(referenced_array);304// Pack the scene.305PackedScene *packed_scene = memnew(PackedScene);306const Error err = packed_scene->pack(scene);307REQUIRE(err == OK);308309// Instantiate the scene.310_TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));311REQUIRE(created != nullptr);312CHECK(created->get_name() == "TestScene");313CHECK(created->get_child_count() == 2);314Array created_array = created->get_reference_array_property();315REQUIRE_FALSE(created_array.size() == referenced_array.size());316REQUIRE(created_array.size() == override_node_array.size());317REQUIRE_FALSE(created_array.size() == created->get_child_count());318319// Iterate over all nodes, since the ordering is not guaranteed.320for (int i = 0; i < override_node_array.size(); i++) {321bool node_found = false;322for (int j = 0; j < created_array.size(); j++) {323if (override_node_array[i].identity_compare(created_array[j])) {324node_found = true;325}326}327CHECK(node_found);328}329root->queue_free();330memdelete(scene);331}332}333334#ifdef TOOLS_ENABLED335TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with no overrides") {336GDREGISTER_CLASS(_TestInstancePlaceholderNode);337338// Create the internal scene.339_TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);340internal->set_name("InternalNode");341Node *referenced = memnew(Node);342referenced->set_name("OriginalReference");343internal->add_child(referenced);344referenced->set_owner(internal);345internal->set_reference_property(referenced);346347// Pack the internal scene.348PackedScene *internal_scene = memnew(PackedScene);349Error err = internal_scene->pack(internal);350REQUIRE(err == OK);351352const String internal_path = TestUtils::get_temp_path("instance_placeholder_test_internal.tscn");353err = ResourceSaver::save(internal_scene, internal_path);354REQUIRE(err == OK);355356Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);357REQUIRE(err == OK);358359// Create the main scene.360Node *root = memnew(Node);361root->set_name("MainNode");362Node *overriding = memnew(Node);363overriding->set_name("OverridingReference");364365_TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));366internal_created->set_scene_instance_load_placeholder(true);367root->add_child(internal_created);368internal_created->set_owner(root);369370root->add_child(overriding);371overriding->set_owner(root);372// Here we introduce an error, we override the property with an internal node to the instance placeholder.373// The InstancePlaceholder is now forced to properly resolve the Node.374internal_created->set("reference_property", NodePath("OriginalReference"));375376// Pack the main scene.377PackedScene *main_scene = memnew(PackedScene);378err = main_scene->pack(root);379REQUIRE(err == OK);380381const String main_path = TestUtils::get_temp_path("instance_placeholder_test_main.tscn");382err = ResourceSaver::save(main_scene, main_path);383REQUIRE(err == OK);384385// // Instantiate the scene.386Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);387REQUIRE(err == OK);388389Node *instanced_main_node = main_scene_loaded->instantiate();390REQUIRE(instanced_main_node != nullptr);391SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);392CHECK(instanced_main_node->get_name() == "MainNode");393REQUIRE(instanced_main_node->get_child_count() == 2);394InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));395REQUIRE(instanced_placeholder != nullptr);396397_TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));398REQUIRE(final_node != nullptr);399REQUIRE(final_node->get_child_count() == 1);400REQUIRE(final_node->get_reference_property().identity_compare(final_node->get_child(0, true)));401402instanced_main_node->queue_free();403memdelete(overriding);404memdelete(root);405memdelete(internal);406DirAccess::remove_file_or_error(internal_path);407DirAccess::remove_file_or_error(main_path);408}409410TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with overrides") {411GDREGISTER_CLASS(_TestInstancePlaceholderNode);412413// Create the internal scene.414_TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);415internal->set_name("InternalNode");416Node *referenced = memnew(Node);417referenced->set_name("OriginalReference");418internal->add_child(referenced);419referenced->set_owner(internal);420internal->set_reference_property(referenced);421422Node *array_ref1 = memnew(Node);423array_ref1->set_name("ArrayRef1");424internal->add_child(array_ref1);425array_ref1->set_owner(internal);426Node *array_ref2 = memnew(Node);427array_ref2->set_name("ArrayRef2");428internal->add_child(array_ref2);429array_ref2->set_owner(internal);430Array referenced_array;431referenced_array.set_typed(Variant::OBJECT, "Node", Variant());432referenced_array.push_back(array_ref1);433referenced_array.push_back(array_ref2);434internal->set_reference_array_property(referenced_array);435436// Pack the internal scene.437PackedScene *internal_scene = memnew(PackedScene);438Error err = internal_scene->pack(internal);439REQUIRE(err == OK);440441const String internal_path = TestUtils::get_temp_path("instance_placeholder_test_internal_override.tscn");442err = ResourceSaver::save(internal_scene, internal_path);443REQUIRE(err == OK);444445Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);446REQUIRE(err == OK);447448// Create the main scene.449Node *root = memnew(Node);450root->set_name("MainNode");451Node *overriding = memnew(Node);452overriding->set_name("OverridingReference");453Node *array_ext = memnew(Node);454array_ext->set_name("ExternalArrayMember");455456_TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));457internal_created->set_scene_instance_load_placeholder(true);458root->add_child(internal_created);459internal_created->set_owner(root);460461root->add_child(overriding);462overriding->set_owner(root);463root->add_child(array_ext);464array_ext->set_owner(root);465// Here we introduce an error, we override the property with an internal node to the instance placeholder.466// The InstancePlaceholder is now forced to properly resolve the Node.467internal_created->set_reference_property(overriding);468Array internal_array = internal_created->get_reference_array_property();469Array override_array;470override_array.set_typed(Variant::OBJECT, "Node", Variant());471for (int i = 0; i < internal_array.size(); i++) {472override_array.push_back(internal_array[i]);473}474override_array.push_back(array_ext);475internal_created->set_reference_array_property(override_array);476477// Pack the main scene.478PackedScene *main_scene = memnew(PackedScene);479err = main_scene->pack(root);480REQUIRE(err == OK);481482const String main_path = TestUtils::get_temp_path("instance_placeholder_test_main_override.tscn");483err = ResourceSaver::save(main_scene, main_path);484REQUIRE(err == OK);485486// // Instantiate the scene.487Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);488REQUIRE(err == OK);489490Node *instanced_main_node = main_scene_loaded->instantiate();491REQUIRE(instanced_main_node != nullptr);492SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);493CHECK(instanced_main_node->get_name() == "MainNode");494REQUIRE(instanced_main_node->get_child_count() == 3);495InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));496REQUIRE(instanced_placeholder != nullptr);497498_TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));499REQUIRE(final_node != nullptr);500REQUIRE(final_node->get_child_count() == 3);501REQUIRE(final_node->get_reference_property().identity_compare(instanced_main_node->get_child(1, true)));502Array final_array = final_node->get_reference_array_property();503REQUIRE(final_array.size() == 3);504Array wanted_node_array = {505instanced_main_node->get_child(2, true), // ExternalArrayMember506final_node->get_child(1, true), // ArrayRef1507final_node->get_child(2, true) // ArrayRef2508};509510// Iterate over all nodes, since the ordering is not guaranteed.511for (int i = 0; i < wanted_node_array.size(); i++) {512bool node_found = false;513for (int j = 0; j < final_array.size(); j++) {514if (wanted_node_array[i].identity_compare(final_array[j])) {515node_found = true;516}517}518CHECK(node_found);519}520521instanced_main_node->queue_free();522memdelete(array_ext);523memdelete(overriding);524memdelete(root);525memdelete(internal);526DirAccess::remove_file_or_error(internal_path);527DirAccess::remove_file_or_error(main_path);528}529#endif // TOOLS_ENABLED530531} //namespace TestInstancePlaceholder532533534