Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/scene/test_node.h
20900 views
1
/**************************************************************************/
2
/* test_node.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
31
#pragma once
32
33
#include "core/object/class_db.h"
34
#include "scene/main/node.h"
35
#include "scene/resources/packed_scene.h"
36
37
#include "tests/test_macros.h"
38
39
namespace TestNode {
40
41
class TestNode : public Node {
42
GDCLASS(TestNode, Node);
43
44
protected:
45
void _notification(int p_what) {
46
switch (p_what) {
47
case NOTIFICATION_INTERNAL_PROCESS: {
48
internal_process_counter++;
49
push_self();
50
} break;
51
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
52
internal_physics_process_counter++;
53
push_self();
54
} break;
55
case NOTIFICATION_PROCESS: {
56
process_counter++;
57
push_self();
58
} break;
59
case NOTIFICATION_PHYSICS_PROCESS: {
60
physics_process_counter++;
61
push_self();
62
} break;
63
}
64
}
65
66
static void _bind_methods() {
67
ClassDB::bind_method(D_METHOD("set_exported_node", "node"), &TestNode::set_exported_node);
68
ClassDB::bind_method(D_METHOD("get_exported_node"), &TestNode::get_exported_node);
69
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "exported_node", PROPERTY_HINT_NODE_TYPE, "Node"), "set_exported_node", "get_exported_node");
70
71
ClassDB::bind_method(D_METHOD("set_exported_nodes", "node"), &TestNode::set_exported_nodes);
72
ClassDB::bind_method(D_METHOD("get_exported_nodes"), &TestNode::get_exported_nodes);
73
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exported_nodes", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_exported_nodes", "get_exported_nodes");
74
}
75
76
private:
77
void push_self() {
78
if (callback_list) {
79
callback_list->push_back(this);
80
}
81
}
82
83
public:
84
int internal_process_counter = 0;
85
int internal_physics_process_counter = 0;
86
int process_counter = 0;
87
int physics_process_counter = 0;
88
89
Node *exported_node = nullptr;
90
Array exported_nodes;
91
92
List<Node *> *callback_list = nullptr;
93
94
void set_exported_node(Node *p_node) { exported_node = p_node; }
95
Node *get_exported_node() const { return exported_node; }
96
97
void set_exported_nodes(const Array &p_nodes) { exported_nodes = p_nodes; }
98
Array get_exported_nodes() const { return exported_nodes; }
99
100
TestNode() {
101
Node *internal = memnew(Node);
102
add_child(internal, false, INTERNAL_MODE_FRONT);
103
internal = memnew(Node);
104
add_child(internal, false, INTERNAL_MODE_BACK);
105
}
106
};
107
108
TEST_CASE("[SceneTree][Node] Testing node operations with a very simple scene tree") {
109
Node *node = memnew(Node);
110
111
// Check initial scene tree setup.
112
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0);
113
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1);
114
115
// Check initial node setup.
116
CHECK(node->get_name() == StringName());
117
CHECK_FALSE(node->is_inside_tree());
118
CHECK_EQ(node->get_parent(), nullptr);
119
ERR_PRINT_OFF;
120
CHECK(node->get_path().is_empty());
121
ERR_PRINT_ON;
122
CHECK_EQ(node->get_child_count(), 0);
123
124
SceneTree::get_singleton()->get_root()->add_child(node);
125
126
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1);
127
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2);
128
129
CHECK(node->get_name() != StringName());
130
CHECK(node->is_inside_tree());
131
CHECK_EQ(SceneTree::get_singleton()->get_root(), node->get_parent());
132
CHECK_FALSE(node->get_path().is_empty());
133
CHECK_EQ(node->get_child_count(), 0);
134
135
SUBCASE("Node should be accessible as first child") {
136
Node *child = SceneTree::get_singleton()->get_root()->get_child(0);
137
CHECK_EQ(child, node);
138
}
139
140
SUBCASE("Node should be accessible via the node path") {
141
Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path());
142
CHECK_EQ(child_by_path, node);
143
144
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node"));
145
CHECK_EQ(child_by_path, nullptr);
146
147
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node"));
148
CHECK_EQ(child_by_path, nullptr);
149
150
node->set_name("Node");
151
152
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path());
153
CHECK_EQ(child_by_path, node);
154
155
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node"));
156
CHECK_EQ(child_by_path, node);
157
158
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node"));
159
CHECK_EQ(child_by_path, node);
160
}
161
162
SUBCASE("Node should be accessible via group") {
163
Vector<Node *> nodes = SceneTree::get_singleton()->get_nodes_in_group("nodes");
164
CHECK(nodes.is_empty());
165
166
node->add_to_group("nodes");
167
168
nodes = SceneTree::get_singleton()->get_nodes_in_group("nodes");
169
CHECK_EQ(nodes.size(), 1);
170
Node *E = nodes.get(0);
171
CHECK_EQ(E, node);
172
}
173
174
SUBCASE("Node should be possible to find") {
175
Node *child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false);
176
CHECK_EQ(child, nullptr);
177
178
node->set_name("Node");
179
180
child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false);
181
CHECK_EQ(child, node);
182
}
183
184
SUBCASE("Node should be possible to remove") {
185
SceneTree::get_singleton()->get_root()->remove_child(node);
186
187
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0);
188
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1);
189
190
CHECK_FALSE(node->is_inside_tree());
191
CHECK_EQ(node->get_parent(), nullptr);
192
ERR_PRINT_OFF;
193
CHECK(node->get_path().is_empty());
194
ERR_PRINT_ON;
195
}
196
197
SUBCASE("Node should be possible to move") {
198
SceneTree::get_singleton()->get_root()->move_child(node, 0);
199
200
Node *child = SceneTree::get_singleton()->get_root()->get_child(0);
201
CHECK_EQ(child, node);
202
CHECK(node->is_inside_tree());
203
}
204
205
SUBCASE("Node should be possible to reparent") {
206
node->reparent(SceneTree::get_singleton()->get_root());
207
208
Node *child = SceneTree::get_singleton()->get_root()->get_child(0);
209
CHECK_EQ(child, node);
210
CHECK(node->is_inside_tree());
211
}
212
213
SUBCASE("Node should be possible to duplicate") {
214
node->set_name("MyName");
215
216
Node *duplicate = node->duplicate();
217
218
CHECK_FALSE(node == duplicate);
219
CHECK_FALSE(duplicate->is_inside_tree());
220
CHECK_EQ(duplicate->get_name(), node->get_name());
221
222
memdelete(duplicate);
223
}
224
225
memdelete(node);
226
}
227
228
TEST_CASE("[SceneTree][Node] Testing node operations with a more complex simple scene tree") {
229
Node *node1 = memnew(Node);
230
Node *node2 = memnew(Node);
231
Node *node1_1 = memnew(Node);
232
233
SceneTree::get_singleton()->get_root()->add_child(node1);
234
SceneTree::get_singleton()->get_root()->add_child(node2);
235
236
node1->add_child(node1_1);
237
238
CHECK(node1_1->is_inside_tree());
239
CHECK_EQ(node1_1->get_parent(), node1);
240
CHECK_EQ(node1->get_child_count(), 1);
241
242
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2);
243
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
244
245
SUBCASE("Nodes should be accessible via get_child(..)") {
246
Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
247
CHECK_EQ(child1, node1);
248
249
Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1);
250
CHECK_EQ(child2, node2);
251
252
Node *child1_1 = node1->get_child(0);
253
CHECK_EQ(child1_1, node1_1);
254
}
255
256
SUBCASE("Removed nodes should also remove their children from the scene tree") {
257
// Should also remove node1_1 from the scene tree.
258
SceneTree::get_singleton()->get_root()->remove_child(node1);
259
260
CHECK_EQ(node1->get_child_count(), 1);
261
262
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1);
263
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2);
264
265
// First child should now be the second node.
266
Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
267
CHECK_EQ(child1, node2);
268
}
269
270
SUBCASE("Removed children nodes should not affect their parent in the scene tree") {
271
node1->remove_child(node1_1);
272
273
CHECK_EQ(node1_1->get_parent(), nullptr);
274
CHECK_EQ(node1->get_child_count(), 0);
275
276
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 3);
277
}
278
279
SUBCASE("Nodes should be in the expected order when a node is moved to the back") {
280
SceneTree::get_singleton()->get_root()->move_child(node1, 1);
281
282
Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
283
CHECK_EQ(child1, node2);
284
285
Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1);
286
CHECK_EQ(child2, node1);
287
}
288
289
SUBCASE("Nodes should be in the expected order when a node is moved to the front") {
290
SceneTree::get_singleton()->get_root()->move_child(node2, 0);
291
292
Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
293
CHECK_EQ(child1, node2);
294
295
Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1);
296
CHECK_EQ(child2, node1);
297
}
298
299
SUBCASE("Nodes should be in the expected order when reparented (remove/add)") {
300
CHECK_EQ(node2->get_child_count(), 0);
301
302
node1->remove_child(node1_1);
303
CHECK_EQ(node1->get_child_count(), 0);
304
CHECK_EQ(node1_1->get_parent(), nullptr);
305
306
node2->add_child(node1_1);
307
CHECK_EQ(node2->get_child_count(), 1);
308
CHECK_EQ(node1_1->get_parent(), node2);
309
310
Node *child = node2->get_child(0);
311
CHECK_EQ(child, node1_1);
312
313
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2);
314
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
315
}
316
317
SUBCASE("Nodes should be in the expected order when reparented") {
318
CHECK_EQ(node2->get_child_count(), 0);
319
320
node1_1->reparent(node2);
321
322
CHECK_EQ(node1->get_child_count(), 0);
323
CHECK_EQ(node2->get_child_count(), 1);
324
CHECK_EQ(node1_1->get_parent(), node2);
325
326
Node *child = node2->get_child(0);
327
CHECK_EQ(child, node1_1);
328
329
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2);
330
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
331
}
332
333
SUBCASE("Nodes should be possible to find") {
334
Node *child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false);
335
CHECK_EQ(child, nullptr);
336
337
TypedArray<Node> children = SceneTree::get_singleton()->get_root()->find_children("NestedNode", "", true, false);
338
CHECK_EQ(children.size(), 0);
339
340
node1->set_name("Node1");
341
node2->set_name("Node2");
342
node1_1->set_name("NestedNode");
343
344
child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false);
345
CHECK_EQ(child, node1_1);
346
347
children = SceneTree::get_singleton()->get_root()->find_children("NestedNode", "", true, false);
348
CHECK_EQ(children.size(), 1);
349
CHECK_EQ(Object::cast_to<Node>(children[0]), node1_1);
350
351
// First node that matches with the name is node1.
352
child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false);
353
CHECK_EQ(child, node1);
354
355
children = SceneTree::get_singleton()->get_root()->find_children("Node?", "", true, false);
356
CHECK_EQ(children.size(), 2);
357
CHECK_EQ(Object::cast_to<Node>(children[0]), node1);
358
CHECK_EQ(Object::cast_to<Node>(children[1]), node2);
359
360
SceneTree::get_singleton()->get_root()->move_child(node2, 0);
361
362
// It should be node2, as it is now the first one in the tree.
363
child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false);
364
CHECK_EQ(child, node2);
365
366
children = SceneTree::get_singleton()->get_root()->find_children("Node?", "", true, false);
367
CHECK_EQ(children.size(), 2);
368
CHECK_EQ(Object::cast_to<Node>(children[0]), node2);
369
CHECK_EQ(Object::cast_to<Node>(children[1]), node1);
370
}
371
372
SUBCASE("Nodes should be accessible via their node path") {
373
Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1->get_path());
374
CHECK_EQ(child_by_path, node1);
375
376
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node2->get_path());
377
CHECK_EQ(child_by_path, node2);
378
379
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1_1->get_path());
380
CHECK_EQ(child_by_path, node1_1);
381
382
node1->set_name("Node1");
383
node1_1->set_name("NestedNode");
384
385
child_by_path = node1->get_node_or_null(NodePath("NestedNode"));
386
CHECK_EQ(child_by_path, node1_1);
387
388
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node1/NestedNode"));
389
CHECK_EQ(child_by_path, node1_1);
390
391
child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node1/NestedNode"));
392
CHECK_EQ(child_by_path, node1_1);
393
}
394
395
SUBCASE("Nodes should be accessible via their groups") {
396
Vector<Node *> nodes = SceneTree::get_singleton()->get_nodes_in_group("nodes");
397
CHECK(nodes.is_empty());
398
399
nodes = SceneTree::get_singleton()->get_nodes_in_group("other_nodes");
400
CHECK(nodes.is_empty());
401
402
node1->add_to_group("nodes");
403
node2->add_to_group("other_nodes");
404
node1_1->add_to_group("nodes");
405
node1_1->add_to_group("other_nodes");
406
407
nodes = SceneTree::get_singleton()->get_nodes_in_group("nodes");
408
CHECK_EQ(nodes.size(), 2);
409
410
Node *E = nodes.get(0);
411
CHECK_EQ(E, node1);
412
E = nodes.get(1);
413
CHECK_EQ(E, node1_1);
414
415
// Clear and try again with the other group.
416
nodes.clear();
417
418
nodes = SceneTree::get_singleton()->get_nodes_in_group("other_nodes");
419
CHECK_EQ(nodes.size(), 2);
420
421
E = nodes.get(0);
422
CHECK_EQ(E, node1_1);
423
E = nodes.get(1);
424
CHECK_EQ(E, node2);
425
426
// Clear and try again with the other group and one node removed.
427
nodes.clear();
428
429
node1->remove_from_group("nodes");
430
nodes = SceneTree::get_singleton()->get_nodes_in_group("nodes");
431
CHECK_EQ(nodes.size(), 1);
432
433
E = nodes.get(0);
434
CHECK_EQ(E, node1_1);
435
}
436
437
SUBCASE("Nodes added as siblings of another node should be right next to it") {
438
node1->remove_child(node1_1);
439
440
node1->add_sibling(node1_1);
441
442
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 3);
443
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
444
445
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(0), node1);
446
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(1), node1_1);
447
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(2), node2);
448
}
449
450
SUBCASE("Replaced nodes should be be removed and the replacing node added") {
451
SceneTree::get_singleton()->get_root()->remove_child(node2);
452
453
node1->replace_by(node2);
454
455
CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1);
456
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 3);
457
458
CHECK_FALSE(node1->is_inside_tree());
459
CHECK(node2->is_inside_tree());
460
461
CHECK_EQ(node1->get_parent(), nullptr);
462
CHECK_EQ(node2->get_parent(), SceneTree::get_singleton()->get_root());
463
CHECK_EQ(node2->get_child_count(), 1);
464
CHECK_EQ(node2->get_child(0), node1_1);
465
}
466
467
SUBCASE("Replacing nodes should keep the groups of the replaced nodes") {
468
SceneTree::get_singleton()->get_root()->remove_child(node2);
469
470
node1->add_to_group("nodes");
471
node1->replace_by(node2, true);
472
473
Vector<Node *> nodes = SceneTree::get_singleton()->get_nodes_in_group("nodes");
474
CHECK_EQ(nodes.size(), 1);
475
476
Node *E = nodes.get(0);
477
CHECK_EQ(E, node2);
478
}
479
480
SUBCASE("Duplicating a node should also duplicate the children") {
481
node1->set_name("MyName1");
482
node1_1->set_name("MyName1_1");
483
Node *duplicate1 = node1->duplicate();
484
485
CHECK_EQ(duplicate1->get_child_count(), node1->get_child_count());
486
Node *duplicate1_1 = duplicate1->get_child(0);
487
488
CHECK_EQ(duplicate1_1->get_child_count(), node1_1->get_child_count());
489
490
CHECK_EQ(duplicate1->get_name(), node1->get_name());
491
CHECK_EQ(duplicate1_1->get_name(), node1_1->get_name());
492
493
CHECK_FALSE(duplicate1->is_inside_tree());
494
CHECK_FALSE(duplicate1_1->is_inside_tree());
495
496
memdelete(duplicate1_1);
497
memdelete(duplicate1);
498
}
499
500
memdelete(node1_1);
501
memdelete(node1);
502
memdelete(node2);
503
}
504
505
TEST_CASE("[SceneTree][Node] Duplicating node with internal children") {
506
GDREGISTER_CLASS(TestNode);
507
508
TestNode *node = memnew(TestNode);
509
Node *child = memnew(Node);
510
child->set_name("Child");
511
node->add_child(child);
512
513
int child_count = node->get_child_count();
514
515
Node *dup = node->duplicate();
516
CHECK(dup->get_child_count() == child_count);
517
CHECK(dup->has_node(String("Child")));
518
519
memdelete(node);
520
memdelete(dup);
521
}
522
523
TEST_CASE("[SceneTree][Node]Exported node checks") {
524
TestNode *node = memnew(TestNode);
525
SceneTree::get_singleton()->get_root()->add_child(node);
526
527
Node *child = memnew(Node);
528
child->set_name("Child");
529
node->add_child(child);
530
child->set_owner(node);
531
532
Node *child2 = memnew(Node);
533
child2->set_name("Child2");
534
node->add_child(child2);
535
child2->set_owner(node);
536
537
Array children;
538
children.append(child);
539
540
node->set("exported_node", child);
541
node->set("exported_nodes", children);
542
543
SUBCASE("Property of duplicated node should point to duplicated child") {
544
GDREGISTER_CLASS(TestNode);
545
546
TestNode *dup = Object::cast_to<TestNode>(node->duplicate());
547
Node *new_exported = Object::cast_to<Node>(dup->get("exported_node"));
548
CHECK(new_exported == dup->get_child(0, false));
549
550
memdelete(dup);
551
}
552
553
#ifdef TOOLS_ENABLED
554
SUBCASE("Saving instance with exported nodes should not store the unchanged property") {
555
Ref<PackedScene> ps;
556
ps.instantiate();
557
ps->pack(node);
558
559
String scene_path = TestUtils::get_temp_path("test_scene.tscn");
560
ps->set_path(scene_path);
561
562
Node *root = memnew(Node);
563
564
Node *sub_child = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
565
root->add_child(sub_child);
566
sub_child->set_owner(root);
567
568
Ref<PackedScene> ps2;
569
ps2.instantiate();
570
ps2->pack(root);
571
572
scene_path = TestUtils::get_temp_path("new_test_scene.tscn");
573
ResourceSaver::save(ps2, scene_path);
574
memdelete(root);
575
576
bool is_wrong = false;
577
Ref<FileAccess> fa = FileAccess::open(scene_path, FileAccess::READ);
578
while (!fa->eof_reached()) {
579
const String line = fa->get_line();
580
if (line.begins_with("exported_node")) {
581
// The property was saved, while it shouldn't.
582
is_wrong = true;
583
break;
584
}
585
}
586
CHECK_FALSE(is_wrong);
587
}
588
589
SUBCASE("Saving instance with exported nodes should store property if changed") {
590
Ref<PackedScene> ps;
591
ps.instantiate();
592
ps->pack(node);
593
594
String scene_path = TestUtils::get_temp_path("test_scene.tscn");
595
ps->set_path(scene_path);
596
597
Node *root = memnew(Node);
598
599
Node *sub_child = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
600
root->add_child(sub_child);
601
sub_child->set_owner(root);
602
603
sub_child->set("exported_node", sub_child->get_child(1, false));
604
605
children = Array();
606
children.append(sub_child->get_child(1, false));
607
sub_child->set("exported_nodes", children);
608
609
Ref<PackedScene> ps2;
610
ps2.instantiate();
611
ps2->pack(root);
612
613
scene_path = TestUtils::get_temp_path("new_test_scene2.tscn");
614
ResourceSaver::save(ps2, scene_path);
615
memdelete(root);
616
617
int stored_properties = 0;
618
Ref<FileAccess> fa = FileAccess::open(scene_path, FileAccess::READ);
619
while (!fa->eof_reached()) {
620
const String line = fa->get_line();
621
if (line.begins_with("exported_node")) {
622
stored_properties++;
623
}
624
}
625
CHECK_EQ(stored_properties, 2);
626
}
627
#endif // TOOLS_ENABLED
628
629
memdelete(node);
630
}
631
632
TEST_CASE("[Node] Processing checks") {
633
Node *node = memnew(Node);
634
635
SUBCASE("Processing") {
636
CHECK_FALSE(node->is_processing());
637
638
node->set_process(true);
639
640
CHECK(node->is_processing());
641
642
node->set_process(false);
643
644
CHECK_FALSE(node->is_processing());
645
}
646
647
SUBCASE("Physics processing") {
648
CHECK_FALSE(node->is_physics_processing());
649
650
node->set_physics_process(true);
651
652
CHECK(node->is_physics_processing());
653
654
node->set_physics_process(false);
655
656
CHECK_FALSE(node->is_physics_processing());
657
}
658
659
SUBCASE("Unhandled input processing") {
660
CHECK_FALSE(node->is_processing_unhandled_input());
661
662
node->set_process_unhandled_input(true);
663
664
CHECK(node->is_processing_unhandled_input());
665
666
node->set_process_unhandled_input(false);
667
668
CHECK_FALSE(node->is_processing_unhandled_input());
669
}
670
671
SUBCASE("Input processing") {
672
CHECK_FALSE(node->is_processing_input());
673
674
node->set_process_input(true);
675
676
CHECK(node->is_processing_input());
677
678
node->set_process_input(false);
679
680
CHECK_FALSE(node->is_processing_input());
681
}
682
683
SUBCASE("Unhandled key input processing") {
684
CHECK_FALSE(node->is_processing_unhandled_key_input());
685
686
node->set_process_unhandled_key_input(true);
687
688
CHECK(node->is_processing_unhandled_key_input());
689
690
node->set_process_unhandled_key_input(false);
691
692
CHECK_FALSE(node->is_processing_unhandled_key_input());
693
}
694
695
SUBCASE("Shortcut input processing") {
696
CHECK_FALSE(node->is_processing_shortcut_input());
697
698
node->set_process_shortcut_input(true);
699
700
CHECK(node->is_processing_shortcut_input());
701
702
node->set_process_shortcut_input(false);
703
704
CHECK_FALSE(node->is_processing_shortcut_input());
705
}
706
707
SUBCASE("Internal processing") {
708
CHECK_FALSE(node->is_processing_internal());
709
710
node->set_process_internal(true);
711
712
CHECK(node->is_processing_internal());
713
714
node->set_process_internal(false);
715
716
CHECK_FALSE(node->is_processing_internal());
717
}
718
719
SUBCASE("Process priority") {
720
CHECK_EQ(0, node->get_process_priority());
721
722
node->set_process_priority(1);
723
724
CHECK_EQ(1, node->get_process_priority());
725
}
726
727
SUBCASE("Physics process priority") {
728
CHECK_EQ(0, node->get_physics_process_priority());
729
730
node->set_physics_process_priority(1);
731
732
CHECK_EQ(1, node->get_physics_process_priority());
733
}
734
735
memdelete(node);
736
}
737
738
TEST_CASE("[SceneTree][Node] Test the processing") {
739
TestNode *node = memnew(TestNode);
740
SceneTree::get_singleton()->get_root()->add_child(node);
741
742
SUBCASE("No process") {
743
CHECK_EQ(0, node->process_counter);
744
CHECK_EQ(0, node->physics_process_counter);
745
}
746
747
SUBCASE("Process") {
748
node->set_process(true);
749
SceneTree::get_singleton()->process(0);
750
751
CHECK_EQ(1, node->process_counter);
752
CHECK_EQ(0, node->physics_process_counter);
753
CHECK_EQ(0, node->internal_process_counter);
754
CHECK_EQ(0, node->internal_physics_process_counter);
755
}
756
757
SUBCASE("Physics process") {
758
node->set_physics_process(true);
759
SceneTree::get_singleton()->physics_process(0);
760
761
CHECK_EQ(0, node->process_counter);
762
CHECK_EQ(1, node->physics_process_counter);
763
CHECK_EQ(0, node->internal_process_counter);
764
CHECK_EQ(0, node->internal_physics_process_counter);
765
}
766
767
SUBCASE("Normal and physics process") {
768
node->set_process(true);
769
node->set_physics_process(true);
770
SceneTree::get_singleton()->process(0);
771
SceneTree::get_singleton()->physics_process(0);
772
773
CHECK_EQ(1, node->process_counter);
774
CHECK_EQ(1, node->physics_process_counter);
775
CHECK_EQ(0, node->internal_process_counter);
776
CHECK_EQ(0, node->internal_physics_process_counter);
777
}
778
779
SUBCASE("Internal, normal and physics process") {
780
node->set_process_internal(true);
781
node->set_physics_process_internal(true);
782
SceneTree::get_singleton()->process(0);
783
SceneTree::get_singleton()->physics_process(0);
784
785
CHECK_EQ(0, node->process_counter);
786
CHECK_EQ(0, node->physics_process_counter);
787
CHECK_EQ(1, node->internal_process_counter);
788
CHECK_EQ(1, node->internal_physics_process_counter);
789
}
790
791
SUBCASE("All processing") {
792
node->set_process(true);
793
node->set_physics_process(true);
794
node->set_process_internal(true);
795
node->set_physics_process_internal(true);
796
SceneTree::get_singleton()->process(0);
797
SceneTree::get_singleton()->physics_process(0);
798
799
CHECK_EQ(1, node->process_counter);
800
CHECK_EQ(1, node->physics_process_counter);
801
CHECK_EQ(1, node->internal_process_counter);
802
CHECK_EQ(1, node->internal_physics_process_counter);
803
}
804
805
SUBCASE("All processing twice") {
806
node->set_process(true);
807
node->set_physics_process(true);
808
node->set_process_internal(true);
809
node->set_physics_process_internal(true);
810
SceneTree::get_singleton()->process(0);
811
SceneTree::get_singleton()->physics_process(0);
812
SceneTree::get_singleton()->process(0);
813
SceneTree::get_singleton()->physics_process(0);
814
815
CHECK_EQ(2, node->process_counter);
816
CHECK_EQ(2, node->physics_process_counter);
817
CHECK_EQ(2, node->internal_process_counter);
818
CHECK_EQ(2, node->internal_physics_process_counter);
819
}
820
821
SUBCASE("Enable and disable processing") {
822
node->set_process(true);
823
node->set_physics_process(true);
824
node->set_process_internal(true);
825
node->set_physics_process_internal(true);
826
SceneTree::get_singleton()->process(0);
827
SceneTree::get_singleton()->physics_process(0);
828
829
node->set_process(false);
830
node->set_physics_process(false);
831
node->set_process_internal(false);
832
node->set_physics_process_internal(false);
833
SceneTree::get_singleton()->process(0);
834
SceneTree::get_singleton()->physics_process(0);
835
836
CHECK_EQ(1, node->process_counter);
837
CHECK_EQ(1, node->physics_process_counter);
838
CHECK_EQ(1, node->internal_process_counter);
839
CHECK_EQ(1, node->internal_physics_process_counter);
840
}
841
842
memdelete(node);
843
}
844
845
TEST_CASE("[SceneTree][Node] Test the process priority") {
846
List<Node *> process_order;
847
848
TestNode *node = memnew(TestNode);
849
node->callback_list = &process_order;
850
SceneTree::get_singleton()->get_root()->add_child(node);
851
852
TestNode *node2 = memnew(TestNode);
853
node2->callback_list = &process_order;
854
SceneTree::get_singleton()->get_root()->add_child(node2);
855
856
TestNode *node3 = memnew(TestNode);
857
node3->callback_list = &process_order;
858
SceneTree::get_singleton()->get_root()->add_child(node3);
859
860
TestNode *node4 = memnew(TestNode);
861
node4->callback_list = &process_order;
862
SceneTree::get_singleton()->get_root()->add_child(node4);
863
864
SUBCASE("Process priority") {
865
node->set_process(true);
866
node->set_process_priority(20);
867
node2->set_process(true);
868
node2->set_process_priority(10);
869
node3->set_process(true);
870
node3->set_process_priority(40);
871
node4->set_process(true);
872
node4->set_process_priority(30);
873
874
SceneTree::get_singleton()->process(0);
875
876
CHECK_EQ(4, process_order.size());
877
List<Node *>::Element *E = process_order.front();
878
CHECK_EQ(E->get(), node2);
879
E = E->next();
880
CHECK_EQ(E->get(), node);
881
E = E->next();
882
CHECK_EQ(E->get(), node4);
883
E = E->next();
884
CHECK_EQ(E->get(), node3);
885
}
886
887
SUBCASE("Physics process priority") {
888
node->set_physics_process(true);
889
node->set_physics_process_priority(20);
890
node2->set_physics_process(true);
891
node2->set_physics_process_priority(10);
892
node3->set_physics_process(true);
893
node3->set_physics_process_priority(40);
894
node4->set_physics_process(true);
895
node4->set_physics_process_priority(30);
896
897
SceneTree::get_singleton()->physics_process(0);
898
899
CHECK_EQ(4, process_order.size());
900
List<Node *>::Element *E = process_order.front();
901
CHECK_EQ(E->get(), node2);
902
E = E->next();
903
CHECK_EQ(E->get(), node);
904
E = E->next();
905
CHECK_EQ(E->get(), node4);
906
E = E->next();
907
CHECK_EQ(E->get(), node3);
908
}
909
910
memdelete(node);
911
memdelete(node2);
912
memdelete(node3);
913
memdelete(node4);
914
}
915
916
} // namespace TestNode
917
918