diff options
-rw-r--r-- | tests/display_server_mock.h | 150 | ||||
-rw-r--r-- | tests/scene/test_text_edit.h | 1 | ||||
-rw-r--r-- | tests/test_macros.h | 16 | ||||
-rw-r--r-- | tests/test_main.cpp | 5 |
4 files changed, 165 insertions, 7 deletions
diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h new file mode 100644 index 0000000000..1736f2c452 --- /dev/null +++ b/tests/display_server_mock.h @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* display_server_mock.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISPLAY_SERVER_MOCK_H +#define DISPLAY_SERVER_MOCK_H + +#include "servers/display_server_headless.h" + +#include "servers/rendering/dummy/rasterizer_dummy.h" + +// Specialized DisplayServer for unittests based on DisplayServerHeadless, that +// additionally supports rudimentary InputEvent handling and mouse position. +class DisplayServerMock : public DisplayServerHeadless { +private: + friend class DisplayServer; + + Point2i mouse_position = Point2i(-1, -1); // Outside of Window. + bool window_over = false; + Callable event_callback; + Callable input_event_callback; + + static Vector<String> get_rendering_drivers_func() { + Vector<String> drivers; + drivers.push_back("dummy"); + return drivers; + } + + static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { + r_error = OK; + RasterizerDummy::make_current(); + return memnew(DisplayServerMock()); + } + + static void _dispatch_input_events(const Ref<InputEvent> &p_event) { + static_cast<DisplayServerMock *>(get_singleton())->_dispatch_input_event(p_event); + } + + void _dispatch_input_event(const Ref<InputEvent> &p_event) { + Variant ev = p_event; + Variant *evp = &ev; + Variant ret; + Callable::CallError ce; + + if (input_event_callback.is_valid()) { + input_event_callback.callp((const Variant **)&evp, 1, ret, ce); + } + } + + void _set_mouse_position(const Point2i &p_position) { + if (mouse_position == p_position) { + return; + } + mouse_position = p_position; + _set_window_over(Rect2i(Point2i(0, 0), window_get_size()).has_point(p_position)); + } + + void _set_window_over(bool p_over) { + if (p_over == window_over) { + return; + } + window_over = p_over; + _send_window_event(p_over ? WINDOW_EVENT_MOUSE_ENTER : WINDOW_EVENT_MOUSE_EXIT); + } + + void _send_window_event(WindowEvent p_event) { + if (!event_callback.is_null()) { + Variant event = int(p_event); + Variant *eventp = &event; + Variant ret; + Callable::CallError ce; + event_callback.callp((const Variant **)&eventp, 1, ret, ce); + } + } + +public: + bool has_feature(Feature p_feature) const override { + switch (p_feature) { + case FEATURE_MOUSE: + return true; + default: { + } + } + return false; + } + + String get_name() const override { return "mock"; } + + // You can simulate DisplayServer-events by calling this function. + // The events will be deliverd to Godot's Input-system. + // Mouse-events (Button & Motion) will additionally update the DisplayServer's mouse position. + void simulate_event(Ref<InputEvent> p_event) { + Ref<InputEventMouse> me = p_event; + if (me.is_valid()) { + _set_mouse_position(me->get_position()); + } + Input::get_singleton()->parse_input_event(p_event); + } + + virtual Point2i mouse_get_position() const override { return mouse_position; } + + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override { + return Size2i(1920, 1080); + } + + virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + event_callback = p_callable; + } + + virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + input_event_callback = p_callable; + } + + static void register_mock_driver() { + register_create_function("mock", create_func, get_rendering_drivers_func); + } + + DisplayServerMock() { + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); + } + ~DisplayServerMock() {} +}; + +#endif // DISPLAY_SERVER_MOCK_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 944f9cb9f6..f2663b037d 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -3271,6 +3271,7 @@ TEST_CASE("[SceneTree][TextEdit] mouse") { TEST_CASE("[SceneTree][TextEdit] caret") { TextEdit *text_edit = memnew(TextEdit); + text_edit->set_context_menu_enabled(false); // Prohibit sending InputEvents to the context menu. SceneTree::get_singleton()->get_root()->add_child(text_edit); text_edit->set_size(Size2(800, 200)); diff --git a/tests/test_macros.h b/tests/test_macros.h index 3074c1abf5..80a93c8327 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -31,6 +31,8 @@ #ifndef TEST_MACROS_H #define TEST_MACROS_H +#include "display_server_mock.h" + #include "core/core_globals.h" #include "core/input/input_map.h" #include "core/object/message_queue.h" @@ -139,13 +141,15 @@ int register_test_command(String p_command, TestFunc p_function); // SEND_GUI_MOUSE_MOTION_EVENT - takes an object, position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(code_edit, Vector2(50, 50), MouseButtonMask::LEFT, KeyModifierMask::META); // SEND_GUI_DOUBLE_CLICK - takes an object, position and modifiers. e.g SEND_GUI_DOUBLE_CLICK(code_edit, Vector2(50, 50), KeyModifierMask::META); +#define _SEND_DISPLAYSERVER_EVENT(m_event) ((DisplayServerMock *)(DisplayServer::get_singleton()))->simulate_event(m_event); + #define SEND_GUI_ACTION(m_object, m_action) \ { \ const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(m_action); \ const List<Ref<InputEvent>>::Element *first_event = events->front(); \ Ref<InputEventKey> event = first_event->get(); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -153,7 +157,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ Ref<InputEventKey> event = InputEventKey::create_reference(m_input); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -176,7 +180,7 @@ int register_test_command(String p_command, TestFunc p_function); #define SEND_GUI_MOUSE_BUTTON_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers) \ { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -184,7 +188,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ event->set_pressed(false); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -192,7 +196,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, 0, m_modifers); \ event->set_double_click(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -207,7 +211,7 @@ int register_test_command(String p_command, TestFunc p_function); event->set_button_mask(m_mask); \ event->set_relative(Vector2(10, 10)); \ _UPDATE_EVENT_MODIFERS(event, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ CoreGlobals::print_error_enabled = errors_enabled; \ } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index c7f2d4cbfb..ea6058f707 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -107,6 +107,7 @@ #include "modules/modules_tests.gen.h" +#include "tests/display_server_mock.h" #include "tests/test_macros.h" #include "scene/theme/theme_db.h" @@ -126,6 +127,7 @@ int test_main(int argc, char *argv[]) { args.push_back(String::utf8(argv[i])); } OS::get_singleton()->set_cmdline("", args, List<String>()); + DisplayServerMock::register_mock_driver(); // Run custom test tools. if (test_commands) { @@ -200,11 +202,12 @@ struct GodotTestCaseListener : public doctest::IReporter { memnew(MessageQueue); memnew(Input); + Input::get_singleton()->set_use_accumulated_input(false); Error err = OK; OS::get_singleton()->set_has_server_feature_callback(nullptr); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { - if (String("headless") == DisplayServer::get_create_function_name(i)) { + if (String("mock") == DisplayServer::get_create_function_name(i)) { DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, err); break; } |