t Add unit testing to Godot using DocTest and added to GitHub Actions CI

Implements exit codes into the engine so tests can return their statuses.
Ideally we don't do this, and we use FIXUP logic to 'begin' and 'end' the engine execution for tests specifically.

Since realistically we're initialising the engine here we don't want to do that, since String should not require an engine startup to test a single header.

This lowers the complexity of running the unit tests and even for
physics should be possible to implement such a fix.
This commit is contained in:
RevoluPowered 2020-07-20 17:35:34 +01:00 committed by Gordon MacPherson
parent 93b50a62e3
commit 579342810f
24 changed files with 7417 additions and 1397 deletions

View File

@ -62,6 +62,11 @@ jobs:
run: |
scons -j2 verbose=yes warnings=all werror=yes platform=linuxbsd tools=yes target=release_debug module_mono_enabled=yes mono_glue=no
# Execute unit tests for the editor
- name: Unit Tests
run: |
./bin/godot.linuxbsd.opt.tools.64.mono --test
linux-template:
runs-on: "ubuntu-20.04"
name: Template w/ Mono (target=release, tools=no)

View File

@ -51,6 +51,11 @@ jobs:
run: |
scons -j2 verbose=yes warnings=all werror=yes platform=osx tools=yes target=release_debug
# Execute unit tests for the editor
- name: Unit Tests
run: |
./bin/godot.osx.opt.tools.64 --test
macos-template:
runs-on: "macos-latest"
name: Template (target=release, tools=no)

View File

@ -56,6 +56,11 @@ jobs:
run: |
scons -j2 verbose=yes warnings=all werror=yes platform=windows tools=yes target=release_debug
# Execute unit tests for the editor
- name: Unit Tests
run: |
./bin/godot.windows.opt.tools.64.exe --test
# Build Product Upload (tested and working)
# sorry this is disabled until github can give us some more space as we would hit our limit very quickly
# tested this code and it works fine so just enable it to get them back

4
.gitignore vendored
View File

@ -11,11 +11,13 @@ doc/_build/
# CLion
cmake-build-debug
# clangd
.clangd/
# Android specific
.gradle
local.properties
*.iml
.idea
.gradletasknamecache
project.properties
platform/android/java/lib/.cxx/

View File

@ -126,6 +126,11 @@ Copyright: 2018, Eric Lasota
2018, Microsoft Corp.
License: Expat
Files: ./thirdparty/doctest/
Comment: doctest
Copyright: 2016-2019, Viktor Kirilov
License: Expat
Files: ./thirdparty/enet/
Comment: ENet
Copyright: 2002-2020, Lee Salzman

View File

@ -641,6 +641,9 @@ if selected_platform in platform_list:
}
)
# enable test framework globally and inform it of configuration method
env.Append(CPPDEFINES=["DOCTEST_CONFIG_IMPLEMENT"])
scons_cache_path = os.environ.get("SCONS_CACHE")
if scons_cache_path != None:
CacheDir(scons_cache_path)

View File

@ -35,6 +35,8 @@
#include "core/safe_refcount.h"
#include "core/ustring.h"
class Main;
struct StaticCString {
const char *ptr;
static StaticCString create(const char *p_ptr);
@ -73,7 +75,7 @@ class StringName {
void unref();
friend void register_core_types();
friend void unregister_core_types();
friend class Main;
static Mutex mutex;
static void setup();
static void cleanup();

View File

@ -9,7 +9,6 @@ env.main_sources = []
env.add_source_files(env.main_sources, "*.cpp")
env.Depends("#main/splash.gen.h", "#main/splash.png")
env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", run_in_subprocess(main_builders.make_splash))

View File

@ -30,6 +30,7 @@
#include "main.h"
#include "core/core_string_names.h"
#include "core/crypto/crypto.h"
#include "core/debugger/engine_debugger.h"
#include "core/input/input.h"
@ -75,12 +76,14 @@
#include "servers/xr_server.h"
#ifdef TOOLS_ENABLED
#include "editor/doc_data.h"
#include "editor/doc_data_class_path.gen.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/progress_dialog.h"
#include "editor/project_manager.h"
#endif
/* Static members */
@ -186,7 +189,8 @@ static String get_full_version_string() {
// to have less code in main.cpp.
void initialize_physics() {
/// 3D Physics Server
physics_server = PhysicsServer3DManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServer3DManager::setting_property_name));
physics_server = PhysicsServer3DManager::new_server(
ProjectSettings::get_singleton()->get(PhysicsServer3DManager::setting_property_name));
if (!physics_server) {
// Physics server not found, Use the default physics
physics_server = PhysicsServer3DManager::new_default_server();
@ -195,7 +199,8 @@ void initialize_physics() {
physics_server->init();
/// 2D Physics server
physics_2d_server = PhysicsServer2DManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServer2DManager::setting_property_name));
physics_2d_server = PhysicsServer2DManager::new_server(
ProjectSettings::get_singleton()->get(PhysicsServer2DManager::setting_property_name));
if (!physics_2d_server) {
// Physics server not found, Use the default physics
physics_2d_server = PhysicsServer2DManager::new_default_server();
@ -254,20 +259,25 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" -h, --help Display this help message.\n");
OS::get_singleton()->print(" --version Display the version string.\n");
OS::get_singleton()->print(" -v, --verbose Use verbose stdout mode.\n");
OS::get_singleton()->print(" --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n");
OS::get_singleton()->print(
" --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n");
OS::get_singleton()->print("\n");
OS::get_singleton()->print("Run options:\n");
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n");
OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n");
OS::get_singleton()->print(
" -p, --project-manager Start the project manager, even if a project is auto-detected.\n");
#endif
OS::get_singleton()->print(" -q, --quit Quit after the first iteration.\n");
OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n");
OS::get_singleton()->print(" --path <directory> Path to a project (<directory> must contain a 'project.godot' file).\n");
OS::get_singleton()->print(
" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n");
OS::get_singleton()->print(
" --path <directory> Path to a project (<directory> must contain a 'project.godot' file).\n");
OS::get_singleton()->print(" -u, --upwards Scan folders upwards for project.godot file.\n");
OS::get_singleton()->print(" --main-pack <file> Path to a pack (.pck) file to load.\n");
OS::get_singleton()->print(" --render-thread <mode> Render thread mode ('unsafe', 'safe', 'separate').\n");
OS::get_singleton()->print(
" --render-thread <mode> Render thread mode ('unsafe', 'safe', 'separate').\n");
OS::get_singleton()->print(" --remote-fs <address> Remote filesystem (<host/IP>[:<port>] address).\n");
OS::get_singleton()->print(" --remote-fs-password <password> Password for remote filesystem.\n");
@ -308,9 +318,12 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --resolution <W>x<H> Request window resolution.\n");
OS::get_singleton()->print(" --position <X>,<Y> Request window position.\n");
OS::get_singleton()->print(" --low-dpi Force low-DPI mode (macOS and Windows only).\n");
OS::get_singleton()->print(" --no-window Disable window creation (Windows only). Useful together with --script.\n");
OS::get_singleton()->print(" --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n");
OS::get_singleton()->print(" --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n");
OS::get_singleton()->print(
" --no-window Disable window creation (Windows only). Useful together with --script.\n");
OS::get_singleton()->print(
" --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n");
OS::get_singleton()->print(
" --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n");
OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n");
OS::get_singleton()->print(" --tablet-driver Tablet input driver (");
for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) {
@ -324,35 +337,51 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print("Debug options:\n");
OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n");
OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
OS::get_singleton()->print(
" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n");
OS::get_singleton()->print(" --gpu-abort Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n");
OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
OS::get_singleton()->print(
" --gpu-abort Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n");
OS::get_singleton()->print(
" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
#if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
#endif
OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n");
OS::get_singleton()->print(" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n");
OS::get_singleton()->print(" --disable-crash-handler Disable crash handler when supported by the platform code.\n");
OS::get_singleton()->print(" --fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
OS::get_singleton()->print(
" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
OS::get_singleton()->print(
" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n");
OS::get_singleton()->print(
" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n");
OS::get_singleton()->print(
" --disable-crash-handler Disable crash handler when supported by the platform code.\n");
OS::get_singleton()->print(
" --fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
OS::get_singleton()->print(" --print-fps Print the frames per second to the stdout.\n");
OS::get_singleton()->print("\n");
OS::get_singleton()->print("Standalone tools:\n");
OS::get_singleton()->print(" -s, --script <script> Run a script.\n");
OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n");
OS::get_singleton()->print(
" --check-only Only parse for errors and quit (use with --script).\n");
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" --export <preset> <path> Export the project using the given preset and matching release template. The preset name should match one defined in export_presets.cfg.\n");
OS::get_singleton()->print(" <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe'). The target directory should exist.\n");
OS::get_singleton()->print(
" --export <preset> <path> Export the project using the given preset and matching release template. The preset name should match one defined in export_presets.cfg.\n");
OS::get_singleton()->print(
" <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe'). The target directory should exist.\n");
OS::get_singleton()->print(" --export-debug <preset> <path> Same as --export, but using the debug template.\n");
OS::get_singleton()->print(" --export-pack <preset> <path> Same as --export, but only export the game pack for the given preset. The <path> extension determines whether it will be in PCK or ZIP format.\n");
OS::get_singleton()->print(" --doctool <path> Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n");
OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
OS::get_singleton()->print(
" --export-pack <preset> <path> Same as --export, but only export the game pack for the given preset. The <path> extension determines whether it will be in PCK or ZIP format.\n");
OS::get_singleton()->print(
" --doctool <path> Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n");
OS::get_singleton()->print(
" --no-docbase Disallow dumping the base types (used with --doctool).\n");
OS::get_singleton()->print(
" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
#ifdef DEBUG_METHODS_ENABLED
OS::get_singleton()->print(" --gdnative-generate-json-api Generate JSON dump of the Godot API for GDNative bindings.\n");
OS::get_singleton()->print(
" --gdnative-generate-json-api Generate JSON dump of the Godot API for GDNative bindings.\n");
#endif
OS::get_singleton()->print(" --test <test> Run a unit test [");
const char **test_names = tests_get_names();
@ -366,6 +395,23 @@ void Main::print_help(const char *p_binary) {
#endif
}
int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) {
#ifdef TOOLS_ENABLED // templates can't run unit test tool
OS::get_singleton()->initialize();
StringName::setup();
for (int x = 0; x < argc; x++) {
if (strncmp(argv[x], "--test", 6)) {
tests_need_run = true;
return test_main(argc, argv);
}
}
StringName::cleanup();
OS::get_singleton()->finalize();
#endif
tests_need_run = false;
return 0;
}
/* Engine initialization
*
* Consists of several methods that are called by each platform's specific main(argc, argv).
@ -418,7 +464,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
ClassDB::register_class<Performance>();
engine->add_singleton(Engine::Singleton("Performance", performance));
GLOBAL_DEF("debug/settings/crash_handler/message", String("Please include this when reporting the bug on https://github.com/godotengine/godot/issues"));
GLOBAL_DEF("debug/settings/crash_handler/message",
String("Please include this when reporting the bug on https://github.com/godotengine/godot/issues"));
MAIN_PRINT("Main: Parse CMDLine");
@ -523,7 +570,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
if (!found) {
OS::get_singleton()->print("Unknown audio driver '%s', aborting.\nValid options are ", audio_driver.utf8().get_data());
OS::get_singleton()->print("Unknown audio driver '%s', aborting.\nValid options are ",
audio_driver.utf8().get_data());
for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
if (i == AudioDriverManager::get_driver_count() - 1) {
@ -559,7 +607,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
if (!found) {
OS::get_singleton()->print("Unknown display driver '%s', aborting.\nValid options are ", display_driver.utf8().get_data());
OS::get_singleton()->print("Unknown display driver '%s', aborting.\nValid options are ",
display_driver.utf8().get_data());
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (i == DisplayServer::get_create_function_count() - 1) {
@ -607,7 +656,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
if (!found) {
OS::get_singleton()->print("Unknown tablet driver '%s', aborting.\n", tablet_driver.utf8().get_data());
OS::get_singleton()->print("Unknown tablet driver '%s', aborting.\n",
tablet_driver.utf8().get_data());
goto error;
}
@ -629,7 +679,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (vm.find("x") == -1) { // invalid parameter format
OS::get_singleton()->print("Invalid resolution '%s', it should be e.g. '1280x720'.\n", vm.utf8().get_data());
OS::get_singleton()->print("Invalid resolution '%s', it should be e.g. '1280x720'.\n",
vm.utf8().get_data());
goto error;
}
@ -637,7 +688,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
int h = vm.get_slice("x", 1).to_int();
if (w <= 0 || h <= 0) {
OS::get_singleton()->print("Invalid resolution '%s', width and height must be above 0.\n", vm.utf8().get_data());
OS::get_singleton()->print("Invalid resolution '%s', width and height must be above 0.\n",
vm.utf8().get_data());
goto error;
}
@ -658,7 +710,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (vm.find(",") == -1) { // invalid parameter format
OS::get_singleton()->print("Invalid position '%s', it should be e.g. '80,128'.\n", vm.utf8().get_data());
OS::get_singleton()->print("Invalid position '%s', it should be e.g. '80,128'.\n",
vm.utf8().get_data());
goto error;
}
@ -754,7 +807,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// We still pass it to the main arguments since the argument handling itself is not done in this function
main_args.push_back(I->get());
#endif
} else if (I->get() == "--export" || I->get() == "--export-debug" || I->get() == "--export-pack") { // Export project
} else if (I->get() == "--export" || I->get() == "--export-debug" ||
I->get() == "--export-pack") { // Export project
editor = true;
main_args.push_back(I->get());
@ -847,7 +901,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (I->next()) {
debug_uri = I->next()->get();
if (debug_uri.find("://") == -1) { // wrong address
OS::get_singleton()->print("Invalid debug host address, it should be of the form <protocol>://<host/IP>:<port>.\n");
OS::get_singleton()->print(
"Invalid debug host address, it should be of the form <protocol>://<host/IP>:<port>.\n");
goto error;
}
N = I->next()->next();
@ -888,7 +943,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#ifdef TOOLS_ENABLED
if (editor && project_manager) {
OS::get_singleton()->print("Error: Command line arguments implied opening both editor and project manager, which is not possible. Aborting.\n");
OS::get_singleton()->print(
"Error: Command line arguments implied opening both editor and project manager, which is not possible. Aborting.\n");
goto error;
}
#endif
@ -935,15 +991,35 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->ensure_user_data_dir();
GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60);
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1")); // No negative and limit to 500 due to crashes
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc",
PropertyInfo(Variant::INT,
"memory/limits/multithreaded_server/rid_pool_prealloc",
PROPERTY_HINT_RANGE,
"0,500,1")); // No negative and limit to 500 due to crashes
GLOBAL_DEF("network/limits/debugger/max_chars_per_second", 32768);
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater"));
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_chars_per_second",
PropertyInfo(Variant::INT,
"network/limits/debugger/max_chars_per_second",
PROPERTY_HINT_RANGE,
"0, 4096, 1, or_greater"));
GLOBAL_DEF("network/limits/debugger/max_queued_messages", 2048);
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_queued_messages", PropertyInfo(Variant::INT, "network/limits/debugger/max_queued_messages", PROPERTY_HINT_RANGE, "0, 8192, 1, or_greater"));
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_queued_messages",
PropertyInfo(Variant::INT,
"network/limits/debugger/max_queued_messages",
PROPERTY_HINT_RANGE,
"0, 8192, 1, or_greater"));
GLOBAL_DEF("network/limits/debugger/max_errors_per_second", 400);
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_errors_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_errors_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_errors_per_second",
PropertyInfo(Variant::INT,
"network/limits/debugger/max_errors_per_second",
PROPERTY_HINT_RANGE,
"0, 200, 1, or_greater"));
GLOBAL_DEF("network/limits/debugger/max_warnings_per_second", 400);
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_warnings_per_second", PropertyInfo(Variant::INT, "network/limits/debugger/max_warnings_per_second", PROPERTY_HINT_RANGE, "0, 200, 1, or_greater"));
ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger/max_warnings_per_second",
PropertyInfo(Variant::INT,
"network/limits/debugger/max_warnings_per_second",
PROPERTY_HINT_RANGE,
"0, 200, 1, or_greater"));
EngineDebugger::initialize(debug_uri, skip_breakpoints, breakpoints);
@ -978,8 +1054,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF("logging/file_logging/enable_file_logging.pc", true);
GLOBAL_DEF("logging/file_logging/log_path", "user://logs/godot.log");
GLOBAL_DEF("logging/file_logging/max_log_files", 5);
ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers
if (!project_manager && !editor && FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) {
ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files",
PropertyInfo(Variant::INT,
"logging/file_logging/max_log_files",
PROPERTY_HINT_RANGE,
"0,20,1,or_greater")); //no negative numbers
if (!project_manager && !editor && FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) &&
GLOBAL_GET("logging/file_logging/enable_file_logging")) {
// Don't create logs for the project manager as they would be written to
// the current working directory, which is inconvenient.
String base_path = GLOBAL_GET("logging/file_logging/log_path");
@ -1020,7 +1101,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->set_cmdline(execpath, main_args);
GLOBAL_DEF("rendering/quality/driver/driver_name", "Vulkan");
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_name", PropertyInfo(Variant::STRING, "rendering/quality/driver/driver_name", PROPERTY_HINT_ENUM, "Vulkan,GLES2"));
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_name",
PropertyInfo(Variant::STRING,
"rendering/quality/driver/driver_name",
PROPERTY_HINT_ENUM, "Vulkan,GLES2"));
if (display_driver == "") {
display_driver = GLOBAL_GET("rendering/quality/driver/driver_name");
}
@ -1029,24 +1113,39 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF("rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround", false);
GLOBAL_DEF("display/window/size/width", 1024);
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/width", PropertyInfo(Variant::INT, "display/window/size/width", PROPERTY_HINT_RANGE, "0,7680,or_greater")); // 8K resolution
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/width",
PropertyInfo(Variant::INT, "display/window/size/width",
PROPERTY_HINT_RANGE,
"0,7680,or_greater")); // 8K resolution
GLOBAL_DEF("display/window/size/height", 600);
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/height", PropertyInfo(Variant::INT, "display/window/size/height", PROPERTY_HINT_RANGE, "0,4320,or_greater")); // 8K resolution
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/height",
PropertyInfo(Variant::INT, "display/window/size/height",
PROPERTY_HINT_RANGE,
"0,4320,or_greater")); // 8K resolution
GLOBAL_DEF("display/window/size/resizable", true);
GLOBAL_DEF("display/window/size/borderless", false);
GLOBAL_DEF("display/window/size/fullscreen", false);
GLOBAL_DEF("display/window/size/always_on_top", false);
GLOBAL_DEF("display/window/size/test_width", 0);
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_width", PropertyInfo(Variant::INT, "display/window/size/test_width", PROPERTY_HINT_RANGE, "0,7680,or_greater")); // 8K resolution
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_width",
PropertyInfo(Variant::INT,
"display/window/size/test_width",
PROPERTY_HINT_RANGE,
"0,7680,or_greater")); // 8K resolution
GLOBAL_DEF("display/window/size/test_height", 0);
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_height", PropertyInfo(Variant::INT, "display/window/size/test_height", PROPERTY_HINT_RANGE, "0,4320,or_greater")); // 8K resolution
ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_height",
PropertyInfo(Variant::INT,
"display/window/size/test_height",
PROPERTY_HINT_RANGE,
"0,4320,or_greater")); // 8K resolution
if (use_custom_res) {
if (!force_res) {
window_size.width = GLOBAL_GET("display/window/size/width");
window_size.height = GLOBAL_GET("display/window/size/height");
if (globals->has_setting("display/window/size/test_width") && globals->has_setting("display/window/size/test_height")) {
if (globals->has_setting("display/window/size/test_width") &&
globals->has_setting("display/window/size/test_height")) {
int tw = globals->get("display/window/size/test_width");
if (tw > 0) {
window_size.width = tw;
@ -1106,8 +1205,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
/* todo restore
OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false);
video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false);
OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false);
video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false);
*/
GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation", 2);
GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation.mobile", 3);
@ -1180,10 +1279,15 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/physics_fps", 60));
ProjectSettings::get_singleton()->set_custom_property_info("physics/common/physics_fps", PropertyInfo(Variant::INT, "physics/common/physics_fps", PROPERTY_HINT_RANGE, "1,120,1,or_greater"));
ProjectSettings::get_singleton()->set_custom_property_info("physics/common/physics_fps",
PropertyInfo(Variant::INT, "physics/common/physics_fps",
PROPERTY_HINT_RANGE, "1,120,1,or_greater"));
Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5));
Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0));
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", PROPERTY_HINT_RANGE, "0,120,1,or_greater"));
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps",
PropertyInfo(Variant::INT,
"debug/settings/fps/force_fps",
PROPERTY_HINT_RANGE, "0,120,1,or_greater"));
GLOBAL_DEF("debug/settings/stdout/print_fps", false);
GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false);
@ -1194,12 +1298,21 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (frame_delay == 0) {
frame_delay = GLOBAL_DEF("application/run/frame_delay_msec", 0);
ProjectSettings::get_singleton()->set_custom_property_info("application/run/frame_delay_msec", PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater")); // No negative numbers
ProjectSettings::get_singleton()->set_custom_property_info("application/run/frame_delay_msec",
PropertyInfo(Variant::INT,
"application/run/frame_delay_msec",
PROPERTY_HINT_RANGE,
"0,100,1,or_greater")); // No negative numbers
}
OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false));
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(GLOBAL_DEF("application/run/low_processor_mode_sleep_usec", 6900)); // Roughly 144 FPS
ProjectSettings::get_singleton()->set_custom_property_info("application/run/low_processor_mode_sleep_usec", PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater")); // No negative numbers
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(
GLOBAL_DEF("application/run/low_processor_mode_sleep_usec", 6900)); // Roughly 144 FPS
ProjectSettings::get_singleton()->set_custom_property_info("application/run/low_processor_mode_sleep_usec",
PropertyInfo(Variant::INT,
"application/run/low_processor_mode_sleep_usec",
PROPERTY_HINT_RANGE,
"0,33200,1,or_greater")); // No negative numbers
GLOBAL_DEF("display/window/ios/hide_home_indicator", true);
@ -1286,14 +1399,16 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
String rendering_driver; // temp broken
Error err;
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags, window_size, err);
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags,
window_size, err);
if (err != OK) {
//ok i guess we can't use this display server, try other ones
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (i == display_driver_idx) {
continue; //don't try the same twice
}
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags, window_size, err);
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_flags,
window_size, err);
if (err == OK) {
break;
}
@ -1314,7 +1429,9 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
rendering_server = memnew(RenderingServerRaster);
if (OS::get_singleton()->get_render_thread_mode() != OS::RENDER_THREAD_UNSAFE) {
rendering_server = memnew(RenderingServerWrapMT(rendering_server, OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD));
rendering_server = memnew(RenderingServerWrapMT(rendering_server,
OS::get_singleton()->get_render_thread_mode() ==
OS::RENDER_SEPARATE_THREAD));
}
rendering_server->init();
@ -1379,7 +1496,10 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
String boot_logo_path = GLOBAL_DEF("application/boot_splash/image", String());
bool boot_logo_scale = GLOBAL_DEF("application/boot_splash/fullsize", true);
bool boot_logo_filter = GLOBAL_DEF("application/boot_splash/use_filter", true);
ProjectSettings::get_singleton()->set_custom_property_info("application/boot_splash/image", PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"));
ProjectSettings::get_singleton()->set_custom_property_info("application/boot_splash/image",
PropertyInfo(Variant::STRING,
"application/boot_splash/image",
PROPERTY_HINT_FILE, "*.png"));
Ref<Image> boot_logo;
@ -1396,7 +1516,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
Color boot_bg_color = GLOBAL_DEF("application/boot_splash/bg_color", boot_splash_bg_color);
if (boot_logo.is_valid()) {
OS::get_singleton()->_msec_splash = OS::get_singleton()->get_ticks_msec();
RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale, boot_logo_filter);
RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale,
boot_logo_filter);
} else {
#ifndef NO_DEFAULT_BOOT_LOGO
@ -1421,21 +1542,31 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
}
MAIN_PRINT("Main: DCC");
RenderingServer::get_singleton()->set_default_clear_color(GLOBAL_DEF("rendering/environment/default_clear_color", Color(0.3, 0.3, 0.3)));
RenderingServer::get_singleton()->set_default_clear_color(
GLOBAL_DEF("rendering/environment/default_clear_color", Color(0.3, 0.3, 0.3)));
MAIN_PRINT("Main: END");
GLOBAL_DEF("application/config/icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon", PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp"));
ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon",
PropertyInfo(Variant::STRING, "application/config/icon",
PROPERTY_HINT_FILE, "*.png,*.webp"));
GLOBAL_DEF("application/config/macos_native_icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/macos_native_icon", PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"));
ProjectSettings::get_singleton()->set_custom_property_info("application/config/macos_native_icon",
PropertyInfo(Variant::STRING,
"application/config/macos_native_icon",
PROPERTY_HINT_FILE, "*.icns"));
GLOBAL_DEF("application/config/windows_native_icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/windows_native_icon", PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico"));
ProjectSettings::get_singleton()->set_custom_property_info("application/config/windows_native_icon",
PropertyInfo(Variant::STRING,
"application/config/windows_native_icon",
PROPERTY_HINT_FILE, "*.ico"));
Input *id = Input::get_singleton();
if (id) {
if (bool(GLOBAL_DEF("input_devices/pointing/emulate_touch_from_mouse", false)) && !(editor || project_manager)) {
if (bool(GLOBAL_DEF("input_devices/pointing/emulate_touch_from_mouse", false)) &&
!(editor || project_manager)) {
bool found_touchscreen = false;
for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) {
if (DisplayServer::get_singleton()->screen_is_touchscreen(i)) {
@ -1460,10 +1591,14 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
GLOBAL_DEF("display/mouse_cursor/custom_image", String());
GLOBAL_DEF("display/mouse_cursor/custom_image_hotspot", Vector2());
GLOBAL_DEF("display/mouse_cursor/tooltip_position_offset", Point2(10, 10));
ProjectSettings::get_singleton()->set_custom_property_info("display/mouse_cursor/custom_image", PropertyInfo(Variant::STRING, "display/mouse_cursor/custom_image", PROPERTY_HINT_FILE, "*.png,*.webp"));
ProjectSettings::get_singleton()->set_custom_property_info("display/mouse_cursor/custom_image",
PropertyInfo(Variant::STRING,
"display/mouse_cursor/custom_image",
PROPERTY_HINT_FILE, "*.png,*.webp"));
if (String(ProjectSettings::get_singleton()->get("display/mouse_cursor/custom_image")) != String()) {
Ref<Texture2D> cursor = ResourceLoader::load(ProjectSettings::get_singleton()->get("display/mouse_cursor/custom_image"));
Ref<Texture2D> cursor = ResourceLoader::load(
ProjectSettings::get_singleton()->get("display/mouse_cursor/custom_image"));
if (cursor.is_valid()) {
Vector2 hotspot = ProjectSettings::get_singleton()->get("display/mouse_cursor/custom_image_hotspot");
Input::get_singleton()->set_custom_mouse_cursor(cursor, Input::CURSOR_ARROW, hotspot);
@ -1544,7 +1679,6 @@ bool Main::start() {
String positional_arg;
String game_path;
String script;
String test;
bool check_only = false;
#ifdef TOOLS_ENABLED
@ -1555,10 +1689,12 @@ bool Main::start() {
#endif
main_timer_sync.init(OS::get_singleton()->get_ticks_usec());
List<String> args = OS::get_singleton()->get_cmdline_args();
// parameters that do not have an argument to the right
for (int i = 0; i < args.size(); i++) {
//parameters that do not have an argument to the right
// Doctest Unit Testing Handler
// Designed to override and pass arguments to the unit test handler.
if (args[i] == "--check-only") {
check_only = true;
#ifdef TOOLS_ENABLED
@ -1591,8 +1727,6 @@ bool Main::start() {
bool parsed_pair = true;
if (args[i] == "-s" || args[i] == "--script") {
script = args[i + 1];
} else if (args[i] == "--test") {
test = args[i + 1];
#ifdef TOOLS_ENABLED
} else if (args[i] == "--doctool") {
doc_tool = args[i + 1];
@ -1624,7 +1758,8 @@ bool Main::start() {
String main_loop_type;
#ifdef TOOLS_ENABLED
if (doc_tool != "") {
Engine::get_singleton()->set_editor_hint(true); // Needed to instance editor-only classes for their default values
Engine::get_singleton()->set_editor_hint(
true); // Needed to instance editor-only classes for their default values
{
DirAccessRef da = DirAccess::open(doc_tool);
@ -1694,7 +1829,7 @@ bool Main::start() {
print_line("Generating new docs...");
doc.save_classes(index_path, doc_data_classes);
return false;
return FAILED;
}
if (_export_preset != "") {
@ -1702,7 +1837,7 @@ bool Main::start() {
String err = "Command line includes export parameter option, but no destination path was given.\n";
err += "Please specify the binary's file path to export to. Aborting export.";
ERR_PRINT(err);
return false;
return FAILED;
}
}
#endif
@ -1716,16 +1851,7 @@ bool Main::start() {
main_loop = memnew(SceneTree);
};
if (test != "") {
#ifdef TOOLS_ENABLED
main_loop = test_main(test, args);
if (!main_loop) {
return false;
}
#endif
} else if (script != "") {
if (script != "") {
Ref<Script> script_res = ResourceLoader::load(script);
ERR_FAIL_COND_V_MSG(script_res.is_null(), false, "Can't load script: " + script);
@ -1733,7 +1859,7 @@ bool Main::start() {
if (!script_res->is_valid()) {
OS::get_singleton()->set_exit_code(1);
}
return false;
return FAILED;
}
if (script_res->can_instance()) {
@ -1744,13 +1870,15 @@ bool Main::start() {
if (obj) {
memdelete(obj);
}
ERR_FAIL_V_MSG(false, vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script));
ERR_FAIL_V_MSG(false,
vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.",
script));
}
script_loop->set_init_script(script_res);
main_loop = script_loop;
} else {
return false;
return FAILED;
}
} else {
@ -1764,7 +1892,7 @@ bool Main::start() {
if (!main_loop) {
if (!ClassDB::class_exists(main_loop_type)) {
DisplayServer::get_singleton()->alert("Error: MainLoop type doesn't exist: " + main_loop_type);
return false;
return FAILED;
} else {
Object *ml = ClassDB::instance(main_loop_type);
ERR_FAIL_COND_V_MSG(!ml, false, "Can't instance MainLoop type.");
@ -1832,7 +1960,9 @@ bool Main::start() {
Object *obj = ClassDB::instance(ibt);
ERR_CONTINUE_MSG(obj == nullptr, "Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt));
ERR_CONTINUE_MSG(obj == nullptr,
"Cannot instance script for autoload, expected 'Node' inheritance, got: " +
String(ibt));
n = Object::cast_to<Node>(obj);
n->set_script(script_res);
@ -1880,7 +2010,8 @@ bool Main::start() {
String stretch_mode = GLOBAL_DEF("display/window/stretch/mode", "disabled");
String stretch_aspect = GLOBAL_DEF("display/window/stretch/aspect", "ignore");
Size2i stretch_size = Size2(GLOBAL_DEF("display/window/size/width", 0), GLOBAL_DEF("display/window/size/height", 0));
Size2i stretch_size = Size2(GLOBAL_DEF("display/window/size/width", 0),
GLOBAL_DEF("display/window/size/height", 0));
Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED;
if (stretch_mode == "canvas_items") {
@ -1924,10 +2055,14 @@ bool Main::start() {
int shadow_atlas_q3_subdiv = GLOBAL_GET("rendering/quality/shadow_atlas/quadrant_3_subdiv");
sml->get_root()->set_shadow_atlas_size(shadow_atlas_size);
sml->get_root()->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(shadow_atlas_q0_subdiv));
sml->get_root()->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(shadow_atlas_q1_subdiv));
sml->get_root()->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(shadow_atlas_q2_subdiv));
sml->get_root()->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(shadow_atlas_q3_subdiv));
sml->get_root()->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(
shadow_atlas_q0_subdiv));
sml->get_root()->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(
shadow_atlas_q1_subdiv));
sml->get_root()->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(
shadow_atlas_q2_subdiv));
sml->get_root()->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(
shadow_atlas_q3_subdiv));
bool snap_controls = GLOBAL_DEF("gui/common/snap_controls_to_pixels", true);
sml->get_root()->set_snap_controls_to_pixels(snap_controls);
@ -1937,30 +2072,51 @@ bool Main::start() {
int texture_filter = GLOBAL_DEF("rendering/canvas_textures/default_texture_filter", 1);
int texture_repeat = GLOBAL_DEF("rendering/canvas_textures/default_texture_repeat", 0);
sml->get_root()->set_default_canvas_item_texture_filter(Viewport::DefaultCanvasItemTextureFilter(texture_filter));
sml->get_root()->set_default_canvas_item_texture_repeat(Viewport::DefaultCanvasItemTextureRepeat(texture_repeat));
sml->get_root()->set_default_canvas_item_texture_filter(
Viewport::DefaultCanvasItemTextureFilter(texture_filter));
sml->get_root()->set_default_canvas_item_texture_repeat(
Viewport::DefaultCanvasItemTextureRepeat(texture_repeat));
} else {
GLOBAL_DEF("display/window/stretch/mode", "disabled");
ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/mode", PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"));
ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/mode",
PropertyInfo(Variant::STRING,
"display/window/stretch/mode",
PROPERTY_HINT_ENUM,
"disabled,canvas_items,viewport"));
GLOBAL_DEF("display/window/stretch/aspect", "ignore");
ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect", PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"));
ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect",
PropertyInfo(Variant::STRING,
"display/window/stretch/aspect",
PROPERTY_HINT_ENUM,
"ignore,keep,keep_width,keep_height,expand"));
GLOBAL_DEF("display/window/stretch/shrink", 1.0);
ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::FLOAT, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1.0,8.0,0.1"));
ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink",
PropertyInfo(Variant::FLOAT,
"display/window/stretch/shrink",
PROPERTY_HINT_RANGE,
"1.0,8.0,0.1"));
sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true));
sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true));
GLOBAL_DEF("gui/common/snap_controls_to_pixels", true);
GLOBAL_DEF("rendering/quality/dynamic_fonts/use_oversampling", true);
GLOBAL_DEF("rendering/canvas_textures/default_texture_filter", 1);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/canvas_textures/default_texture_filter", PropertyInfo(Variant::INT, "rendering/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"));
ProjectSettings::get_singleton()->set_custom_property_info(
"rendering/canvas_textures/default_texture_filter",
PropertyInfo(Variant::INT, "rendering/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM,
"Nearest,Linear,MipmapLinear,MipmapNearest"));
GLOBAL_DEF("rendering/canvas_textures/default_texture_repeat", 0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/canvas_textures/default_texture_repeat", PropertyInfo(Variant::INT, "rendering/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM, "Disable,Enable,Mirror"));
ProjectSettings::get_singleton()->set_custom_property_info(
"rendering/canvas_textures/default_texture_repeat",
PropertyInfo(Variant::INT, "rendering/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM,
"Disable,Enable,Mirror"));
}
#ifdef TOOLS_ENABLED
if (editor) {
bool editor_embed_subwindows = EditorSettings::get_singleton()->get_setting("interface/editor/single_window_mode");
bool editor_embed_subwindows = EditorSettings::get_singleton()->get_setting(
"interface/editor/single_window_mode");
if (editor_embed_subwindows) {
sml->get_root()->set_embed_subwindows_hint(true);
@ -1973,7 +2129,8 @@ bool Main::start() {
local_game_path = game_path.replace("\\", "/");
if (!local_game_path.begins_with("res://")) {
bool absolute = (local_game_path.size() > 1) && (local_game_path[0] == '/' || local_game_path[1] == ':');
bool absolute =
(local_game_path.size() > 1) && (local_game_path[0] == '/' || local_game_path[1] == ':');
if (!absolute) {
if (ProjectSettings::get_singleton()->is_using_datapack()) {
@ -1989,7 +2146,8 @@ bool Main::start() {
} else {
DirAccess *da = DirAccess::open(local_game_path.substr(0, sep));
if (da) {
local_game_path = da->get_current_dir().plus_file(local_game_path.substr(sep + 1, local_game_path.length()));
local_game_path = da->get_current_dir().plus_file(
local_game_path.substr(sep + 1, local_game_path.length()));
memdelete(da);
}
}
@ -2059,7 +2217,7 @@ bool Main::start() {
}
#ifdef TOOLS_ENABLED
if (project_manager || (script == "" && test == "" && game_path == "" && !editor)) {
if (project_manager || (script == "" && game_path == "" && !editor)) {
Engine::get_singleton()->set_editor_hint(true);
ProjectManager *pmanager = memnew(ProjectManager);
ProgressDialog *progress_dialog = memnew(ProgressDialog);
@ -2072,12 +2230,16 @@ bool Main::start() {
if (project_manager || editor) {
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CONSOLE_WINDOW)) {
// Hide console window if requested (Windows-only).
bool hide_console = EditorSettings::get_singleton()->get_setting("interface/editor/hide_console_window");
bool hide_console = EditorSettings::get_singleton()->get_setting(
"interface/editor/hide_console_window");
DisplayServer::get_singleton()->console_set_visible(!hide_console);
}
// Load SSL Certificates from Editor Settings (or builtin)
Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String());
Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting(
"network/ssl/editor_ssl_certificates")
.
operator String());
}
#endif
}
@ -2089,7 +2251,7 @@ bool Main::start() {
OS::get_singleton()->set_main_loop(main_loop);
return true;
return OK;
}
/* Main iteration
@ -2107,6 +2269,7 @@ uint32_t Main::frames = 0;
uint32_t Main::frame = 0;
bool Main::force_redraw_requested = false;
int Main::iterating = 0;
bool Main::is_iterating() {
return iterating > 0;
}
@ -2182,7 +2345,8 @@ bool Main::iteration() {
message_queue->flush();
physics_process_ticks = MAX(physics_process_ticks, OS::get_singleton()->get_ticks_usec() - physics_begin); // keep the largest one for reference
physics_process_ticks = MAX(physics_process_ticks, OS::get_singleton()->get_ticks_usec() -
physics_begin); // keep the largest one for reference
physics_process_max = MAX(OS::get_singleton()->get_ticks_usec() - physics_begin, physics_process_max);
Engine::get_singleton()->_physics_frames++;
}
@ -2198,7 +2362,8 @@ bool Main::iteration() {
RenderingServer::get_singleton()->sync(); //sync if still drawing from previous frames.
if (DisplayServer::get_singleton()->can_any_window_draw() && RenderingServer::get_singleton()->is_render_loop_enabled()) {
if (DisplayServer::get_singleton()->can_any_window_draw() &&
RenderingServer::get_singleton()->is_render_loop_enabled()) {
if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) {
if (RenderingServer::get_singleton()->has_changed()) {
RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
@ -2260,10 +2425,12 @@ bool Main::iteration() {
auto_build_solutions = false;
// Only relevant when running the editor.
if (!editor) {
ERR_FAIL_V_MSG(true, "Command line option --build-solutions was passed, but no project is being edited. Aborting.");
ERR_FAIL_V_MSG(true,
"Command line option --build-solutions was passed, but no project is being edited. Aborting.");
}
if (!EditorNode::get_singleton()->call_build()) {
ERR_FAIL_V_MSG(true, "Command line option --build-solutions was passed, but the build callback failed. Aborting.");
ERR_FAIL_V_MSG(true,
"Command line option --build-solutions was passed, but the build callback failed. Aborting.");
}
}
#endif

View File

@ -45,7 +45,7 @@ class Main {
public:
static bool is_project_manager();
static int test_entrypoint(int argc, char *argv[], bool &tests_need_run);
static Error setup(const char *execpath, int argc, char *argv[], bool p_second_phase = true);
static Error setup2(Thread::ID p_main_tid_override = 0);
static bool start();
@ -58,4 +58,19 @@ public:
static void cleanup();
};
// Test main override is for the testing behaviour
#define TEST_MAIN_OVERRIDE \
bool run_test = false; \
int return_code = Main::test_entrypoint(argc, argv, run_test); \
if (run_test) { \
return return_code; \
}
#define TEST_MAIN_PARAM_OVERRIDE(argc, argv) \
bool run_test = false; \
int return_code = Main::test_entrypoint(argc, argv, run_test); \
if (run_test) { \
return return_code; \
}
#endif // MAIN_H

View File

@ -47,10 +47,14 @@
#include "test_render.h"
#include "test_shader_lang.h"
#include "test_string.h"
#include "test_validate_testing.h"
#include "thirdparty/doctest/doctest.h"
const char **tests_get_names() {
static const char *test_names[] = {
"string",
"*",
"all",
"math",
"basis",
"physics_2d",
@ -72,75 +76,38 @@ const char **tests_get_names() {
return test_names;
}
MainLoop *test_main(String p_test, const List<String> &p_args) {
if (p_test == "string") {
return TestString::test();
int test_main(int argc, char *argv[]) {
// doctest runner for when legacy unit tests are no found
doctest::Context test_context;
List<String> valid_arguments;
// clean arguments of --test from the args
int argument_count = 0;
for (int x = 0; x < argc; x++) {
if (strncmp(argv[x], "--test", 6) != 0) {
valid_arguments.push_back(String(argv[x]));
argument_count++;
}
}
if (p_test == "math") {
return TestMath::test();
// convert godot command line arguments back to standard arguments.
char **args = new char *[valid_arguments.size()];
for (int x = 0; x < valid_arguments.size(); x++) {
// operation to convert godot string to non wchar string
const char *str = valid_arguments[x].utf8().ptr();
// allocate the string copy
args[x] = new char[strlen(str) + 1];
// copy this into memory
std::memcpy(args[x], str, strlen(str) + 1);
}
if (p_test == "basis") {
return TestBasis::test();
}
test_context.applyCommandLine(valid_arguments.size(), args);
if (p_test == "physics_2d") {
return TestPhysics2D::test();
}
if (p_test == "physics_3d") {
return TestPhysics3D::test();
}
if (p_test == "render") {
return TestRender::test();
}
if (p_test == "oa_hash_map") {
return TestOAHashMap::test();
}
if (p_test == "class_db") {
return TestClassDB::test();
}
#ifndef _3D_DISABLED
if (p_test == "gui") {
return TestGUI::test();
}
#endif
if (p_test == "shaderlang") {
return TestShaderLang::test();
}
if (p_test == "gd_tokenizer") {
return TestGDScript::test(TestGDScript::TEST_TOKENIZER);
}
if (p_test == "gd_parser") {
return TestGDScript::test(TestGDScript::TEST_PARSER);
}
if (p_test == "gd_compiler") {
return TestGDScript::test(TestGDScript::TEST_COMPILER);
}
if (p_test == "gd_bytecode") {
return TestGDScript::test(TestGDScript::TEST_BYTECODE);
}
if (p_test == "ordered_hash_map") {
return TestOrderedHashMap::test();
}
if (p_test == "astar") {
return TestAStar::test();
}
print_line("Unknown test: " + p_test);
return nullptr;
test_context.setOption("order-by", "name");
test_context.setOption("abort-after", 5);
test_context.setOption("no-breaks", true);
delete[] args;
return test_context.run();
}
#else
@ -153,8 +120,8 @@ const char **tests_get_names() {
return test_names;
}
MainLoop *test_main(String p_test, const List<String> &p_args) {
return nullptr;
int test_main(int argc, char *argv[]) {
return 0;
}
#endif

View File

@ -36,6 +36,6 @@
#include "core/ustring.h"
const char **tests_get_names();
MainLoop *test_main(String p_test, const List<String> &p_args);
int test_main(int argc, char *argv[]);
#endif // TEST_MAIN_H

File diff suppressed because it is too large Load Diff

View File

@ -31,12 +31,768 @@
#ifndef TEST_STRING_H
#define TEST_STRING_H
#include <inttypes.h>
#include <stdio.h>
#include <wchar.h>
#include "core/io/ip_address.h"
#include "core/os/main_loop.h"
#include "core/os/os.h"
#include "core/ustring.h"
#ifdef MODULE_REGEX_ENABLED
#include "modules/regex/regex.h"
#endif
#include "thirdparty/doctest/doctest.h"
namespace TestString {
MainLoop *test();
TEST_CASE("[String] Assign from cstr") {
String s = "Hello";
CHECK(wcscmp(s.c_str(), L"Hello") == 0);
}
TEST_CASE("[String] Assign from string (operator=)") {
String s = "Dolly";
const String &t = s;
CHECK(wcscmp(t.c_str(), L"Dolly") == 0);
}
TEST_CASE("[String] Assign from c-string (copycon)") {
String s("Sheep");
const String &t(s);
CHECK(wcscmp(t.c_str(), L"Sheep") == 0);
}
TEST_CASE("[String] Assign from c-widechar (operator=)") {
String s(L"Give me");
CHECK(wcscmp(s.c_str(), L"Give me") == 0);
}
TEST_CASE("[String] Assign from c-widechar (copycon)") {
String s(L"Wool");
CHECK(wcscmp(s.c_str(), L"Wool") == 0);
}
TEST_CASE("[String] Comparisons (equal)") {
String s = "Test Compare";
CHECK(s == "Test Compare");
CHECK(s == L"Test Compare");
CHECK(s == String("Test Compare"));
}
TEST_CASE("[String] Comparisons (not equal)") {
String s = "Test Compare";
CHECK(s != "Peanut");
CHECK(s != L"Coconut");
CHECK(s != String("Butter"));
}
TEST_CASE("[String] Comparisons (operator <)") {
String s = "Bees";
CHECK(s < "Elephant");
CHECK(!(s < L"Amber"));
CHECK(!(s < String("Beatrix")));
}
TEST_CASE("[String] Concatenation") {
String s;
s += "Have";
s += ' ';
s += 'a';
s += String(" ");
s = s + L"Nice";
s = s + " ";
s = s + String("Day");
CHECK(s == "Have a Nice Day");
}
TEST_CASE("[String] Testing size and length of string") {
// todo: expand this test to do more tests on size() as it is complicated under the hood.
CHECK(String("Mellon").size() == 7);
CHECK(String("Mellon1").size() == 8);
// length works fine and is easier to test
CHECK(String("Mellon").length() == 6);
CHECK(String("Mellon1").length() == 7);
CHECK(String("Mellon2").length() == 7);
CHECK(String("Mellon3").length() == 7);
}
TEST_CASE("[String] Testing for empty string") {
CHECK(!String("Mellon").empty());
// do this more than once, to check for string corruption
CHECK(String("").empty());
CHECK(String("").empty());
CHECK(String("").empty());
}
TEST_CASE("[String] Operator []") {
String a = "Kugar Sane";
a[0] = 'S';
a[6] = 'C';
CHECK(a == "Sugar Cane");
CHECK(a[1] == 'u');
}
TEST_CASE("[String] Case function test") {
String a = "MoMoNgA";
CHECK(a.to_upper() == "MOMONGA");
CHECK(a.nocasecmp_to("momonga") == 0);
}
TEST_CASE("[String] UTF8") {
/* how can i embed UTF in here? */
static const CharType ustr[] = { 0x304A, 0x360F, 0x3088, 0x3046, 0 };
//static const wchar_t ustr[] = { 'P', 0xCE, 'p',0xD3, 0 };
String s = ustr;
s.parse_utf8(s.utf8().get_data());
CHECK(s == ustr);
}
TEST_CASE("[String] ASCII") {
String s = L"Primero Leche";
String t = s.ascii().get_data();
CHECK(s == t);
}
TEST_CASE("[String] Substr") {
String s = "Killer Baby";
CHECK(s.substr(3, 4) == "ler ");
}
TEST_CASE("[string] Find") {
String s = "Pretty Woman";
s.find("Revenge of the Monster Truck");
CHECK(s.find("tty") == 3);
CHECK(s.find("Revenge of the Monster Truck") == -1);
}
TEST_CASE("[String] find no case") {
String s = "Pretty Whale";
CHECK(s.findn("WHA") == 7);
CHECK(s.findn("Revenge of the Monster SawFish") == -1);
}
TEST_CASE("[String] Find and replace") {
String s = "Happy Birthday, Anna!";
s = s.replace("Birthday", "Halloween");
CHECK(s == "Happy Halloween, Anna!");
}
TEST_CASE("[String] Insertion") {
String s = "Who is Frederic?";
s = s.insert(s.find("?"), " Chopin");
CHECK(s == "Who is Frederic Chopin?");
}
TEST_CASE("[String] Number to string") {
CHECK(String::num(3.141593) == "3.141593");
}
TEST_CASE("[String] String to integer") {
static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" };
static const int num[4] = { 1237461283, -22, 0, -1123412 };
for (int i = 0; i < 4; i++) {
CHECK(String(nums[i]).to_int() == num[i]);
}
}
TEST_CASE("[String] String to float") {
static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" };
static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 };
for (int i = 0; i < 4; i++) {
CHECK(!(ABS(String(nums[i]).to_double() - num[i]) > 0.00001));
}
}
TEST_CASE("[String] Slicing") {
String s = "Mars,Jupiter,Saturn,Uranus";
const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
for (int i = 0; i < s.get_slice_count(","); i++) {
CHECK(s.get_slice(",", i) == slices[i]);
}
}
TEST_CASE("[String] Erasing") {
String s = "Josephine is such a cute girl!";
s.erase(s.find("cute "), String("cute ").length());
CHECK(s == "Josephine is such a girl!");
}
#ifdef MODULE_REGEX_ENABLED
TEST_CASE("[String] Regex substitution") {
String s = "Double all the vowels.";
RegEx re("(?<vowel>[aeiou])");
s = re.sub(s, "$0$vowel", true);
CHECK(s == "Doouublee aall thee vooweels.");
}
#endif
struct test_27_data {
char const *data;
char const *begin;
bool expected;
};
TEST_CASE("[String] Begins with") {
test_27_data tc[] = {
{ "res://foobar", "res://", true },
{ "res", "res://", false },
{ "abc", "abc", true }
};
size_t count = sizeof(tc) / sizeof(tc[0]);
bool state = true;
for (size_t i = 0; state && i < count; ++i) {
String s = tc[i].data;
state = s.begins_with(tc[i].begin) == tc[i].expected;
if (state) {
String sb = tc[i].begin;
state = s.begins_with(sb) == tc[i].expected;
}
CHECK(state);
if (!state) {
break;
}
};
CHECK(state);
}
TEST_CASE("[String] sprintf") {
String format, output;
Array args;
bool error;
// %%
format = "fish %% frog";
args.clear();
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish % frog"));
//////// INTS
// Int
format = "fish %d frog";
args.clear();
args.push_back(5);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 5 frog"));
// Int left padded with zeroes.
format = "fish %05d frog";
args.clear();
args.push_back(5);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 00005 frog"));
// Int left padded with spaces.
format = "fish %5d frog";
args.clear();
args.push_back(5);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 5 frog"));
// Int right padded with spaces.
format = "fish %-5d frog";
args.clear();
args.push_back(5);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 5 frog"));
// Int with sign (positive).
format = "fish %+d frog";
args.clear();
args.push_back(5);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish +5 frog"));
// Negative int.
format = "fish %d frog";
args.clear();
args.push_back(-5);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish -5 frog"));
// Hex (lower)
format = "fish %x frog";
args.clear();
args.push_back(45);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 2d frog"));
// Hex (upper)
format = "fish %X frog";
args.clear();
args.push_back(45);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 2D frog"));
// Octal
format = "fish %o frog";
args.clear();
args.push_back(99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 143 frog"));
////// REALS
// Real
format = "fish %f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 99.990000 frog"));
// Real left-padded
format = "fish %11f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 99.990000 frog"));
// Real right-padded
format = "fish %-11f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 99.990000 frog"));
// Real given int.
format = "fish %f frog";
args.clear();
args.push_back(99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 99.000000 frog"));
// Real with sign (positive).
format = "fish %+f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish +99.990000 frog"));
// Real with 1 decimals.
format = "fish %.1f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 100.0 frog"));
// Real with 12 decimals.
format = "fish %.12f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 99.990000000000 frog"));
// Real with no decimals.
format = "fish %.f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 100 frog"));
/////// Strings.
// String
format = "fish %s frog";
args.clear();
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish cheese frog"));
// String left-padded
format = "fish %10s frog";
args.clear();
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish cheese frog"));
// String right-padded
format = "fish %-10s frog";
args.clear();
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish cheese frog"));
///// Characters
// Character as string.
format = "fish %c frog";
args.clear();
args.push_back("A");
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish A frog"));
// Character as int.
format = "fish %c frog";
args.clear();
args.push_back(65);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish A frog"));
///// Dynamic width
// String dynamic width
format = "fish %*s frog";
args.clear();
args.push_back(10);
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error == false);
REQUIRE(output == String("fish cheese frog"));
// Int dynamic width
format = "fish %*d frog";
args.clear();
args.push_back(10);
args.push_back(99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
REQUIRE(output == String("fish 99 frog"));
// Float dynamic width
format = "fish %*.*f frog";
args.clear();
args.push_back(10);
args.push_back(3);
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error == false);
CHECK(output == String("fish 99.990 frog"));
///// Errors
// More formats than arguments.
format = "fish %s %s frog";
args.clear();
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "not enough arguments for format string");
// More arguments than formats.
format = "fish %s frog";
args.clear();
args.push_back("hello");
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "not all arguments converted during string formatting");
// Incomplete format.
format = "fish %10";
args.clear();
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "incomplete format");
// Bad character in format string
format = "fish %&f frog";
args.clear();
args.push_back("cheese");
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "unsupported format character");
// Too many decimals.
format = "fish %2.2.2f frog";
args.clear();
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "too many decimal points in format");
// * not a number
format = "fish %*f frog";
args.clear();
args.push_back("cheese");
args.push_back(99.99);
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "* wants number");
// Character too long.
format = "fish %c frog";
args.clear();
args.push_back("sc");
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "%c requires number or single-character string");
// Character bad type.
format = "fish %c frog";
args.clear();
args.push_back(Array());
output = format.sprintf(args, &error);
REQUIRE(error);
CHECK(output == "%c requires number or single-character string");
}
TEST_CASE("[String] IPVX address to string") {
IP_Address ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, true);
IP_Address ip2("fe80::52e5:49ff:fe93:1baf");
IP_Address ip3("::ffff:192.168.0.1");
String ip4 = "192.168.0.1";
CHECK(ip4.is_valid_ip_address());
ip4 = "192.368.0.1";
CHECK(!ip4.is_valid_ip_address());
String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
CHECK(ip6.is_valid_ip_address());
ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334";
CHECK(!ip6.is_valid_ip_address());
ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334";
CHECK(!ip6.is_valid_ip_address());
ip6 = "2001:0db8::0:8a2e:370:7334";
CHECK(ip6.is_valid_ip_address());
ip6 = "::ffff:192.168.0.1";
CHECK(ip6.is_valid_ip_address());
}
TEST_CASE("[String] Capitalize against many strings") {
String input = "bytes2var";
String output = "Bytes 2 Var";
CHECK(input.capitalize() == output);
input = "linear2db";
output = "Linear 2 Db";
CHECK(input.capitalize() == output);
input = "vector3";
output = "Vector 3";
CHECK(input.capitalize() == output);
input = "sha256";
output = "Sha 256";
CHECK(input.capitalize() == output);
input = "2db";
output = "2 Db";
CHECK(input.capitalize() == output);
input = "PascalCase";
output = "Pascal Case";
CHECK(input.capitalize() == output);
input = "PascalPascalCase";
output = "Pascal Pascal Case";
CHECK(input.capitalize() == output);
input = "snake_case";
output = "Snake Case";
CHECK(input.capitalize() == output);
input = "snake_snake_case";
output = "Snake Snake Case";
CHECK(input.capitalize() == output);
input = "sha256sum";
output = "Sha 256 Sum";
CHECK(input.capitalize() == output);
input = "cat2dog";
output = "Cat 2 Dog";
CHECK(input.capitalize() == output);
input = "function(name)";
output = "Function(name)";
CHECK(input.capitalize() == output);
input = "snake_case_function(snake_case_arg)";
output = "Snake Case Function(snake Case Arg)";
CHECK(input.capitalize() == output);
input = "snake_case_function( snake_case_arg )";
output = "Snake Case Function( Snake Case Arg )";
CHECK(input.capitalize() == output);
}
TEST_CASE("[String] Checking string is empty when it should be") {
bool state = true;
bool success;
String a = "";
success = a[0] == 0;
if (!success) {
state = false;
}
String b = "Godot";
success = b[b.size()] == 0;
if (!success) {
state = false;
}
const String c = "";
success = c[0] == 0;
if (!success) {
state = false;
}
const String d = "Godot";
success = d[d.size()] == 0;
if (!success) {
state = false;
}
CHECK(state);
}
TEST_CASE("[String] lstrip and rstrip") {
#define STRIP_TEST(x) \
{ \
bool success = x; \
state = state && success; \
}
bool state = true;
// strip none
STRIP_TEST(String("abc").lstrip("") == "abc");
STRIP_TEST(String("abc").rstrip("") == "abc");
// strip one
STRIP_TEST(String("abc").lstrip("a") == "bc");
STRIP_TEST(String("abc").rstrip("c") == "ab");
// strip lots
STRIP_TEST(String("bababbababccc").lstrip("ab") == "ccc");
STRIP_TEST(String("aaabcbcbcbbcbbc").rstrip("cb") == "aaa");
// strip empty string
STRIP_TEST(String("").lstrip("") == "");
STRIP_TEST(String("").rstrip("") == "");
// strip to empty string
STRIP_TEST(String("abcabcabc").lstrip("bca") == "");
STRIP_TEST(String("abcabcabc").rstrip("bca") == "");
// don't strip wrong end
STRIP_TEST(String("abc").lstrip("c") == "abc");
STRIP_TEST(String("abca").lstrip("a") == "bca");
STRIP_TEST(String("abc").rstrip("a") == "abc");
STRIP_TEST(String("abca").rstrip("a") == "abc");
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
// and the same second as "ÿ" (\u00ff)
STRIP_TEST(String::utf8("¿").lstrip(String::utf8("µÿ")) == String::utf8("¿"));
STRIP_TEST(String::utf8("¿").rstrip(String::utf8("µÿ")) == String::utf8("¿"));
STRIP_TEST(String::utf8("µ¿ÿ").lstrip(String::utf8("µÿ")) == String::utf8("¿ÿ"));
STRIP_TEST(String::utf8("µ¿ÿ").rstrip(String::utf8("µÿ")) == String::utf8("µ¿"));
// the above tests repeated with additional superfluous strip chars
// strip none
STRIP_TEST(String("abc").lstrip("qwjkl") == "abc");
STRIP_TEST(String("abc").rstrip("qwjkl") == "abc");
// strip one
STRIP_TEST(String("abc").lstrip("qwajkl") == "bc");
STRIP_TEST(String("abc").rstrip("qwcjkl") == "ab");
// strip lots
STRIP_TEST(String("bababbababccc").lstrip("qwabjkl") == "ccc");
STRIP_TEST(String("aaabcbcbcbbcbbc").rstrip("qwcbjkl") == "aaa");
// strip empty string
STRIP_TEST(String("").lstrip("qwjkl") == "");
STRIP_TEST(String("").rstrip("qwjkl") == "");
// strip to empty string
STRIP_TEST(String("abcabcabc").lstrip("qwbcajkl") == "");
STRIP_TEST(String("abcabcabc").rstrip("qwbcajkl") == "");
// don't strip wrong end
STRIP_TEST(String("abc").lstrip("qwcjkl") == "abc");
STRIP_TEST(String("abca").lstrip("qwajkl") == "bca");
STRIP_TEST(String("abc").rstrip("qwajkl") == "abc");
STRIP_TEST(String("abca").rstrip("qwajkl") == "abc");
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
// and the same second as "ÿ" (\u00ff)
STRIP_TEST(String::utf8("¿").lstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿"));
STRIP_TEST(String::utf8("¿").rstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿"));
STRIP_TEST(String::utf8("µ¿ÿ").lstrip(String::utf8("qwaµÿjkl")) == String::utf8("¿ÿ"));
STRIP_TEST(String::utf8("µ¿ÿ").rstrip(String::utf8("qwaµÿjkl")) == String::utf8("µ¿"));
CHECK(state);
#undef STRIP_TEST
}
TEST_CASE("[String] ensuring empty string into parse_utf8 passes empty string") {
String empty;
CHECK(empty.parse_utf8(NULL, -1));
}
TEST_CASE("[String] Cyrillic to_lower()") {
String upper = String::utf8("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ");
String lower = String::utf8("абвгдеёжзийклмнопрстуфхцчшщъыьэюя");
String test = upper.to_lower();
bool state = test == lower;
CHECK(state);
}
TEST_CASE("[String] Count and countn functionality") {
#define COUNT_TEST(x) \
{ \
bool success = x; \
state = state && success; \
}
bool state = true;
COUNT_TEST(String("").count("Test") == 0);
COUNT_TEST(String("Test").count("") == 0);
COUNT_TEST(String("Test").count("test") == 0);
COUNT_TEST(String("Test").count("TEST") == 0);
COUNT_TEST(String("TEST").count("TEST") == 1);
COUNT_TEST(String("Test").count("Test") == 1);
COUNT_TEST(String("aTest").count("Test") == 1);
COUNT_TEST(String("Testa").count("Test") == 1);
COUNT_TEST(String("TestTestTest").count("Test") == 3);
COUNT_TEST(String("TestTestTest").count("TestTest") == 1);
COUNT_TEST(String("TestGodotTestGodotTestGodot").count("Test") == 3);
COUNT_TEST(String("TestTestTestTest").count("Test", 4, 8) == 1);
COUNT_TEST(String("TestTestTestTest").count("Test", 4, 12) == 2);
COUNT_TEST(String("TestTestTestTest").count("Test", 4, 16) == 3);
COUNT_TEST(String("TestTestTestTest").count("Test", 4) == 3);
COUNT_TEST(String("Test").countn("test") == 1);
COUNT_TEST(String("Test").countn("TEST") == 1);
COUNT_TEST(String("testTest-Testatest").countn("tEst") == 4);
COUNT_TEST(String("testTest-TeStatest").countn("tEsT", 4, 16) == 2);
CHECK(state);
}
} // namespace TestString
#endif // TEST_STRING_H

View File

@ -0,0 +1,42 @@
/*************************************************************************/
/* test_validate_testing.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 TEST_VALIDATE_TESTING_H
#define TEST_VALIDATE_TESTING_H
#include "core/os/os.h"
#include "thirdparty/doctest/doctest.h"
TEST_CASE("Validate Test will always pass") {
CHECK(true);
}
#endif // TEST_VALIDATE_TESTING_H

View File

@ -67,6 +67,9 @@ int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
printf("cwd %s\n", cwd);
os = new OSIPhone(width, height, data_dir);
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
char *fargv[64];
for (int i = 0; i < argc; i++) {
fargv[i] = argv[i];

View File

@ -131,6 +131,10 @@ int main(int argc, char *argv[]) {
/* clang-format on */
os = new OS_JavaScript();
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
Main::setup(argv[0], argc - 1, &argv[1], false);
emscripten_set_main_loop(main_loop_callback, -1, false);
emscripten_pause_main_loop(); // Will need to wait for FS sync.

View File

@ -41,6 +41,9 @@ int main(int argc, char *argv[]) {
setlocale(LC_CTYPE, "");
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
char *cwd = (char *)malloc(PATH_MAX);
ERR_FAIL_COND_V(!cwd, ERR_OUT_OF_MEMORY);
char *ret = getcwd(cwd, PATH_MAX);

View File

@ -37,7 +37,7 @@
int main(int argc, char **argv) {
#if defined(VULKAN_ENABLED)
//MoltenVK - enable full component swizzling support
// MoltenVK - enable full component swizzling support
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif
@ -60,6 +60,9 @@ int main(int argc, char **argv) {
OS_OSX os;
Error err;
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
if (os.open_with_filename != "") {
char *argv_c = (char *)malloc(os.open_with_filename.utf8().size());
memcpy(argv_c, os.open_with_filename.utf8().get_data(), os.open_with_filename.utf8().size());

View File

@ -34,6 +34,9 @@
int main(int argc, char *argv[]) {
OS_Server os;
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
Error err = Main::setup(argv[0], argc - 1, &argv[1]);
if (err != OK)
return 255;

View File

@ -146,6 +146,8 @@ int widechar_main(int argc, wchar_t **argv) {
argv_utf8[i] = wc_to_utf8(argv[i]);
}
TEST_MAIN_PARAM_OVERRIDE(argc, argv_utf8)
Error err = Main::setup(argv_utf8[0], argc - 1, &argv_utf8[1]);
if (err != OK) {
@ -186,10 +188,12 @@ int _main() {
return result;
}
int main(int _argc, char **_argv) {
int main(int argc, char **argv) {
// override the arguments for the test handler / if symbol is provided
// TEST_MAIN_OVERRIDE
// _argc and _argv are ignored
// we are going to use the WideChar version of them instead
#ifdef CRASH_HANDLER_EXCEPTION
__try {
return _main();

View File

@ -74,6 +74,14 @@ Files extracted from upstream source:
- all .cpp, .h, and .txt files in ConvectionKernels/
## doctest
- Upstream: https://github.com/onqtam/doctest
- Version: 1c8da00 (2.4.0)
- License: MIT
Extracted from .zip provided. Extracted license and header only.
## enet
- Upstream: http://enet.bespin.org

21
thirdparty/doctest/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2019 Viktor Kirilov
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.

6205
thirdparty/doctest/doctest.h vendored Normal file

File diff suppressed because it is too large Load Diff