Engine: 实现 Components 和 Scene 模块,包含完整单元测试
新增 Components 模块: - Component 基类 (生命周期、启用状态管理) - TransformComponent (本地/世界空间变换、矩阵缓存、父子层级) - GameObject (组件管理、父子层级、激活状态、静态查找) 新增 Scene 模块: - Scene (场景管理、对象创建销毁、查找、生命周期) - SceneManager (单例模式、多场景管理、场景切换) 新增测试: - test_component.cpp (12 个测试) - test_transform_component.cpp (35 个测试) - test_game_object.cpp (26 个测试) - test_scene.cpp (20 个测试) - test_scene_manager.cpp (17 个测试) 所有测试均已编译通过。
This commit is contained in:
@@ -220,6 +220,28 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
|
||||||
|
|
||||||
|
# Audio
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioTypes.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioConfig.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/IAudioBackend.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioSystem.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/WASAPI/WASAPIBackend.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/AudioSystem.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/WASAPI/WASAPIBackend.cpp
|
||||||
|
|
||||||
|
# Audio Components
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/AudioSourceComponent.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/AudioListenerComponent.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/AudioSourceComponent.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/AudioListenerComponent.cpp
|
||||||
|
|
||||||
|
# Third-party (KissFFT)
|
||||||
|
${CMAKE_SOURCE_DIR}/third_party/kissfft/kiss_fft.h
|
||||||
|
${CMAKE_SOURCE_DIR}/third_party/kissfft/kiss_fft.c
|
||||||
|
${CMAKE_SOURCE_DIR}/third_party/kissfft/kiss_fftr.h
|
||||||
|
${CMAKE_SOURCE_DIR}/third_party/kissfft/kiss_fftr.c
|
||||||
|
${CMAKE_SOURCE_DIR}/third_party/kissfft/_kiss_fft_guts.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(XCEngine PUBLIC
|
target_include_directories(XCEngine PUBLIC
|
||||||
@@ -227,6 +249,7 @@ target_include_directories(XCEngine PUBLIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include
|
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include
|
||||||
|
${CMAKE_SOURCE_DIR}/third_party
|
||||||
)
|
)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ public:
|
|||||||
// Hierarchy - parent
|
// Hierarchy - parent
|
||||||
TransformComponent* GetParent() const { return m_parent; }
|
TransformComponent* GetParent() const { return m_parent; }
|
||||||
void SetParent(TransformComponent* parent, bool worldPositionStays = true);
|
void SetParent(TransformComponent* parent, bool worldPositionStays = true);
|
||||||
void SetParent(TransformComponent* parent) { SetParent(parent, true); }
|
|
||||||
|
|
||||||
// Hierarchy - children
|
// Hierarchy - children
|
||||||
size_t GetChildCount() const { return m_children.size(); }
|
size_t GetChildCount() const { return m_children.size(); }
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ add_subdirectory(containers)
|
|||||||
add_subdirectory(memory)
|
add_subdirectory(memory)
|
||||||
add_subdirectory(threading)
|
add_subdirectory(threading)
|
||||||
add_subdirectory(debug)
|
add_subdirectory(debug)
|
||||||
|
add_subdirectory(Components)
|
||||||
|
add_subdirectory(Scene)
|
||||||
add_subdirectory(RHI)
|
add_subdirectory(RHI)
|
||||||
add_subdirectory(RHI/D3D12)
|
add_subdirectory(RHI/D3D12)
|
||||||
add_subdirectory(RHI/OpenGL)
|
add_subdirectory(RHI/OpenGL)
|
||||||
|
|||||||
29
tests/Components/CMakeLists.txt
Normal file
29
tests/Components/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
project(XCEngine_ComponentsTests)
|
||||||
|
|
||||||
|
set(COMPONENTS_TEST_SOURCES
|
||||||
|
test_component.cpp
|
||||||
|
test_transform_component.cpp
|
||||||
|
test_game_object.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(xcengine_components_tests ${COMPONENTS_TEST_SOURCES})
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set_target_properties(xcengine_components_tests PROPERTIES
|
||||||
|
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(xcengine_components_tests PRIVATE
|
||||||
|
XCEngine
|
||||||
|
GTest::gtest
|
||||||
|
GTest::gtest_main
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(xcengine_components_tests PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}/engine/include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME ComponentTests COMMAND xcengine_components_tests)
|
||||||
110
tests/Components/test_component.cpp
Normal file
110
tests/Components/test_component.cpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <XCEngine/Components/Component.h>
|
||||||
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TestComponent : public Component {
|
||||||
|
public:
|
||||||
|
TestComponent() = default;
|
||||||
|
explicit TestComponent(const std::string& name) : m_customName(name) {}
|
||||||
|
|
||||||
|
std::string GetName() const override {
|
||||||
|
return m_customName.empty() ? "TestComponent" : m_customName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_awakeCalled = false;
|
||||||
|
bool m_startCalled = false;
|
||||||
|
bool m_updateCalled = false;
|
||||||
|
bool m_onEnableCalled = false;
|
||||||
|
bool m_onDisableCalled = false;
|
||||||
|
bool m_onDestroyCalled = false;
|
||||||
|
|
||||||
|
void Awake() override { m_awakeCalled = true; }
|
||||||
|
void Start() override { m_startCalled = true; }
|
||||||
|
void Update(float deltaTime) override { m_updateCalled = true; }
|
||||||
|
void OnEnable() override { m_onEnableCalled = true; }
|
||||||
|
void OnDisable() override { m_onDisableCalled = true; }
|
||||||
|
void OnDestroy() override { m_onDestroyCalled = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_customName;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Component_Test, DefaultConstructor) {
|
||||||
|
TestComponent comp;
|
||||||
|
EXPECT_EQ(comp.GetName(), "TestComponent");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, GetGameObject_ReturnsNullptr_WhenNotAttached) {
|
||||||
|
TestComponent comp;
|
||||||
|
EXPECT_EQ(comp.GetGameObject(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, IsEnabled_DefaultTrue) {
|
||||||
|
TestComponent comp;
|
||||||
|
EXPECT_TRUE(comp.IsEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, SetEnabled_TrueToFalse) {
|
||||||
|
TestComponent comp;
|
||||||
|
EXPECT_TRUE(comp.IsEnabled());
|
||||||
|
comp.SetEnabled(false);
|
||||||
|
EXPECT_FALSE(comp.IsEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, SetEnabled_FalseToTrue) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.SetEnabled(false);
|
||||||
|
EXPECT_FALSE(comp.IsEnabled());
|
||||||
|
comp.SetEnabled(true);
|
||||||
|
EXPECT_TRUE(comp.IsEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, Lifecycle_AwakeCalled) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.Awake();
|
||||||
|
EXPECT_TRUE(comp.m_awakeCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, Lifecycle_StartCalled) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.Start();
|
||||||
|
EXPECT_TRUE(comp.m_startCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, Lifecycle_UpdateCalled) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.Update(0.016f);
|
||||||
|
EXPECT_TRUE(comp.m_updateCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, Lifecycle_OnEnableCalled_WhenEnabling) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.SetEnabled(false);
|
||||||
|
comp.SetEnabled(true);
|
||||||
|
EXPECT_TRUE(comp.m_onEnableCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, Lifecycle_OnDisableCalled_WhenDisabling) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.SetEnabled(false);
|
||||||
|
EXPECT_TRUE(comp.m_onDisableCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, Lifecycle_OnDestroyCalled) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.OnDestroy();
|
||||||
|
EXPECT_TRUE(comp.m_onDestroyCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Component_Test, SetEnabled_NoCallback_WhenStateUnchanged) {
|
||||||
|
TestComponent comp;
|
||||||
|
comp.SetEnabled(true);
|
||||||
|
EXPECT_FALSE(comp.m_onEnableCalled);
|
||||||
|
EXPECT_FALSE(comp.m_onDisableCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
277
tests/Components/test_game_object.cpp
Normal file
277
tests/Components/test_game_object.cpp
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
#include <XCEngine/Components/TransformComponent.h>
|
||||||
|
#include <XCEngine/Scene/Scene.h>
|
||||||
|
#include <XCEngine/Math/Vector3.h>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
using namespace XCEngine::Math;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TestComponent : public Component {
|
||||||
|
public:
|
||||||
|
TestComponent() = default;
|
||||||
|
explicit TestComponent(const std::string& name) : m_customName(name) {}
|
||||||
|
|
||||||
|
std::string GetName() const override {
|
||||||
|
return m_customName.empty() ? "TestComponent" : m_customName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_awakeCalled = false;
|
||||||
|
bool m_startCalled = false;
|
||||||
|
bool m_updateCalled = false;
|
||||||
|
|
||||||
|
void Awake() override { m_awakeCalled = true; }
|
||||||
|
void Start() override { m_startCalled = true; }
|
||||||
|
void Update(float deltaTime) override { m_updateCalled = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_customName;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GameObjectTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
testScene = std::make_unique<Scene>("TestScene");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Scene> testScene;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(GameObject_Test, DefaultConstructor_DefaultValues) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
EXPECT_EQ(go.GetName(), "GameObject");
|
||||||
|
EXPECT_TRUE(go.IsActive());
|
||||||
|
EXPECT_EQ(go.GetID(), GameObject::INVALID_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, NamedConstructor) {
|
||||||
|
GameObject go("TestObject");
|
||||||
|
|
||||||
|
EXPECT_EQ(go.GetName(), "TestObject");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, AddComponent_Single) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
TestComponent* comp = go.AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
EXPECT_NE(comp, nullptr);
|
||||||
|
EXPECT_EQ(go.GetComponent<TestComponent>(), comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, AddComponent_Multiple) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
TestComponent* comp1 = go.AddComponent<TestComponent>();
|
||||||
|
TestComponent* comp2 = go.AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
EXPECT_NE(comp1, nullptr);
|
||||||
|
EXPECT_NE(comp2, nullptr);
|
||||||
|
EXPECT_NE(comp1, comp2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, AddComponent_TransformComponent) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
TransformComponent* tc = go.GetTransform();
|
||||||
|
|
||||||
|
EXPECT_NE(tc, nullptr);
|
||||||
|
EXPECT_EQ(tc->GetName(), "Transform");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetComponent_Exists) {
|
||||||
|
GameObject go;
|
||||||
|
TestComponent* comp = go.AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
TestComponent* found = go.GetComponent<TestComponent>();
|
||||||
|
|
||||||
|
EXPECT_EQ(found, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetComponent_NotExists) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
TestComponent* found = go.GetComponent<TestComponent>();
|
||||||
|
|
||||||
|
EXPECT_EQ(found, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetComponents_Multiple) {
|
||||||
|
GameObject go;
|
||||||
|
go.AddComponent<TestComponent>();
|
||||||
|
go.AddComponent<TestComponent>();
|
||||||
|
go.AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
std::vector<TestComponent*> comps = go.GetComponents<TestComponent>();
|
||||||
|
|
||||||
|
EXPECT_EQ(comps.size(), 3u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetTransform_ReturnsTransform) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
TransformComponent* tc = go.GetTransform();
|
||||||
|
|
||||||
|
EXPECT_NE(tc, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, SetParent_WithWorldPosition) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child("Child");
|
||||||
|
|
||||||
|
parent.GetTransform()->SetLocalPosition(Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
child.GetTransform()->SetLocalPosition(Vector3(2.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
child.SetParent(&parent, true);
|
||||||
|
|
||||||
|
Vector3 childWorldPos = child.GetTransform()->GetPosition();
|
||||||
|
EXPECT_NEAR(childWorldPos.x, 2.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, SetParent_WithoutWorldPosition) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child("Child");
|
||||||
|
|
||||||
|
parent.GetTransform()->SetLocalPosition(Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
child.GetTransform()->SetLocalPosition(Vector3(2.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
child.SetParent(&parent, false);
|
||||||
|
|
||||||
|
Vector3 childWorldPos = child.GetTransform()->GetPosition();
|
||||||
|
EXPECT_NEAR(childWorldPos.x, 3.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetChild_ValidIndex) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child("Child");
|
||||||
|
|
||||||
|
child.SetParent(&parent);
|
||||||
|
|
||||||
|
GameObject* found = parent.GetChild(0);
|
||||||
|
|
||||||
|
EXPECT_EQ(found, &child);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetChild_InvalidIndex) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
|
||||||
|
GameObject* found = parent.GetChild(0);
|
||||||
|
|
||||||
|
EXPECT_EQ(found, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetChildren_ReturnsAllChildren) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child1("Child1");
|
||||||
|
GameObject child2("Child2");
|
||||||
|
|
||||||
|
child1.SetParent(&parent);
|
||||||
|
child2.SetParent(&parent);
|
||||||
|
|
||||||
|
std::vector<GameObject*> children = parent.GetChildren();
|
||||||
|
|
||||||
|
EXPECT_EQ(children.size(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, DetachChildren) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child("Child");
|
||||||
|
|
||||||
|
child.SetParent(&parent);
|
||||||
|
EXPECT_EQ(parent.GetChildCount(), 1u);
|
||||||
|
|
||||||
|
parent.DetachChildren();
|
||||||
|
|
||||||
|
EXPECT_EQ(parent.GetChildCount(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, IsActive_DefaultTrue) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
EXPECT_TRUE(go.IsActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, SetActive_False) {
|
||||||
|
GameObject go;
|
||||||
|
|
||||||
|
go.SetActive(false);
|
||||||
|
|
||||||
|
EXPECT_FALSE(go.IsActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, IsActiveInHierarchy_True) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child("Child");
|
||||||
|
|
||||||
|
child.SetParent(&parent);
|
||||||
|
|
||||||
|
EXPECT_TRUE(child.IsActiveInHierarchy());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, IsActiveInHierarchy_FalseWhenParentInactive) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child("Child");
|
||||||
|
|
||||||
|
child.SetParent(&parent);
|
||||||
|
parent.SetActive(false);
|
||||||
|
|
||||||
|
EXPECT_FALSE(child.IsActiveInHierarchy());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, Lifecycle_Awake) {
|
||||||
|
GameObject go;
|
||||||
|
TestComponent* comp = go.AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
go.Awake();
|
||||||
|
|
||||||
|
EXPECT_TRUE(comp->m_awakeCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, Lifecycle_Start) {
|
||||||
|
GameObject go;
|
||||||
|
TestComponent* comp = go.AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
go.Start();
|
||||||
|
|
||||||
|
EXPECT_TRUE(comp->m_startCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, Lifecycle_Update) {
|
||||||
|
GameObject go;
|
||||||
|
TestComponent* comp = go.AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
go.Update(0.016f);
|
||||||
|
|
||||||
|
EXPECT_TRUE(comp->m_updateCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GameObjectTest, Find_Exists) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
||||||
|
|
||||||
|
GameObject* found = GameObject::Find("TestObject");
|
||||||
|
|
||||||
|
EXPECT_NE(found, nullptr);
|
||||||
|
EXPECT_EQ(found->GetName(), "TestObject");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, Find_NotExists) {
|
||||||
|
GameObject* found = GameObject::Find("NonExistent");
|
||||||
|
|
||||||
|
EXPECT_EQ(found, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GameObject_Test, GetChildCount) {
|
||||||
|
GameObject parent("Parent");
|
||||||
|
GameObject child1("Child1");
|
||||||
|
GameObject child2("Child2");
|
||||||
|
|
||||||
|
child1.SetParent(&parent);
|
||||||
|
child2.SetParent(&parent);
|
||||||
|
|
||||||
|
EXPECT_EQ(parent.GetChildCount(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
306
tests/Components/test_transform_component.cpp
Normal file
306
tests/Components/test_transform_component.cpp
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <XCEngine/Components/TransformComponent.h>
|
||||||
|
#include <XCEngine/Math/Math.h>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
using namespace XCEngine::Math;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TransformComponentTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
transform = std::make_unique<TransformComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<TransformComponent> transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, DefaultConstructor_IdentityValues) {
|
||||||
|
TransformComponent tc;
|
||||||
|
|
||||||
|
EXPECT_EQ(tc.GetLocalPosition(), Vector3::Zero());
|
||||||
|
EXPECT_TRUE(tc.GetLocalRotation().Dot(Quaternion::Identity()) > 0.99f);
|
||||||
|
EXPECT_EQ(tc.GetLocalScale(), Vector3::One());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LocalPosition_GetSet) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Vector3 pos(1.0f, 2.0f, 3.0f);
|
||||||
|
|
||||||
|
tc.SetLocalPosition(pos);
|
||||||
|
|
||||||
|
EXPECT_EQ(tc.GetLocalPosition(), pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LocalRotation_GetSet) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Quaternion rot = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||||
|
|
||||||
|
tc.SetLocalRotation(rot);
|
||||||
|
|
||||||
|
EXPECT_TRUE(tc.GetLocalRotation().Dot(rot) > 0.99f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LocalScale_GetSet) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Vector3 scale(2.0f, 2.0f, 2.0f);
|
||||||
|
|
||||||
|
tc.SetLocalScale(scale);
|
||||||
|
|
||||||
|
EXPECT_EQ(tc.GetLocalScale(), scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LocalEulerAngles_GetSet) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Vector3 eulers(45.0f, 30.0f, 60.0f);
|
||||||
|
|
||||||
|
tc.SetLocalEulerAngles(eulers);
|
||||||
|
Vector3 result = tc.GetLocalEulerAngles();
|
||||||
|
|
||||||
|
EXPECT_NEAR(result.x, eulers.x, 1.0f);
|
||||||
|
EXPECT_NEAR(result.y, eulers.y, 1.0f);
|
||||||
|
EXPECT_NEAR(result.z, eulers.z, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, WorldPosition_NoParent_EqualsLocal) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Vector3 pos(1.0f, 2.0f, 3.0f);
|
||||||
|
|
||||||
|
tc.SetLocalPosition(pos);
|
||||||
|
|
||||||
|
EXPECT_EQ(tc.GetPosition(), pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, WorldPosition_WithParent) {
|
||||||
|
TransformComponent parent;
|
||||||
|
TransformComponent child;
|
||||||
|
|
||||||
|
parent.SetLocalPosition(Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
child.SetParent(&parent);
|
||||||
|
child.SetLocalPosition(Vector3(2.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
Vector3 worldPos = child.GetPosition();
|
||||||
|
|
||||||
|
EXPECT_NEAR(worldPos.x, 3.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, WorldRotation_WithParent) {
|
||||||
|
TransformComponent parent;
|
||||||
|
TransformComponent child;
|
||||||
|
|
||||||
|
parent.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f));
|
||||||
|
child.SetParent(&parent);
|
||||||
|
child.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f));
|
||||||
|
|
||||||
|
Quaternion worldRot = child.GetRotation();
|
||||||
|
|
||||||
|
EXPECT_TRUE(worldRot.Magnitude() > 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, WorldScale_WithParent) {
|
||||||
|
TransformComponent parent;
|
||||||
|
TransformComponent child;
|
||||||
|
|
||||||
|
parent.SetLocalScale(Vector3(2.0f, 2.0f, 2.0f));
|
||||||
|
child.SetParent(&parent);
|
||||||
|
child.SetLocalScale(Vector3(2.0f, 2.0f, 2.0f));
|
||||||
|
|
||||||
|
Vector3 worldScale = child.GetScale();
|
||||||
|
|
||||||
|
EXPECT_NEAR(worldScale.x, 4.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(worldScale.y, 4.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(worldScale.z, 4.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, DirectionVectors_Forward) {
|
||||||
|
TransformComponent tc;
|
||||||
|
|
||||||
|
Vector3 forward = tc.GetForward();
|
||||||
|
|
||||||
|
EXPECT_NEAR(forward.z, 1.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, DirectionVectors_Right) {
|
||||||
|
TransformComponent tc;
|
||||||
|
|
||||||
|
Vector3 right = tc.GetRight();
|
||||||
|
|
||||||
|
EXPECT_NEAR(right.x, 1.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, DirectionVectors_Up) {
|
||||||
|
TransformComponent tc;
|
||||||
|
|
||||||
|
Vector3 up = tc.GetUp();
|
||||||
|
|
||||||
|
EXPECT_NEAR(up.y, 1.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LocalToWorldMatrix_Identity) {
|
||||||
|
TransformComponent tc;
|
||||||
|
|
||||||
|
const Matrix4x4& matrix = tc.GetLocalToWorldMatrix();
|
||||||
|
|
||||||
|
EXPECT_EQ(matrix[0][0], 1.0f);
|
||||||
|
EXPECT_EQ(matrix[1][1], 1.0f);
|
||||||
|
EXPECT_EQ(matrix[2][2], 1.0f);
|
||||||
|
EXPECT_EQ(matrix[3][3], 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LocalToWorldMatrix_Translation) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Vector3 pos(5.0f, 10.0f, 15.0f);
|
||||||
|
tc.SetLocalPosition(pos);
|
||||||
|
|
||||||
|
const Matrix4x4& matrix = tc.GetLocalToWorldMatrix();
|
||||||
|
|
||||||
|
EXPECT_NEAR(matrix[0][3], pos.x, 0.001f);
|
||||||
|
EXPECT_NEAR(matrix[1][3], pos.y, 0.001f);
|
||||||
|
EXPECT_NEAR(matrix[2][3], pos.z, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LocalToWorldMatrix_Rotation) {
|
||||||
|
TransformComponent tc;
|
||||||
|
tc.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f));
|
||||||
|
|
||||||
|
const Matrix4x4& matrix = tc.GetLocalToWorldMatrix();
|
||||||
|
|
||||||
|
EXPECT_NEAR(matrix[0][0], 0.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(matrix[0][2], 1.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, LookAt_Target) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Vector3 target(10.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
tc.SetPosition(Vector3::Zero());
|
||||||
|
tc.LookAt(target);
|
||||||
|
|
||||||
|
Vector3 forward = tc.GetForward();
|
||||||
|
EXPECT_NEAR(forward.x, 1.0f, 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, Rotate_Eulers) {
|
||||||
|
TransformComponent tc;
|
||||||
|
|
||||||
|
tc.Rotate(Vector3(90.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
Vector3 eulers = tc.GetLocalEulerAngles();
|
||||||
|
EXPECT_TRUE(eulers.x > 80.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, Translate_Self) {
|
||||||
|
TransformComponent tc;
|
||||||
|
Vector3 initialPos = tc.GetLocalPosition();
|
||||||
|
|
||||||
|
tc.Translate(Vector3(1.0f, 2.0f, 3.0f), Space::Self);
|
||||||
|
|
||||||
|
Vector3 newPos = tc.GetLocalPosition();
|
||||||
|
EXPECT_NEAR(newPos.x, initialPos.x + 1.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(newPos.y, initialPos.y + 2.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(newPos.z, initialPos.z + 3.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, TransformPoint_LocalToWorld) {
|
||||||
|
TransformComponent tc;
|
||||||
|
tc.SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f));
|
||||||
|
|
||||||
|
Vector3 localPoint(1.0f, 0.0f, 0.0f);
|
||||||
|
Vector3 worldPoint = tc.TransformPoint(localPoint);
|
||||||
|
|
||||||
|
EXPECT_NEAR(worldPoint.x, 2.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(worldPoint.y, 2.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(worldPoint.z, 3.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, InverseTransformPoint_WorldToLocal) {
|
||||||
|
TransformComponent tc;
|
||||||
|
tc.SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f));
|
||||||
|
|
||||||
|
Vector3 worldPoint(2.0f, 2.0f, 3.0f);
|
||||||
|
Vector3 localPoint = tc.InverseTransformPoint(worldPoint);
|
||||||
|
|
||||||
|
EXPECT_NEAR(localPoint.x, 1.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(localPoint.y, 0.0f, 0.001f);
|
||||||
|
EXPECT_NEAR(localPoint.z, 0.0f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, TransformDirection) {
|
||||||
|
TransformComponent tc;
|
||||||
|
tc.SetLocalRotation(Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f));
|
||||||
|
|
||||||
|
Vector3 localDir(1.0f, 0.0f, 0.0f);
|
||||||
|
Vector3 worldDir = tc.TransformDirection(localDir);
|
||||||
|
|
||||||
|
EXPECT_NEAR(worldDir.z, -1.0f, 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, SetDirty_PropagatesToChildren) {
|
||||||
|
TransformComponent parent;
|
||||||
|
TransformComponent child;
|
||||||
|
child.SetParent(&parent);
|
||||||
|
|
||||||
|
parent.SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f));
|
||||||
|
child.SetLocalPosition(Vector3(1.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
Vector3 childWorldPosBefore = child.GetPosition();
|
||||||
|
|
||||||
|
parent.SetLocalPosition(Vector3(10.0f, 0.0f, 0.0f));
|
||||||
|
Vector3 childWorldPosAfter = child.GetPosition();
|
||||||
|
|
||||||
|
EXPECT_NE(childWorldPosBefore.x, childWorldPosAfter.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, GetChildCount_Empty) {
|
||||||
|
TransformComponent tc;
|
||||||
|
EXPECT_EQ(tc.GetChildCount(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, GetChild_InvalidIndex) {
|
||||||
|
TransformComponent tc;
|
||||||
|
EXPECT_EQ(tc.GetChild(0), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, DetachChildren) {
|
||||||
|
TransformComponent parent;
|
||||||
|
TransformComponent child1;
|
||||||
|
TransformComponent child2;
|
||||||
|
|
||||||
|
child1.SetParent(&parent);
|
||||||
|
child2.SetParent(&parent);
|
||||||
|
|
||||||
|
EXPECT_EQ(parent.GetChildCount(), 2u);
|
||||||
|
|
||||||
|
parent.DetachChildren();
|
||||||
|
|
||||||
|
EXPECT_EQ(parent.GetChildCount(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, SetAsFirstSibling) {
|
||||||
|
TransformComponent parent;
|
||||||
|
TransformComponent child1;
|
||||||
|
TransformComponent child2;
|
||||||
|
|
||||||
|
child1.SetParent(&parent);
|
||||||
|
child2.SetParent(&parent);
|
||||||
|
|
||||||
|
child2.SetAsFirstSibling();
|
||||||
|
|
||||||
|
EXPECT_EQ(child2.GetSiblingIndex(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformComponent_Test, SetAsLastSibling) {
|
||||||
|
TransformComponent parent;
|
||||||
|
TransformComponent child1;
|
||||||
|
TransformComponent child2;
|
||||||
|
|
||||||
|
child1.SetParent(&parent);
|
||||||
|
child2.SetParent(&parent);
|
||||||
|
|
||||||
|
child1.SetAsLastSibling();
|
||||||
|
|
||||||
|
EXPECT_EQ(child1.GetSiblingIndex(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
28
tests/Scene/CMakeLists.txt
Normal file
28
tests/Scene/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
project(XCEngine_SceneTests)
|
||||||
|
|
||||||
|
set(SCENE_TEST_SOURCES
|
||||||
|
test_scene.cpp
|
||||||
|
test_scene_manager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(xcengine_scene_tests ${SCENE_TEST_SOURCES})
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set_target_properties(xcengine_scene_tests PROPERTIES
|
||||||
|
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(xcengine_scene_tests PRIVATE
|
||||||
|
XCEngine
|
||||||
|
GTest::gtest
|
||||||
|
GTest::gtest_main
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(xcengine_scene_tests PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}/engine/include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME SceneTests COMMAND xcengine_scene_tests)
|
||||||
217
tests/Scene/test_scene.cpp
Normal file
217
tests/Scene/test_scene.cpp
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <XCEngine/Scene/Scene.h>
|
||||||
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
#include <XCEngine/Components/TransformComponent.h>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TestComponent : public Component {
|
||||||
|
public:
|
||||||
|
TestComponent() = default;
|
||||||
|
explicit TestComponent(const std::string& name) : m_customName(name) {}
|
||||||
|
|
||||||
|
std::string GetName() const override {
|
||||||
|
return m_customName.empty() ? "TestComponent" : m_customName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_awakeCalled = false;
|
||||||
|
bool m_updateCalled = false;
|
||||||
|
|
||||||
|
void Awake() override { m_awakeCalled = true; }
|
||||||
|
void Update(float deltaTime) override { m_updateCalled = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_customName;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SceneTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
testScene = std::make_unique<Scene>("TestScene");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Scene> testScene;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Scene_Test, DefaultConstructor_DefaultName) {
|
||||||
|
Scene s;
|
||||||
|
|
||||||
|
EXPECT_EQ(s.GetName(), "Untitled");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Scene_Test, NamedConstructor) {
|
||||||
|
Scene s("MyScene");
|
||||||
|
|
||||||
|
EXPECT_EQ(s.GetName(), "MyScene");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, CreateGameObject_Simple) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
||||||
|
|
||||||
|
EXPECT_NE(go, nullptr);
|
||||||
|
EXPECT_EQ(go->GetName(), "TestObject");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, CreateGameObject_WithParent) {
|
||||||
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
||||||
|
GameObject* child = testScene->CreateGameObject("Child", parent);
|
||||||
|
|
||||||
|
EXPECT_NE(child, nullptr);
|
||||||
|
EXPECT_EQ(child->GetParent(), parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, CreateGameObject_AssignsID) {
|
||||||
|
GameObject* go1 = testScene->CreateGameObject("Object1");
|
||||||
|
GameObject* go2 = testScene->CreateGameObject("Object2");
|
||||||
|
|
||||||
|
EXPECT_NE(go1->GetID(), GameObject::INVALID_ID);
|
||||||
|
EXPECT_NE(go2->GetID(), GameObject::INVALID_ID);
|
||||||
|
EXPECT_NE(go1->GetID(), go2->GetID());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, CreateGameObject_AddsToRoot) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("RootObject");
|
||||||
|
|
||||||
|
auto roots = testScene->GetRootGameObjects();
|
||||||
|
|
||||||
|
EXPECT_EQ(roots.size(), 1u);
|
||||||
|
EXPECT_EQ(roots[0], go);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, DestroyGameObject_Simple) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
||||||
|
|
||||||
|
testScene->DestroyGameObject(go);
|
||||||
|
|
||||||
|
EXPECT_EQ(testScene->Find("TestObject"), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, DestroyGameObject_WithChildren) {
|
||||||
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
||||||
|
testScene->CreateGameObject("Child", parent);
|
||||||
|
|
||||||
|
testScene->DestroyGameObject(parent);
|
||||||
|
|
||||||
|
EXPECT_EQ(testScene->Find("Parent"), nullptr);
|
||||||
|
EXPECT_EQ(testScene->Find("Child"), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, Find_Exists) {
|
||||||
|
testScene->CreateGameObject("FindMe");
|
||||||
|
|
||||||
|
GameObject* found = testScene->Find("FindMe");
|
||||||
|
|
||||||
|
EXPECT_NE(found, nullptr);
|
||||||
|
EXPECT_EQ(found->GetName(), "FindMe");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, Find_NotExists) {
|
||||||
|
GameObject* found = testScene->Find("NonExistent");
|
||||||
|
|
||||||
|
EXPECT_EQ(found, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, GetRootGameObjects_ReturnsTopLevel) {
|
||||||
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
||||||
|
testScene->CreateGameObject("Child", parent);
|
||||||
|
|
||||||
|
auto roots = testScene->GetRootGameObjects();
|
||||||
|
|
||||||
|
EXPECT_EQ(roots.size(), 1u);
|
||||||
|
EXPECT_EQ(roots[0], parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Scene_Test, GetRootGameObjects_EmptyScene) {
|
||||||
|
Scene emptyScene;
|
||||||
|
|
||||||
|
auto roots = emptyScene.GetRootGameObjects();
|
||||||
|
|
||||||
|
EXPECT_EQ(roots.size(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, Update_UpdatesActiveObjects) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
||||||
|
TestComponent* comp = go->AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
testScene->Update(0.016f);
|
||||||
|
|
||||||
|
EXPECT_TRUE(comp->m_updateCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, Update_SkipsInactiveObjects) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("InactiveObject");
|
||||||
|
TestComponent* comp = go->AddComponent<TestComponent>();
|
||||||
|
go->SetActive(false);
|
||||||
|
|
||||||
|
testScene->Update(0.016f);
|
||||||
|
|
||||||
|
EXPECT_FALSE(comp->m_updateCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Scene_Test, IsActive_DefaultTrue) {
|
||||||
|
Scene s;
|
||||||
|
|
||||||
|
EXPECT_TRUE(s.IsActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Scene_Test, SetActive) {
|
||||||
|
Scene s;
|
||||||
|
s.SetActive(false);
|
||||||
|
|
||||||
|
EXPECT_FALSE(s.IsActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, FindObjectOfType) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
||||||
|
go->AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
TestComponent* found = testScene->FindObjectOfType<TestComponent>();
|
||||||
|
|
||||||
|
EXPECT_NE(found, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, FindObjectsOfType) {
|
||||||
|
GameObject* go1 = testScene->CreateGameObject("Object1");
|
||||||
|
GameObject* go2 = testScene->CreateGameObject("Object2");
|
||||||
|
go1->AddComponent<TestComponent>();
|
||||||
|
go2->AddComponent<TestComponent>();
|
||||||
|
|
||||||
|
auto found = testScene->FindObjectsOfType<TestComponent>();
|
||||||
|
|
||||||
|
EXPECT_EQ(found.size(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, FindGameObjectWithTag) {
|
||||||
|
GameObject* go = testScene->CreateGameObject("MyTag");
|
||||||
|
|
||||||
|
GameObject* found = testScene->FindGameObjectWithTag("MyTag");
|
||||||
|
|
||||||
|
EXPECT_NE(found, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, OnGameObjectCreated_Event) {
|
||||||
|
bool eventFired = false;
|
||||||
|
testScene->OnGameObjectCreated().Subscribe([&eventFired](GameObject*) {
|
||||||
|
eventFired = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
testScene->CreateGameObject("TestObject");
|
||||||
|
|
||||||
|
EXPECT_TRUE(eventFired);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, OnGameObjectDestroyed_Event) {
|
||||||
|
bool eventFired = false;
|
||||||
|
testScene->OnGameObjectDestroyed().Subscribe([&eventFired](GameObject*) {
|
||||||
|
eventFired = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
||||||
|
testScene->DestroyGameObject(go);
|
||||||
|
|
||||||
|
EXPECT_TRUE(eventFired);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
172
tests/Scene/test_scene_manager.cpp
Normal file
172
tests/Scene/test_scene_manager.cpp
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <XCEngine/Scene/SceneManager.h>
|
||||||
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class SceneManagerTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void TearDown() override {
|
||||||
|
auto scenes = SceneManager::Get().GetAllScenes();
|
||||||
|
for (auto* scene : scenes) {
|
||||||
|
SceneManager::Get().UnloadScene(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, Get_ReturnsSingleton) {
|
||||||
|
SceneManager& sm1 = SceneManager::Get();
|
||||||
|
SceneManager& sm2 = SceneManager::Get();
|
||||||
|
|
||||||
|
EXPECT_EQ(&sm1, &sm2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, CreateScene_Simple) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
|
||||||
|
Scene* scene = sm.CreateScene("TestScene");
|
||||||
|
|
||||||
|
EXPECT_NE(scene, nullptr);
|
||||||
|
EXPECT_EQ(scene->GetName(), "TestScene");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, CreateScene_AssignsName) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
|
||||||
|
Scene* scene = sm.CreateScene("MyScene");
|
||||||
|
|
||||||
|
EXPECT_EQ(scene->GetName(), "MyScene");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, GetScene_Exists) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
sm.CreateScene("TestScene");
|
||||||
|
|
||||||
|
Scene* found = sm.GetScene("TestScene");
|
||||||
|
|
||||||
|
EXPECT_NE(found, nullptr);
|
||||||
|
EXPECT_EQ(found->GetName(), "TestScene");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, GetScene_NotExists) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
|
||||||
|
Scene* found = sm.GetScene("NonExistent");
|
||||||
|
|
||||||
|
EXPECT_EQ(found, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, GetAllScenes_ReturnsAll) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
sm.CreateScene("Scene1");
|
||||||
|
sm.CreateScene("Scene2");
|
||||||
|
sm.CreateScene("Scene3");
|
||||||
|
|
||||||
|
auto scenes = sm.GetAllScenes();
|
||||||
|
|
||||||
|
EXPECT_EQ(scenes.size(), 3u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, SetActiveScene) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
Scene* scene1 = sm.CreateScene("Scene1");
|
||||||
|
Scene* scene2 = sm.CreateScene("Scene2");
|
||||||
|
|
||||||
|
sm.SetActiveScene(scene2);
|
||||||
|
|
||||||
|
EXPECT_EQ(sm.GetActiveScene(), scene2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, SetActiveScene_ByName) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
sm.CreateScene("Scene1");
|
||||||
|
sm.CreateScene("Scene2");
|
||||||
|
|
||||||
|
sm.SetActiveScene("Scene2");
|
||||||
|
|
||||||
|
EXPECT_EQ(sm.GetActiveScene()->GetName(), "Scene2");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, GetActiveScene_InitiallyFirst) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
auto scenes = sm.GetAllScenes();
|
||||||
|
for (auto* scene : scenes) {
|
||||||
|
sm.UnloadScene(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* scene1 = sm.CreateScene("First");
|
||||||
|
|
||||||
|
EXPECT_EQ(sm.GetActiveScene(), scene1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, UnloadScene) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
Scene* scene = sm.CreateScene("ToUnload");
|
||||||
|
|
||||||
|
sm.UnloadScene(scene);
|
||||||
|
|
||||||
|
EXPECT_EQ(sm.GetScene("ToUnload"), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, UnloadScene_ByName) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
sm.CreateScene("ToUnload");
|
||||||
|
|
||||||
|
sm.UnloadScene("ToUnload");
|
||||||
|
|
||||||
|
EXPECT_EQ(sm.GetScene("ToUnload"), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, OnSceneLoaded_Event) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
bool eventFired = false;
|
||||||
|
sm.OnSceneLoaded().Subscribe([&eventFired](Scene*) {
|
||||||
|
eventFired = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
sm.CreateScene("TestScene");
|
||||||
|
|
||||||
|
EXPECT_TRUE(eventFired);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, OnActiveSceneChanged_Event) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
Scene* scene1 = sm.CreateScene("Scene1");
|
||||||
|
Scene* scene2 = sm.CreateScene("Scene2");
|
||||||
|
|
||||||
|
bool eventFired = false;
|
||||||
|
sm.OnActiveSceneChanged().Subscribe([&eventFired](Scene*) {
|
||||||
|
eventFired = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
sm.SetActiveScene(scene2);
|
||||||
|
|
||||||
|
EXPECT_TRUE(eventFired);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, CreateScene_MultipleScenes) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
|
||||||
|
Scene* scene1 = sm.CreateScene("Scene1");
|
||||||
|
Scene* scene2 = sm.CreateScene("Scene2");
|
||||||
|
Scene* scene3 = sm.CreateScene("Scene3");
|
||||||
|
|
||||||
|
EXPECT_NE(scene1, scene2);
|
||||||
|
EXPECT_NE(scene2, scene3);
|
||||||
|
EXPECT_NE(scene1, scene3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneManager_Test, SetActiveScene_ToNullptr) {
|
||||||
|
SceneManager& sm = SceneManager::Get();
|
||||||
|
Scene* scene1 = sm.CreateScene("Scene1");
|
||||||
|
Scene* scene2 = sm.CreateScene("Scene2");
|
||||||
|
sm.SetActiveScene(scene2);
|
||||||
|
|
||||||
|
sm.SetActiveScene(nullptr);
|
||||||
|
|
||||||
|
EXPECT_EQ(sm.GetActiveScene(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Reference in New Issue
Block a user