Merge branch 'cs_unit_tests' into 'master'

Introduce unit tests for editor

See merge request OpenMW/openmw!2821
depth-refraction
psi29a 2 years ago
commit 6d8f3c7bce

@ -47,12 +47,13 @@ variables:
- df -h
- du -sh .
- cmake --install .
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite --gtest_output="xml:tests.xml"; fi
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite --gtest_output="xml:openmw_tests.xml"; fi
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw-cs-tests --gtest_output="xml:openmw_cs_tests.xml"; fi
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi
- ccache -s
- df -h
- if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then gcovr --xml-pretty --exclude-unreachable-branches --print-summary --root "${CI_PROJECT_DIR}" -j $(nproc) -o ../coverage.xml; fi
- ls | grep -v -e '^extern$' -e '^install$' -e '^tests.xml$' | xargs -I '{}' rm -rf './{}'
- ls | grep -v -e '^extern$' -e '^install$' -e '^openmw_tests.xml$' -e '^openmw_cs_tests.xml$' | xargs -I '{}' rm -rf './{}'
- cd ..
- df -h
- du -sh build/
@ -175,7 +176,7 @@ Ubuntu_GCC_tests:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
.Ubuntu_GCC_tests_Debug:
extends: Ubuntu_GCC
@ -191,7 +192,7 @@ Ubuntu_GCC_tests:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
Ubuntu_GCC_tests_asan:
extends: Ubuntu_GCC
@ -209,7 +210,7 @@ Ubuntu_GCC_tests_asan:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
Ubuntu_GCC_tests_ubsan:
extends: Ubuntu_GCC
@ -226,7 +227,7 @@ Ubuntu_GCC_tests_ubsan:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
.Ubuntu_GCC_tests_tsan:
extends: Ubuntu_GCC
@ -244,7 +245,7 @@ Ubuntu_GCC_tests_ubsan:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
Ubuntu_GCC_tests_coverage:
extends: .Ubuntu_GCC_tests_Debug
@ -263,7 +264,7 @@ Ubuntu_GCC_tests_coverage:
coverage_report:
coverage_format: cobertura
path: coverage.xml
junit: build/tests.xml
junit: build/*_tests.xml
.Ubuntu_Static_Deps:
extends: Ubuntu_Clang
@ -304,7 +305,7 @@ Ubuntu_GCC_tests_coverage:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
Ubuntu_Clang:
extends: .Ubuntu
@ -344,7 +345,7 @@ Ubuntu_Clang_Tidy:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
Ubuntu_Clang_tests_Debug:
extends: Ubuntu_Clang
@ -359,7 +360,7 @@ Ubuntu_Clang_tests_Debug:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
reports:
junit: build/tests.xml
junit: build/*_tests.xml
Ubuntu_Clang_integration_tests:
extends: .Ubuntu_Image

@ -101,6 +101,7 @@ if [[ "${BUILD_TESTS_ONLY}" ]]; then
-DBUILD_BULLETOBJECTTOOL=OFF \
-DBUILD_NIFTEST=OFF \
-DBUILD_UNITTESTS=${BUILD_UNITTESTS} \
-DBUILD_OPENCS_TESTS=${BUILD_UNITTESTS} \
-DBUILD_BENCHMARKS=${BUILD_BENCHMARKS} \
..
else

@ -1117,6 +1117,7 @@ fi
if [ -n "${TEST_FRAMEWORK}" ]; then
add_cmake_opts -DBUILD_UNITTESTS=ON
add_cmake_opts -DBUILD_OPENCS_TESTS=ON
fi
if [ -n "$ACTIVATE_MSVC" ]; then

@ -54,13 +54,14 @@ option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
option(BUILD_NAVMESHTOOL "Build navmesh tool" ON)
option(BUILD_BULLETOBJECTTOOL "Build Bullet object tool" ON)
option(BUILD_OPENCS_TESTS "Build OpenMW Construction Set tests" OFF)
set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up.
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE)
else()
if (BUILD_LAUNCHER OR BUILD_OPENCS OR BUILD_WIZARD OR BUILD_OPENCS_TESTS)
set(USE_QT TRUE)
else()
set(USE_QT FALSE)
endif()
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
@ -294,6 +295,11 @@ if (OPENMW_USE_SYSTEM_YAML_CPP)
find_package(yaml-cpp REQUIRED)
endif()
if ((BUILD_UNITTESTS OR BUILD_OPENCS_TESTS) AND OPENMW_USE_SYSTEM_GOOGLETEST)
find_package(GTest 1.10 REQUIRED)
find_package(GMock 1.10 REQUIRED)
endif()
add_subdirectory(extern)
# Sound setup
@ -621,7 +627,7 @@ endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clan
add_subdirectory (extern/osg-ffmpeg-videoplayer)
add_subdirectory (extern/oics)
add_subdirectory (extern/Base64)
if (BUILD_OPENCS AND Qt5_FOUND)
if ((BUILD_OPENCS OR BUILD_OPENCS_TESTS) AND Qt5_FOUND)
add_subdirectory (extern/osgQt)
endif()
@ -657,7 +663,7 @@ if (BUILD_ESSIMPORTER)
add_subdirectory (apps/essimporter )
endif()
if (BUILD_OPENCS)
if (BUILD_OPENCS OR BUILD_OPENCS_TESTS)
add_subdirectory (apps/opencs)
endif()
@ -686,6 +692,10 @@ if (BUILD_BULLETOBJECTTOOL)
add_subdirectory( apps/bulletobjecttool )
endif()
if (BUILD_OPENCS_TESTS)
add_subdirectory(apps/opencs_tests)
endif()
if (WIN32)
if (MSVC)
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )

@ -1,4 +1,4 @@
set (OPENCS_SRC main.cpp
set (OPENCS_SRC
${CMAKE_SOURCE_DIR}/files/windows/opencs.rc
)
@ -143,7 +143,7 @@ set (OPENCS_UI
${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui
)
source_group (openmw-cs FILES ${OPENCS_SRC} ${OPENCS_HDR})
source_group (openmw-cs FILES main.cpp ${OPENCS_SRC} ${OPENCS_HDR})
if(WIN32)
set(QT_USE_QTMAIN TRUE)
@ -167,19 +167,34 @@ else()
set (OPENCS_OPENMW_CFG "")
endif(APPLE)
openmw_add_executable(openmw-cs
MACOSX_BUNDLE
add_library(openmw-cs-lib
${OPENCS_SRC}
${OPENCS_UI_HDR}
${OPENCS_MOC_SRC}
${OPENCS_RES_SRC}
${OPENCS_MAC_ICON}
${OPENCS_CFG}
${OPENCS_DEFAULT_FILTERS_FILE}
${OPENCS_OPENMW_CFG}
)
if(APPLE)
set_target_properties(openmw-cs-lib PROPERTIES OUTPUT_NAME openmw-cs)
if(BUILD_OPENCS)
openmw_add_executable(openmw-cs
MACOSX_BUNDLE
${OPENCS_MAC_ICON}
${OPENCS_CFG}
${OPENCS_DEFAULT_FILTERS_FILE}
${OPENCS_OPENMW_CFG}
main.cpp
)
target_link_libraries(openmw-cs openmw-cs-lib)
if (BUILD_WITH_CODE_COVERAGE)
target_compile_options(openmw-cs-lib PRIVATE --coverage)
target_link_libraries(openmw-cs-lib gcov)
endif()
endif()
if(APPLE AND BUILD_OPENCS)
set(OPENCS_BUNDLE_NAME "OpenMW-CS")
set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources")
@ -210,9 +225,9 @@ if(APPLE)
add_custom_command(TARGET openmw-cs
POST_BUILD
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${OPENCS_BUNDLE_RESOURCES_DIR}/resources")
endif(APPLE)
endif()
target_link_libraries(openmw-cs
target_link_libraries(openmw-cs-lib
# CMake's built-in OSG finder does not use pkgconfig, so we have to
# manually ensure the order is correct for inter-library dependencies.
# This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.
@ -228,10 +243,13 @@ target_link_libraries(openmw-cs
components_qt
)
target_link_libraries(openmw-cs Qt::Widgets Qt::Core Qt::Network Qt::OpenGL)
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL)
if (WIN32)
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
target_link_libraries(openmw-cs-lib ${Boost_LOCALE_LIBRARY})
endif()
if (WIN32 AND BUILD_OPENCS)
INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".")
get_generator_is_multi_config(multi_config)
@ -251,22 +269,21 @@ if (MSVC)
endif (CMAKE_CL_64)
endif (MSVC)
if(APPLE)
if(APPLE AND BUILD_OPENCS)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT Bundle)
endif()
if(USE_QT)
set_property(TARGET openmw-cs PROPERTY AUTOMOC ON)
set_property(TARGET openmw-cs-lib PROPERTY AUTOMOC ON)
endif(USE_QT)
if (BUILD_WITH_CODE_COVERAGE)
target_compile_options(openmw-cs PRIVATE --coverage)
target_link_libraries(openmw-cs gcov)
target_compile_options(openmw-cs-lib PRIVATE --coverage)
target_link_libraries(openmw-cs-lib gcov)
endif()
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
target_precompile_headers(openmw-cs PRIVATE
target_precompile_headers(openmw-cs-lib PRIVATE
<boost/filesystem.hpp>
<boost/program_options/options_description.hpp>

@ -0,0 +1,32 @@
file(GLOB OPENCS_TESTS_SRC_FILES
main.cpp
model/world/testinfocollection.cpp
)
source_group(apps\\openmw-cs-tests FILES ${OPENCS_TESTS_SRC_FILES})
openmw_add_executable(openmw-cs-tests ${OPENCS_TESTS_SRC_FILES})
target_include_directories(openmw-cs-tests SYSTEM PRIVATE ${GTEST_INCLUDE_DIRS})
target_include_directories(openmw-cs-tests SYSTEM PRIVATE ${GMOCK_INCLUDE_DIRS})
target_link_libraries(openmw-cs-tests PRIVATE
openmw-cs-lib
GTest::GTest
GMock::GMock
)
if (UNIX AND NOT APPLE)
target_link_libraries(openmw-cs-tests PRIVATE ${CMAKE_THREAD_LIBS_INIT})
endif()
if (BUILD_WITH_CODE_COVERAGE)
target_compile_options(openmw-cs-tests PRIVATE --coverage)
target_link_libraries(openmw-cs-tests PRIVATE gcov)
endif()
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
target_precompile_headers(openmw-cs-tests PRIVATE
<gtest/gtest.h>
)
endif()

@ -0,0 +1,7 @@
#include <gtest/gtest.h>
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -0,0 +1,122 @@
#include "apps/opencs/model/world/infocollection.hpp"
#include "components/esm3/esmreader.hpp"
#include "components/esm3/esmwriter.hpp"
#include "components/esm3/formatversion.hpp"
#include "components/esm3/loaddial.hpp"
#include "components/esm3/loadinfo.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <array>
#include <span>
#include <sstream>
namespace CSMWorld
{
namespace
{
using namespace ::testing;
std::unique_ptr<std::stringstream> saveDialogueWithInfos(
const ESM::Dialogue& dialogue, std::span<const ESM::DialInfo> infos)
{
auto stream = std::make_unique<std::stringstream>();
ESM::ESMWriter writer;
writer.setFormatVersion(ESM::CurrentSaveGameFormatVersion);
writer.save(*stream);
writer.startRecord(ESM::REC_DIAL);
dialogue.save(writer);
writer.endRecord(ESM::REC_DIAL);
for (const ESM::DialInfo& info : infos)
{
writer.startRecord(ESM::REC_INFO);
info.save(writer);
writer.endRecord(ESM::REC_INFO);
}
return stream;
}
void loadDialogueWithInfos(bool base, std::unique_ptr<std::stringstream> stream, InfoCollection& infoCollection,
InfosByTopic& infosByTopic)
{
ESM::ESMReader reader;
reader.open(std::move(stream), "test");
ASSERT_TRUE(reader.hasMoreRecs());
ASSERT_EQ(reader.getRecName().toInt(), ESM::REC_DIAL);
reader.getRecHeader();
bool isDeleted;
ESM::Dialogue dialogue;
dialogue.load(reader, isDeleted);
while (reader.hasMoreRecs())
{
ASSERT_EQ(reader.getRecName().toInt(), ESM::REC_INFO);
reader.getRecHeader();
infoCollection.load(reader, base, dialogue, infosByTopic);
}
}
void saveAndLoadDialogueWithInfos(const ESM::Dialogue& dialogue, std::span<const ESM::DialInfo> infos,
bool base, InfoCollection& infoCollection, InfosByTopic& infosByTopic)
{
loadDialogueWithInfos(base, saveDialogueWithInfos(dialogue, infos), infoCollection, infosByTopic);
}
TEST(CSMWorldInfoCollectionTest, loadShouldAddRecord)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue1");
ESM::DialInfo info;
info.blank();
info.mId = ESM::RefId::stringRefId("info1");
const bool base = true;
InfosByTopic infosByTopic;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infosByTopic);
EXPECT_EQ(collection.getSize(), 1);
ASSERT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue1#info1")), 0);
const Record<Info>& record = collection.getRecord(0);
ASSERT_EQ(record.mState, RecordBase::State_BaseOnly);
EXPECT_EQ(record.mBase.mTopicId, dialogue.mId);
EXPECT_EQ(record.mBase.mOriginalId, info.mId);
EXPECT_EQ(record.mBase.mId, ESM::RefId::stringRefId("dialogue1#info1"));
}
TEST(CSMWorldInfoCollectionTest, loadShouldUpdateRecord)
{
ESM::Dialogue dialogue;
dialogue.blank();
dialogue.mId = ESM::RefId::stringRefId("dialogue1");
ESM::DialInfo info;
info.blank();
info.mId = ESM::RefId::stringRefId("info1");
const bool base = true;
InfosByTopic infosByTopic;
InfoCollection collection;
saveAndLoadDialogueWithInfos(dialogue, std::array{ info }, base, collection, infosByTopic);
ESM::DialInfo updatedInfo = info;
updatedInfo.mActor = ESM::RefId::stringRefId("newActor");
saveAndLoadDialogueWithInfos(dialogue, std::array{ updatedInfo }, base, collection, infosByTopic);
ASSERT_EQ(collection.searchId(ESM::RefId::stringRefId("dialogue1#info1")), 0);
const Record<Info>& record = collection.getRecord(0);
ASSERT_EQ(record.mState, RecordBase::State_BaseOnly);
EXPECT_EQ(record.mBase.mActor, ESM::RefId::stringRefId("newActor"));
}
}
}

@ -1,8 +1,3 @@
if (OPENMW_USE_SYSTEM_GOOGLETEST)
find_package(GTest 1.10 REQUIRED)
find_package(GMock 1.10 REQUIRED)
endif()
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS})

@ -303,7 +303,7 @@ if (NOT OPENMW_USE_SYSTEM_ICU)
set(ICU_LIBRARIES ICU::i18n ICU::uc ICU::data PARENT_SCOPE)
endif()
if (BUILD_UNITTESTS AND NOT OPENMW_USE_SYSTEM_GOOGLETEST)
if ((BUILD_UNITTESTS OR BUILD_OPENCS_TESTS) AND NOT OPENMW_USE_SYSTEM_GOOGLETEST)
cmake_minimum_required(VERSION 3.11)
include(FetchContent)

Loading…
Cancel
Save