From 579342810f5c453e91063c54ca6544300ea09090 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Mon, 20 Jul 2020 17:35:34 +0100 Subject: t Add unit testing to Godot using DocTest and added to GitHub Actions CI Implements exit codes into the engine so tests can return their statuses. Ideally we don't do this, and we use FIXUP logic to 'begin' and 'end' the engine execution for tests specifically. Since realistically we're initialising the engine here we don't want to do that, since String should not require an engine startup to test a single header. This lowers the complexity of running the unit tests and even for physics should be possible to implement such a fix. --- .github/workflows/linux_builds.yml | 5 + .github/workflows/macos_builds.yml | 5 + .github/workflows/windows_builds.yml | 5 + .gitignore | 4 +- COPYRIGHT.txt | 5 + SConstruct | 3 + core/string_name.h | 4 +- main/SCsub | 1 - main/main.cpp | 395 +- main/main.h | 17 +- main/tests/test_main.cpp | 111 +- main/tests/test_main.h | 2 +- main/tests/test_string.cpp | 1207 ------ main/tests/test_string.h | 758 +++- main/tests/test_validate_testing.h | 42 + platform/iphone/godot_iphone.cpp | 3 + platform/javascript/javascript_main.cpp | 4 + platform/linuxbsd/godot_linuxbsd.cpp | 3 + platform/osx/godot_main_osx.mm | 5 +- platform/server/godot_server.cpp | 3 + platform/windows/godot_windows.cpp | 8 +- thirdparty/README.md | 8 + thirdparty/doctest/LICENSE.txt | 21 + thirdparty/doctest/doctest.h | 6205 +++++++++++++++++++++++++++++++ 24 files changed, 7422 insertions(+), 1402 deletions(-) delete mode 100644 main/tests/test_string.cpp create mode 100644 main/tests/test_validate_testing.h create mode 100644 thirdparty/doctest/LICENSE.txt create mode 100644 thirdparty/doctest/doctest.h diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index d91be544a3..be7737593a 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -62,6 +62,11 @@ jobs: run: | scons -j2 verbose=yes warnings=all werror=yes platform=linuxbsd tools=yes target=release_debug module_mono_enabled=yes mono_glue=no + # Execute unit tests for the editor + - name: Unit Tests + run: | + ./bin/godot.linuxbsd.opt.tools.64.mono --test + linux-template: runs-on: "ubuntu-20.04" name: Template w/ Mono (target=release, tools=no) diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index f6d357e706..4a9fa910d0 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -51,6 +51,11 @@ jobs: run: | scons -j2 verbose=yes warnings=all werror=yes platform=osx tools=yes target=release_debug + # Execute unit tests for the editor + - name: Unit Tests + run: | + ./bin/godot.osx.opt.tools.64 --test + macos-template: runs-on: "macos-latest" name: Template (target=release, tools=no) diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 0c7f23036e..c58bae8275 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -56,6 +56,11 @@ jobs: run: | scons -j2 verbose=yes warnings=all werror=yes platform=windows tools=yes target=release_debug + # Execute unit tests for the editor + - name: Unit Tests + run: | + ./bin/godot.windows.opt.tools.64.exe --test + # Build Product Upload (tested and working) # sorry this is disabled until github can give us some more space as we would hit our limit very quickly # tested this code and it works fine so just enable it to get them back diff --git a/.gitignore b/.gitignore index f4af79929c..6af581040c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,11 +11,13 @@ doc/_build/ # CLion cmake-build-debug +# clangd +.clangd/ + # Android specific .gradle local.properties *.iml -.idea .gradletasknamecache project.properties platform/android/java/lib/.cxx/ diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index fc3079c361..e5d30e3328 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -126,6 +126,11 @@ Copyright: 2018, Eric Lasota 2018, Microsoft Corp. License: Expat +Files: ./thirdparty/doctest/ +Comment: doctest +Copyright: 2016-2019, Viktor Kirilov +License: Expat + Files: ./thirdparty/enet/ Comment: ENet Copyright: 2002-2020, Lee Salzman diff --git a/SConstruct b/SConstruct index e7ca8b3030..e23aa1cdbc 100644 --- a/SConstruct +++ b/SConstruct @@ -641,6 +641,9 @@ if selected_platform in platform_list: } ) + # enable test framework globally and inform it of configuration method + env.Append(CPPDEFINES=["DOCTEST_CONFIG_IMPLEMENT"]) + scons_cache_path = os.environ.get("SCONS_CACHE") if scons_cache_path != None: CacheDir(scons_cache_path) diff --git a/core/string_name.h b/core/string_name.h index df6b458581..886ddd0ee7 100644 --- a/core/string_name.h +++ b/core/string_name.h @@ -35,6 +35,8 @@ #include "core/safe_refcount.h" #include "core/ustring.h" +class Main; + struct StaticCString { const char *ptr; static StaticCString create(const char *p_ptr); @@ -73,7 +75,7 @@ class StringName { void unref(); friend void register_core_types(); friend void unregister_core_types(); - + friend class Main; static Mutex mutex; static void setup(); static void cleanup(); diff --git a/main/SCsub b/main/SCsub index 7a301b82bc..bf188d7328 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)) diff --git a/main/main.cpp b/main/main.cpp index a500e173a2..e60bdda18d 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" @@ -75,12 +76,14 @@ #include "servers/xr_server.h" #ifdef TOOLS_ENABLED + #include "editor/doc_data.h" #include "editor/doc_data_class_path.gen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/progress_dialog.h" #include "editor/project_manager.h" + #endif /* Static members */ @@ -186,7 +189,8 @@ static String get_full_version_string() { // to have less code in main.cpp. void initialize_physics() { /// 3D Physics Server - physics_server = PhysicsServer3DManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServer3DManager::setting_property_name)); + physics_server = PhysicsServer3DManager::new_server( + ProjectSettings::get_singleton()->get(PhysicsServer3DManager::setting_property_name)); if (!physics_server) { // Physics server not found, Use the default physics physics_server = PhysicsServer3DManager::new_default_server(); @@ -195,7 +199,8 @@ void initialize_physics() { physics_server->init(); /// 2D Physics server - physics_2d_server = PhysicsServer2DManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServer2DManager::setting_property_name)); + physics_2d_server = PhysicsServer2DManager::new_server( + ProjectSettings::get_singleton()->get(PhysicsServer2DManager::setting_property_name)); if (!physics_2d_server) { // Physics server not found, Use the default physics physics_2d_server = PhysicsServer2DManager::new_default_server(); @@ -254,20 +259,25 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" -h, --help Display this help message.\n"); OS::get_singleton()->print(" --version Display the version string.\n"); OS::get_singleton()->print(" -v, --verbose Use verbose stdout mode.\n"); - OS::get_singleton()->print(" --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n"); + OS::get_singleton()->print( + " --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n"); OS::get_singleton()->print("\n"); OS::get_singleton()->print("Run options:\n"); #ifdef TOOLS_ENABLED OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n"); - OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n"); + OS::get_singleton()->print( + " -p, --project-manager Start the project manager, even if a project is auto-detected.\n"); #endif OS::get_singleton()->print(" -q, --quit Quit after the first iteration.\n"); - OS::get_singleton()->print(" -l, --language Use a specific locale ( being a two-letter code).\n"); - OS::get_singleton()->print(" --path Path to a project ( must contain a 'project.godot' file).\n"); + OS::get_singleton()->print( + " -l, --language Use a specific locale ( being a two-letter code).\n"); + OS::get_singleton()->print( + " --path Path to a project ( 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 Path to a pack (.pck) file to load.\n"); - OS::get_singleton()->print(" --render-thread Render thread mode ('unsafe', 'safe', 'separate').\n"); + OS::get_singleton()->print( + " --render-thread Render thread mode ('unsafe', 'safe', 'separate').\n"); OS::get_singleton()->print(" --remote-fs
Remote filesystem ([:] address).\n"); OS::get_singleton()->print(" --remote-fs-password Password for remote filesystem.\n"); @@ -308,9 +318,12 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --resolution x Request window resolution.\n"); OS::get_singleton()->print(" --position , Request window position.\n"); OS::get_singleton()->print(" --low-dpi Force low-DPI mode (macOS and Windows only).\n"); - OS::get_singleton()->print(" --no-window Disable window creation (Windows only). Useful together with --script.\n"); - OS::get_singleton()->print(" --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n"); - OS::get_singleton()->print(" --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n"); + OS::get_singleton()->print( + " --no-window Disable window creation (Windows only). Useful together with --script.\n"); + OS::get_singleton()->print( + " --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n"); + OS::get_singleton()->print( + " --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n"); OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n"); OS::get_singleton()->print(" --tablet-driver Tablet input driver ("); for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) { @@ -324,35 +337,51 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print("Debug options:\n"); OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n"); - OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n"); + OS::get_singleton()->print( + " -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n"); OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n"); - OS::get_singleton()->print(" --gpu-abort Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n"); - OS::get_singleton()->print(" --remote-debug Remote debug (://[:], 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 Remote debug (://[:], 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 Simulate high CPU load (delay each frame by milliseconds).\n"); - OS::get_singleton()->print(" --time-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 Force a fixed number of frames per second. This setting disables real-time synchronization.\n"); + OS::get_singleton()->print( + " --frame-delay Simulate high CPU load (delay each frame by milliseconds).\n"); + OS::get_singleton()->print( + " --time-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 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