diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/SCsub | 3 | ||||
-rw-r--r-- | main/main.cpp | 392 | ||||
-rw-r--r-- | main/main.h | 17 | ||||
-rw-r--r-- | main/tests/test_gdscript.cpp | 982 | ||||
-rw-r--r-- | main/tests/test_main.cpp | 111 | ||||
-rw-r--r-- | main/tests/test_main.h | 2 | ||||
-rw-r--r-- | main/tests/test_string.cpp | 1207 | ||||
-rw-r--r-- | main/tests/test_string.h | 758 | ||||
-rw-r--r-- | main/tests/test_validate_testing.h | 42 |
9 files changed, 1206 insertions, 2308 deletions
diff --git a/main/SCsub b/main/SCsub index 7a301b82bc..793e5ed827 100644 --- a/main/SCsub +++ b/main/SCsub @@ -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)) @@ -21,7 +20,7 @@ env.CommandNoCache( env.Depends("#main/app_icon.gen.h", "#main/app_icon.png") env.CommandNoCache("#main/app_icon.gen.h", "#main/app_icon.png", run_in_subprocess(main_builders.make_app_icon)) -if env["tools"]: +if env["tests"]: SConscript("tests/SCsub") lib = env.add_library("main", env.main_sources) diff --git a/main/main.cpp b/main/main.cpp index ef24555027..fad85ddf51 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -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" @@ -54,7 +55,6 @@ #include "main/performance.h" #include "main/splash.gen.h" #include "main/splash_editor.gen.h" -#include "main/tests/test_main.h" #include "modules/modules_enabled.gen.h" #include "modules/register_module_types.h" #include "platform/register_platform_apis.h" @@ -74,13 +74,19 @@ #include "servers/rendering/rendering_server_wrap_mt.h" #include "servers/xr_server.h" +#ifdef TESTS_ENABLED +#include "main/tests/test_main.h" +#endif + #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 +192,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 +202,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 +262,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 +321,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,36 +340,53 @@ 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 +#ifdef TESTS_ENABLED OS::get_singleton()->print(" --test <test> Run a unit test ["); const char **test_names = tests_get_names(); const char *comma = ""; @@ -364,6 +397,26 @@ void Main::print_help(const char *p_binary) { } OS::get_singleton()->print("].\n"); #endif + OS::get_singleton()->print("\n"); +#endif +} + +int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) { +#ifdef TESTS_ENABLED + for (int x = 0; x < argc; x++) { + if (strncmp(argv[x], "--test", 6) == 0) { + tests_need_run = true; + OS::get_singleton()->initialize(); + StringName::setup(); + int status = test_main(argc, argv); + StringName::cleanup(); + // TODO: fix OS::singleton cleanup + return status; + } + } +#endif + tests_need_run = false; + return 0; } /* Engine initialization @@ -418,7 +471,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 +577,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 +614,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 +663,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 +686,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 +695,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 +717,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 +814,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 +908,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 +950,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 +998,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 +1061,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 +1108,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 +1120,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 +1212,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 +1286,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 +1305,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 +1406,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 +1436,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 +1503,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 +1523,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 +1549,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 +1598,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 +1686,6 @@ bool Main::start() { String positional_arg; String game_path; String script; - String test; bool check_only = false; #ifdef TOOLS_ENABLED @@ -1555,10 +1696,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 +1734,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 +1765,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); @@ -1716,16 +1858,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); @@ -1744,7 +1877,9 @@ 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); @@ -1832,7 +1967,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 +2017,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 +2062,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 +2079,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 +2136,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 +2153,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 +2224,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 +2237,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 } @@ -2107,6 +2276,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 +2352,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 +2369,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 +2432,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 diff --git a/main/main.h b/main/main.h index 308128735c..20c0bebefa 100644 --- a/main/main.h +++ b/main/main.h @@ -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 diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 10586c6495..a50311972f 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -33,854 +33,93 @@ #include "core/os/file_access.h" #include "core/os/main_loop.h" #include "core/os/os.h" +#include "core/string_builder.h" #include "modules/modules_enabled.gen.h" #ifdef MODULE_GDSCRIPT_ENABLED -#include "modules/gdscript/gdscript.h" -#include "modules/gdscript/gdscript_compiler.h" #include "modules/gdscript/gdscript_parser.h" #include "modules/gdscript/gdscript_tokenizer.h" -namespace TestGDScript { - -static void _print_indent(int p_ident, const String &p_text) { - String txt; - for (int i = 0; i < p_ident; i++) { - txt += '\t'; - } - - print_line(txt + p_text); -} +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif -static String _parser_extends(const GDScriptParser::ClassNode *p_class) { - String txt = "extends "; - if (String(p_class->extends_file) != "") { - txt += "\"" + p_class->extends_file + "\""; - if (p_class->extends_class.size()) { - txt += "."; - } - } +namespace TestGDScript { - for (int i = 0; i < p_class->extends_class.size(); i++) { - if (i != 0) { - txt += "."; - } +static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) { + GDScriptTokenizer tokenizer; + tokenizer.set_source_code(p_code); - txt += p_class->extends_class[i]; + int tab_size = 4; +#ifdef TOOLS_ENABLED + if (EditorSettings::get_singleton()) { + tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size"); } +#endif // TOOLS_ENABLED + String tab = String(" ").repeat(tab_size); - return txt; -} + GDScriptTokenizer::Token current = tokenizer.scan(); + while (current.type != GDScriptTokenizer::Token::TK_EOF) { + StringBuilder token; + token += " --> "; // Padding for line number. -static String _parser_expr(const GDScriptParser::Node *p_expr) { - String txt; - switch (p_expr->type) { - case GDScriptParser::Node::TYPE_IDENTIFIER: { - const GDScriptParser::IdentifierNode *id_node = static_cast<const GDScriptParser::IdentifierNode *>(p_expr); - txt = id_node->name; - } break; - case GDScriptParser::Node::TYPE_CONSTANT: { - const GDScriptParser::ConstantNode *c_node = static_cast<const GDScriptParser::ConstantNode *>(p_expr); - if (c_node->value.get_type() == Variant::STRING) { - txt = "\"" + String(c_node->value) + "\""; - } else { - txt = c_node->value; - } - - } break; - case GDScriptParser::Node::TYPE_SELF: { - txt = "self"; - } break; - case GDScriptParser::Node::TYPE_ARRAY: { - const GDScriptParser::ArrayNode *arr_node = static_cast<const GDScriptParser::ArrayNode *>(p_expr); - txt += "["; - for (int i = 0; i < arr_node->elements.size(); i++) { - if (i > 0) { - txt += ", "; - } - txt += _parser_expr(arr_node->elements[i]); - } - txt += "]"; - } break; - case GDScriptParser::Node::TYPE_DICTIONARY: { - const GDScriptParser::DictionaryNode *dict_node = static_cast<const GDScriptParser::DictionaryNode *>(p_expr); - txt += "{"; - for (int i = 0; i < dict_node->elements.size(); i++) { - if (i > 0) { - txt += ", "; - } + for (int l = current.start_line; l <= current.end_line; l++) { + print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab)); + } - const GDScriptParser::DictionaryNode::Pair &p = dict_node->elements[i]; - txt += _parser_expr(p.key); - txt += ":"; - txt += _parser_expr(p.value); + { + // Print carets to point at the token. + StringBuilder pointer; + pointer += " "; // Padding for line number. + int rightmost_column = current.rightmost_column; + if (current.end_line > current.start_line) { + rightmost_column--; // Don't point to the newline as a column. } - txt += "}"; - } break; - case GDScriptParser::Node::TYPE_OPERATOR: { - const GDScriptParser::OperatorNode *c_node = static_cast<const GDScriptParser::OperatorNode *>(p_expr); - switch (c_node->op) { - case GDScriptParser::OperatorNode::OP_PARENT_CALL: - txt += "."; - [[fallthrough]]; - case GDScriptParser::OperatorNode::OP_CALL: { - ERR_FAIL_COND_V(c_node->arguments.size() < 1, ""); - String func_name; - const GDScriptParser::Node *nfunc = c_node->arguments[0]; - int arg_ofs = 0; - if (nfunc->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) { - const GDScriptParser::BuiltInFunctionNode *bif_node = static_cast<const GDScriptParser::BuiltInFunctionNode *>(nfunc); - func_name = GDScriptFunctions::get_func_name(bif_node->function); - arg_ofs = 1; - } else if (nfunc->type == GDScriptParser::Node::TYPE_TYPE) { - const GDScriptParser::TypeNode *t_node = static_cast<const GDScriptParser::TypeNode *>(nfunc); - func_name = Variant::get_type_name(t_node->vtype); - arg_ofs = 1; - } else { - ERR_FAIL_COND_V(c_node->arguments.size() < 2, ""); - nfunc = c_node->arguments[1]; - ERR_FAIL_COND_V(nfunc->type != GDScriptParser::Node::TYPE_IDENTIFIER, ""); - - if (c_node->arguments[0]->type != GDScriptParser::Node::TYPE_SELF) { - func_name = _parser_expr(c_node->arguments[0]) + "."; - } - - func_name += _parser_expr(nfunc); - arg_ofs = 2; - } - - txt += func_name + "("; - - for (int i = arg_ofs; i < c_node->arguments.size(); i++) { - const GDScriptParser::Node *arg = c_node->arguments[i]; - if (i > arg_ofs) { - txt += ", "; - } - txt += _parser_expr(arg); - } - - txt += ")"; - - } break; - case GDScriptParser::OperatorNode::OP_INDEX: { - ERR_FAIL_COND_V(c_node->arguments.size() != 2, ""); - - //index with [] - txt = _parser_expr(c_node->arguments[0]) + "[" + _parser_expr(c_node->arguments[1]) + "]"; - - } break; - case GDScriptParser::OperatorNode::OP_INDEX_NAMED: { - ERR_FAIL_COND_V(c_node->arguments.size() != 2, ""); - - txt = _parser_expr(c_node->arguments[0]) + "." + _parser_expr(c_node->arguments[1]); - - } break; - case GDScriptParser::OperatorNode::OP_NEG: { - txt = "-" + _parser_expr(c_node->arguments[0]); - } break; - case GDScriptParser::OperatorNode::OP_NOT: { - txt = "not " + _parser_expr(c_node->arguments[0]); - } break; - case GDScriptParser::OperatorNode::OP_BIT_INVERT: { - txt = "~" + _parser_expr(c_node->arguments[0]); - } break; - case GDScriptParser::OperatorNode::OP_IN: { - txt = _parser_expr(c_node->arguments[0]) + " in " + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_EQUAL: { - txt = _parser_expr(c_node->arguments[0]) + "==" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_NOT_EQUAL: { - txt = _parser_expr(c_node->arguments[0]) + "!=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_LESS: { - txt = _parser_expr(c_node->arguments[0]) + "<" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_LESS_EQUAL: { - txt = _parser_expr(c_node->arguments[0]) + "<=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_GREATER: { - txt = _parser_expr(c_node->arguments[0]) + ">" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_GREATER_EQUAL: { - txt = _parser_expr(c_node->arguments[0]) + ">=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_AND: { - txt = _parser_expr(c_node->arguments[0]) + " and " + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_OR: { - txt = _parser_expr(c_node->arguments[0]) + " or " + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ADD: { - txt = _parser_expr(c_node->arguments[0]) + "+" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_SUB: { - txt = _parser_expr(c_node->arguments[0]) + "-" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_MUL: { - txt = _parser_expr(c_node->arguments[0]) + "*" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_DIV: { - txt = _parser_expr(c_node->arguments[0]) + "/" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_MOD: { - txt = _parser_expr(c_node->arguments[0]) + "%" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: { - txt = _parser_expr(c_node->arguments[0]) + "<<" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: { - txt = _parser_expr(c_node->arguments[0]) + ">>" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN: { - txt = _parser_expr(c_node->arguments[0]) + "=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_ADD: { - txt = _parser_expr(c_node->arguments[0]) + "+=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_SUB: { - txt = _parser_expr(c_node->arguments[0]) + "-=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_MUL: { - txt = _parser_expr(c_node->arguments[0]) + "*=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_DIV: { - txt = _parser_expr(c_node->arguments[0]) + "/=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_MOD: { - txt = _parser_expr(c_node->arguments[0]) + "%=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT: { - txt = _parser_expr(c_node->arguments[0]) + "<<=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: { - txt = _parser_expr(c_node->arguments[0]) + ">>=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_AND: { - txt = _parser_expr(c_node->arguments[0]) + "&=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_OR: { - txt = _parser_expr(c_node->arguments[0]) + "|=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_XOR: { - txt = _parser_expr(c_node->arguments[0]) + "^=" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_BIT_AND: { - txt = _parser_expr(c_node->arguments[0]) + "&" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_BIT_OR: { - txt = _parser_expr(c_node->arguments[0]) + "|" + _parser_expr(c_node->arguments[1]); - } break; - case GDScriptParser::OperatorNode::OP_BIT_XOR: { - txt = _parser_expr(c_node->arguments[0]) + "^" + _parser_expr(c_node->arguments[1]); - } break; - default: { + for (int col = 1; col < rightmost_column; col++) { + if (col < current.leftmost_column) { + pointer += " "; + } else { + pointer += "^"; } } - - } break; - case GDScriptParser::Node::TYPE_CAST: { - const GDScriptParser::CastNode *cast_node = static_cast<const GDScriptParser::CastNode *>(p_expr); - txt = _parser_expr(cast_node->source_node) + " as " + cast_node->cast_type.to_string(); - - } break; - case GDScriptParser::Node::TYPE_NEWLINE: { - //skippie - } break; - default: { - ERR_FAIL_V_MSG("", "Parser bug at " + itos(p_expr->line) + ", invalid expression type: " + itos(p_expr->type)); + print_line(pointer.as_string()); } - } - return txt; -} + token += current.get_name(); -static void _parser_show_block(const GDScriptParser::BlockNode *p_block, int p_indent) { - for (int i = 0; i < p_block->statements.size(); i++) { - const GDScriptParser::Node *statement = p_block->statements[i]; - - switch (statement->type) { - case GDScriptParser::Node::TYPE_CONTROL_FLOW: { - const GDScriptParser::ControlFlowNode *cf_node = static_cast<const GDScriptParser::ControlFlowNode *>(statement); - switch (cf_node->cf_type) { - case GDScriptParser::ControlFlowNode::CF_IF: { - ERR_FAIL_COND(cf_node->arguments.size() != 1); - String txt; - txt += "if "; - txt += _parser_expr(cf_node->arguments[0]); - txt += ":"; - _print_indent(p_indent, txt); - ERR_FAIL_COND(!cf_node->body); - _parser_show_block(cf_node->body, p_indent + 1); - if (cf_node->body_else) { - _print_indent(p_indent, "else:"); - _parser_show_block(cf_node->body_else, p_indent + 1); - } - - } break; - case GDScriptParser::ControlFlowNode::CF_FOR: { - ERR_FAIL_COND(cf_node->arguments.size() != 2); - String txt; - txt += "for "; - txt += _parser_expr(cf_node->arguments[0]); - txt += " in "; - txt += _parser_expr(cf_node->arguments[1]); - txt += ":"; - _print_indent(p_indent, txt); - ERR_FAIL_COND(!cf_node->body); - _parser_show_block(cf_node->body, p_indent + 1); - - } break; - case GDScriptParser::ControlFlowNode::CF_WHILE: { - ERR_FAIL_COND(cf_node->arguments.size() != 1); - String txt; - txt += "while "; - txt += _parser_expr(cf_node->arguments[0]); - txt += ":"; - _print_indent(p_indent, txt); - ERR_FAIL_COND(!cf_node->body); - _parser_show_block(cf_node->body, p_indent + 1); - - } break; - case GDScriptParser::ControlFlowNode::CF_MATCH: { - // FIXME: Implement - } break; - case GDScriptParser::ControlFlowNode::CF_CONTINUE: { - _print_indent(p_indent, "continue"); - } break; - case GDScriptParser::ControlFlowNode::CF_BREAK: { - _print_indent(p_indent, "break"); - } break; - case GDScriptParser::ControlFlowNode::CF_RETURN: { - if (cf_node->arguments.size()) { - _print_indent(p_indent, "return " + _parser_expr(cf_node->arguments[0])); - } else { - _print_indent(p_indent, "return "); - } - } break; - } - - } break; - case GDScriptParser::Node::TYPE_LOCAL_VAR: { - const GDScriptParser::LocalVarNode *lv_node = static_cast<const GDScriptParser::LocalVarNode *>(statement); - _print_indent(p_indent, "var " + String(lv_node->name)); - } break; - default: { - //expression i guess - _print_indent(p_indent, _parser_expr(statement)); - } + if (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::LITERAL || current.type == GDScriptTokenizer::Token::IDENTIFIER || current.type == GDScriptTokenizer::Token::ANNOTATION) { + token += "("; + token += Variant::get_type_name(current.literal.get_type()); + token += ") "; + token += current.literal; } - } -} -static void _parser_show_function(const GDScriptParser::FunctionNode *p_func, int p_indent, GDScriptParser::BlockNode *p_initializer = nullptr) { - String txt; - if (p_func->_static) { - txt = "static "; - } - txt += "func "; - if (p_func->name == "") { // initializer - txt += "[built-in-initializer]"; - } else { - txt += String(p_func->name); - } - txt += "("; - - for (int i = 0; i < p_func->arguments.size(); i++) { - if (i != 0) { - txt += ", "; - } - txt += "var " + String(p_func->arguments[i]); - if (i >= (p_func->arguments.size() - p_func->default_values.size())) { - int defarg = i - (p_func->arguments.size() - p_func->default_values.size()); - txt += "="; - txt += _parser_expr(p_func->default_values[defarg]); - } - } - - txt += ")"; - - //todo constructor check! - - txt += ":"; - - _print_indent(p_indent, txt); - if (p_initializer) { - _parser_show_block(p_initializer, p_indent + 1); - } - _parser_show_block(p_func->body, p_indent + 1); -} - -static void _parser_show_class(const GDScriptParser::ClassNode *p_class, int p_indent, const Vector<String> &p_code) { - if (p_indent == 0 && (String(p_class->extends_file) != "" || p_class->extends_class.size())) { - _print_indent(p_indent, _parser_extends(p_class)); - print_line("\n"); - } - - for (int i = 0; i < p_class->subclasses.size(); i++) { - const GDScriptParser::ClassNode *subclass = p_class->subclasses[i]; - String line = "class " + subclass->name; - if (String(subclass->extends_file) != "" || subclass->extends_class.size()) { - line += " " + _parser_extends(subclass); - } - line += ":"; - _print_indent(p_indent, line); - _parser_show_class(subclass, p_indent + 1, p_code); - print_line("\n"); - } - - for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) { - const GDScriptParser::ClassNode::Constant &constant = E->get(); - _print_indent(p_indent, "const " + String(E->key()) + "=" + _parser_expr(constant.expression)); - } - - for (int i = 0; i < p_class->variables.size(); i++) { - const GDScriptParser::ClassNode::Member &m = p_class->variables[i]; - - _print_indent(p_indent, "var " + String(m.identifier)); - } + print_line(token.as_string()); - print_line("\n"); + print_line("-------------------------------------------------------"); - for (int i = 0; i < p_class->static_functions.size(); i++) { - _parser_show_function(p_class->static_functions[i], p_indent); - print_line("\n"); + current = tokenizer.scan(); } - for (int i = 0; i < p_class->functions.size(); i++) { - if (String(p_class->functions[i]->name) == "_init") { - _parser_show_function(p_class->functions[i], p_indent, p_class->initializer); - } else { - _parser_show_function(p_class->functions[i], p_indent); - } - print_line("\n"); - } - //_parser_show_function(p_class->initializer,p_indent); - print_line("\n"); -} - -static String _disassemble_addr(const Ref<GDScript> &p_script, const GDScriptFunction &func, int p_addr) { - int addr = p_addr & GDScriptFunction::ADDR_MASK; - - switch (p_addr >> GDScriptFunction::ADDR_BITS) { - case GDScriptFunction::ADDR_TYPE_SELF: { - return "self"; - } break; - case GDScriptFunction::ADDR_TYPE_CLASS: { - return "class"; - } break; - case GDScriptFunction::ADDR_TYPE_MEMBER: { - return "member(" + p_script->debug_get_member_by_index(addr) + ")"; - } break; - case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: { - return "class_const(" + func.get_global_name(addr) + ")"; - } break; - case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: { - Variant v = func.get_constant(addr); - String txt; - if (v.get_type() == Variant::STRING || v.get_type() == Variant::NODE_PATH) { - txt = "\"" + String(v) + "\""; - } else { - txt = v; - } - return "const(" + txt + ")"; - } break; - case GDScriptFunction::ADDR_TYPE_STACK: { - return "stack(" + itos(addr) + ")"; - } break; - case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: { - return "var_stack(" + itos(addr) + ")"; - } break; - case GDScriptFunction::ADDR_TYPE_GLOBAL: { - return "global(" + func.get_global_name(addr) + ")"; - } break; - case GDScriptFunction::ADDR_TYPE_NIL: { - return "nil"; - } break; - } - - return "<err>"; + print_line(current.get_name()); // Should be EOF } -static void _disassemble_class(const Ref<GDScript> &p_class, const Vector<String> &p_code) { - const Map<StringName, GDScriptFunction *> &mf = p_class->debug_get_member_functions(); +static void test_parser(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) { + GDScriptParser parser; + Error err = parser.parse(p_code, p_script_path, false); - for (const Map<StringName, GDScriptFunction *>::Element *E = mf.front(); E; E = E->next()) { - const GDScriptFunction &func = *E->get(); - const int *code = func.get_code(); - int codelen = func.get_code_size(); - String defargs; - if (func.get_default_argument_count()) { - defargs = "defarg at: "; - for (int i = 0; i < func.get_default_argument_count(); i++) { - if (i > 0) { - defargs += ","; - } - defargs += itos(func.get_default_argument_addr(i)); - } - defargs += " "; + if (err != OK) { + const List<GDScriptParser::ParserError> &errors = parser.get_errors(); + for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) { + const GDScriptParser::ParserError &error = E->get(); + print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message)); } - print_line("== function " + String(func.get_name()) + "() :: stack size: " + itos(func.get_max_stack_size()) + " " + defargs + "=="); - -#define DADDR(m_ip) (_disassemble_addr(p_class, func, code[ip + m_ip])) - - for (int ip = 0; ip < codelen;) { - int incr = 0; - String txt = itos(ip) + " "; - - switch (code[ip]) { - case GDScriptFunction::OPCODE_OPERATOR: { - int op = code[ip + 1]; - txt += " op "; - - String opname = Variant::get_operator_name(Variant::Operator(op)); - - txt += DADDR(4); - txt += " = "; - txt += DADDR(2); - txt += " " + opname + " "; - txt += DADDR(3); - incr += 5; - - } break; - case GDScriptFunction::OPCODE_SET: { - txt += "set "; - txt += DADDR(1); - txt += "["; - txt += DADDR(2); - txt += "]="; - txt += DADDR(3); - incr += 4; - - } break; - case GDScriptFunction::OPCODE_GET: { - txt += " get "; - txt += DADDR(3); - txt += "="; - txt += DADDR(1); - txt += "["; - txt += DADDR(2); - txt += "]"; - incr += 4; - - } break; - case GDScriptFunction::OPCODE_SET_NAMED: { - txt += " set_named "; - txt += DADDR(1); - txt += "[\""; - txt += func.get_global_name(code[ip + 2]); - txt += "\"]="; - txt += DADDR(3); - incr += 4; - - } break; - case GDScriptFunction::OPCODE_GET_NAMED: { - txt += " get_named "; - txt += DADDR(3); - txt += "="; - txt += DADDR(1); - txt += "[\""; - txt += func.get_global_name(code[ip + 2]); - txt += "\"]"; - incr += 4; - - } break; - case GDScriptFunction::OPCODE_SET_MEMBER: { - txt += " set_member "; - txt += "[\""; - txt += func.get_global_name(code[ip + 1]); - txt += "\"]="; - txt += DADDR(2); - incr += 3; - - } break; - case GDScriptFunction::OPCODE_GET_MEMBER: { - txt += " get_member "; - txt += DADDR(2); - txt += "="; - txt += "[\""; - txt += func.get_global_name(code[ip + 1]); - txt += "\"]"; - incr += 3; - - } break; - case GDScriptFunction::OPCODE_ASSIGN: { - txt += " assign "; - txt += DADDR(1); - txt += "="; - txt += DADDR(2); - incr += 3; - - } break; - case GDScriptFunction::OPCODE_ASSIGN_TRUE: { - txt += " assign "; - txt += DADDR(1); - txt += "= true"; - incr += 2; - - } break; - case GDScriptFunction::OPCODE_ASSIGN_FALSE: { - txt += " assign "; - txt += DADDR(1); - txt += "= false"; - incr += 2; - - } break; - case GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN: { - txt += " assign typed builtin ("; - txt += Variant::get_type_name((Variant::Type)code[ip + 1]); - txt += ") "; - txt += DADDR(2); - txt += " = "; - txt += DADDR(3); - incr += 4; - - } break; - case GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE: { - Variant className = func.get_constant(code[ip + 1]); - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(className.operator Object *()); - - txt += " assign typed native ("; - txt += nc->get_name().operator String(); - txt += ") "; - txt += DADDR(2); - txt += " = "; - txt += DADDR(3); - incr += 4; - - } break; - case GDScriptFunction::OPCODE_CAST_TO_SCRIPT: { - txt += " cast "; - txt += DADDR(3); - txt += "="; - txt += DADDR(1); - txt += " as "; - txt += DADDR(2); - incr += 4; - - } break; - case GDScriptFunction::OPCODE_CONSTRUCT: { - Variant::Type t = Variant::Type(code[ip + 1]); - int argc = code[ip + 2]; - - txt += " construct "; - txt += DADDR(3 + argc); - txt += " = "; - - txt += Variant::get_type_name(t) + "("; - for (int i = 0; i < argc; i++) { - if (i > 0) { - txt += ", "; - } - txt += DADDR(i + 3); - } - txt += ")"; - - incr = 4 + argc; - - } break; - case GDScriptFunction::OPCODE_CONSTRUCT_ARRAY: { - int argc = code[ip + 1]; - txt += " make_array "; - txt += DADDR(2 + argc); - txt += " = [ "; - - for (int i = 0; i < argc; i++) { - if (i > 0) { - txt += ", "; - } - txt += DADDR(2 + i); - } - - txt += "]"; - - incr += 3 + argc; - - } break; - case GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY: { - int argc = code[ip + 1]; - txt += " make_dict "; - txt += DADDR(2 + argc * 2); - txt += " = { "; - - for (int i = 0; i < argc; i++) { - if (i > 0) { - txt += ", "; - } - txt += DADDR(2 + i * 2 + 0); - txt += ":"; - txt += DADDR(2 + i * 2 + 1); - } - - txt += "}"; - - incr += 3 + argc * 2; - - } break; - - case GDScriptFunction::OPCODE_CALL: - case GDScriptFunction::OPCODE_CALL_RETURN: { - bool ret = code[ip] == GDScriptFunction::OPCODE_CALL_RETURN; - - if (ret) { - txt += " call-ret "; - } else { - txt += " call "; - } - - int argc = code[ip + 1]; - if (ret) { - txt += DADDR(4 + argc) + "="; - } - - txt += DADDR(2) + "."; - txt += String(func.get_global_name(code[ip + 3])); - txt += "("; - - for (int i = 0; i < argc; i++) { - if (i > 0) { - txt += ", "; - } - txt += DADDR(4 + i); - } - txt += ")"; - - incr = 5 + argc; - - } break; - case GDScriptFunction::OPCODE_CALL_BUILT_IN: { - txt += " call-built-in "; - - int argc = code[ip + 2]; - txt += DADDR(3 + argc) + "="; - - txt += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(code[ip + 1])); - txt += "("; - - for (int i = 0; i < argc; i++) { - if (i > 0) { - txt += ", "; - } - txt += DADDR(3 + i); - } - txt += ")"; - - incr = 4 + argc; - - } break; - case GDScriptFunction::OPCODE_CALL_SELF_BASE: { - txt += " call-self-base "; - - int argc = code[ip + 2]; - txt += DADDR(3 + argc) + "="; - - txt += func.get_global_name(code[ip + 1]); - txt += "("; - - for (int i = 0; i < argc; i++) { - if (i > 0) { - txt += ", "; - } - txt += DADDR(3 + i); - } - txt += ")"; - - incr = 4 + argc; - - } break; - case GDScriptFunction::OPCODE_YIELD: { - txt += " yield "; - incr = 1; - - } break; - case GDScriptFunction::OPCODE_YIELD_SIGNAL: { - txt += " yield_signal "; - txt += DADDR(1); - txt += ","; - txt += DADDR(2); - incr = 3; - } break; - case GDScriptFunction::OPCODE_YIELD_RESUME: { - txt += " yield resume: "; - txt += DADDR(1); - incr = 2; - } break; - case GDScriptFunction::OPCODE_JUMP: { - txt += " jump "; - txt += itos(code[ip + 1]); - - incr = 2; - - } break; - case GDScriptFunction::OPCODE_JUMP_IF: { - txt += " jump-if "; - txt += DADDR(1); - txt += " to "; - txt += itos(code[ip + 2]); - - incr = 3; - } break; - case GDScriptFunction::OPCODE_JUMP_IF_NOT: { - txt += " jump-if-not "; - txt += DADDR(1); - txt += " to "; - txt += itos(code[ip + 2]); - - incr = 3; - } break; - case GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT: { - txt += " jump-to-default-argument "; - incr = 1; - } break; - case GDScriptFunction::OPCODE_RETURN: { - txt += " return "; - txt += DADDR(1); - - incr = 2; - - } break; - case GDScriptFunction::OPCODE_ITERATE_BEGIN: { - txt += " for-init " + DADDR(4) + " in " + DADDR(2) + " counter " + DADDR(1) + " end " + itos(code[ip + 3]); - incr += 5; - - } break; - case GDScriptFunction::OPCODE_ITERATE: { - txt += " for-loop " + DADDR(4) + " in " + DADDR(2) + " counter " + DADDR(1) + " end " + itos(code[ip + 3]); - incr += 5; - - } break; - case GDScriptFunction::OPCODE_LINE: { - int line = code[ip + 1] - 1; - if (line >= 0 && line < p_code.size()) { - txt = "\n" + itos(line + 1) + ": " + p_code[line] + "\n"; - } else { - txt = ""; - } - incr += 2; - } break; - case GDScriptFunction::OPCODE_END: { - txt += " end"; - incr += 1; - } break; - case GDScriptFunction::OPCODE_ASSERT: { - txt += " assert "; - txt += DADDR(1); - incr += 2; + } - } break; - } + GDScriptParser::TreePrinter printer; - if (incr == 0) { - ERR_BREAK_MSG(true, "Unhandled opcode: " + itos(code[ip])); - } - - ip += incr; - if (txt != "") { - print_line(txt); - } - } - } + printer.print_tree(parser); } MainLoop *test(TestType p_type) { @@ -891,12 +130,12 @@ MainLoop *test(TestType p_type) { } String test = cmdlargs.back()->get(); - if (!test.ends_with(".gd") && !test.ends_with(".gdc")) { + if (!test.ends_with(".gd")) { print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test); return nullptr; } - FileAccess *fa = FileAccess::open(test, FileAccess::READ); + FileAccessRef fa = FileAccess::open(test, FileAccess::READ); ERR_FAIL_COND_V_MSG(!fa, nullptr, "Could not open file: " + test); Vector<uint8_t> buf; @@ -910,7 +149,6 @@ MainLoop *test(TestType p_type) { Vector<String> lines; int last = 0; - for (int i = 0; i <= code.length(); i++) { if (code[i] == '\n' || code[i] == 0) { lines.push_back(code.substr(last, i - last)); @@ -918,104 +156,18 @@ MainLoop *test(TestType p_type) { } } - if (p_type == TEST_TOKENIZER) { - GDScriptTokenizerText tk; - tk.set_code(code); - int line = -1; - while (tk.get_token() != GDScriptTokenizer::TK_EOF) { - String text; - if (tk.get_token() == GDScriptTokenizer::TK_IDENTIFIER) { - text = "'" + tk.get_token_identifier() + "' (identifier)"; - } else if (tk.get_token() == GDScriptTokenizer::TK_CONSTANT) { - const Variant &c = tk.get_token_constant(); - if (c.get_type() == Variant::STRING) { - text = "\"" + String(c) + "\""; - } else { - text = c; - } - - text = text + " (" + Variant::get_type_name(c.get_type()) + " constant)"; - } else if (tk.get_token() == GDScriptTokenizer::TK_ERROR) { - text = "ERROR: " + tk.get_token_error(); - } else if (tk.get_token() == GDScriptTokenizer::TK_NEWLINE) { - text = "newline (" + itos(tk.get_token_line()) + ") + indent: " + itos(tk.get_token_line_indent()); - } else if (tk.get_token() == GDScriptTokenizer::TK_BUILT_IN_FUNC) { - text = "'" + String(GDScriptFunctions::get_func_name(tk.get_token_built_in_func())) + "' (built-in function)"; - } else { - text = tk.get_token_name(tk.get_token()); - } - - if (tk.get_token_line() != line) { - int from = line + 1; - line = tk.get_token_line(); - - for (int i = from; i <= line; i++) { - int l = i - 1; - if (l >= 0 && l < lines.size()) { - print_line("\n" + itos(i) + ": " + lines[l] + "\n"); - } - } - } - print_line("\t(" + itos(tk.get_token_column()) + "): " + text); - tk.advance(); - } - } - - if (p_type == TEST_PARSER) { - GDScriptParser parser; - Error err = parser.parse(code); - if (err) { - print_line("Parse Error:\n" + itos(parser.get_error_line()) + ":" + itos(parser.get_error_column()) + ":" + parser.get_error()); - memdelete(fa); - return nullptr; - } - - const GDScriptParser::Node *root = parser.get_parse_tree(); - ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, nullptr); - const GDScriptParser::ClassNode *cnode = static_cast<const GDScriptParser::ClassNode *>(root); - - _parser_show_class(cnode, 0, lines); - } - - if (p_type == TEST_COMPILER) { - GDScriptParser parser; - - Error err = parser.parse(code); - if (err) { - print_line("Parse Error:\n" + itos(parser.get_error_line()) + ":" + itos(parser.get_error_column()) + ":" + parser.get_error()); - memdelete(fa); - return nullptr; - } - - Ref<GDScript> gds; - gds.instance(); - - GDScriptCompiler gdc; - err = gdc.compile(&parser, gds.ptr()); - if (err) { - print_line("Compile Error:\n" + itos(gdc.get_error_line()) + ":" + itos(gdc.get_error_column()) + ":" + gdc.get_error()); - return nullptr; - } - - Ref<GDScript> current = gds; - - while (current.is_valid()) { - print_line("** CLASS **"); - _disassemble_class(current, lines); - - current = current->get_base(); - } - - } else if (p_type == TEST_BYTECODE) { - Vector<uint8_t> buf2 = GDScriptTokenizerBuffer::parse_code_string(code); - String dst = test.get_basename() + ".gdc"; - FileAccess *fw = FileAccess::open(dst, FileAccess::WRITE); - fw->store_buffer(buf2.ptr(), buf2.size()); - memdelete(fw); + switch (p_type) { + case TEST_TOKENIZER: + test_tokenizer(code, lines); + break; + case TEST_PARSER: + test_parser(code, test, lines); + break; + case TEST_COMPILER: + case TEST_BYTECODE: + print_line("Not implemented."); } - memdelete(fa); - return nullptr; } diff --git a/main/tests/test_main.cpp b/main/tests/test_main.cpp index 5ebdaf1741..91eff28f86 100644 --- a/main/tests/test_main.cpp +++ b/main/tests/test_main.cpp @@ -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(); - } - - if (p_test == "math") { - return TestMath::test(); - } - - if (p_test == "basis") { - return TestBasis::test(); - } - - 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; +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++; + } + } + + // 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); + } + + test_context.applyCommandLine(valid_arguments.size(), args); + + 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 diff --git a/main/tests/test_main.h b/main/tests/test_main.h index bdb1668d21..8273b74eac 100644 --- a/main/tests/test_main.h +++ b/main/tests/test_main.h @@ -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 diff --git a/main/tests/test_string.cpp b/main/tests/test_string.cpp deleted file mode 100644 index 73d59b0088..0000000000 --- a/main/tests/test_string.cpp +++ /dev/null @@ -1,1207 +0,0 @@ -/*************************************************************************/ -/* test_string.cpp */ -/*************************************************************************/ -/* 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. */ -/*************************************************************************/ - -#include "test_string.h" - -#include "core/io/ip_address.h" -#include "core/os/os.h" -#include "core/ustring.h" - -#include "modules/modules_enabled.gen.h" -#ifdef MODULE_REGEX_ENABLED -#include "modules/regex/regex.h" -#endif - -#include <stdio.h> -#include <wchar.h> - -namespace TestString { - -bool test_1() { - OS::get_singleton()->print("\n\nTest 1: Assign from cstr\n"); - - String s = "Hello"; - - OS::get_singleton()->print("\tExpected: Hello\n"); - OS::get_singleton()->print("\tResulted: %ls\n", s.c_str()); - - return (wcscmp(s.c_str(), L"Hello") == 0); -} - -bool test_2() { - OS::get_singleton()->print("\n\nTest 2: Assign from string (operator=)\n"); - - String s = "Dolly"; - const String &t = s; - - OS::get_singleton()->print("\tExpected: Dolly\n"); - OS::get_singleton()->print("\tResulted: %ls\n", t.c_str()); - - return (wcscmp(t.c_str(), L"Dolly") == 0); -} - -bool test_3() { - OS::get_singleton()->print("\n\nTest 3: Assign from c-string (copycon)\n"); - - String s("Sheep"); - const String &t(s); - - OS::get_singleton()->print("\tExpected: Sheep\n"); - OS::get_singleton()->print("\tResulted: %ls\n", t.c_str()); - - return (wcscmp(t.c_str(), L"Sheep") == 0); -} - -bool test_4() { - OS::get_singleton()->print("\n\nTest 4: Assign from c-widechar (operator=)\n"); - - String s(L"Give me"); - - OS::get_singleton()->print("\tExpected: Give me\n"); - OS::get_singleton()->print("\tResulted: %ls\n", s.c_str()); - - return (wcscmp(s.c_str(), L"Give me") == 0); -} - -bool test_5() { - OS::get_singleton()->print("\n\nTest 5: Assign from c-widechar (copycon)\n"); - - String s(L"Wool"); - - OS::get_singleton()->print("\tExpected: Wool\n"); - OS::get_singleton()->print("\tResulted: %ls\n", s.c_str()); - - return (wcscmp(s.c_str(), L"Wool") == 0); -} - -bool test_6() { - OS::get_singleton()->print("\n\nTest 6: comparisons (equal)\n"); - - String s = "Test Compare"; - - OS::get_singleton()->print("\tComparing to \"Test Compare\"\n"); - - if (!(s == "Test Compare")) { - return false; - } - - if (!(s == L"Test Compare")) { - return false; - } - - if (!(s == String("Test Compare"))) { - return false; - } - - return true; -} - -bool test_7() { - OS::get_singleton()->print("\n\nTest 7: comparisons (unequal)\n"); - - String s = "Test Compare"; - - OS::get_singleton()->print("\tComparing to \"Test Compare\"\n"); - - if (!(s != "Peanut")) { - return false; - } - - if (!(s != L"Coconut")) { - return false; - } - - if (!(s != String("Butter"))) { - return false; - } - - return true; -} - -bool test_8() { - OS::get_singleton()->print("\n\nTest 8: comparisons (operator<)\n"); - - String s = "Bees"; - - OS::get_singleton()->print("\tComparing to \"Bees\"\n"); - - if (!(s < "Elephant")) { - return false; - } - - if (s < L"Amber") { - return false; - } - - if (s < String("Beatrix")) { - return false; - } - - return true; -} - -bool test_9() { - OS::get_singleton()->print("\n\nTest 9: Concatenation\n"); - - String s; - - s += "Have"; - s += ' '; - s += 'a'; - s += String(" "); - s = s + L"Nice"; - s = s + " "; - s = s + String("Day"); - - OS::get_singleton()->print("\tComparing to \"Have a Nice Day\"\n"); - - return (s == "Have a Nice Day"); -} - -bool test_10() { - OS::get_singleton()->print("\n\nTest 10: Misc funcs (size/length/empty/etc)\n"); - - if (!String("").empty()) { - return false; - } - - if (String("Mellon").size() != 7) { - return false; - } - - if (String("Oranges").length() != 7) { - return false; - } - - return true; -} - -bool test_11() { - OS::get_singleton()->print("\n\nTest 11: Operator[]\n"); - - String a = "Kugar Sane"; - - a[0] = 'S'; - a[6] = 'C'; - - if (a != "Sugar Cane") { - return false; - } - - if (a[1] != 'u') { - return false; - } - - return true; -} - -bool test_12() { - OS::get_singleton()->print("\n\nTest 12: case functions\n"); - - String a = "MoMoNgA"; - - if (a.to_upper() != "MOMONGA") { - return false; - } - - if (a.nocasecmp_to("momonga") != 0) { - return false; - } - - return true; -} - -bool test_13() { - OS::get_singleton()->print("\n\nTest 13: UTF8\n"); - - /* 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; - - OS::get_singleton()->print("\tUnicode: %ls\n", ustr); - s.parse_utf8(s.utf8().get_data()); - OS::get_singleton()->print("\tConvert/Parse UTF8: %ls\n", s.c_str()); - - return (s == ustr); -} - -bool test_14() { - OS::get_singleton()->print("\n\nTest 14: ASCII\n"); - - String s = L"Primero Leche"; - OS::get_singleton()->print("\tAscii: %s\n", s.ascii().get_data()); - - String t = s.ascii().get_data(); - return (s == t); -} - -bool test_15() { - OS::get_singleton()->print("\n\nTest 15: substr\n"); - - String s = "Killer Baby"; - OS::get_singleton()->print("\tsubstr(3,4) of \"%ls\" is \"%ls\"\n", s.c_str(), s.substr(3, 4).c_str()); - - return (s.substr(3, 4) == "ler "); -} - -bool test_16() { - OS::get_singleton()->print("\n\nTest 16: find\n"); - - String s = "Pretty Woman"; - OS::get_singleton()->print("\tString: %ls\n", s.c_str()); - OS::get_singleton()->print("\t\"tty\" is at %i pos.\n", s.find("tty")); - OS::get_singleton()->print("\t\"Revenge of the Monster Truck\" is at %i pos.\n", s.find("Revenge of the Monster Truck")); - - if (s.find("tty") != 3) { - return false; - } - - if (s.find("Revenge of the Monster Truck") != -1) { - return false; - } - - return true; -} - -bool test_17() { - OS::get_singleton()->print("\n\nTest 17: find no case\n"); - - String s = "Pretty Whale"; - OS::get_singleton()->print("\tString: %ls\n", s.c_str()); - OS::get_singleton()->print("\t\"WHA\" is at %i pos.\n", s.findn("WHA")); - OS::get_singleton()->print("\t\"Revenge of the Monster SawFish\" is at %i pos.\n", s.findn("Revenge of the Monster Truck")); - - if (s.findn("WHA") != 7) { - return false; - } - - if (s.findn("Revenge of the Monster SawFish") != -1) { - return false; - } - - return true; -} - -bool test_18() { - OS::get_singleton()->print("\n\nTest 18: find no case\n"); - - String s = "Pretty Whale"; - OS::get_singleton()->print("\tString: %ls\n", s.c_str()); - OS::get_singleton()->print("\t\"WHA\" is at %i pos.\n", s.findn("WHA")); - OS::get_singleton()->print("\t\"Revenge of the Monster SawFish\" is at %i pos.\n", s.findn("Revenge of the Monster Truck")); - - if (s.findn("WHA") != 7) { - return false; - } - - if (s.findn("Revenge of the Monster SawFish") != -1) { - return false; - } - - return true; -} - -bool test_19() { - OS::get_singleton()->print("\n\nTest 19: Search & replace\n"); - - String s = "Happy Birthday, Anna!"; - OS::get_singleton()->print("\tString: %ls\n", s.c_str()); - - s = s.replace("Birthday", "Halloween"); - OS::get_singleton()->print("\tReplaced Birthday/Halloween: %ls.\n", s.c_str()); - - return (s == "Happy Halloween, Anna!"); -} - -bool test_20() { - OS::get_singleton()->print("\n\nTest 20: Insertion\n"); - - String s = "Who is Frederic?"; - - OS::get_singleton()->print("\tString: %ls\n", s.c_str()); - s = s.insert(s.find("?"), " Chopin"); - OS::get_singleton()->print("\tInserted Chopin: %ls.\n", s.c_str()); - - return (s == "Who is Frederic Chopin?"); -} - -bool test_21() { - OS::get_singleton()->print("\n\nTest 21: Number -> String\n"); - - OS::get_singleton()->print("\tPi is %f\n", 33.141593); - OS::get_singleton()->print("\tPi String is %ls\n", String::num(3.141593).c_str()); - - return String::num(3.141593) == "3.141593"; -} - -bool test_22() { - OS::get_singleton()->print("\n\nTest 22: String -> Int\n"); - - 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++) { -#ifdef __MINGW32__ // MinGW can't handle normal format specifiers for some reason. So we need special code just for MinGW. - OS::get_singleton()->print("\tString: \"%s\" as Int is %I64i\n", nums[i], (long long)(String(nums[i]).to_int())); -#else - OS::get_singleton()->print("\tString: \"%s\" as Int is %lli\n", nums[i], (long long)(String(nums[i]).to_int())); -#endif - if (String(nums[i]).to_int() != num[i]) { - return false; - } - } - - return true; -} - -bool test_23() { - OS::get_singleton()->print("\n\nTest 23: String -> Float\n"); - - 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++) { - OS::get_singleton()->print("\tString: \"%s\" as Float is %f\n", nums[i], String(nums[i]).to_double()); - - if (ABS(String(nums[i]).to_double() - num[i]) > 0.00001) { - return false; - } - } - - return true; -} - -bool test_24() { - OS::get_singleton()->print("\n\nTest 24: Slicing\n"); - - String s = "Mars,Jupiter,Saturn,Uranus"; - - const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" }; - - OS::get_singleton()->print("\tSlicing \"%ls\" by \"%s\"..\n", s.c_str(), ","); - - for (int i = 0; i < s.get_slice_count(","); i++) { - OS::get_singleton()->print("\t\t%i- %ls\n", i + 1, s.get_slice(",", i).c_str()); - - if (s.get_slice(",", i) != slices[i]) { - return false; - } - } - - return true; -} - -bool test_25() { - OS::get_singleton()->print("\n\nTest 25: Erasing\n"); - - String s = "Josephine is such a cute girl!"; - - OS::get_singleton()->print("\tString: %ls\n", s.c_str()); - OS::get_singleton()->print("\tRemoving \"cute\"\n"); - - s.erase(s.find("cute "), String("cute ").length()); - OS::get_singleton()->print("\tResult: %ls\n", s.c_str()); - - return (s == "Josephine is such a girl!"); -} - -bool test_26() { - OS::get_singleton()->print("\n\nTest 26: RegEx substitution\n"); - -#ifndef MODULE_REGEX_ENABLED - OS::get_singleton()->print("\tRegEx module disabled, can't run test."); - return false; -#else - String s = "Double all the vowels."; - - OS::get_singleton()->print("\tString: %ls\n", s.c_str()); - OS::get_singleton()->print("\tRepeating instances of 'aeiou' once\n"); - - RegEx re("(?<vowel>[aeiou])"); - s = re.sub(s, "$0$vowel", true); - - OS::get_singleton()->print("\tResult: %ls\n", s.c_str()); - - return (s == "Doouublee aall thee vooweels."); -#endif -} - -struct test_27_data { - char const *data; - char const *begin; - bool expected; -}; - -bool test_27() { - OS::get_singleton()->print("\n\nTest 27: begins_with\n"); - 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; - } - if (!state) { - OS::get_singleton()->print("\n\t Failure on:\n\t\tstring: %s\n\t\tbegin: %s\n\t\texpected: %s\n", tc[i].data, tc[i].begin, tc[i].expected ? "true" : "false"); - break; - } - }; - return state; -}; - -bool test_28() { - OS::get_singleton()->print("\n\nTest 28: sprintf\n"); - - bool success, state = true; - char output_format[] = "\tTest:\t%ls => %ls (%s)\n"; - String format, output; - Array args; - bool error; - - // %% - format = "fish %% frog"; - args.clear(); - output = format.sprintf(args, &error); - success = (output == String("fish % frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - //////// INTS - - // Int - format = "fish %d frog"; - args.clear(); - args.push_back(5); - output = format.sprintf(args, &error); - success = (output == String("fish 5 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Int left padded with zeroes. - format = "fish %05d frog"; - args.clear(); - args.push_back(5); - output = format.sprintf(args, &error); - success = (output == String("fish 00005 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Int left padded with spaces. - format = "fish %5d frog"; - args.clear(); - args.push_back(5); - output = format.sprintf(args, &error); - success = (output == String("fish 5 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Int right padded with spaces. - format = "fish %-5d frog"; - args.clear(); - args.push_back(5); - output = format.sprintf(args, &error); - success = (output == String("fish 5 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Int with sign (positive). - format = "fish %+d frog"; - args.clear(); - args.push_back(5); - output = format.sprintf(args, &error); - success = (output == String("fish +5 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Negative int. - format = "fish %d frog"; - args.clear(); - args.push_back(-5); - output = format.sprintf(args, &error); - success = (output == String("fish -5 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Hex (lower) - format = "fish %x frog"; - args.clear(); - args.push_back(45); - output = format.sprintf(args, &error); - success = (output == String("fish 2d frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Hex (upper) - format = "fish %X frog"; - args.clear(); - args.push_back(45); - output = format.sprintf(args, &error); - success = (output == String("fish 2D frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Octal - format = "fish %o frog"; - args.clear(); - args.push_back(99); - output = format.sprintf(args, &error); - success = (output == String("fish 143 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ////// REALS - - // Real - format = "fish %f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == String("fish 99.990000 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Real left-padded - format = "fish %11f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == String("fish 99.990000 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Real right-padded - format = "fish %-11f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == String("fish 99.990000 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Real given int. - format = "fish %f frog"; - args.clear(); - args.push_back(99); - output = format.sprintf(args, &error); - success = (output == String("fish 99.000000 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Real with sign (positive). - format = "fish %+f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == String("fish +99.990000 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Real with 1 decimals. - format = "fish %.1f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == String("fish 100.0 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Real with 12 decimals. - format = "fish %.12f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == String("fish 99.990000000000 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Real with no decimals. - format = "fish %.f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == String("fish 100 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - /////// Strings. - - // String - format = "fish %s frog"; - args.clear(); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == String("fish cheese frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // String left-padded - format = "fish %10s frog"; - args.clear(); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == String("fish cheese frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // String right-padded - format = "fish %-10s frog"; - args.clear(); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == String("fish cheese frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ///// Characters - - // Character as string. - format = "fish %c frog"; - args.clear(); - args.push_back("A"); - output = format.sprintf(args, &error); - success = (output == String("fish A frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Character as int. - format = "fish %c frog"; - args.clear(); - args.push_back(65); - output = format.sprintf(args, &error); - success = (output == String("fish A frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ///// Dynamic width - - // String dynamic width - format = "fish %*s frog"; - args.clear(); - args.push_back(10); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == String("fish cheese frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Int dynamic width - format = "fish %*d frog"; - args.clear(); - args.push_back(10); - args.push_back(99); - output = format.sprintf(args, &error); - success = (output == String("fish 99 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // 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); - success = (output == String("fish 99.990 frog") && !error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ///// Errors - - // More formats than arguments. - format = "fish %s %s frog"; - args.clear(); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == "not enough arguments for format string" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // More arguments than formats. - format = "fish %s frog"; - args.clear(); - args.push_back("hello"); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == "not all arguments converted during string formatting" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Incomplete format. - format = "fish %10"; - args.clear(); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == "incomplete format" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Bad character in format string - format = "fish %&f frog"; - args.clear(); - args.push_back("cheese"); - output = format.sprintf(args, &error); - success = (output == "unsupported format character" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Too many decimals. - format = "fish %2.2.2f frog"; - args.clear(); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == "too many decimal points in format" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // * not a number - format = "fish %*f frog"; - args.clear(); - args.push_back("cheese"); - args.push_back(99.99); - output = format.sprintf(args, &error); - success = (output == "* wants number" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Character too long. - format = "fish %c frog"; - args.clear(); - args.push_back("sc"); - output = format.sprintf(args, &error); - success = (output == "%c requires number or single-character string" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - // Character bad type. - format = "fish %c frog"; - args.clear(); - args.push_back(Array()); - output = format.sprintf(args, &error); - success = (output == "%c requires number or single-character string" && error); - OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - return state; -} - -bool test_29() { - bool state = true; - - IP_Address ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); - OS::get_singleton()->print("ip0 is %ls\n", String(ip0).c_str()); - - IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, true); - OS::get_singleton()->print("ip6 is %ls\n", String(ip).c_str()); - - IP_Address ip2("fe80::52e5:49ff:fe93:1baf"); - OS::get_singleton()->print("ip6 is %ls\n", String(ip2).c_str()); - - IP_Address ip3("::ffff:192.168.0.1"); - OS::get_singleton()->print("ip6 is %ls\n", String(ip3).c_str()); - - String ip4 = "192.168.0.1"; - bool success = ip4.is_valid_ip_address(); - OS::get_singleton()->print("Is valid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ip4 = "192.368.0.1"; - success = (!ip4.is_valid_ip_address()); - OS::get_singleton()->print("Is invalid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; - success = ip6.is_valid_ip_address(); - OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334"; - success = (!ip6.is_valid_ip_address()); - OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334"; - success = (!ip6.is_valid_ip_address()); - OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ip6 = "2001:0db8::0:8a2e:370:7334"; - success = (ip6.is_valid_ip_address()); - OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - ip6 = "::ffff:192.168.0.1"; - success = (ip6.is_valid_ip_address()); - OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - state = state && success; - - return state; -}; - -bool test_30() { - bool state = true; - bool success = true; - String input = "bytes2var"; - String output = "Bytes 2 Var"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "linear2db"; - output = "Linear 2 Db"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "vector3"; - output = "Vector 3"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "sha256"; - output = "Sha 256"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "2db"; - output = "2 Db"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "PascalCase"; - output = "Pascal Case"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "PascalPascalCase"; - output = "Pascal Pascal Case"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "snake_case"; - output = "Snake Case"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "snake_snake_case"; - output = "Snake Snake Case"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "sha256sum"; - output = "Sha 256 Sum"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "cat2dog"; - output = "Cat 2 Dog"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "function(name)"; - output = "Function(name)"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls (existing incorrect behavior): %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "snake_case_function(snake_case_arg)"; - output = "Snake Case Function(snake Case Arg)"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls (existing incorrect behavior): %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - input = "snake_case_function( snake_case_arg )"; - output = "Snake Case Function( Snake Case Arg )"; - success = (input.capitalize() == output); - state = state && success; - OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); - - return state; -} - -bool test_31() { - bool state = true; - bool success; - - String a = ""; - success = a[0] == 0; - OS::get_singleton()->print("Is 0 String[0]:, %s\n", success ? "OK" : "FAIL"); - if (!success) { - state = false; - } - - String b = "Godot"; - success = b[b.size()] == 0; - OS::get_singleton()->print("Is 0 String[size()]:, %s\n", success ? "OK" : "FAIL"); - if (!success) { - state = false; - } - - const String c = ""; - success = c[0] == 0; - OS::get_singleton()->print("Is 0 const String[0]:, %s\n", success ? "OK" : "FAIL"); - if (!success) { - state = false; - } - - const String d = "Godot"; - success = d[d.size()] == 0; - OS::get_singleton()->print("Is 0 const String[size()]:, %s\n", success ? "OK" : "FAIL"); - if (!success) { - state = false; - } - - return state; -}; - -bool test_32() { -#define STRIP_TEST(x) \ - { \ - bool success = x; \ - state = state && success; \ - if (!success) { \ - OS::get_singleton()->print("\tfailed at: %s\n", #x); \ - } \ - } - - OS::get_singleton()->print("\n\nTest 32: lstrip and rstrip\n"); - 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("µ¿")); - - return state; - -#undef STRIP_TEST -} - -bool test_33() { - OS::get_singleton()->print("\n\nTest 33: parse_utf8(null, -1)\n"); - - String empty; - return empty.parse_utf8(nullptr, -1); -} - -bool test_34() { - OS::get_singleton()->print("\n\nTest 34: Cyrillic to_lower()\n"); - - String upper = String::utf8("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"); - String lower = String::utf8("абвгдеёжзийклмнопрстуфхцчшщъыьэюя"); - - String test = upper.to_lower(); - - bool state = test == lower; - - return state; -} - -bool test_35() { -#define COUNT_TEST(x) \ - { \ - bool success = x; \ - state = state && success; \ - if (!success) { \ - OS::get_singleton()->print("\tfailed at: %s\n", #x); \ - } \ - } - - OS::get_singleton()->print("\n\nTest 35: count and countn function\n"); - 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); - - return state; -} - -typedef bool (*TestFunc)(); - -TestFunc test_funcs[] = { - - test_1, - test_2, - test_3, - test_4, - test_5, - test_6, - test_7, - test_8, - test_9, - test_10, - test_11, - test_12, - test_13, - test_14, - test_15, - test_16, - test_17, - test_18, - test_19, - test_20, - test_21, - test_22, - test_23, - test_24, - test_25, - test_26, - test_27, - test_28, - test_29, - test_30, - test_31, - test_32, - test_33, - test_34, - test_35, - nullptr - -}; - -MainLoop *test() { - /** A character length != wchar_t may be forced, so the tests won't work */ - - static_assert(sizeof(CharType) == sizeof(wchar_t)); - - int count = 0; - int passed = 0; - - while (true) { - if (!test_funcs[count]) { - break; - } - bool pass = test_funcs[count](); - if (pass) { - passed++; - } - OS::get_singleton()->print("\t%s\n", pass ? "PASS" : "FAILED"); - - count++; - } - - OS::get_singleton()->print("\n\n\n"); - OS::get_singleton()->print("*************\n"); - OS::get_singleton()->print("***TOTALS!***\n"); - OS::get_singleton()->print("*************\n"); - - OS::get_singleton()->print("Passed %i of %i tests\n", passed, count); - - return nullptr; -} - -} // namespace TestString diff --git a/main/tests/test_string.h b/main/tests/test_string.h index 96fa811126..25fd513a1a 100644 --- a/main/tests/test_string.h +++ b/main/tests/test_string.h @@ -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 diff --git a/main/tests/test_validate_testing.h b/main/tests/test_validate_testing.h new file mode 100644 index 0000000000..5be7d45185 --- /dev/null +++ b/main/tests/test_validate_testing.h @@ -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 |