summaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/CMakeLists.txt231
-rw-r--r--test/unit/app/app_test.cpp320
-rw-r--r--test/unit/app/system_linux_test.cpp64
-rw-r--r--test/unit/app/system_mock.h48
-rw-r--r--test/unit/app/system_windows_test.cpp62
-rw-r--r--test/unit/common/CMakeLists.txt16
-rw-r--r--test/unit/common/colobot.ini15
-rw-r--r--test/unit/common/image_test.cpp57
-rw-r--r--test/unit/common/profile_test.cpp44
-rw-r--r--test/unit/graphics/core/device_mock.h107
-rw-r--r--test/unit/graphics/engine/engine_mock.h14
-rw-r--r--test/unit/graphics/engine/lightman_test.cpp150
-rw-r--r--test/unit/main.cpp24
-rw-r--r--test/unit/math/gendata.m86
-rw-r--r--test/unit/math/geometry_test.cpp350
-rw-r--r--test/unit/math/matrix_test.cpp312
-rw-r--r--test/unit/math/vector_test.cpp72
-rw-r--r--test/unit/ui/CMakeLists.txt41
-rw-r--r--test/unit/ui/edit_test.cpp80
-rw-r--r--test/unit/ui/mocks/text_mock.h25
-rw-r--r--test/unit/ui/stubs/app_stub.cpp52
-rw-r--r--test/unit/ui/stubs/engine_stub.cpp104
-rw-r--r--test/unit/ui/stubs/particle_stub.cpp205
-rw-r--r--test/unit/ui/stubs/restext_stub.cpp12
-rw-r--r--test/unit/ui/stubs/robotmain_stub.cpp25
25 files changed, 2516 insertions, 0 deletions
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
new file mode 100644
index 0000000..3d8a38c
--- /dev/null
+++ b/test/unit/CMakeLists.txt
@@ -0,0 +1,231 @@
+set(SRC_DIR ${colobot_SOURCE_DIR}/src)
+
+# Additional libraries per platform
+if (${MXE}) # MXE requires special treatment
+ set(PLATFORM_LIBS ${MXE_LIBS})
+elseif (${PLATFORM_WINDOWS})
+ # because it isn't included in standard linking libraries
+ set(PLATFORM_LIBS "-lintl")
+elseif(${PLATFORM_LINUX})
+ # for clock_gettime
+ set(PLATFORM_LIBS "-lrt")
+endif()
+
+
+# Configure file
+configure_file(${SRC_DIR}/common/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/common/config.h)
+
+# Platform-dependent implementation of system.h
+if (${PLATFORM_WINDOWS})
+ set(SYSTEM_CPP_MODULE "system_windows.cpp")
+elseif(${PLATFORM_LINUX})
+ set(SYSTEM_CPP_MODULE "system_linux.cpp")
+else()
+ set(SYSTEM_CPP_MODULE "system_other.cpp")
+endif()
+
+# Code sources
+set(COLOBOT_SOURCES
+${SRC_DIR}/app/app.cpp
+${SRC_DIR}/app/system.cpp
+${SRC_DIR}/app/${SYSTEM_CPP_MODULE}
+${SRC_DIR}/common/event.cpp
+${SRC_DIR}/common/image.cpp
+${SRC_DIR}/common/iman.cpp
+${SRC_DIR}/common/logger.cpp
+${SRC_DIR}/common/misc.cpp
+${SRC_DIR}/common/profile.cpp
+${SRC_DIR}/common/restext.cpp
+${SRC_DIR}/common/stringutils.cpp
+${SRC_DIR}/graphics/core/color.cpp
+${SRC_DIR}/graphics/engine/camera.cpp
+${SRC_DIR}/graphics/engine/cloud.cpp
+${SRC_DIR}/graphics/engine/engine.cpp
+${SRC_DIR}/graphics/engine/lightman.cpp
+${SRC_DIR}/graphics/engine/lightning.cpp
+${SRC_DIR}/graphics/engine/modelfile.cpp
+${SRC_DIR}/graphics/engine/modelmanager.cpp
+${SRC_DIR}/graphics/engine/particle.cpp
+${SRC_DIR}/graphics/engine/planet.cpp
+${SRC_DIR}/graphics/engine/pyro.cpp
+${SRC_DIR}/graphics/engine/terrain.cpp
+${SRC_DIR}/graphics/engine/text.cpp
+${SRC_DIR}/graphics/engine/water.cpp
+${SRC_DIR}/graphics/opengl/gldevice.cpp
+${SRC_DIR}/object/auto/auto.cpp
+${SRC_DIR}/object/auto/autobase.cpp
+${SRC_DIR}/object/auto/autoconvert.cpp
+${SRC_DIR}/object/auto/autoderrick.cpp
+${SRC_DIR}/object/auto/autodestroyer.cpp
+${SRC_DIR}/object/auto/autoegg.cpp
+${SRC_DIR}/object/auto/autoenergy.cpp
+${SRC_DIR}/object/auto/autofactory.cpp
+${SRC_DIR}/object/auto/autoflag.cpp
+${SRC_DIR}/object/auto/autohuston.cpp
+${SRC_DIR}/object/auto/autoinfo.cpp
+${SRC_DIR}/object/auto/autojostle.cpp
+${SRC_DIR}/object/auto/autokid.cpp
+${SRC_DIR}/object/auto/autolabo.cpp
+${SRC_DIR}/object/auto/automush.cpp
+${SRC_DIR}/object/auto/autonest.cpp
+${SRC_DIR}/object/auto/autonuclear.cpp
+${SRC_DIR}/object/auto/autopara.cpp
+${SRC_DIR}/object/auto/autoportico.cpp
+${SRC_DIR}/object/auto/autoradar.cpp
+${SRC_DIR}/object/auto/autorepair.cpp
+${SRC_DIR}/object/auto/autoresearch.cpp
+${SRC_DIR}/object/auto/autoroot.cpp
+${SRC_DIR}/object/auto/autosafe.cpp
+${SRC_DIR}/object/auto/autostation.cpp
+${SRC_DIR}/object/auto/autotower.cpp
+${SRC_DIR}/object/brain.cpp
+${SRC_DIR}/object/mainmovie.cpp
+${SRC_DIR}/object/motion/motion.cpp
+${SRC_DIR}/object/motion/motionant.cpp
+${SRC_DIR}/object/motion/motionbee.cpp
+${SRC_DIR}/object/motion/motionhuman.cpp
+${SRC_DIR}/object/motion/motionmother.cpp
+${SRC_DIR}/object/motion/motionspider.cpp
+${SRC_DIR}/object/motion/motiontoto.cpp
+${SRC_DIR}/object/motion/motionvehicle.cpp
+${SRC_DIR}/object/motion/motionworm.cpp
+${SRC_DIR}/object/object.cpp
+${SRC_DIR}/object/robotmain.cpp
+${SRC_DIR}/object/task/task.cpp
+${SRC_DIR}/object/task/taskadvance.cpp
+${SRC_DIR}/object/task/taskbuild.cpp
+${SRC_DIR}/object/task/taskfire.cpp
+${SRC_DIR}/object/task/taskfireant.cpp
+${SRC_DIR}/object/task/taskflag.cpp
+${SRC_DIR}/object/task/taskgoto.cpp
+${SRC_DIR}/object/task/taskgungoal.cpp
+${SRC_DIR}/object/task/taskinfo.cpp
+${SRC_DIR}/object/task/taskmanager.cpp
+${SRC_DIR}/object/task/taskmanip.cpp
+${SRC_DIR}/object/task/taskpen.cpp
+${SRC_DIR}/object/task/taskrecover.cpp
+${SRC_DIR}/object/task/taskreset.cpp
+${SRC_DIR}/object/task/tasksearch.cpp
+${SRC_DIR}/object/task/taskshield.cpp
+${SRC_DIR}/object/task/taskspiderexplo.cpp
+${SRC_DIR}/object/task/tasktake.cpp
+${SRC_DIR}/object/task/taskterraform.cpp
+${SRC_DIR}/object/task/taskturn.cpp
+${SRC_DIR}/object/task/taskwait.cpp
+${SRC_DIR}/physics/physics.cpp
+${SRC_DIR}/script/cbottoken.cpp
+${SRC_DIR}/script/cmdtoken.cpp
+${SRC_DIR}/script/script.cpp
+${SRC_DIR}/ui/button.cpp
+${SRC_DIR}/ui/check.cpp
+${SRC_DIR}/ui/color.cpp
+${SRC_DIR}/ui/compass.cpp
+${SRC_DIR}/ui/control.cpp
+${SRC_DIR}/ui/displayinfo.cpp
+${SRC_DIR}/ui/displaytext.cpp
+${SRC_DIR}/ui/edit.cpp
+${SRC_DIR}/ui/editvalue.cpp
+${SRC_DIR}/ui/gauge.cpp
+${SRC_DIR}/ui/group.cpp
+${SRC_DIR}/ui/image.cpp
+${SRC_DIR}/ui/interface.cpp
+${SRC_DIR}/ui/key.cpp
+${SRC_DIR}/ui/label.cpp
+${SRC_DIR}/ui/list.cpp
+${SRC_DIR}/ui/maindialog.cpp
+${SRC_DIR}/ui/mainmap.cpp
+${SRC_DIR}/ui/mainshort.cpp
+${SRC_DIR}/ui/map.cpp
+${SRC_DIR}/ui/scroll.cpp
+${SRC_DIR}/ui/shortcut.cpp
+${SRC_DIR}/ui/slider.cpp
+${SRC_DIR}/ui/studio.cpp
+${SRC_DIR}/ui/target.cpp
+${SRC_DIR}/ui/window.cpp
+)
+
+set(OPENAL_SOURCES "")
+
+if (${OPENAL_SOUND})
+ set(OPENAL_SOURCES
+ ${SRC_DIR}/sound/oalsound/alsound.cpp
+ ${SRC_DIR}/sound/oalsound/buffer.cpp
+ ${SRC_DIR}/sound/oalsound/channel.cpp
+ )
+endif()
+
+# Optional libraries
+set(OPTIONAL_LIBS "")
+
+if (${OPENAL_SOUND})
+ set(OPTIONAL_LIBS ${OPENAL_LIBRARY})
+ set(OPTIONAL_INCLUDES ${OPENAL_INCLUDE_DIR})
+endif()
+
+
+# Platform-dependent tests
+if (${PLATFORM_WINDOWS})
+ set(PLATFORM_TESTS app/system_windows_test.cpp)
+elseif(${PLATFORM_LINUX})
+ set(PLATFORM_TESTS app/system_linux_test.cpp)
+endif()
+
+# Tests
+set(UT_SOURCES
+main.cpp
+app/app_test.cpp
+graphics/engine/lightman_test.cpp
+math/geometry_test.cpp
+math/matrix_test.cpp
+math/vector_test.cpp
+${PLATFORM_TESTS}
+)
+
+# Local
+include_directories(
+.
+common
+math
+${SRC_DIR}
+${CMAKE_CURRENT_BINARY_DIR}
+)
+
+# System
+include_directories(
+SYSTEM
+${GTEST_INCLUDE_DIR}
+${GMOCK_INCLUDE_DIR}
+${SDL_INCLUDE_DIR}
+${SDLIMAGE_INCLUDE_DIR}
+${SDLTTF_INCLUDE_DIR}
+${PNG_INCLUDE_DIRS}
+${GLEW_INCLUDE_PATH}
+${Boost_INCLUDE_DIRS}
+${OPTIONAL_INCLUDE_DIRS}
+${LIBSNDFILE_INCLUDE_DIR}
+)
+
+set(LIBS
+gtest
+gmock
+CBot
+${SDL_LIBRARY}
+${SDLIMAGE_LIBRARY}
+${SDLTTF_LIBRARY}
+${OPENGL_LIBRARY}
+${PNG_LIBRARIES}
+${GLEW_LIBRARY}
+${Boost_LIBRARIES}
+${OPTIONAL_LIBS}
+${PLATFORM_LIBS}
+${LIBSNDFILE_LIBRARY}
+)
+
+add_executable(colobot_ut ${COLOBOT_SOURCES} ${UT_SOURCES} ${OPENAL_SOURCES})
+target_link_libraries(colobot_ut ${LIBS})
+
+add_test(colobot_ut ./colobot_ut)
+
+# TODO: change the unit cases to independent automated tests to be included in colobot_ut
+add_subdirectory(common)
+add_subdirectory(ui)
diff --git a/test/unit/app/app_test.cpp b/test/unit/app/app_test.cpp
new file mode 100644
index 0000000..8c1e899
--- /dev/null
+++ b/test/unit/app/app_test.cpp
@@ -0,0 +1,320 @@
+#include "app/app.h"
+
+#if defined(PLATFORM_WINDOWS)
+ #include "app/system_windows.h"
+#elif defined(PLATFORM_LINUX)
+ #include "app/system_linux.h"
+#else
+ #include "app/system_other.h"
+#endif
+
+#include "app/system_mock.h"
+
+#include "common/logger.h"
+
+#include <gtest/gtest.h>
+
+using testing::_;
+using testing::InSequence;
+using testing::Return;
+
+struct FakeSystemTimeStamp : public SystemTimeStamp
+{
+ FakeSystemTimeStamp(int uid) : uid(uid), time(0) {}
+
+ int uid;
+ long long time;
+};
+
+
+class CApplicationWrapper : public CApplication
+{
+public:
+ virtual Event CreateUpdateEvent() override
+ {
+ return CApplication::CreateUpdateEvent();
+ }
+};
+
+class ApplicationUT : public testing::Test
+{
+protected:
+ ApplicationUT();
+
+ virtual void SetUp() override;
+ virtual void TearDown() override;
+
+ void NextInstant(long long diff);
+
+ SystemTimeStamp* CreateTimeStamp();
+ void DestroyTimeStamp(SystemTimeStamp *stamp);
+ void CopyTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *src);
+ void GetCurrentTimeStamp(SystemTimeStamp *stamp);
+ long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after);
+
+ void TestCreateUpdateEvent(long long relTimeExact, long long absTimeExact,
+ float relTime, float absTime,
+ long long relTimeReal, long long absTimeReal);
+
+protected:
+ CLogger logger;
+ CApplicationWrapper* app;
+ CSystemUtilsMock* systemUtils;
+
+private:
+ int m_stampUid;
+ long long m_currentTime;
+};
+
+ApplicationUT::ApplicationUT()
+ : m_stampUid(0)
+ , m_currentTime(0)
+{}
+
+void ApplicationUT::SetUp()
+{
+ systemUtils = new CSystemUtilsMock();
+
+ ON_CALL(*systemUtils, CreateTimeStamp()).WillByDefault(Invoke(this, &ApplicationUT::CreateTimeStamp));
+ ON_CALL(*systemUtils, DestroyTimeStamp(_)).WillByDefault(Invoke(this, &ApplicationUT::DestroyTimeStamp));
+ ON_CALL(*systemUtils, CopyTimeStamp(_, _)).WillByDefault(Invoke(this, &ApplicationUT::CopyTimeStamp));
+ ON_CALL(*systemUtils, GetCurrentTimeStamp(_)).WillByDefault(Invoke(this, &ApplicationUT::GetCurrentTimeStamp));
+ ON_CALL(*systemUtils, TimeStampExactDiff(_, _)).WillByDefault(Invoke(this, &ApplicationUT::TimeStampExactDiff));
+
+ EXPECT_CALL(*systemUtils, CreateTimeStamp()).Times(3 + PCNT_MAX*2);
+ app = new CApplicationWrapper();
+}
+
+void ApplicationUT::TearDown()
+{
+ EXPECT_CALL(*systemUtils, DestroyTimeStamp(_)).Times(3 + PCNT_MAX*2);
+ delete app;
+ app = nullptr;
+
+ delete systemUtils;
+ systemUtils = nullptr;
+}
+
+SystemTimeStamp* ApplicationUT::CreateTimeStamp()
+{
+ return new FakeSystemTimeStamp(++m_stampUid);
+}
+
+void ApplicationUT::DestroyTimeStamp(SystemTimeStamp *stamp)
+{
+ delete static_cast<FakeSystemTimeStamp*>(stamp);
+}
+
+void ApplicationUT::CopyTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *src)
+{
+ *static_cast<FakeSystemTimeStamp*>(dst) = *static_cast<FakeSystemTimeStamp*>(src);
+}
+
+void ApplicationUT::GetCurrentTimeStamp(SystemTimeStamp *stamp)
+{
+ static_cast<FakeSystemTimeStamp*>(stamp)->time = m_currentTime;
+}
+
+long long ApplicationUT::TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after)
+{
+ return static_cast<FakeSystemTimeStamp*>(after)->time - static_cast<FakeSystemTimeStamp*>(before)->time;
+}
+
+void ApplicationUT::NextInstant(long long diff)
+{
+ m_currentTime += diff;
+}
+
+void ApplicationUT::TestCreateUpdateEvent(long long relTimeExact, long long absTimeExact,
+ float relTime, float absTime,
+ long long relTimeReal, long long absTimeReal)
+{
+ {
+ InSequence seq;
+ EXPECT_CALL(*systemUtils, CopyTimeStamp(_, _));
+ EXPECT_CALL(*systemUtils, GetCurrentTimeStamp(_));
+ EXPECT_CALL(*systemUtils, TimeStampExactDiff(_, _)).Times(2);
+ }
+
+ Event event = app->CreateUpdateEvent();
+ EXPECT_EQ(EVENT_FRAME, event.type);
+ EXPECT_FLOAT_EQ(relTime, event.rTime);
+ EXPECT_FLOAT_EQ(relTime, app->GetRelTime());
+ EXPECT_FLOAT_EQ(absTime, app->GetAbsTime());
+ EXPECT_EQ(relTimeExact, app->GetExactRelTime());
+ EXPECT_EQ(absTimeExact, app->GetExactAbsTime());
+ EXPECT_EQ(relTimeReal, app->GetRealRelTime());
+ EXPECT_EQ(absTimeReal, app->GetRealAbsTime());
+}
+
+
+TEST_F(ApplicationUT, UpdateEventTimeCalculation_SimulationSuspended)
+{
+ app->SuspendSimulation();
+ Event event = app->CreateUpdateEvent();
+ EXPECT_EQ(EVENT_NULL, event.type);
+}
+
+TEST_F(ApplicationUT, UpdateEventTimeCalculation_NormalOperation)
+{
+ // 1st update
+
+ long long relTimeExact = 1111;
+ long long absTimeExact = relTimeExact;
+ float relTime = relTimeExact / 1e9f;
+ float absTime = absTimeExact / 1e9f;
+ long long relTimeReal = relTimeExact;
+ long long absTimeReal = absTimeExact;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+
+ // 2nd update
+
+ relTimeExact = 2222;
+ absTimeExact += relTimeExact;
+ relTime = relTimeExact / 1e9f;
+ absTime = absTimeExact / 1e9f;
+ relTimeReal = relTimeExact;
+ absTimeReal = absTimeExact;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+}
+
+TEST_F(ApplicationUT, UpdateEventTimeCalculation_NegativeTimeOperation)
+{
+ // 1st update
+
+ long long relTimeExact = 2222;
+ long long absTimeExact = relTimeExact;
+ float relTime = relTimeExact / 1e9f;
+ float absTime = absTimeExact / 1e9f;
+ long long relTimeReal = relTimeExact;
+ long long absTimeReal = absTimeExact;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+
+ // 2nd update
+
+ NextInstant(-1111);
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*systemUtils, CopyTimeStamp(_, _));
+ EXPECT_CALL(*systemUtils, GetCurrentTimeStamp(_));
+ EXPECT_CALL(*systemUtils, TimeStampExactDiff(_, _)).Times(2);
+ }
+ Event event = app->CreateUpdateEvent();
+ EXPECT_EQ(EVENT_NULL, event.type);
+}
+
+TEST_F(ApplicationUT, UpdateEventTimeCalculation_ChangingSimulationSpeed)
+{
+ EXPECT_CALL(*systemUtils, GetCurrentTimeStamp(_));
+ app->SetSimulationSpeed(2.0f);
+
+ // 1st update -- speed 2x
+
+ long long relTimeReal = 100;
+ long long absTimeReal = relTimeReal;
+ long long relTimeExact = relTimeReal*2;
+ long long absTimeExact = absTimeReal*2;
+ float relTime = relTimeExact / 1e9f;
+ float absTime = absTimeExact / 1e9f;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+
+ // 2nd update -- speed 2x
+
+ relTimeReal = 200;
+ absTimeReal += relTimeReal;
+ relTimeExact = relTimeReal*2;
+ absTimeExact += relTimeReal*2;
+ relTime = relTimeExact / 1e9f;
+ absTime = absTimeExact / 1e9f;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+
+ // 3rd update -- speed 4x
+ EXPECT_CALL(*systemUtils, GetCurrentTimeStamp(_));
+ app->SetSimulationSpeed(4.0f);
+
+ relTimeReal = 300;
+ absTimeReal += relTimeReal;
+ relTimeExact = relTimeReal*4;
+ absTimeExact += relTimeReal*4;
+ relTime = relTimeExact / 1e9f;
+ absTime = absTimeExact / 1e9f;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+
+ // 4th update -- speed 1x
+ EXPECT_CALL(*systemUtils, GetCurrentTimeStamp(_));
+ app->SetSimulationSpeed(1.0f);
+
+ relTimeReal = 400;
+ absTimeReal += relTimeReal;
+ relTimeExact = relTimeReal;
+ absTimeExact += relTimeReal;
+ relTime = relTimeExact / 1e9f;
+ absTime = absTimeExact / 1e9f;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+}
+
+TEST_F(ApplicationUT, UpdateEventTimeCalculation_SuspendingAndResumingSimulation)
+{
+ // 1st update -- simulation enabled
+
+ long long relTimeReal = 1000;
+ long long absTimeReal = relTimeReal;
+ long long relTimeExact = relTimeReal;
+ long long absTimeExact = absTimeReal;
+ float relTime = relTimeExact / 1e9f;
+ float absTime = absTimeExact / 1e9f;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+
+ // 2nd update -- simulation suspended
+
+ app->SuspendSimulation();
+
+ long long suspensionTime = 5000;
+
+ NextInstant(suspensionTime);
+
+ // 3rd update -- simulation resumed
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*systemUtils, GetCurrentTimeStamp(_));
+ EXPECT_CALL(*systemUtils, CopyTimeStamp(_, _));
+ }
+ app->ResumeSimulation();
+
+ relTimeReal = 200;
+ absTimeReal += relTimeReal;
+ relTimeExact = relTimeReal;
+ absTimeExact += relTimeReal;
+ relTime = relTimeExact / 1e9f;
+ absTime = absTimeExact / 1e9f;
+
+ NextInstant(relTimeReal);
+
+ TestCreateUpdateEvent(relTimeExact, absTimeExact, relTime, absTime, relTimeReal, absTimeReal);
+}
diff --git a/test/unit/app/system_linux_test.cpp b/test/unit/app/system_linux_test.cpp
new file mode 100644
index 0000000..b0a05ca
--- /dev/null
+++ b/test/unit/app/system_linux_test.cpp
@@ -0,0 +1,64 @@
+#include "app/system.h"
+#include "app/system_linux.h"
+
+#include <gtest/gtest.h>
+
+class CSystemUtilsLinuxWrapper : public CSystemUtilsLinux
+{
+public:
+ CSystemUtilsLinuxWrapper() {}
+};
+
+class SystemUtilsLinuxUT : public testing::Test
+{
+protected:
+ static const long long SEC = 1000000000;
+
+ CSystemUtilsLinuxWrapper systemUtils;
+};
+
+
+TEST_F(SystemUtilsLinuxUT, TimeStampDiff)
+{
+ SystemTimeStamp before, after;
+
+ before.clockTime.tv_sec = 1;
+ before.clockTime.tv_nsec = 100;
+
+ after.clockTime.tv_sec = 1;
+ after.clockTime.tv_nsec = 900;
+
+ long long tDiff = systemUtils.TimeStampExactDiff(&before, &after);
+ EXPECT_EQ( 800, tDiff);
+
+ tDiff = systemUtils.TimeStampExactDiff(&after, &before);
+ EXPECT_EQ(-800, tDiff);
+
+ // -------
+
+ before.clockTime.tv_sec = 2;
+ before.clockTime.tv_nsec = 200;
+
+ after.clockTime.tv_sec = 3;
+ after.clockTime.tv_nsec = 500;
+
+ tDiff = systemUtils.TimeStampExactDiff(&before, &after);
+ EXPECT_EQ( SEC + 300, tDiff);
+
+ tDiff = systemUtils.TimeStampExactDiff(&after, &before);
+ EXPECT_EQ(-SEC - 300, tDiff);
+
+ // -------
+
+ before.clockTime.tv_sec = 3;
+ before.clockTime.tv_nsec = 200;
+
+ after.clockTime.tv_sec = 4;
+ after.clockTime.tv_nsec = 100;
+
+ tDiff = systemUtils.TimeStampExactDiff(&before, &after);
+ EXPECT_EQ( SEC - 100, tDiff);
+
+ tDiff = systemUtils.TimeStampExactDiff(&after, &before);
+ EXPECT_EQ(-SEC + 100, tDiff);
+}
diff --git a/test/unit/app/system_mock.h b/test/unit/app/system_mock.h
new file mode 100644
index 0000000..470a4e1
--- /dev/null
+++ b/test/unit/app/system_mock.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "app/system.h"
+
+#include <gmock/gmock.h>
+
+class CSystemUtilsMock : public CSystemUtils
+{
+public:
+ CSystemUtilsMock(bool defaultExpects = false)
+ {
+ if (defaultExpects)
+ SetDefaultExpects();
+ }
+
+ virtual ~CSystemUtilsMock() {}
+
+ void SetDefaultExpects()
+ {
+ using testing::_;
+ using testing::Return;
+ using testing::AnyNumber;
+
+ EXPECT_CALL(*this, CreateTimeStamp()).Times(AnyNumber()).WillRepeatedly(Return(nullptr));
+ EXPECT_CALL(*this, DestroyTimeStamp(_)).Times(AnyNumber());
+ EXPECT_CALL(*this, CopyTimeStamp(_, _)).Times(AnyNumber());
+ EXPECT_CALL(*this, GetCurrentTimeStamp(_)).Times(AnyNumber());
+
+ EXPECT_CALL(*this, GetTimeStampResolution(_)).Times(AnyNumber()).WillRepeatedly(Return(0.0f));
+ EXPECT_CALL(*this, GetTimeStampExactResolution()).Times(AnyNumber()).WillRepeatedly(Return(0ll));
+ EXPECT_CALL(*this, TimeStampDiff(_, _, _)).Times(AnyNumber()).WillRepeatedly(Return(0.0f));
+ EXPECT_CALL(*this, TimeStampExactDiff(_, _)).Times(AnyNumber()).WillRepeatedly(Return(0ll));
+ }
+
+ MOCK_METHOD0(Init, void());
+
+ MOCK_METHOD3(SystemDialog, SystemDialogResult(SystemDialogType, const std::string &title, const std::string &message));
+ MOCK_METHOD3(ConsoleSystemDialog, SystemDialogResult(SystemDialogType type, const std::string& title, const std::string& message));
+
+ MOCK_METHOD0(CreateTimeStamp, SystemTimeStamp*());
+ MOCK_METHOD1(DestroyTimeStamp, void (SystemTimeStamp *stamp));
+ MOCK_METHOD2(CopyTimeStamp, void (SystemTimeStamp *dst, SystemTimeStamp *src));
+ MOCK_METHOD1(GetCurrentTimeStamp, void (SystemTimeStamp *stamp));
+ MOCK_METHOD1(GetTimeStampResolution, float (SystemTimeUnit unit));
+ MOCK_METHOD0(GetTimeStampExactResolution, long long());
+ MOCK_METHOD3(TimeStampDiff, float(SystemTimeStamp *before, SystemTimeStamp *after, SystemTimeUnit unit));
+ MOCK_METHOD2(TimeStampExactDiff, long long(SystemTimeStamp *before, SystemTimeStamp *after));
+};
diff --git a/test/unit/app/system_windows_test.cpp b/test/unit/app/system_windows_test.cpp
new file mode 100644
index 0000000..79f8c7f
--- /dev/null
+++ b/test/unit/app/system_windows_test.cpp
@@ -0,0 +1,62 @@
+#include "app/system.h"
+#include "app/system_windows.h"
+
+#include <gtest/gtest.h>
+
+class CSystemUtilsWindowsWrapper : public CSystemUtilsWindows
+{
+public:
+ CSystemUtilsWindowsWrapper() {}
+
+ void SetFrequency(long long counterFrequency)
+ {
+ m_counterFrequency = counterFrequency;
+ }
+};
+
+class SystemUtilsWindowsUT : public testing::Test
+{
+protected:
+ static const long long SEC = 1000000000;
+
+ CSystemUtilsWindowsWrapper systemUtils;
+};
+
+
+TEST_F(SystemUtilsWindowsUT, TimerResolution)
+{
+ systemUtils.SetFrequency(SEC);
+ EXPECT_EQ(1u, systemUtils.GetTimeStampExactResolution());
+
+ systemUtils.SetFrequency(SEC/3);
+ EXPECT_EQ(3u, systemUtils.GetTimeStampExactResolution());
+}
+
+TEST_F(SystemUtilsWindowsUT, TimeStampDiff)
+{
+ systemUtils.SetFrequency(SEC);
+
+ SystemTimeStamp before, after;
+
+ before.counterValue = 100;
+ after.counterValue = 200;
+
+ long long tDiff = systemUtils.TimeStampExactDiff(&before, &after);
+ EXPECT_EQ( 100, tDiff);
+
+ tDiff = systemUtils.TimeStampExactDiff(&after, &before);
+ EXPECT_EQ(-100, tDiff);
+
+ // -------
+
+ systemUtils.SetFrequency(SEC/3);
+
+ before.counterValue = 200;
+ after.counterValue = 400;
+
+ tDiff = systemUtils.TimeStampExactDiff(&before, &after);
+ EXPECT_EQ( 200*3, tDiff);
+
+ tDiff = systemUtils.TimeStampExactDiff(&after, &before);
+ EXPECT_EQ(-200*3, tDiff);
+}
diff --git a/test/unit/common/CMakeLists.txt b/test/unit/common/CMakeLists.txt
new file mode 100644
index 0000000..aebf17a
--- /dev/null
+++ b/test/unit/common/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SRC_DIR ${colobot_SOURCE_DIR}/src)
+
+include_directories(
+${SRC_DIR}
+${GTEST_INCLUDE_DIR}
+)
+
+add_executable(image_test ${SRC_DIR}/common/image.cpp image_test.cpp)
+target_link_libraries(image_test ${SDL_LIBRARY} ${SDLIMAGE_LIBRARY} ${PNG_LIBRARIES})
+
+file(COPY colobot.ini DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+
+# add_executable(profile_test ${SRC_DIR}/common/profile.cpp ${SRC_DIR}/common/logger.cpp profile_test.cpp)
+# target_link_libraries(profile_test gtest ${Boost_LIBRARIES})
+
+# add_test(profile_test ./profile_test)
diff --git a/test/unit/common/colobot.ini b/test/unit/common/colobot.ini
new file mode 100644
index 0000000..2ca37ee
--- /dev/null
+++ b/test/unit/common/colobot.ini
@@ -0,0 +1,15 @@
+[test_float]
+float_value=1.5
+
+[test_string]
+string_value=Hello world
+
+[test_int]
+int_value=42
+
+[test_multi]
+entry1=1
+entry2=2
+entry3=3
+entry4=4
+entry5=5
diff --git a/test/unit/common/image_test.cpp b/test/unit/common/image_test.cpp
new file mode 100644
index 0000000..2b20a17
--- /dev/null
+++ b/test/unit/common/image_test.cpp
@@ -0,0 +1,57 @@
+#include "common/image.h"
+
+#include <SDL.h>
+#include <stdio.h>
+
+/* For now, just a simple test: loading a file from image
+ * and saving it to another in PNG. */
+
+int main(int argc, char *argv[])
+{
+ if (argc != 3)
+ {
+ printf("Usage: %s in_image out_image\n", argv[0]);
+ return 0;
+ }
+
+ CImage image;
+
+ if (! image.Load(argv[1]))
+ {
+ std::string err = image.GetError();
+ printf("Error loading '%s': %s\n", argv[1], err.c_str());
+ return 1;
+ }
+ Gfx::Color color;
+ std::string str;
+
+ color = image.GetPixel(Math::IntPoint(0, 0));
+ str = color.ToString();
+ printf("pixel @ (0,0): %s\n", str.c_str());
+
+ color = image.GetPixel(Math::IntPoint(0, 1));
+ str = color.ToString();
+ printf("pixel @ (0,1): %s\n", str.c_str());
+
+ color = image.GetPixel(Math::IntPoint(1, 0));
+ str = color.ToString();
+ printf("pixel @ (1,0): %s\n", str.c_str());
+
+ color = image.GetPixel(Math::IntPoint(1, 1));
+ str = color.ToString();
+ printf("pixel @ (1,1): %s\n", str.c_str());
+
+ image.SetPixel(Math::IntPoint(0, 0), Gfx::Color(0.1f, 0.2f, 0.3f, 0.0f));
+ image.SetPixel(Math::IntPoint(1, 0), Gfx::Color(0.3f, 0.2f, 0.1f, 1.0f));
+ image.SetPixel(Math::IntPoint(0, 1), Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f));
+ image.SetPixel(Math::IntPoint(1, 1), Gfx::Color(0.0f, 0.0f, 0.0f, 1.0f));
+
+ if (! image.SavePNG(argv[2]))
+ {
+ std::string err = image.GetError();
+ printf("Error saving PNG '%s': %s\n", argv[2], err.c_str());
+ return 2;
+ }
+
+ return 0;
+}
diff --git a/test/unit/common/profile_test.cpp b/test/unit/common/profile_test.cpp
new file mode 100644
index 0000000..dabcba6
--- /dev/null
+++ b/test/unit/common/profile_test.cpp
@@ -0,0 +1,44 @@
+#include "common/profile.h"
+#include "common/logger.h"
+#include "app/system.h"
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <gtest/gtest.h>
+
+
+class CProfileTest : public testing::Test
+{
+protected:
+ CLogger m_logger;
+ CProfile m_profile;
+
+};
+
+TEST_F(CProfileTest, ReadTest)
+{
+ ASSERT_TRUE(m_profile.InitCurrentDirectory()); // load colobot.ini file
+
+ std::string result;
+ ASSERT_TRUE(m_profile.GetLocalProfileString("test_string", "string_value", result));
+ ASSERT_STREQ("Hello world", result.c_str());
+
+ int int_value;
+ ASSERT_TRUE(m_profile.GetLocalProfileInt("test_int", "int_value", int_value));
+ ASSERT_EQ(42, int_value);
+
+ float float_value;
+ ASSERT_TRUE(m_profile.GetLocalProfileFloat("test_float", "float_value", float_value));
+ ASSERT_FLOAT_EQ(1.5, float_value);
+
+ std::vector<std::string> list;
+ list = m_profile.GetLocalProfileSection("test_multi", "entry");
+ ASSERT_EQ(5u, list.size());
+}
+
+int main(int argc, char *argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/unit/graphics/core/device_mock.h b/test/unit/graphics/core/device_mock.h
new file mode 100644
index 0000000..80e214f
--- /dev/null
+++ b/test/unit/graphics/core/device_mock.h
@@ -0,0 +1,107 @@
+#pragma once
+
+#include "graphics/core/device.h"
+
+#include <gmock/gmock.h>
+
+class CDeviceMock : public Gfx::CDevice
+{
+public:
+ CDeviceMock() {}
+
+ MOCK_METHOD0(DebugHook, void());
+
+ MOCK_METHOD0(Create, bool());
+ MOCK_METHOD0(Destroy, void());
+
+ MOCK_METHOD0(BeginScene, void());
+ MOCK_METHOD0(EndScene, void());
+
+ MOCK_METHOD0(Clear, void());
+
+ MOCK_METHOD2(SetTransform, void(Gfx::TransformType type, const Math::Matrix &matrix));
+ MOCK_METHOD1(GetTransform, const Math::Matrix& (Gfx::TransformType type));
+ MOCK_METHOD2(MultiplyTransform, void(Gfx::TransformType type, const Math::Matrix &matrix));
+
+ MOCK_METHOD1(SetMaterial, void(const Gfx::Material &material));
+ MOCK_METHOD0(GetMaterial, const Gfx::Material&());
+
+ MOCK_METHOD0(GetMaxLightCount, int());
+
+ MOCK_METHOD2(SetLight, void(int index, const Gfx::Light &light));
+ MOCK_METHOD1(GetLight, const Gfx::Light&(int index));
+
+ MOCK_METHOD2(SetLightEnabled, void(int index, bool enabled));
+ MOCK_METHOD1(GetLightEnabled, bool(int index));
+
+ MOCK_METHOD2(CreateTexture, Gfx::Texture(CImage *image, const Gfx::TextureCreateParams &params));
+ MOCK_METHOD2(CreateTexture, Gfx::Texture(ImageData *data, const Gfx::TextureCreateParams &params));
+
+ MOCK_METHOD1(DestroyTexture, void(const Gfx::Texture &texture));
+ MOCK_METHOD0(DestroyAllTextures, void());
+
+ MOCK_METHOD0(GetMaxTextureStageCount, int());
+
+ MOCK_METHOD2(SetTexture, void(int index, const Gfx::Texture &texture));
+ MOCK_METHOD2(SetTexture, void(int index, unsigned int textureId));
+ MOCK_METHOD1(GetTexture, Gfx::Texture(int index));
+
+ MOCK_METHOD2(SetTextureEnabled, void(int index, bool enabled));
+ MOCK_METHOD1(GetTextureEnabled, bool(int index));
+
+ MOCK_METHOD2(SetTextureStageParams, void(int index, const Gfx::TextureStageParams &params));
+ MOCK_METHOD1(GetTextureStageParams, Gfx::TextureStageParams(int index));
+
+ MOCK_METHOD3(SetTextureStageWrap, void(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT));
+
+ MOCK_METHOD4(DrawPrimitive, void(Gfx::PrimitiveType type, const Gfx::Vertex *vertices, int vertexCount, Gfx::Color color));
+ MOCK_METHOD4(DrawPrimitive, void(Gfx::PrimitiveType type, const Gfx::VertexTex2 *vertices, int vertexCount, Gfx::Color color));
+ MOCK_METHOD3(DrawPrimitive, void(Gfx::PrimitiveType type, const Gfx::VertexCol *vertices, int vertexCount));
+
+ MOCK_METHOD3(CreateStaticBuffer, unsigned int(Gfx::PrimitiveType primitiveType, const Gfx::Vertex* vertices, int vertexCount));
+ MOCK_METHOD3(CreateStaticBuffer, unsigned int(Gfx::PrimitiveType primitiveType, const Gfx::VertexTex2* vertices, int vertexCount));
+ MOCK_METHOD3(CreateStaticBuffer, unsigned int(Gfx::PrimitiveType primitiveType, const Gfx::VertexCol* vertices, int vertexCount));
+
+ MOCK_METHOD4(UpdateStaticBuffer, void(unsigned int bufferId, Gfx::PrimitiveType primitiveType, const Gfx::Vertex* vertices, int vertexCount));
+ MOCK_METHOD4(UpdateStaticBuffer, void(unsigned int bufferId, Gfx::PrimitiveType primitiveType, const Gfx::VertexTex2* vertices, int vertexCount));
+ MOCK_METHOD4(UpdateStaticBuffer, void(unsigned int bufferId, Gfx::PrimitiveType primitiveType, const Gfx::VertexCol* vertices, int vertexCount));
+
+ MOCK_METHOD1(DrawStaticBuffer, void(unsigned int bufferId));
+
+ MOCK_METHOD1(DestroyStaticBuffer, void(unsigned int bufferId));
+
+ MOCK_METHOD2(ComputeSphereVisibility, int(const Math::Vector &center, float radius));
+
+ MOCK_METHOD2(SetRenderState, void(Gfx::RenderState state, bool enabled));
+ MOCK_METHOD1(GetRenderState, bool(Gfx::RenderState state));
+
+ MOCK_METHOD1(SetDepthTestFunc, void(Gfx::CompFunc func));
+ MOCK_METHOD0(GetDepthTestFunc, Gfx::CompFunc());
+
+ MOCK_METHOD1(SetDepthBias, void(float factor));
+ MOCK_METHOD0(GetDepthBias, float());
+
+ MOCK_METHOD2(SetAlphaTestFunc, void(Gfx::CompFunc func, float refValue));
+ MOCK_METHOD2(GetAlphaTestFunc, void(Gfx::CompFunc &func, float &refValue));
+
+ MOCK_METHOD2(SetBlendFunc, void(Gfx::BlendFunc srcBlend, Gfx::BlendFunc dstBlend));
+ MOCK_METHOD2(GetBlendFunc, void(Gfx::BlendFunc &srcBlend, Gfx::BlendFunc &dstBlend));
+
+ MOCK_METHOD1(SetClearColor, void(const Gfx::Color &color));
+ MOCK_METHOD0(GetClearColor, Gfx::Color());
+
+ MOCK_METHOD1(SetGlobalAmbient, void(const Gfx::Color &color));
+ MOCK_METHOD0(GetGlobalAmbient, Gfx::Color());
+
+ MOCK_METHOD5(SetFogParams, void(Gfx::FogMode mode, const Gfx::Color &color, float start, float end, float density));
+ MOCK_METHOD5(GetFogParams, void(Gfx::FogMode &mode, Gfx::Color &color, float &start, float &end, float &density));
+
+ MOCK_METHOD1(SetCullMode, void(Gfx::CullMode mode));
+ MOCK_METHOD0(GetCullMode, Gfx::CullMode());
+
+ MOCK_METHOD1(SetShadeModel, void(Gfx::ShadeModel model));
+ MOCK_METHOD0(GetShadeModel, Gfx::ShadeModel());
+
+ MOCK_METHOD1(SetFillMode, void(Gfx::FillMode mode));
+ MOCK_METHOD0(GetFillMode, Gfx::FillMode());
+};
diff --git a/test/unit/graphics/engine/engine_mock.h b/test/unit/graphics/engine/engine_mock.h
new file mode 100644
index 0000000..1a15eca
--- /dev/null
+++ b/test/unit/graphics/engine/engine_mock.h
@@ -0,0 +1,14 @@
+#include "graphics/engine/engine.h"
+
+#include <gmock/gmock.h>
+
+class CEngineMock : public Gfx::CEngine
+{
+public:
+ CEngineMock() : Gfx::CEngine(nullptr) {}
+
+ MOCK_METHOD0(GetPause, bool());
+
+ MOCK_METHOD0(GetEyePt, Math::Vector());
+ MOCK_METHOD0(GetLookatPt, Math::Vector());
+};
diff --git a/test/unit/graphics/engine/lightman_test.cpp b/test/unit/graphics/engine/lightman_test.cpp
new file mode 100644
index 0000000..e2dc785
--- /dev/null
+++ b/test/unit/graphics/engine/lightman_test.cpp
@@ -0,0 +1,150 @@
+#include "graphics/engine/lightman.h"
+
+#include "app/system_mock.h"
+
+#include "graphics/core/device_mock.h"
+#include "graphics/engine/engine_mock.h"
+
+#include <gtest/gtest.h>
+
+using namespace Gfx;
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+
+class LightManagerUT : public testing::Test
+{
+protected:
+ LightManagerUT()
+ : systemUtils(true)
+ , lightManager(&engine)
+ {}
+
+ void PrepareLightTesting(int maxLights, Math::Vector eyePos);
+ void CheckLightSorting(EngineObjectType objectType, const std::vector<int>& expectedLights);
+ void CheckLight(int index, const Light& light);
+ void AddLight(int type, LightPriority priority, bool used, bool enabled,
+ Math::Vector pos, EngineObjectType includeType, EngineObjectType excludeType);
+
+
+ CSystemUtilsMock systemUtils;
+ CLightManager lightManager;
+ CEngineMock engine;
+ CDeviceMock device;
+
+private:
+ std::vector<DynamicLight> dynamicLights;
+ std::vector<int> expectedLightTypes;
+ int maxLightsCount;
+};
+
+void LightManagerUT::PrepareLightTesting(int maxLights, Math::Vector eyePos)
+{
+ maxLightsCount = maxLights;
+
+ EXPECT_CALL(device, GetMaxLightCount()).WillOnce(Return(maxLights));
+ lightManager.SetDevice(&device);
+
+ ON_CALL(device, SetLight(_, _)).WillByDefault(Invoke(this, &LightManagerUT::CheckLight));
+
+ EXPECT_CALL(engine, GetEyePt()).WillRepeatedly(Return(eyePos));
+}
+
+void LightManagerUT::CheckLightSorting(EngineObjectType objectType, const std::vector<int>& expectedLights)
+{
+ expectedLightTypes = expectedLights;
+
+ EXPECT_CALL(device, SetLight(_, _)).Times(expectedLights.size());
+
+ for (int i = 0; i < static_cast<int>( expectedLights.size() ); ++i)
+ EXPECT_CALL(device, SetLightEnabled(i, true));
+
+ for (int i = expectedLights.size(); i < maxLightsCount; ++i)
+ EXPECT_CALL(device, SetLightEnabled(i, false));
+
+ lightManager.UpdateDeviceLights(objectType);
+}
+
+void LightManagerUT::CheckLight(int index, const Light& light)
+{
+ ASSERT_TRUE(index >= 0 && index < static_cast<int>( expectedLightTypes.size() ));
+ ASSERT_EQ(expectedLightTypes[index], light.type);
+}
+
+void LightManagerUT::AddLight(int type, LightPriority priority, bool used, bool enabled,
+ Math::Vector pos, EngineObjectType includeType, EngineObjectType excludeType)
+{
+ int rank = lightManager.CreateLight(priority);
+
+ Light light;
+ light.type = static_cast<LightType>(type);
+ light.position = pos;
+ lightManager.SetLight(rank, light);
+
+ lightManager.SetLightEnabled(rank, enabled);
+ lightManager.SetLightIncludeType(rank, includeType);
+ lightManager.SetLightExcludeType(rank, excludeType);
+
+ if (!used)
+ lightManager.DeleteLight(rank);
+}
+
+TEST_F(LightManagerUT, LightSorting_UnusedOrDisabledAreSkipped)
+{
+ const int lightCount = 10;
+ const Math::Vector eyePos(0.0f, 0.0f, 0.0f);
+ PrepareLightTesting(lightCount, eyePos);
+
+ AddLight(1, LIGHT_PRI_LOW, false, true, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(2, LIGHT_PRI_LOW, true, false, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(3, LIGHT_PRI_LOW, false, false, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+
+ std::vector<int> expectedLights;
+ CheckLightSorting(ENG_OBJTYPE_TERRAIN, expectedLights);
+}
+
+TEST_F(LightManagerUT, LightSorting_IncludeTypesAreIncluded)
+{
+ const int lightCount = 10;
+ const Math::Vector eyePos(0.0f, 0.0f, 0.0f);
+ PrepareLightTesting(lightCount, eyePos);
+
+ AddLight(1, LIGHT_PRI_LOW, true, true, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(2, LIGHT_PRI_LOW, true, true, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_TERRAIN, ENG_OBJTYPE_NULL);
+ AddLight(3, LIGHT_PRI_LOW, true, true, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_QUARTZ, ENG_OBJTYPE_NULL);
+
+ std::vector<int> expectedLights = { 1, 2 };
+ CheckLightSorting(ENG_OBJTYPE_TERRAIN, expectedLights);
+}
+
+TEST_F(LightManagerUT, LightSorting_ExcludeTypesAreExcluded)
+{
+ const int lightCount = 10;
+ const Math::Vector eyePos(0.0f, 0.0f, 0.0f);
+ PrepareLightTesting(lightCount, eyePos);
+
+ AddLight(1, LIGHT_PRI_LOW, true, true, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(2, LIGHT_PRI_LOW, true, true, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_TERRAIN);
+ AddLight(3, LIGHT_PRI_LOW, true, true, Math::Vector(0.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_QUARTZ);
+
+ std::vector<int> expectedLights = { 1, 3 };
+ CheckLightSorting(ENG_OBJTYPE_TERRAIN, expectedLights);
+}
+
+TEST_F(LightManagerUT, LightSorting_SortingAccordingToDistance)
+{
+ const int lightCount = 3;
+ const Math::Vector eyePos(0.0f, 0.0f, 0.0f);
+ PrepareLightTesting(lightCount, eyePos);
+
+ AddLight(1, LIGHT_PRI_HIGH, true, true, Math::Vector(10.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(2, LIGHT_PRI_LOW, true, true, Math::Vector(4.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(3, LIGHT_PRI_HIGH, true, true, Math::Vector(20.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(4, LIGHT_PRI_LOW, true, true, Math::Vector(11.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(5, LIGHT_PRI_LOW, true, true, Math::Vector(100.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+ AddLight(6, LIGHT_PRI_HIGH, true, true, Math::Vector(21.0f, 0.0f, 0.0f), ENG_OBJTYPE_NULL, ENG_OBJTYPE_NULL);
+
+ std::vector<int> expectedLights = { 2, 1, 3 };
+ CheckLightSorting(ENG_OBJTYPE_TERRAIN, expectedLights);
+}
diff --git a/test/unit/main.cpp b/test/unit/main.cpp
new file mode 100644
index 0000000..e978630
--- /dev/null
+++ b/test/unit/main.cpp
@@ -0,0 +1,24 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+#include "gtest/gtest.h"
+
+int main(int argc, char* argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/test/unit/math/gendata.m b/test/unit/math/gendata.m
new file mode 100644
index 0000000..5c13491
--- /dev/null
+++ b/test/unit/math/gendata.m
@@ -0,0 +1,86 @@
+% Script in Octave for generating test data
+
+1;
+
+% Returns the minor matrix
+function m = minor(A, r, c)
+
+ m = A;
+ m(r,:) = [];
+ m(:,c) = [];
+
+end;
+
+% Returns the cofactor matrix
+function m = cofactors(A)
+
+ m = zeros(rows(A), columns(A));
+
+ for r = [1 : rows(A)]
+ for c = [1 : columns(A)]
+ m(r, c) = det(minor(A, r, c));
+ if (mod(r + c, 2) == 1)
+ m(r, c) = -m(r, c);
+ end;
+ end;
+ end;
+
+end;
+
+% Prints the matrix as C++ code
+function printout(A, name)
+
+ printf('const float %s[16] = \n', name);
+ printf('{\n');
+
+ for c = [1 : columns(A)]
+ for r = [1 : rows(A)]
+ printf(' %f', A(r,c));
+ if (! ( (r == 4) && (c == 4) ) )
+ printf(',');
+ end;
+ printf('\n');
+ end;
+ end;
+
+ printf('};\n');
+
+end;
+
+printf('// Cofactors\n');
+A = randn(4,4);
+printout(A, 'COF_MAT');
+printf('\n');
+printout(cofactors(A), 'COF_RESULT');
+printf('\n');
+
+printf('\n');
+
+printf('// Det\n');
+A = randn(4,4);
+printout(A, 'DET_MAT');
+printf('\n');
+printf('const float DET_RESULT = %f;', det(A));
+printf('\n');
+
+printf('\n');
+
+printf('// Invert\n');
+A = randn(4,4);
+printout(A, 'INV_MAT');
+printf('\n');
+printout(inv(A), 'COF_RESULT');
+printf('\n');
+
+printf('\n');
+
+printf('// Multiplication\n');
+A = randn(4,4);
+printout(A, 'MUL_A');
+printf('\n');
+B = randn(4,4);
+printout(B, 'MUL_B');
+printf('\n');
+C = A * B;
+printout(C, 'MUL_RESULT');
+printf('\n');
diff --git a/test/unit/math/geometry_test.cpp b/test/unit/math/geometry_test.cpp
new file mode 100644
index 0000000..7c3e26a
--- /dev/null
+++ b/test/unit/math/geometry_test.cpp
@@ -0,0 +1,350 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+/* Unit tests for functions in geometry.h */
+
+#include "math/func.h"
+#include "math/geometry.h"
+
+#include "gtest/gtest.h"
+
+
+const float TEST_TOLERANCE = 1e-5;
+
+
+// Test for rewritten function RotateAngle()
+TEST(GeometryTest, RotateAngleTest)
+{
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(0.0f, 0.0f), 0.0f, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(1.0f, 0.0f), 0.0f, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(1.0f, 1.0f), 0.25f * Math::PI, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(0.0f, 2.0f), 0.5f * Math::PI, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(-0.5f, 0.5f), 0.75f * Math::PI, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(-1.0f, 0.0f), Math::PI, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(-1.0f, -1.0f), 1.25f * Math::PI, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(0.0f, -2.0f), 1.5f * Math::PI, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::IsEqual(Math::RotateAngle(1.0f, -1.0f), 1.75f * Math::PI, TEST_TOLERANCE));
+}
+
+// Tests for other altered, complex or uncertain functions
+
+/*
+
+ TODO: write meaningful tests with proper test values
+
+int TestAngle()
+{
+ const Math::Vector u(-0.0786076246943884, 0.2231249091714256, -1.1601361718477805);
+ const Math::Vector v(-1.231228742001907, -1.720549809950561, -0.690468438834111);
+
+ float mathResult = Math::Angle(u, v);
+ float oldMathResult = Angle(VEC_TO_D3DVEC(u), VEC_TO_D3DVEC(v));
+
+ if (! Math::IsEqual(mathResult, oldMathResult, TEST_TOLERANCE) )
+ return __LINE__;
+
+ return 0;
+}
+
+int TestRotateView()
+{
+ const Math::Vector center(0.617909142705555, 0.896939729454538, -0.615041943652284);
+ const float angleH = 44.5;
+ const float angleV = 12.3;
+ const float dist = 34.76;
+
+ Math::Vector mathResult = Math::RotateView(center, angleH, angleV, dist);
+ Math::Vector oldMathResult = D3DVEC_TO_VEC(RotateView(VEC_TO_D3DVEC(center), angleH, angleV, dist));
+
+ if (! Math::VectorsEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLookatPoint()
+{
+ const Math::Vector eye(-2.451183170579471, 0.241270270546559, -0.490677411454893);
+ const float angleH = 48.4;
+ const float angleV = 32.4;
+ const float length = 74.44;
+
+ Math::Vector mathResult = Math::LookatPoint(eye, angleH, angleV, length);
+ Math::Vector oldMathResult = D3DVEC_TO_VEC(LookatPoint(VEC_TO_D3DVEC(eye), angleH, angleV, length));
+
+ if (! Math::VectorsEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestProjection()
+{
+ const Math::Vector a(0.852064846846319, -0.794279497087496, -0.655779805476688);
+ const Math::Vector b(-0.245838834102304, -0.841115596038861, 0.470457161487799);
+ const Math::Vector p(2.289326061164255, -0.505511362271196, 0.660204551169491);
+
+ Math::Vector mathResult = Math::Projection(a, b, p);
+ Math::Vector oldMathResult = D3DVEC_TO_VEC(Projection(VEC_TO_D3DVEC(a), VEC_TO_D3DVEC(b), VEC_TO_D3DVEC(p)));
+
+ if (! Math::VectorsEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadViewMatrix()
+{
+ const Math::Vector from(2.5646013154868874, -0.6058794133917031, -0.0441195127419744);
+ const Math::Vector at(0.728044925765569, -0.206343977871841, 2.543158236935463);
+ const Math::Vector worldUp(-1.893738133660711, -1.009584441407070, 0.521745988225582);
+
+ Math::Matrix mathResult;
+ Math::LoadViewMatrix(mathResult, from, at, worldUp);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DVECTOR fromD3D = VEC_TO_D3DVEC(from);
+ D3DVECTOR atD3D = VEC_TO_D3DVEC(at);
+ D3DVECTOR worldUpD3D = VEC_TO_D3DVEC(worldUp);
+ D3DUtil_SetViewMatrix(mat, fromD3D, atD3D, worldUpD3D);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadProjectionMatrix()
+{
+ const float fov = 76.3f;
+ const float aspect = 0.891f;
+ const float nearPlane = 12.3f;
+ const float farPlane = 1238.9f;
+
+ Math::Matrix mathResult;
+ Math::LoadProjectionMatrix(mathResult, fov, aspect, nearPlane, farPlane);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DUtil_SetProjectionMatrix(mat, fov, aspect, nearPlane, farPlane);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadTranslationMatrix()
+{
+ const Math::Vector translation(-0.3631590720995237, 1.6976327614875211, 0.0148815191502145);
+
+ Math::Matrix mathResult;
+ Math::LoadTranslationMatrix(mathResult, translation);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DUtil_SetTranslateMatrix(mat, translation.x, translation.y, translation.z);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadScaleMatrix()
+{
+ const Math::Vector scale(0.612236460285503, -0.635566935025364, -0.254321375332065);
+
+ Math::Matrix mathResult;
+ Math::LoadScaleMatrix(mathResult, scale);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DUtil_SetScaleMatrix(mat, scale.x, scale.y, scale.z);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadRotationXMatrix()
+{
+ const float angle = 0.513790685774275;
+
+ Math::Matrix mathResult;
+ Math::LoadRotationXMatrix(mathResult, angle);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DUtil_SetRotateXMatrix(mat, angle);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadRotationYMatrix()
+{
+ const float angle = -0.569166650127303;
+
+ Math::Matrix mathResult;
+ Math::LoadRotationYMatrix(mathResult, angle);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DUtil_SetRotateYMatrix(mat, angle);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadRotationZMatrix()
+{
+ const float angle = 0.380448034347452;
+
+ Math::Matrix mathResult;
+ Math::LoadRotationZMatrix(mathResult, angle);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DUtil_SetRotateZMatrix(mat, angle);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadRotationMatrix()
+{
+ const float angle = -0.987747190637790;
+ const Math::Vector dir(-0.113024727688331, -0.781265998072571, 1.838972397076884);
+
+ Math::Matrix mathResult;
+ Math::LoadRotationMatrix(mathResult, dir, angle);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ D3DVECTOR dirD3D = VEC_TO_D3DVEC(dir);
+ D3DUtil_SetRotationMatrix(mat, dirD3D, angle);
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadRotationXZYMatrix()
+{
+ const Math::Vector angles(-0.841366567984597, -0.100543315396357, 1.610647811559988);
+
+ Math::Matrix mathResult;
+ Math::LoadRotationXZYMatrix(mathResult, angles);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ MatRotateXZY(mat, VEC_TO_D3DVEC(angles));
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestLoadRotationZXYMatrix()
+{
+ const Math::Vector angles(0.275558495480206, -0.224328265970090, 0.943077216574253);
+
+ Math::Matrix mathResult;
+ Math::LoadRotationZXYMatrix(mathResult, angles);
+
+ Math::Matrix oldMathResult;
+ {
+ D3DMATRIX mat;
+ MatRotateZXY(mat, VEC_TO_D3DVEC(angles));
+ oldMathResult = D3DMAT_TO_MAT(mat);
+ }
+
+ if (! Math::MatricesEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+int TestTransform()
+{
+ Math::Matrix transformMatrix(
+ (float[4][4])
+ {
+ { -0.9282074720977896, 0.6794734970319730, -1.3234304946882685, 0.0925294727863890 },
+ { -0.0395527963683484, 0.2897634352353881, 1.9144398570315440, -1.4062267508968478 },
+ { 0.9133323625282361, -0.6741836434774530, -0.2188812951424338, -1.0089184339952666 },
+ { 0.0f, 0.0f, 0.0f, 1.0f }
+ }
+ );
+ Math::Vector vector(-0.314596433318370, -0.622681232583150, -0.371307535743574);
+
+ Math::Vector mathResult = Math::Transform(transformMatrix, vector);
+ Math::Vector oldMathResult = Transform(transformMatrix, vector);
+
+ if (! Math::VectorsEqual(mathResult, oldMathResult, TEST_TOLERANCE))
+ return __LINE__;
+
+ return 0;
+}
+
+*/
+
diff --git a/test/unit/math/matrix_test.cpp b/test/unit/math/matrix_test.cpp
new file mode 100644
index 0000000..5f5c3af
--- /dev/null
+++ b/test/unit/math/matrix_test.cpp
@@ -0,0 +1,312 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+/*
+ Unit tests for Matrix struct
+
+ Test data was randomly generated and the expected results
+ calculated using GNU Octave.
+ */
+
+#include "math/func.h"
+#include "math/matrix.h"
+
+#include "gtest/gtest.h"
+
+
+const float TEST_TOLERANCE = 1e-6;
+
+
+TEST(MatrixTest, TransposeTest)
+{
+ const Math::Matrix mat(
+ (float[4][4])
+ {
+ { -0.07011674491203920, 1.26145596067429810, 2.09476603598066902, 0.35560176915570696 },
+ { -1.34075615966224704, 1.17988499016709314, 0.00601713429241016, -0.75213676977972566 },
+ { 0.59186722295223981, 0.88089224074765293, 0.70994467464257294, 0.36730385425340212 },
+ { -0.95649396555068111, 0.75912182022565566, 1.34883305778387186, -1.34957997578168754 }
+ }
+ );
+
+ const Math::Matrix expectedTranspose(
+ (float[4][4])
+ {
+ { -0.07011674491203920, -1.34075615966224704, 0.59186722295223981, -0.95649396555068111 },
+ { 1.26145596067429810, 1.17988499016709314, 0.88089224074765293, 0.75912182022565566 },
+ { 2.09476603598066902, 0.00601713429241016, 0.70994467464257294, 1.34883305778387186 },
+ { 0.35560176915570696, -0.75213676977972566, 0.36730385425340212, -1.34957997578168754 }
+ }
+ );
+
+ Math::Matrix transpose = Math::Transpose(mat);
+
+ EXPECT_TRUE(Math::MatricesEqual(transpose, expectedTranspose, TEST_TOLERANCE));
+}
+
+TEST(MatrixTest, CofactorTest)
+{
+ const Math::Matrix mat1(
+ (float[4][4])
+ {
+ { 0.610630320796245, 1.059932357918312, -1.581674311378210, 1.782214448453331 },
+ { 0.191028848211526, -0.813898708757524, 1.516114203870644, 0.395202639476002 },
+ { 0.335142750345279, -0.346586619596529, 0.545382042472336, -0.879268918923072 },
+ { 1.417588151657198, 1.450841789070141, 0.219080104196171, 0.378724047481655 }
+ }
+ );
+
+ const Math::Matrix expectedCofactors1(
+ (float[4][4])
+ {
+ { -2.402679369186782, 2.282452509293019, 1.722732204057644, -0.746939701104385 },
+ { -0.687677756877654, 1.168949180331164, -0.985354966837796, -1.334071111592705 },
+ { -5.115621958424845, 4.229724770159009, 2.529000630782808, 1.481632618355891 },
+ { 0.147480897398694, -2.140677680337111, -1.207189492265546, 0.151236920408051 }
+ }
+ );
+
+ for (int r = 0; r < 4; ++r)
+ {
+ for (int c = 0; c < 4; ++c)
+ {
+ float ret = mat1.Cofactor(r, c);
+ float exp = expectedCofactors1.m[4*c+r];
+ EXPECT_TRUE(Math::IsEqual(ret, exp, TEST_TOLERANCE));
+ }
+ }
+
+ const Math::Matrix mat2(
+ (float[4][4])
+ {
+ { 0.9845099464982393, -0.9091233416532389, -0.6272243714245945, 0.4645001858944354 },
+ { -0.1333308471483736, 0.9128181433725897, -1.0937461393836190, 0.3180936795928376 },
+ { -0.0654324396846289, 0.1014641705415945, 1.5107709042683430, -0.0240560430414690 },
+ { 0.0179638644093347, -1.0695585982782767, -0.1741250853101032, 1.0803106709464336 }
+ }
+ );
+
+ const Math::Matrix expectedCofactors2(
+ (float[4][4])
+ {
+ { 2.0861102207614466, 0.2989010779528912, 0.0746276150537432, 0.2732659822656097 },
+ { 0.6850002886584565, 1.5513169659641379, -0.0503743176545917, 1.5163672441575642 },
+ { 1.2385556680997216, 1.1827709562505695, 1.2282813085138962, 1.3483789679871401 },
+ { -1.0710790241539783, -0.5589604503588883, 0.0100959837872308, 1.1897872684455839 }
+ }
+ );
+
+
+ for (int r = 0; r < 4; ++r)
+ {
+ for (int c = 0; c < 4; ++c)
+ {
+ float ret = mat2.Cofactor(r, c);
+ float exp = expectedCofactors2.m[4*c+r];
+ EXPECT_TRUE(Math::IsEqual(ret, exp, TEST_TOLERANCE));
+ }
+ }
+}
+
+TEST(MatrixTest, DetTest)
+{
+ const Math::Matrix mat1(
+ (float[4][4])
+ {
+ { -0.95880162984708284, 0.24004047608997131, -0.78172309932665407, -0.11604124457222834 },
+ { -0.36230592086261376, -0.75778166876017261, 0.33041059404631740, -1.06001391941094836 },
+ { 0.00260215210936187, 1.27485610196385113, -0.26149859846418033, -0.59669701186364876 },
+ { 0.36899429848485432, 3.01720896813933104, 2.10311476609438719, -1.68627076626448269 }
+ }
+ );
+
+ const float expectedDet1 = 4.07415413729671;
+
+ float ret1 = mat1.Det();
+ EXPECT_TRUE(Math::IsEqual(ret1, expectedDet1, TEST_TOLERANCE));
+
+ const Math::Matrix mat2(
+ (float[4][4])
+ {
+ { -1.0860073221346871, 0.9150354098189495, -0.2723201933559999, 0.2922832160271507 },
+ { -1.0248331304801788, -2.5081237461125205, -1.0277123574586633, -0.2254690663329798 },
+ { -1.4227635282899367, -0.0403846809122684, 0.9216148477171653, 1.2517067488015878 },
+ { -0.1160254467152022, 0.8270675274393656, 1.0327218739781614, -0.3674886870220400 }
+ }
+ );
+
+ const float expectedDet2 = -6.35122307880942;
+
+ float ret2 = mat2.Det();
+ EXPECT_TRUE(Math::IsEqual(ret2, expectedDet2, TEST_TOLERANCE));
+}
+
+TEST(MatrixTest, InverseTest)
+{
+ const Math::Matrix mat1(
+ (float[4][4])
+ {
+ { -2.2829352811514658, -0.9103222363187888, 0.2792976509411680, -0.7984393573193174 },
+ { 2.4823665798689589, -0.0599056759070980, 0.3832364352926366, -1.6404257204372739 },
+ { -0.3841952272526398, -0.8377700696457873, -0.3416328338427138, 1.1746577275723329 },
+ { 0.1746031241954947, -0.4952532117949962, 0.2155084379835037, -1.6586460437329220 }
+ }
+ );
+
+ const Math::Matrix expectedInverse1(
+ (float[4][4])
+ {
+ { -0.119472603171041, 0.331675963276297, 0.187516809009720, -0.137720814290806 },
+ { -0.387591686166085, -0.487284946727583, -0.798527541290274, 0.102991635972060 },
+ { 2.601905603425902, 2.606899016264679, -0.528006148839176, -4.204703326522837 },
+ { 0.441220327151392, 0.519128136207318, 0.189567009205522, -1.194469716136194 }
+ }
+ );
+
+ Math::Matrix inverse1 = mat1.Inverse();
+
+ EXPECT_TRUE(Math::MatricesEqual(inverse1, expectedInverse1, TEST_TOLERANCE));
+
+ const Math::Matrix mat2(
+ (float[4][4])
+ {
+ { -0.05464332404298505, -0.64357755258235749, -0.13017671677619302, -0.56742332785888006 },
+ { 0.29048383600458222, -0.91517047043724875, 0.84517524415561684, 0.51628195547960565 },
+ { 0.00946488004480186, -0.89077382212689293, 0.73565573766341397, -0.15932513521840930 },
+ { -1.01244718912499132, -0.27840911963972276, -0.39189681211309862, 1.18315064340192055 }
+ }
+ );
+
+ const Math::Matrix expectedInverse2(
+ (float[4][4])
+ {
+ { 0.771302711132012, 1.587542278361995, -2.003075114445104, -0.592574156227379 },
+ { -1.208929259769431, -0.786598967848473, 0.607335305808052, -0.154759693303324 },
+ { -1.500037668208218, -0.774300278997914, 1.917800427261255, -0.123268572651291 },
+ { -0.121314770937944, 0.916925149209746, -0.935924950785014, 0.260875394250671 }
+ }
+ );
+
+ Math::Matrix inverse2 = mat2.Inverse();
+
+ EXPECT_TRUE(Math::MatricesEqual(inverse2, expectedInverse2, TEST_TOLERANCE));
+}
+
+TEST(MatrixTest, MultiplyTest)
+{
+ const Math::Matrix mat1A(
+ (float[4][4])
+ {
+ { 0.6561727049162027, -1.4180263627131411, -0.8271026046117423, 2.3919331748512578 },
+ { -0.6035665535146352, 0.0150827348790615, -0.7090794192822540, 0.9057604704594814 },
+ { -0.9871045001223655, -0.4980646811455065, 0.3806177002298990, 0.1520583649240934 },
+ { -0.2721911170792712, 0.7627928194552067, -0.1504091336784158, 0.9747545351840121 }
+ }
+ );
+
+ const Math::Matrix mat1B(
+ (float[4][4])
+ {
+ { -0.2643735892448818, -0.7542994492819621, 0.6082322350568750, 0.0581733424861419 },
+ { 1.0293246070431237, 0.1979285388251341, -0.2932031385332818, 0.8838407179018929 },
+ { 0.3448687251553114, 0.5031654871245456, 0.7554693012922442, -0.4845315903845708 },
+ { -1.8662838497278593, -0.7843850624747805, 0.1389026096476257, -1.3686415408300689 }
+ }
+ );
+
+ const Math::Matrix expectedMultiply1(
+ (float[4][4])
+ {
+ { -6.382352236417988, -3.067984733682130, 0.522270304251466, -4.088079444498280 },
+ { -1.759853366848825, -0.608994052024491, -0.781406179437379, -0.917870775786188 },
+ { -0.404226802169062, 0.718232546720114, -0.145688356880835, -0.890167707987175 },
+ { -1.013918490922430, -0.483971504099758, -0.367442194643757, -0.602858486133615 }
+ }
+ );
+
+ Math::Matrix multiply1 = Math::MultiplyMatrices(mat1A, mat1B);
+ EXPECT_TRUE(Math::MatricesEqual(multiply1, expectedMultiply1, TEST_TOLERANCE));
+
+ const Math::Matrix mat2A(
+ (float[4][4])
+ {
+ { 0.8697203025776754, 2.1259475710644935, 1.7856691009707812, -2.1563963348328126 },
+ { 1.5888074489288735, -0.0794849733953615, 0.7307782768677457, 0.7943129159612630 },
+ { 0.2859761537233830, -0.6231231890384962, -0.0496743172880377, -0.8137857518646087 },
+ { 1.2670547229512983, -0.5305171374831831, -0.4987412674062375, -1.1257327113869595 }
+ }
+ );
+
+ const Math::Matrix mat2B(
+ (float[4][4])
+ {
+ { 1.1321105701165317, 0.1759563504574463, -2.0675778912000418, 1.4840339814245538 },
+ { -1.5117280888829916, -0.0933013188828093, -0.2079262944351640, 0.9575727579539316 },
+ { 0.3615378398970173, 1.2465163589027248, 1.1326150997082589, 0.9921208694352303 },
+ { -0.7357104529373861, -0.4774022005969588, -0.2118739096676499, 1.1427567093270703 }
+ }
+ );
+
+ const Math::Matrix expectedMultiply2(
+ (float[4][4])
+ {
+ { 0.00283516267056338, 3.21001319965989307, 0.23910503934370686, 2.63380716363006107 },
+ { 1.59868505822469742, 0.81869715594617765, -2.60905981088293570, 3.91445839239110294 },
+ { 1.84650099286297942, 0.43504079532852930, -0.34555619012424243, -1.15152951542451487 },
+ { 2.88434318563174585, 0.18818239851585700, -2.83579436909308980, -0.40890672198610400 }
+ }
+ );
+
+ Math::Matrix multiply2 = Math::MultiplyMatrices(mat2A, mat2B);
+ EXPECT_TRUE(Math::MatricesEqual(multiply2, expectedMultiply2, TEST_TOLERANCE));
+}
+
+TEST(MatrixTest, MultiplyVectorTest)
+{
+ const Math::Matrix mat1(
+ (float[4][4])
+ {
+ { 0.188562846910008, -0.015148651460679, 0.394512304108827, 0.906910631257135 },
+ { -0.297506779519667, 0.940119328178913, 0.970957796752517, 0.310559318965526 },
+ { -0.819770525290873, -2.316574438778879, 0.155756069319732, -0.855661405742964 },
+ { 0.000000000000000, 0.000000000000000, 0.000000000000000, 1.000000000000000 }
+ }
+ );
+
+ const Math::Vector vec1(-0.824708565156661, -1.598287748103842, -0.422498044734181);
+
+ const Math::Vector expectedMultiply1(0.608932463260470, -1.356893266403749, 3.457156276255142);
+
+ Math::Vector multiply1 = Math::MatrixVectorMultiply(mat1, vec1, false);
+ EXPECT_TRUE(Math::VectorsEqual(multiply1, expectedMultiply1, TEST_TOLERANCE));
+
+ const Math::Matrix mat2(
+ (float[4][4])
+ {
+ { -0.63287117038834284, 0.55148060401816856, -0.02042395559467368, -1.50367083897656850 },
+ { 0.69629042156335297, 0.12982747869796774, -1.16250029235919405, 1.19084447253756909 },
+ { 0.44164132914357224, -0.15169304045662041, -0.00880583574621390, -0.55817802940035310 },
+ { 0.95680476533530789, -1.51912346889253125, -0.74209769406615944, -0.20938988867903682 }
+ }
+ );
+
+ const Math::Vector vec2(0.330987381051962, 1.494375516393466, 1.483422335561857);
+
+ const Math::Vector expectedMultiply2(0.2816820577317669, 0.0334468811767428, 0.1996974284970455);
+
+ Math::Vector multiply2 = Math::MatrixVectorMultiply(mat2, vec2, true);
+ EXPECT_TRUE(Math::VectorsEqual(multiply2, expectedMultiply2, TEST_TOLERANCE));
+}
diff --git a/test/unit/math/vector_test.cpp b/test/unit/math/vector_test.cpp
new file mode 100644
index 0000000..41bac74
--- /dev/null
+++ b/test/unit/math/vector_test.cpp
@@ -0,0 +1,72 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+/*
+ Unit tests for Vector struct
+
+ Test data was randomly generated and the expected results
+ calculated using GNU Octave.
+ */
+
+#include "math/func.h"
+#include "math/vector.h"
+
+#include "gtest/gtest.h"
+
+
+const float TEST_TOLERANCE = 1e-6;
+
+
+TEST(VectorTest, LengthTest)
+{
+ Math::Vector vec(-1.288447945923275, 0.681452565308134, -0.633761098985957);
+ const float expectedLength = 1.58938001708428;
+
+ EXPECT_TRUE(Math::IsEqual(vec.Length(), expectedLength, TEST_TOLERANCE));
+}
+
+TEST(VectorTest, NormalizeTest)
+{
+ Math::Vector vec(1.848877241804398, -0.157262961268577, -1.963031403332377);
+ const Math::Vector expectedNormalized(0.6844609421393856, -0.0582193085618106, -0.7267212194481797);
+
+ vec.Normalize();
+
+ EXPECT_TRUE(Math::VectorsEqual(vec, expectedNormalized, TEST_TOLERANCE));
+}
+
+TEST(VectorTest, DotTest)
+{
+ Math::Vector vecA(0.8202190530968309, 0.0130926060162780, 0.2411914183883510);
+ Math::Vector vecB(-0.0524083951404069, 1.5564932716738220, -0.8971342631500536);
+
+ float expectedDot = -0.238988896477326;
+
+ EXPECT_TRUE(Math::IsEqual(Math::DotProduct(vecA, vecB), expectedDot, TEST_TOLERANCE));
+}
+
+TEST(VectorTest, CrossTest)
+{
+ Math::Vector vecA(1.37380499798567, 1.18054518384682, 1.95166361293121);
+ Math::Vector vecB(0.891657855926886, 0.447591335394532, -0.901604070087823);
+
+ Math::Vector expectedCross(-1.937932065431669, 2.978844370287636, -0.437739173833581);
+ Math::Vector expectedReverseCross = -expectedCross;
+
+ EXPECT_TRUE(Math::VectorsEqual(vecA.CrossMultiply(vecB), expectedCross, TEST_TOLERANCE));
+
+ EXPECT_TRUE(Math::VectorsEqual(vecB.CrossMultiply(vecA), expectedReverseCross, TEST_TOLERANCE));
+}
diff --git a/test/unit/ui/CMakeLists.txt b/test/unit/ui/CMakeLists.txt
new file mode 100644
index 0000000..c899834
--- /dev/null
+++ b/test/unit/ui/CMakeLists.txt
@@ -0,0 +1,41 @@
+set(SRC_DIR ${colobot_SOURCE_DIR}/src)
+
+include_directories(
+.
+${SRC_DIR}
+${GTEST_INCLUDE_DIR}
+${GMOCK_INCLUDE_DIR}
+)
+
+# Platform-dependent implementation of CSystemUtils
+if (${PLATFORM_WINDOWS})
+ set(SYSTEM_CPP_MODULE "system_windows.cpp")
+elseif(${PLATFORM_LINUX})
+ set(SYSTEM_CPP_MODULE "system_linux.cpp")
+else()
+ set(SYSTEM_CPP_MODULE "system_other.cpp")
+endif()
+
+add_executable(edit_test
+${SRC_DIR}/app/system.cpp
+${SRC_DIR}/app/${SYSTEM_CPP_MODULE}
+${SRC_DIR}/common/event.cpp
+${SRC_DIR}/common/logger.cpp
+${SRC_DIR}/common/misc.cpp
+${SRC_DIR}/common/profile.cpp
+${SRC_DIR}/common/iman.cpp
+${SRC_DIR}/common/stringutils.cpp
+${SRC_DIR}/graphics/engine/text.cpp
+${SRC_DIR}/ui/button.cpp
+${SRC_DIR}/ui/control.cpp
+${SRC_DIR}/ui/edit.cpp
+${SRC_DIR}/ui/scroll.cpp
+stubs/app_stub.cpp
+stubs/engine_stub.cpp
+stubs/particle_stub.cpp
+stubs/restext_stub.cpp
+stubs/robotmain_stub.cpp
+edit_test.cpp)
+target_link_libraries(edit_test gtest gmock ${SDL_LIBRARY} ${SDLTTF_LIBRARY} ${Boost_LIBRARIES})
+
+add_test(edit_test ./edit_test)
diff --git a/test/unit/ui/edit_test.cpp b/test/unit/ui/edit_test.cpp
new file mode 100644
index 0000000..428b66a
--- /dev/null
+++ b/test/unit/ui/edit_test.cpp
@@ -0,0 +1,80 @@
+#include "app/app.h"
+#include "ui/edit.h"
+#include "mocks/text_mock.h"
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <fstream>
+
+class CEditTest : public testing::Test
+{
+public:
+ CEditTest()
+ : m_robotMain(nullptr)
+ , m_engine(nullptr)
+ , m_edit(nullptr)
+ {}
+
+ virtual void SetUp()
+ {
+ m_robotMain = new CRobotMain(&m_app);
+
+ m_engine = new Gfx::CEngine(nullptr);
+
+ m_edit = new Ui::CEdit;
+ }
+
+ virtual void TearDown()
+ {
+ delete m_robotMain;
+ m_robotMain = nullptr;
+ delete m_engine;
+ m_engine = nullptr;
+ delete m_edit;
+ m_edit = nullptr;
+
+ }
+ virtual ~CEditTest()
+ {
+
+ };
+
+protected:
+ CApplication m_app;
+ CRobotMain* m_robotMain;
+ Gfx::CEngine * m_engine;
+ Ui::CEdit * m_edit;
+ CLogger m_logger;
+};
+
+using ::testing::_;
+using ::testing::Return;
+
+TEST_F(CEditTest, WriteTest)
+{
+ ASSERT_TRUE(true);
+ CTextMock * text = dynamic_cast<CTextMock *>(m_engine->GetText());
+ EXPECT_CALL(*text, GetCharWidth(_, _, _, _)).WillRepeatedly(Return(1.0f));
+ EXPECT_CALL(*text, GetStringWidth(_, _, _, _)).WillOnce(Return(1.0f));
+ std::string filename = "test.file";
+ m_edit->SetMaxChar(Ui::EDITSTUDIOMAX);
+ m_edit->SetAutoIndent(true);
+ std::string inputScript = "{\ntext1\ntext2\n\ntext3\n{\ntext4\n}\n}";
+ std::string expectedScript = "{\r\n\ttext1\r\n\ttext2\r\n\t\r\n\ttext3\r\n\t{\r\n\t\ttext4\r\n\t}\r\n}";
+ m_edit->SetText(inputScript.c_str(), true);
+ GetLogger()->Info("Writing text \n");
+ m_edit->WriteText("script.txt");
+
+ std::fstream scriptFile;
+
+ scriptFile.open("script.txt", std::ios_base::binary | std::ios_base::in);
+ std::string outputScript((std::istreambuf_iterator<char>(scriptFile)), std::istreambuf_iterator<char>());
+ ASSERT_STREQ(expectedScript.c_str(), outputScript.c_str());
+}
+
+int main(int argc, char *argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
diff --git a/test/unit/ui/mocks/text_mock.h b/test/unit/ui/mocks/text_mock.h
new file mode 100644
index 0000000..f38b977
--- /dev/null
+++ b/test/unit/ui/mocks/text_mock.h
@@ -0,0 +1,25 @@
+#include "common/logger.h"
+
+#include "graphics/engine/text.h"
+
+#include <gmock/gmock.h>
+
+class CTextMock : public Gfx::CText
+{
+public:
+ CTextMock(Gfx::CEngine* engine) : CText(engine)
+ {
+ }
+
+ virtual ~CTextMock()
+ {
+ };
+
+ MOCK_METHOD4(GetCharWidth, float(Gfx::UTF8Char, Gfx::FontType, float, float));
+ MOCK_METHOD4(GetStringWidth, float(const std::string &text,
+ std::vector<Gfx::FontMetaChar>::iterator format,
+ std::vector<Gfx::FontMetaChar>::iterator end, float size));
+ MOCK_METHOD3(GetStringWidth, float(const std::string &, Gfx::FontType, float));
+
+};
+
diff --git a/test/unit/ui/stubs/app_stub.cpp b/test/unit/ui/stubs/app_stub.cpp
new file mode 100644
index 0000000..9b33e5e
--- /dev/null
+++ b/test/unit/ui/stubs/app_stub.cpp
@@ -0,0 +1,52 @@
+#include "app/app.h"
+
+#include "graphics/opengl/gldevice.h"
+
+template<> CApplication* CSingleton<CApplication>::m_instance = nullptr;
+
+namespace Gfx {
+
+GLDeviceConfig::GLDeviceConfig()
+{
+}
+
+} /* Gfx */
+
+
+CApplication::CApplication()
+{
+}
+
+CApplication::~CApplication()
+{
+}
+
+std::string CApplication::GetDataFilePath(DataDir /* dataDir */, const std::string& subpath)
+{
+ return subpath;
+}
+
+CSoundInterface* CApplication::GetSound()
+{
+ return nullptr;
+}
+
+CEventQueue* CApplication::GetEventQueue()
+{
+ return nullptr;
+}
+
+std::string CApplication::GetDataDirPath()
+{
+ return "";
+}
+
+Event CApplication::CreateUpdateEvent()
+{
+ return Event(EVENT_NULL);
+}
+
+char CApplication::GetLanguageChar()
+{
+ return 'E';
+}
diff --git a/test/unit/ui/stubs/engine_stub.cpp b/test/unit/ui/stubs/engine_stub.cpp
new file mode 100644
index 0000000..0a2777c
--- /dev/null
+++ b/test/unit/ui/stubs/engine_stub.cpp
@@ -0,0 +1,104 @@
+#include "graphics/engine/engine.h"
+#include "graphics/engine/text.h"
+
+#include "mocks/text_mock.h"
+
+template<> Gfx::CEngine* CSingleton<Gfx::CEngine>::m_instance = nullptr;
+
+namespace Gfx {
+
+CEngine::CEngine(CApplication* app) :
+ m_app(app)
+{
+ m_text = new CTextMock(this);
+ m_text->Create();
+}
+
+CEngine::~CEngine()
+{
+ delete m_text;
+ m_text = nullptr;
+}
+
+CParticle* CEngine::GetParticle()
+{
+ return nullptr;
+}
+
+Math::Point CEngine::WindowToInterfaceSize(Math::IntPoint size)
+{
+ return Math::Point(size.x, size.y);
+}
+
+void CEngine::SetState(int state, const Color& color)
+{
+ if (state == m_lastState && color == m_lastColor)
+ return;
+
+ m_lastState = state;
+ m_lastColor = color;
+}
+
+Math::IntPoint CEngine::GetWindowSize()
+{
+ return m_size;
+}
+
+void CEngine::AddStatisticTriangle(int count)
+{
+ m_statisticTriangle += count;
+}
+
+void CEngine::SetMouseType(EngineMouseType type)
+{
+ m_mouseType = type;
+}
+
+bool CEngine::SetTexture(const std::string& /* name */, int /* stage */)
+{
+ return true;
+}
+
+CText* CEngine::GetText()
+{
+ return m_text;
+}
+
+CDevice* CEngine::GetDevice()
+{
+ return m_device;
+}
+
+int CEngine::GetEditIndentValue()
+{
+ return m_editIndentValue;
+}
+
+void CEngine::DeleteTexture(const std::string& /* texName */)
+{
+}
+
+Texture CEngine::LoadTexture(const std::string& /* name */)
+{
+ Texture texture;
+ return texture;
+}
+
+Math::Vector CEngine::GetEyePt()
+{
+ return Math::Vector();
+}
+
+Math::Vector CEngine::GetLookatPt()
+{
+ return Math::Vector();
+}
+
+bool CEngine::GetPause()
+{
+ return false;
+}
+
+
+} /* Gfx */
+
diff --git a/test/unit/ui/stubs/particle_stub.cpp b/test/unit/ui/stubs/particle_stub.cpp
new file mode 100644
index 0000000..34cf973
--- /dev/null
+++ b/test/unit/ui/stubs/particle_stub.cpp
@@ -0,0 +1,205 @@
+#include "graphics/engine/particle.h"
+
+#include "common/logger.h"
+
+
+// Graphics module namespace
+namespace Gfx {
+
+
+CParticle::CParticle(CEngine* /*engine*/)
+{
+}
+
+CParticle::~CParticle()
+{
+}
+
+void CParticle::SetDevice(CDevice* /*device*/)
+{
+}
+
+void CParticle::FlushParticle()
+{
+}
+
+void CParticle::FlushParticle(int /*sheet*/)
+{
+}
+
+int CParticle::CreateParticle(Math::Vector /*pos*/, Math::Vector /*speed*/, Math::Point /*dim*/,
+ ParticleType /*type*/, float /*duration*/, float /*mass*/,
+ float /*windSensitivity*/, int /*sheet*/)
+{
+ return 0;
+}
+
+int CParticle::CreateFrag(Math::Vector /*pos*/, Math::Vector /*speed*/, EngineTriangle */*triangle*/,
+ ParticleType /*type*/, float /*duration*/, float /*mass*/,
+ float /*windSensitivity*/, int /*sheet*/)
+{
+ return 0;
+}
+
+int CParticle::CreatePart(Math::Vector /*pos*/, Math::Vector /*speed*/, ParticleType /*type*/,
+ float /*duration*/, float /*mass*/, float /*weight*/,
+ float /*windSensitivity*/, int /*sheet*/)
+{
+ return 0;
+}
+
+int CParticle::CreateRay(Math::Vector /*pos*/, Math::Vector /*goal*/, ParticleType /*type*/, Math::Point /*dim*/,
+ float /*duration*/, int /*sheet*/)
+{
+ return 0;
+}
+
+int CParticle::CreateTrack(Math::Vector /*pos*/, Math::Vector /*speed*/, Math::Point /*dim*/, ParticleType /*type*/,
+ float /*duration*/, float /*mass*/, float /*length*/, float /*width*/)
+{
+ return 0;
+}
+
+void CParticle::CreateWheelTrace(const Math::Vector &/*p1*/, const Math::Vector &/*p2*/, const Math::Vector &/*p3*/,
+ const Math::Vector &/*p4*/, ParticleType /*type*/)
+{
+}
+
+void CParticle::DeleteParticle(ParticleType /*type*/)
+{
+}
+
+void CParticle::DeleteParticle(int /*channel*/)
+{
+}
+
+void CParticle::SetObjectLink(int /*channel*/, CObject */*object*/)
+{
+}
+
+void CParticle::SetObjectFather(int /*channel*/, CObject */*object*/)
+{
+}
+
+void CParticle::SetPosition(int /*channel*/, Math::Vector /*pos*/)
+{
+}
+
+void CParticle::SetDimension(int /*channel*/, Math::Point /*dim*/)
+{
+}
+
+void CParticle::SetZoom(int /*channel*/, float /*zoom*/)
+{
+}
+
+void CParticle::SetAngle(int /*channel*/, float /*angle*/)
+{
+}
+
+void CParticle::SetIntensity(int /*channel*/, float /*intensity*/)
+{
+}
+
+void CParticle::SetParam(int /*channel*/, Math::Vector /*pos*/, Math::Point /*dim*/, float /*zoom*/, float /*angle*/, float /*intensity*/)
+{
+}
+
+void CParticle::SetPhase(int /*channel*/, ParticlePhase /*phase*/, float /*duration*/)
+{
+}
+
+bool CParticle::GetPosition(int /*channel*/, Math::Vector &/*pos*/)
+{
+ return true;
+}
+
+Color CParticle::GetFogColor(Math::Vector /*pos*/)
+{
+ return Color();
+}
+
+void CParticle::SetFrameUpdate(int /*sheet*/, bool /*update*/)
+{
+}
+
+void CParticle::FrameParticle(float /*rTime*/)
+{
+}
+
+void CParticle::DrawParticle(int /*sheet*/)
+{
+}
+
+bool CParticle::WriteWheelTrace(const char */*filename*/, int /*width*/, int /*height*/, Math::Vector /*dl*/, Math::Vector /*ur*/)
+{
+ return true;
+}
+
+void CParticle::DeleteRank(int /*rank*/)
+{
+}
+
+bool CParticle::CheckChannel(int &/*channel*/)
+{
+ return true;
+}
+
+void CParticle::DrawParticleTriangle(int /*i*/)
+{
+}
+
+void CParticle::DrawParticleNorm(int /*i*/)
+{
+}
+
+void CParticle::DrawParticleFlat(int /*i*/)
+{
+}
+
+void CParticle::DrawParticleFog(int /*i*/)
+{
+}
+
+void CParticle::DrawParticleRay(int /*i*/)
+{
+}
+
+void CParticle::DrawParticleSphere(int /*i*/)
+{
+}
+
+void CParticle::DrawParticleCylinder(int /*i*/)
+{
+}
+
+void CParticle::DrawParticleWheel(int /*i*/)
+{
+}
+
+CObject* CParticle::SearchObjectGun(Math::Vector /*old*/, Math::Vector /*pos*/, ParticleType /*type*/, CObject */*father*/)
+{
+ return nullptr;
+}
+
+CObject* CParticle::SearchObjectRay(Math::Vector /*pos*/, Math::Vector /*goal*/, ParticleType /*type*/, CObject */*father*/)
+{
+ return nullptr;
+}
+
+void CParticle::Play(Sound /*sound*/, Math::Vector /*pos*/, float /*amplitude*/)
+{
+}
+
+bool CParticle::TrackMove(int /*i*/, Math::Vector /*pos*/, float /*progress*/)
+{
+ return true;
+}
+
+void CParticle::TrackDraw(int /*i*/, ParticleType /*type*/)
+{
+}
+
+
+} // namespace Gfx
+
diff --git a/test/unit/ui/stubs/restext_stub.cpp b/test/unit/ui/stubs/restext_stub.cpp
new file mode 100644
index 0000000..004da19
--- /dev/null
+++ b/test/unit/ui/stubs/restext_stub.cpp
@@ -0,0 +1,12 @@
+#include "common/restext.h"
+
+bool GetResource(ResType /* type */, int /* num */, char* /* text */)
+{
+ return true;
+}
+
+bool SearchKey(const char * /* cmd */, InputSlot & /* key */)
+{
+ return true;
+}
+
diff --git a/test/unit/ui/stubs/robotmain_stub.cpp b/test/unit/ui/stubs/robotmain_stub.cpp
new file mode 100644
index 0000000..7988e9d
--- /dev/null
+++ b/test/unit/ui/stubs/robotmain_stub.cpp
@@ -0,0 +1,25 @@
+#include "object/robotmain.h"
+
+
+template<> CRobotMain* CSingleton<CRobotMain>::m_instance = nullptr;
+
+CRobotMain::CRobotMain(CApplication* app)
+{
+}
+
+CRobotMain::~CRobotMain()
+{
+}
+
+bool CRobotMain::GetGlint()
+{
+ return false;
+}
+
+const InputBinding& CRobotMain::GetInputBinding(InputSlot slot)
+{
+ unsigned int index = static_cast<unsigned int>(slot);
+ assert(index >= 0 && index < INPUT_SLOT_MAX);
+ return m_inputBindings[index];
+}
+