test_multiplayer_spawner.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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. static inline Array build_array() {
  36. return Array();
  37. }
  38. template <typename... Targs>
  39. static inline Array build_array(Variant item, Targs... Fargs) {
  40. Array a = build_array(Fargs...);
  41. a.push_front(item);
  42. return a;
  43. }
  44. class Wasp : public Node {
  45. GDCLASS(Wasp, Node);
  46. int _size = 0;
  47. public:
  48. int get_size() const {
  49. return _size;
  50. }
  51. void set_size(int p_size) {
  52. _size = p_size;
  53. }
  54. Wasp() {
  55. set_name("Wasp");
  56. set_scene_file_path("wasp.tscn");
  57. }
  58. };
  59. class SpawnWasps : public Object {
  60. GDCLASS(SpawnWasps, Object);
  61. protected:
  62. static void _bind_methods() {
  63. ClassDB::bind_method(D_METHOD("wasp", "size"), &SpawnWasps::create_wasps);
  64. {
  65. MethodInfo mi;
  66. mi.name = "wasp_error";
  67. mi.arguments.push_back(PropertyInfo(Variant::INT, "size"));
  68. ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "wasp_error", &SpawnWasps::create_wasps_error, mi, varray(), false);
  69. }
  70. ClassDB::bind_method(D_METHOD("echo", "size"), &SpawnWasps::echo_size);
  71. }
  72. public:
  73. Wasp *create_wasps(int p_size) {
  74. Wasp *wasp = memnew(Wasp);
  75. wasp->set_size(p_size);
  76. return wasp;
  77. }
  78. Wasp *create_wasps_error(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
  79. r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
  80. return nullptr;
  81. }
  82. int echo_size(int p_size) {
  83. return p_size;
  84. }
  85. };
  86. TEST_CASE("[Multiplayer][MultiplayerSpawner] Defaults") {
  87. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  88. CHECK_EQ(multiplayer_spawner->get_configuration_warnings().size(), 1);
  89. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  90. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
  91. CHECK_EQ(multiplayer_spawner->get_spawn_path(), NodePath());
  92. CHECK_EQ(multiplayer_spawner->get_spawn_limit(), 0);
  93. CHECK_EQ(multiplayer_spawner->get_spawn_function(), Callable());
  94. memdelete(multiplayer_spawner);
  95. }
  96. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn Path warning") {
  97. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  98. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  99. // If there is no spawn path, there should be a warning.
  100. PackedStringArray warning_messages = multiplayer_spawner->get_configuration_warnings();
  101. REQUIRE_EQ(warning_messages.size(), 1);
  102. CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
  103. // If there is a spawn path, but it doesn't exist a node on it, there should be a warning.
  104. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  105. warning_messages = multiplayer_spawner->get_configuration_warnings();
  106. REQUIRE_EQ(warning_messages.size(), 1);
  107. CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
  108. // If there is a spawn path and a node on it, shouldn't be a warning.
  109. Node *foo = memnew(Node);
  110. foo->set_name("Foo");
  111. SceneTree::get_singleton()->get_root()->add_child(foo);
  112. warning_messages = multiplayer_spawner->get_configuration_warnings();
  113. CHECK_EQ(warning_messages.size(), 0);
  114. memdelete(foo);
  115. memdelete(multiplayer_spawner);
  116. }
  117. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn node") {
  118. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  119. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  120. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  121. Node *foo = memnew(Node);
  122. foo->set_name("Foo");
  123. SceneTree::get_singleton()->get_root()->add_child(foo);
  124. SUBCASE("nullptr if spawn path doesn't exists") {
  125. multiplayer_spawner->set_spawn_path(NodePath("/root/NotExists"));
  126. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  127. }
  128. SUBCASE("Get it after setting spawn path with no signal connections") {
  129. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  130. CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
  131. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  132. }
  133. SUBCASE("Get it after setting spawn path with signal connections") {
  134. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  135. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  136. CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
  137. CHECK(foo->has_connections("child_entered_tree"));
  138. }
  139. SUBCASE("Set a new one should disconnect signals from the old one") {
  140. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  141. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  142. CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
  143. CHECK(foo->has_connections("child_entered_tree"));
  144. Node *bar = memnew(Node);
  145. bar->set_name("Bar");
  146. SceneTree::get_singleton()->get_root()->add_child(bar);
  147. multiplayer_spawner->set_spawn_path(NodePath("/root/Bar"));
  148. CHECK_EQ(multiplayer_spawner->get_spawn_node(), bar);
  149. CHECK(bar->has_connections("child_entered_tree"));
  150. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  151. memdelete(bar);
  152. }
  153. memdelete(foo);
  154. memdelete(multiplayer_spawner);
  155. }
  156. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawnable scene") {
  157. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  158. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  159. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
  160. SUBCASE("Add one") {
  161. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  162. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 1);
  163. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
  164. }
  165. SUBCASE("Add one and if there is a valid spawn path add a connection to it") {
  166. Node *foo = memnew(Node);
  167. foo->set_name("Foo");
  168. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  169. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  170. // Adding now foo to the tree to avoid set_spawn_path() making the connection.
  171. SceneTree::get_singleton()->get_root()->add_child(foo);
  172. multiplayer_spawner->notification(Node::NOTIFICATION_POST_ENTER_TREE);
  173. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  174. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  175. CHECK(foo->has_connections("child_entered_tree"));
  176. memdelete(foo);
  177. }
  178. SUBCASE("Add multiple") {
  179. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  180. multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
  181. multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
  182. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
  183. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
  184. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(1), "other_scene.tscn");
  185. CHECK_EQ(multiplayer_spawner->get_spawnable_scene(2), "yet_another_scene.tscn");
  186. }
  187. SUBCASE("Clear") {
  188. Node *foo = memnew(Node);
  189. foo->set_name("Foo");
  190. SceneTree::get_singleton()->get_root()->add_child(foo);
  191. multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
  192. multiplayer_spawner->add_spawnable_scene("scene.tscn");
  193. multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
  194. multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
  195. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
  196. CHECK(foo->has_connections("child_entered_tree"));
  197. multiplayer_spawner->clear_spawnable_scenes();
  198. CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
  199. CHECK_FALSE(foo->has_connections("child_entered_tree"));
  200. }
  201. memdelete(multiplayer_spawner);
  202. }
  203. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Instantiate custom") {
  204. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  205. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  206. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  207. Node *nest = memnew(Node);
  208. nest->set_name("Nest");
  209. SceneTree::get_singleton()->get_root()->add_child(nest);
  210. multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
  211. SpawnWasps *spawn_wasps = memnew(SpawnWasps);
  212. SUBCASE("Instantiates a node properly") {
  213. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  214. multiplayer_spawner->set_spawn_limit(1);
  215. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  216. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
  217. CHECK_NE(wasp, nullptr);
  218. CHECK_EQ(wasp->get_name(), "Wasp");
  219. CHECK_EQ(wasp->get_size(), 42);
  220. memdelete(wasp);
  221. }
  222. SUBCASE("Instantiates multiple nodes properly if there is no spawn limit") {
  223. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  224. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  225. for (int i = 0; i < 10; i++) {
  226. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(i)));
  227. CHECK_NE(wasp, nullptr);
  228. CHECK_EQ(wasp->get_name(), "Wasp");
  229. CHECK_EQ(wasp->get_size(), i);
  230. nest->add_child(wasp, true);
  231. }
  232. }
  233. SUBCASE("Fails if spawn limit is reached") {
  234. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  235. multiplayer_spawner->set_spawn_limit(1);
  236. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  237. // This one works.
  238. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
  239. CHECK_NE(wasp, nullptr);
  240. CHECK_EQ(wasp->get_name(), "Wasp");
  241. CHECK_EQ(wasp->get_size(), 42);
  242. // Adding to the spawner node to get it tracked.
  243. nest->add_child(wasp);
  244. // This one fails because spawn limit is reached.
  245. ERR_PRINT_OFF;
  246. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(255)), nullptr);
  247. ERR_PRINT_ON;
  248. memdelete(wasp);
  249. }
  250. SUBCASE("Fails if spawn function is not set") {
  251. ERR_PRINT_OFF;
  252. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
  253. ERR_PRINT_ON;
  254. }
  255. SUBCASE("Fails when spawn function fails") {
  256. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  257. multiplayer_spawner->set_spawn_limit(1);
  258. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp_error"));
  259. ERR_PRINT_OFF;
  260. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
  261. ERR_PRINT_ON;
  262. }
  263. SUBCASE("Fails when spawn function not returns a node") {
  264. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  265. multiplayer_spawner->set_spawn_limit(1);
  266. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
  267. ERR_PRINT_OFF;
  268. CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
  269. ERR_PRINT_ON;
  270. }
  271. memdelete(spawn_wasps);
  272. memdelete(nest);
  273. memdelete(multiplayer_spawner);
  274. }
  275. TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn") {
  276. MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
  277. SUBCASE("Fails because is not inside tree") {
  278. ERR_PRINT_OFF;
  279. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  280. ERR_PRINT_ON;
  281. }
  282. SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
  283. CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
  284. Node *nest = memnew(Node);
  285. nest->set_name("Nest");
  286. SceneTree::get_singleton()->get_root()->add_child(nest);
  287. multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
  288. SpawnWasps *spawn_wasps = memnew(SpawnWasps);
  289. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  290. SUBCASE("Spawns a node, track it and add it to spawn node") {
  291. multiplayer_spawner->set_spawn_limit(1);
  292. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  293. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
  294. CHECK_NE(wasp, nullptr);
  295. CHECK_EQ(wasp->get_name(), "Wasp");
  296. CHECK_EQ(wasp->get_size(), 42);
  297. CHECK_EQ(wasp->get_parent(), nest);
  298. CHECK_EQ(nest->get_child_count(), 1);
  299. CHECK_EQ(nest->get_child(0), wasp);
  300. }
  301. SUBCASE("Spawns multiple nodes properly if there is no spawn limit") {
  302. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  303. for (int i = 0; i < 10; i++) {
  304. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(i)));
  305. CHECK_NE(wasp, nullptr);
  306. CHECK_EQ(wasp->get_name(), "Wasp" + String((i == 0) ? "" : itos(i + 1)));
  307. CHECK_EQ(wasp->get_size(), i);
  308. CHECK_EQ(wasp->get_parent(), nest);
  309. CHECK_EQ(nest->get_child_count(), i + 1);
  310. CHECK_EQ(nest->get_child(i), wasp);
  311. }
  312. }
  313. SUBCASE("Fails if spawn limit is reached") {
  314. multiplayer_spawner->set_spawn_limit(1);
  315. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  316. // This one works.
  317. Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
  318. CHECK_NE(wasp, nullptr);
  319. CHECK_EQ(wasp->get_name(), "Wasp");
  320. CHECK_EQ(wasp->get_size(), 42);
  321. CHECK_EQ(wasp->get_parent(), nest);
  322. CHECK_EQ(nest->get_child_count(), 1);
  323. CHECK_EQ(nest->get_child(0), wasp);
  324. // This one fails because spawn limit is reached.
  325. ERR_PRINT_OFF;
  326. CHECK_EQ(multiplayer_spawner->spawn(Variant(255)), nullptr);
  327. ERR_PRINT_ON;
  328. memdelete(wasp);
  329. }
  330. SUBCASE("Fails if spawn function is not set") {
  331. ERR_PRINT_OFF;
  332. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  333. ERR_PRINT_ON;
  334. }
  335. SUBCASE("Fails if spawn node cannot be found") {
  336. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
  337. multiplayer_spawner->set_spawn_path(NodePath(""));
  338. ERR_PRINT_OFF;
  339. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  340. ERR_PRINT_ON;
  341. }
  342. SUBCASE("Fails when instantiate_custom not returns a node") {
  343. multiplayer_spawner->add_spawnable_scene("wasp.tscn");
  344. multiplayer_spawner->set_spawn_limit(1);
  345. multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
  346. ERR_PRINT_OFF;
  347. CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
  348. ERR_PRINT_ON;
  349. }
  350. memdelete(spawn_wasps);
  351. memdelete(nest);
  352. memdelete(multiplayer_spawner);
  353. }
  354. } // namespace TestMultiplayerSpawner