Merge pull request #73477 from Sauermann/fix-viewport-picking-unittest
Add Unit tests for viewport.cpp Physics 2D Picking
This commit is contained in:
commit
40f116f489
2 changed files with 420 additions and 1 deletions
|
@ -31,10 +31,13 @@
|
|||
#ifndef TEST_VIEWPORT_H
|
||||
#define TEST_VIEWPORT_H
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/2d/area_2d.h"
|
||||
#include "scene/2d/collision_shape_2d.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/subviewport_container.h"
|
||||
#include "scene/main/canvas_layer.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/rectangle_shape_2d.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
|
@ -756,6 +759,408 @@ TEST_CASE("[SceneTree][Viewport] Control mouse cursor shape") {
|
|||
}
|
||||
}
|
||||
|
||||
class TestArea2D : public Area2D {
|
||||
GDCLASS(TestArea2D, Area2D);
|
||||
|
||||
void _on_mouse_entered() {
|
||||
enter_id = ++TestArea2D::counter; // > 0, if activated.
|
||||
}
|
||||
|
||||
void _on_mouse_exited() {
|
||||
exit_id = ++TestArea2D::counter; // > 0, if activated.
|
||||
}
|
||||
|
||||
void _on_input_event(Node *p_vp, Ref<InputEvent> p_ev, int p_shape) {
|
||||
last_input_event = p_ev;
|
||||
}
|
||||
|
||||
public:
|
||||
static int counter;
|
||||
int enter_id = 0;
|
||||
int exit_id = 0;
|
||||
Ref<InputEvent> last_input_event;
|
||||
|
||||
void init_signals() {
|
||||
connect(SNAME("mouse_entered"), callable_mp(this, &TestArea2D::_on_mouse_entered));
|
||||
connect(SNAME("mouse_exited"), callable_mp(this, &TestArea2D::_on_mouse_exited));
|
||||
connect(SNAME("input_event"), callable_mp(this, &TestArea2D::_on_input_event));
|
||||
}
|
||||
|
||||
void test_reset() {
|
||||
enter_id = 0;
|
||||
exit_id = 0;
|
||||
last_input_event.unref();
|
||||
}
|
||||
};
|
||||
|
||||
int TestArea2D::counter = 0;
|
||||
|
||||
TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
|
||||
// FIXME: MOUSE_MODE_CAPTURED if-conditions are not testable, because DisplayServerMock doesn't support it.
|
||||
|
||||
struct PickingCollider {
|
||||
TestArea2D *a;
|
||||
CollisionShape2D *c;
|
||||
Ref<RectangleShape2D> r;
|
||||
};
|
||||
|
||||
SceneTree *tree = SceneTree::get_singleton();
|
||||
Window *root = tree->get_root();
|
||||
root->set_physics_object_picking(true);
|
||||
|
||||
Point2i on_background = Point2i(800, 800);
|
||||
Point2i on_outside = Point2i(-1, -1);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
Vector<PickingCollider> v;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
PickingCollider pc;
|
||||
pc.a = memnew(TestArea2D);
|
||||
pc.c = memnew(CollisionShape2D);
|
||||
pc.r = Ref<RectangleShape2D>(memnew(RectangleShape2D));
|
||||
pc.r->set_size(Size2(150, 150));
|
||||
pc.c->set_shape(pc.r);
|
||||
pc.a->add_child(pc.c);
|
||||
pc.a->set_name("A" + itos(i));
|
||||
pc.c->set_name("C" + itos(i));
|
||||
v.push_back(pc);
|
||||
SIGNAL_WATCH(pc.a, SNAME("mouse_entered"));
|
||||
SIGNAL_WATCH(pc.a, SNAME("mouse_exited"));
|
||||
}
|
||||
|
||||
Node2D *node_a = memnew(Node2D);
|
||||
node_a->set_position(Point2i(0, 0));
|
||||
v[0].a->set_position(Point2i(0, 0));
|
||||
v[1].a->set_position(Point2i(0, 100));
|
||||
node_a->add_child(v[0].a);
|
||||
node_a->add_child(v[1].a);
|
||||
Node2D *node_b = memnew(Node2D);
|
||||
node_b->set_position(Point2i(100, 0));
|
||||
v[2].a->set_position(Point2i(0, 0));
|
||||
v[3].a->set_position(Point2i(0, 100));
|
||||
node_b->add_child(v[2].a);
|
||||
node_b->add_child(v[3].a);
|
||||
root->add_child(node_a);
|
||||
root->add_child(node_b);
|
||||
Point2i on_all = Point2i(50, 50);
|
||||
Point2i on_0 = Point2i(10, 10);
|
||||
Point2i on_01 = Point2i(10, 50);
|
||||
Point2i on_02 = Point2i(50, 10);
|
||||
|
||||
Array empty_signal_args_2;
|
||||
empty_signal_args_2.push_back(Array());
|
||||
empty_signal_args_2.push_back(Array());
|
||||
|
||||
Array empty_signal_args_4;
|
||||
empty_signal_args_4.push_back(Array());
|
||||
empty_signal_args_4.push_back(Array());
|
||||
empty_signal_args_4.push_back(Array());
|
||||
empty_signal_args_4.push_back(Array());
|
||||
|
||||
for (PickingCollider E : v) {
|
||||
E.a->init_signals();
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Mouse Motion") {
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
SIGNAL_CHECK(SNAME("mouse_entered"), empty_signal_args_4);
|
||||
SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
|
||||
for (PickingCollider E : v) {
|
||||
CHECK(E.a->enter_id);
|
||||
CHECK_FALSE(E.a->exit_id);
|
||||
E.a->test_reset();
|
||||
}
|
||||
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_01, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
|
||||
SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
if (i < 2) {
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
} else {
|
||||
CHECK(v[i].a->exit_id);
|
||||
}
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
|
||||
SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
if (i < 2) {
|
||||
CHECK(v[i].a->exit_id);
|
||||
} else {
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
}
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Object moved / passive hovering") {
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK(v[i].a->enter_id);
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
node_b->set_position(Point2i(200, 0));
|
||||
tree->physics_process(1);
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
if (i < 2) {
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
} else {
|
||||
CHECK(v[i].a->exit_id);
|
||||
}
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
node_b->set_position(Point2i(100, 0));
|
||||
tree->physics_process(1);
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
if (i < 2) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
} else {
|
||||
CHECK(v[i].a->enter_id);
|
||||
}
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] No Processing") {
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
for (PickingCollider E : v) {
|
||||
E.a->test_reset();
|
||||
}
|
||||
|
||||
v[0].a->set_process_mode(Node::PROCESS_MODE_DISABLED);
|
||||
v[0].c->set_process_mode(Node::PROCESS_MODE_DISABLED);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
CHECK_FALSE(v[0].a->enter_id);
|
||||
CHECK_FALSE(v[0].a->exit_id);
|
||||
CHECK(v[2].a->enter_id);
|
||||
CHECK_FALSE(v[2].a->exit_id);
|
||||
for (PickingCollider E : v) {
|
||||
E.a->test_reset();
|
||||
}
|
||||
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
CHECK_FALSE(v[0].a->enter_id);
|
||||
CHECK_FALSE(v[0].a->exit_id);
|
||||
CHECK_FALSE(v[2].a->enter_id);
|
||||
CHECK(v[2].a->exit_id);
|
||||
|
||||
for (PickingCollider E : v) {
|
||||
E.a->test_reset();
|
||||
}
|
||||
v[0].a->set_process_mode(Node::PROCESS_MODE_ALWAYS);
|
||||
v[0].c->set_process_mode(Node::PROCESS_MODE_ALWAYS);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Multiple events in series") {
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_0, MouseButtonMask::NONE, Key::NONE);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_0 + Point2i(10, 0), MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
if (i < 1) {
|
||||
CHECK(v[i].a->enter_id);
|
||||
} else {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
}
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background + Point2i(10, 10), MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
if (i < 1) {
|
||||
CHECK(v[i].a->exit_id);
|
||||
} else {
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
}
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Disable Picking") {
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE);
|
||||
|
||||
root->set_physics_object_picking(false);
|
||||
CHECK_FALSE(root->get_physics_object_picking());
|
||||
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
root->set_physics_object_picking(true);
|
||||
CHECK(root->get_physics_object_picking());
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] CollisionObject in CanvasLayer") {
|
||||
CanvasLayer *node_c = memnew(CanvasLayer);
|
||||
node_c->set_rotation(Math_PI);
|
||||
node_c->set_offset(Point2i(100, 100));
|
||||
root->add_child(node_c);
|
||||
|
||||
v[2].a->reparent(node_c, false);
|
||||
v[3].a->reparent(node_c, false);
|
||||
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
if (i == 0 || i == 3) {
|
||||
CHECK(v[i].a->enter_id);
|
||||
} else {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
}
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
v[2].a->reparent(node_b, false);
|
||||
v[3].a->reparent(node_b, false);
|
||||
root->remove_child(node_c);
|
||||
memdelete(node_c);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Picking Sort") {
|
||||
root->set_physics_object_picking_sort(true);
|
||||
CHECK(root->get_physics_object_picking_sort());
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Picking Sort Z-Index") {
|
||||
node_a->set_z_index(10);
|
||||
v[0].a->set_z_index(0);
|
||||
v[1].a->set_z_index(2);
|
||||
node_b->set_z_index(5);
|
||||
v[2].a->set_z_index(8);
|
||||
v[3].a->set_z_index(11);
|
||||
v[3].a->set_z_as_relative(false);
|
||||
|
||||
TestArea2D::counter = 0;
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
CHECK(v[0].a->enter_id == 4);
|
||||
CHECK(v[1].a->enter_id == 2);
|
||||
CHECK(v[2].a->enter_id == 1);
|
||||
CHECK(v[3].a->enter_id == 3);
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
TestArea2D::counter = 0;
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
CHECK(v[0].a->exit_id == 4);
|
||||
CHECK(v[1].a->exit_id == 2);
|
||||
CHECK(v[2].a->exit_id == 1);
|
||||
CHECK(v[3].a->exit_id == 3);
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
v[i].a->set_z_as_relative(true);
|
||||
v[i].a->set_z_index(0);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
node_a->set_z_index(0);
|
||||
node_b->set_z_index(0);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Picking Sort Scene Tree Location") {
|
||||
TestArea2D::counter = 0;
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK(v[i].a->enter_id == 4 - i);
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
TestArea2D::counter = 0;
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
CHECK(v[i].a->exit_id == 4 - i);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
}
|
||||
|
||||
root->set_physics_object_picking_sort(false);
|
||||
CHECK_FALSE(root->get_physics_object_picking_sort());
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Mouse Button") {
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
if (i == 0) {
|
||||
CHECK(v[i].a->enter_id);
|
||||
} else {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
}
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_0, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
tree->physics_process(1);
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
CHECK_FALSE(v[i].a->enter_id);
|
||||
CHECK_FALSE(v[i].a->exit_id);
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][Picking2D] Screen Touch") {
|
||||
SEND_GUI_TOUCH_EVENT(on_01, true, false);
|
||||
tree->physics_process(1);
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
if (i < 2) {
|
||||
Ref<InputEventScreenTouch> st = v[i].a->last_input_event;
|
||||
CHECK(st.is_valid());
|
||||
} else {
|
||||
CHECK(v[i].a->last_input_event.is_null());
|
||||
}
|
||||
v[i].a->test_reset();
|
||||
}
|
||||
}
|
||||
|
||||
for (PickingCollider E : v) {
|
||||
SIGNAL_UNWATCH(E.a, SNAME("mouse_entered"));
|
||||
SIGNAL_UNWATCH(E.a, SNAME("mouse_exited"));
|
||||
memdelete(E.c);
|
||||
memdelete(E.a);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Viewport] Embedded Windows") {
|
||||
Window *root = SceneTree::get_singleton()->get_root();
|
||||
Window *w = memnew(Window);
|
||||
|
|
|
@ -177,6 +177,13 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
_UPDATE_EVENT_MODIFERS(event, m_modifers); \
|
||||
event->set_pressed(true);
|
||||
|
||||
#define _CREATE_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
|
||||
Ref<InputEventScreenTouch> event; \
|
||||
event.instantiate(); \
|
||||
event->set_position(m_screen_pos); \
|
||||
event->set_pressed(m_pressed); \
|
||||
event->set_double_tap(m_double);
|
||||
|
||||
#define SEND_GUI_MOUSE_BUTTON_EVENT(m_screen_pos, m_input, m_mask, m_modifers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifers); \
|
||||
|
@ -215,6 +222,13 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
CoreGlobals::print_error_enabled = errors_enabled; \
|
||||
}
|
||||
|
||||
#define SEND_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
|
||||
{ \
|
||||
_CREATE_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
// Utility class / macros for testing signals
|
||||
//
|
||||
// Use SIGNAL_WATCH(*object, "signal_name") to start watching
|
||||
|
|
Loading…
Reference in a new issue