diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 3d751c56528..37dc21aa697 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -152,6 +152,10 @@ jobs: id: cmake-build run: cmake --build build --config ${{ matrix.configuration }} --parallel ${{ matrix.platform.threads || 4 }} + - name: Run CTest + working-directory: build + run: ctest --config ${{ matrix.configuration }} --parallel ${{ matrix.platform.threads || 4 }} --output-on-failure --schedule-random --no-tests=error + - name: Make package if: ${{ steps.cmake-build.outcome == 'success' }} id: make-package @@ -233,3 +237,16 @@ jobs: shutdown_vm: false sync_files: false run: cmake --build build --config ${{ matrix.Configuration }} --parallel 4 + + - name: Run CTest + uses: cross-platform-actions/action@v0.23.0 + with: + operating_system: ${{ matrix.platform.os }} + architecture: ${{ matrix.platform.arch }} + version: ${{ matrix.platform.os-version }} + cpu_count: 4 + memory: 13G + environment_variables: CFLAGS CXXFLAGS + shutdown_vm: false + sync_files: false + run: ctest -C ${{ matrix.Configuration }} --parallel 4 --output-on-failure --schedule-random --no-tests=error diff --git a/.gitmodules b/.gitmodules index d653b46d1ba..62beff6839d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,3 +46,7 @@ [submodule "Externals/xrLuaFix"] path = Externals/xrLuaFix url = https://github.com/OpenXRay/xrLuaFix.git +[submodule "Externals/doctest"] + path = Externals/doctest + url = https://github.com/doctest/doctest.git + branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt index cfa8d64409b..53e5ae289cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,12 +139,16 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT XRAY_USE_DEFAULT_CXX_LIB) if (XRAY_CXX_LIB STREQUAL "libstdc++") add_compile_options(-stdlib=libstdc++) + add_link_options(-stdlib=libstdc++) elseif (XRAY_CXX_LIB STREQUAL "libc++") add_compile_options(-stdlib=libc++) + add_link_options(-stdlib=libc++) if (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") add_compile_options(-lcxxrt) + add_link_options(-lcxxrt) else() add_compile_options(-lc++abi) + add_link_options(-lc++abi) endif() endif() endif() @@ -290,6 +294,13 @@ add_subdirectory(src) add_subdirectory(res) add_subdirectory(misc) +# Tests +option(BUILD_TESTS "Build tests" ON) +if (BUILD_TESTS) + include(CTest) + add_subdirectory(tests) +endif() + get_property(LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS) if ("${LIB64}" STREQUAL "TRUE") diff --git a/Externals/CMakeLists.txt b/Externals/CMakeLists.txt index 6ee73375534..f16a53b225f 100644 --- a/Externals/CMakeLists.txt +++ b/Externals/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(OPCODE) add_subdirectory(ode) #add_subdirectory(NVTT) add_subdirectory(imgui-proj) +add_subdirectory(doctest EXCLUDE_FROM_ALL) if (NOT TARGET xrLuabind) message(FATAL_ERROR diff --git a/Externals/doctest b/Externals/doctest new file mode 160000 index 00000000000..ae7a13539fb --- /dev/null +++ b/Externals/doctest @@ -0,0 +1 @@ +Subproject commit ae7a13539fb71f270b87eb2e874fbac80bc8dda2 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000000..bef9148cd2f --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(xrCore) diff --git a/tests/xrCore/CMakeLists.txt b/tests/xrCore/CMakeLists.txt new file mode 100644 index 00000000000..dd030e23d19 --- /dev/null +++ b/tests/xrCore/CMakeLists.txt @@ -0,0 +1,13 @@ +add_executable( + xrCoreTests + main.cpp + xr_ini_test.cpp) + +target_link_libraries(xrCoreTests xrCore doctest::doctest) +target_include_directories(xrCoreTests PRIVATE "${CMAKE_SOURCE_DIR}/src") +add_test(NAME xrCoreTests COMMAND xrCoreTests) +# https://github.com/doctest/doctest/blob/master/doc/markdown/configuration.md +target_compile_definitions(xrCoreTests PRIVATE + DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + DOCTEST_CONFIG_SUPER_FAST_ASSERTS +) diff --git a/tests/xrCore/main.cpp b/tests/xrCore/main.cpp new file mode 100644 index 00000000000..9f883e65bf9 --- /dev/null +++ b/tests/xrCore/main.cpp @@ -0,0 +1,15 @@ +#define DOCTEST_CONFIG_IMPLEMENT +#include + +#include +#include + +int main(int argc, char** argv) +{ + doctest::Context context; + context.applyCommandLine(argc, argv); + + Memory._initialize(); + + return context.run(); +} diff --git a/tests/xrCore/xr_ini_test.cpp b/tests/xrCore/xr_ini_test.cpp new file mode 100644 index 00000000000..77f5ee32c9a --- /dev/null +++ b/tests/xrCore/xr_ini_test.cpp @@ -0,0 +1,94 @@ +#include + +#include +#include +#include + +#include + +CInifile read_from_string(pcstr str, CInifile::allow_include_func_t allow_include = nullptr) +{ + IReader reader = IReader(const_cast(str), xr_strlen(str)); + return CInifile(&reader, "test.ini", allow_include); +} + +TEST_CASE("parse empty file") +{ + CInifile ini = read_from_string(""); + + CHECK_EQ(ini.section_count(), 0); +} + +TEST_CASE("parse empty section") +{ + CInifile ini = read_from_string("[a]"); + + CHECK_EQ(ini.section_count(), 1); + CHECK_UNARY(ini.section_exist("a")); +} + +TEST_CASE("parse simple section") +{ + CInifile ini = read_from_string( + R"ini( +[a] +key = value +)ini"); + + CHECK_UNARY(ini.section_exist("a")); + CHECK_UNARY(ini.line_exist("a", "key")); + CHECK_EQ(ini.read("a", "key"), "value"); +} + +TEST_CASE("parse integer value") +{ + CInifile ini = read_from_string( + R"ini( +[a] +key = 123 +)ini"); + + CHECK_UNARY(ini.section_exist("a")); + CHECK_UNARY(ini.line_exist("a", "key")); + CHECK_EQ(ini.read("a", "key"), 123); +} + +TEST_CASE("Parse float value") +{ + CInifile ini = read_from_string( + R"ini( +[a] +key = 123.456 +)ini"); + + CHECK_UNARY(ini.section_exist("a")); + CHECK_UNARY(ini.line_exist("a", "key")); + CHECK_EQ(ini.read("a", "key"), 123.456f); +} + +TEST_CASE("Parse quoted value") +{ + CInifile ini = read_from_string( + R"ini( +[a] +key = "value" +)ini"); + + CHECK_UNARY(ini.section_exist("a")); + CHECK_UNARY(ini.line_exist("a", "key")); + CHECK_EQ(ini.read("a", "key"), "\"value\""); +} + +TEST_CASE("Parse multiline value") +{ + CInifile ini = read_from_string( + R"ini( +[a] +key = "multiline +value" +)ini"); + + CHECK_UNARY(ini.section_exist("a")); + CHECK_UNARY(ini.line_exist("a", "key")); + CHECK_EQ(ini.read("a", "key"), "\"multiline\r\nvalue\""); +}