test_multiplayer_spawner.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /**************************************************************************/
  2. /* test_multiplayer_spawner.h */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #pragma once
  31. #include "tests/test_macros.h"
  32. #include "tests/test_utils.h"
  33. #include "../multiplayer_spawner.h"
  34. namespace TestMultiplayerSpawner {
  35. class Wasp : public Node {
  36. GDCLASS(Wasp, Node);
  37. int _size = 0;
  38. public:
  39. int get_size() const {
  40. return _size;
  41. }
  42. void set_size(int p_size) {
  43. _size = p_size;
  44. }
  45. Wasp() {
  46. set_name("Wasp");
  47. set_scene_file_path("wasp.tscn");
  48. }
  49. };
  50. class SpawnWasps : public Object {
  51. GDCLASS(SpawnWasps, Object);
  52. protected:
  53. static void _bind_methods() {
  54. ClassDB::bind_method(D_METHOD("wasp", "size"), &SpawnWasps::create_wasps);
  55. {
  56. MethodInfo mi;
  57. mi.name = "wasp_error";
  58. mi.arguments.push_back(PropertyInfo(Variant::INT, "size"));
  59. ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "wasp_error", &SpawnWasps::create_wasps_error, mi, varray(), false);
  60. }
  61. ClassDB::bind_method(D_METHOD("echo", "size"), &SpawnWasps::echo_size);
  62. }
  63. public:
  64. Wasp *create_wasps(int p_size) {
  65. Wasp *wasp = memnew(Wasp);
  66. wasp->set_size(p_size);
  67. return wasp;
  68. }
  69. Wasp *create_wasps_error(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
  70. r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
  71. return nullptr;
  72. }
  73. int echo_size(int p_size) {
  74. return p_size;
  75. }
  76. };
  77. TEST_CASE("[Multiplayer][MultiplayerSpawner] Defaults") {
  78. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  79. CHECK_EQ(multiplayer_spawner->get_configuration_warnings().size(), 1);
  80. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  81. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
  82. CHECK_EQ(multiplayer_spawner->get_spawn_path(), NodePath());
  83. CHECK_EQ(multiplayer_spawner->get_spawn_limit(), 0);
  84. CHECK_EQ(multiplayer_spawner->get_spawn_function(), Callable());
  85. memdelete(multiplayer_spawner);
  86. }
  87. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn Path warning") {
  88. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  89. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  90. // If there is no spawn path, there should be a warning.
  91. PackedStringArray warning_messages = multiplayer_spawner->get_configuration_warnings();
  92. REQUIRE_EQ(warning_messages.size(), 1);
  93. CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
  94. // If there is a spawn path, but it doesn't exist a node on it, there should be a warning.
  95. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  96. warning_messages = multiplayer_spawner->get_configuration_warnings();
  97. REQUIRE_EQ(warning_messages.size(), 1);
  98. CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
  99. // If there is a spawn path and a node on it, shouldn't be a warning.
  100. Node *foo = memnew(Node);
  101. foo->set_name("Foo");
  102. SceneTree::get_singleton()->get_root()->add_child(foo);
  103. warning_messages = multiplayer_spawner->get_configuration_warnings();
  104. CHECK_EQ(warning_messages.size(), 0);
  105. memdelete(foo);
  106. memdelete(multiplayer_spawner);
  107. }
  108. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn node") {
  109. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  110. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  111. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  112. Node *foo = memnew(Node);
  113. foo->set_name("Foo");
  114. SceneTree::get_singleton()->get_root()->add_child(foo);
  115. SUBCASE("nullptr if spawn path doesn't exists") {
  116. multiplayer_spawner->set_spawn_path(NodePath("/root/NotExists"));
  117. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  118. }
  119. SUBCASE("Get it after setting spawn path with no signal connections") {
  120. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  121. CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
  122. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  123. }
  124. SUBCASE("Get it after setting spawn path with signal connections") {
  125. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  126. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  127. CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
  128. CHECK(foo->has_connections("child_entered_tree"));
  129. }
  130. SUBCASE("Set a new one should disconnect signals from the old one") {
  131. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  132. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  133. CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
  134. CHECK(foo->has_connections("child_entered_tree"));
  135. Node *bar = memnew(Node);
  136. bar->set_name("Bar");
  137. SceneTree::get_singleton()->get_root()->add_child(bar);
  138. multiplayer_spawner->set_spawn_path(NodePath("/root/Bar"));
  139. CHECK_EQ(multiplayer_spawner->get_spawn_node(), bar);
  140. CHECK(bar->has_connections("child_entered_tree"));
  141. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  142. memdelete(bar);
  143. }
  144. memdelete(foo);
  145. memdelete(multiplayer_spawner);
  146. }
  147. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawnable scene") {
  148. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  149. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  150. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
  151. SUBCASE("Add one") {
  152. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  153. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 1);
  154. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
  155. }
  156. SUBCASE("Add one and if there is a valid spawn path add a connection to it") {
  157. Node *foo = memnew(Node);
  158. foo->set_name("Foo");
  159. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  160. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  161. // Adding now foo to the tree to avoid set_spawn_path() making the connection.
  162. SceneTree::get_singleton()->get_root()->add_child(foo);
  163. multiplayer_spawner->notification(Node::NOTIFICATION_POST_ENTER_TREE);
  164. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  165. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  166. CHECK(foo->has_connections("child_entered_tree"));
  167. memdelete(foo);
  168. }
  169. SUBCASE("Add multiple") {
  170. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  171. multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
  172. multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
  173. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
  174. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
  175. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(1), "other_scene.tscn");
  176. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(2), "yet_another_scene.tscn");
  177. }
  178. SUBCASE("Clear") {
  179. Node *foo = memnew(Node);
  180. foo->set_name("Foo");
  181. SceneTree::get_singleton()->get_root()->add_child(foo);
  182. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  183. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  184. multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
  185. multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
  186. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
  187. CHECK(foo->has_connections("child_entered_tree"));
  188. multiplayer_spawner->clear_spawnable_scenes();
  189. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
  190. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  191. }
  192. memdelete(multiplayer_spawner);
  193. }
  194. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Instantiate custom") {
  195. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  196. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  197. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  198. Node *nest = memnew(Node);
  199. nest->set_name("Nest");
  200. SceneTree::get_singleton()->get_root()->add_child(nest);
  201. multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
  202. SpawnWasps *spawn_wasps = memnew(SpawnWasps);
  203. SUBCASE("Instantiates a node properly") {
  204. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  205. multiplayer_spawner->set_spawn_limit(1);
  206. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  207. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
  208. CHECK_NE(wasp, nullptr);
  209. CHECK_EQ(wasp->get_name(), "Wasp");
  210. CHECK_EQ(wasp->get_size(), 42);
  211. memdelete(wasp);
  212. }
  213. SUBCASE("Instantiates multiple nodes properly if there is no spawn limit") {
  214. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  215. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  216. for (int i = 0; i < 10; i++) {
  217. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(i)));
  218. CHECK_NE(wasp, nullptr);
  219. CHECK_EQ(wasp->get_name(), "Wasp");
  220. CHECK_EQ(wasp->get_size(), i);
  221. nest->add_child(wasp, true);
  222. }
  223. }
  224. SUBCASE("Fails if spawn limit is reached") {
  225. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  226. multiplayer_spawner->set_spawn_limit(1);
  227. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  228. // This one works.
  229. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
  230. CHECK_NE(wasp, nullptr);
  231. CHECK_EQ(wasp->get_name(), "Wasp");
  232. CHECK_EQ(wasp->get_size(), 42);
  233. // Adding to the spawner node to get it tracked.
  234. nest->add_child(wasp);
  235. // This one fails because spawn limit is reached.
  236. ERR_PRINT_OFF;
  237. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(255)), nullptr);
  238. ERR_PRINT_ON;
  239. memdelete(wasp);
  240. }
  241. SUBCASE("Fails if spawn function is not set") {
  242. ERR_PRINT_OFF;
  243. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
  244. ERR_PRINT_ON;
  245. }
  246. SUBCASE("Fails when spawn function fails") {
  247. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  248. multiplayer_spawner->set_spawn_limit(1);
  249. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp_error"));
  250. ERR_PRINT_OFF;
  251. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
  252. ERR_PRINT_ON;
  253. }
  254. SUBCASE("Fails when spawn function not returns a node") {
  255. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  256. multiplayer_spawner->set_spawn_limit(1);
  257. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
  258. ERR_PRINT_OFF;
  259. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
  260. ERR_PRINT_ON;
  261. }
  262. memdelete(spawn_wasps);
  263. memdelete(nest);
  264. memdelete(multiplayer_spawner);
  265. }
  266. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn") {
  267. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  268. SUBCASE("Fails because is not inside tree") {
  269. ERR_PRINT_OFF;
  270. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  271. ERR_PRINT_ON;
  272. }
  273. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  274. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  275. Node *nest = memnew(Node);
  276. nest->set_name("Nest");
  277. SceneTree::get_singleton()->get_root()->add_child(nest);
  278. multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
  279. SpawnWasps *spawn_wasps = memnew(SpawnWasps);
  280. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  281. SUBCASE("Spawns a node, track it and add it to spawn node") {
  282. multiplayer_spawner->set_spawn_limit(1);
  283. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  284. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
  285. CHECK_NE(wasp, nullptr);
  286. CHECK_EQ(wasp->get_name(), "Wasp");
  287. CHECK_EQ(wasp->get_size(), 42);
  288. CHECK_EQ(wasp->get_parent(), nest);
  289. CHECK_EQ(nest->get_child_count(), 1);
  290. CHECK_EQ(nest->get_child(0), wasp);
  291. }
  292. SUBCASE("Spawns multiple nodes properly if there is no spawn limit") {
  293. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  294. for (int i = 0; i < 10; i++) {
  295. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(i)));
  296. CHECK_NE(wasp, nullptr);
  297. CHECK_EQ(wasp->get_name(), "Wasp" + String((i == 0) ? "" : itos(i + 1)));
  298. CHECK_EQ(wasp->get_size(), i);
  299. CHECK_EQ(wasp->get_parent(), nest);
  300. CHECK_EQ(nest->get_child_count(), i + 1);
  301. CHECK_EQ(nest->get_child(i), wasp);
  302. }
  303. }
  304. SUBCASE("Fails if spawn limit is reached") {
  305. multiplayer_spawner->set_spawn_limit(1);
  306. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  307. // This one works.
  308. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
  309. CHECK_NE(wasp, nullptr);
  310. CHECK_EQ(wasp->get_name(), "Wasp");
  311. CHECK_EQ(wasp->get_size(), 42);
  312. CHECK_EQ(wasp->get_parent(), nest);
  313. CHECK_EQ(nest->get_child_count(), 1);
  314. CHECK_EQ(nest->get_child(0), wasp);
  315. // This one fails because spawn limit is reached.
  316. ERR_PRINT_OFF;
  317. CHECK_EQ(multiplayer_spawner->spawn(Variant(255)), nullptr);
  318. ERR_PRINT_ON;
  319. memdelete(wasp);
  320. }
  321. SUBCASE("Fails if spawn function is not set") {
  322. ERR_PRINT_OFF;
  323. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  324. ERR_PRINT_ON;
  325. }
  326. SUBCASE("Fails if spawn node cannot be found") {
  327. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  328. multiplayer_spawner->set_spawn_path(NodePath(""));
  329. ERR_PRINT_OFF;
  330. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  331. ERR_PRINT_ON;
  332. }
  333. SUBCASE("Fails when instantiate_custom not returns a node") {
  334. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  335. multiplayer_spawner->set_spawn_limit(1);
  336. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
  337. ERR_PRINT_OFF;
  338. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  339. ERR_PRINT_ON;
  340. }
  341. memdelete(spawn_wasps);
  342. memdelete(nest);
  343. memdelete(multiplayer_spawner);
  344. }
  345. } // namespace TestMultiplayerSpawner