2026-03-20 20:22:04 +08:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
#include <XCEngine/Scene/Scene.h>
|
|
|
|
|
#include <XCEngine/Components/GameObject.h>
|
|
|
|
|
#include <XCEngine/Components/TransformComponent.h>
|
2026-03-24 16:14:05 +08:00
|
|
|
#include <XCEngine/Core/Math/Vector3.h>
|
2026-03-22 03:42:40 +08:00
|
|
|
#include <fstream>
|
|
|
|
|
#include <sstream>
|
2026-03-20 20:22:04 +08:00
|
|
|
|
|
|
|
|
using namespace XCEngine::Components;
|
2026-03-22 03:42:40 +08:00
|
|
|
using namespace XCEngine::Math;
|
2026-03-20 20:22:04 +08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 03:42:40 +08:00
|
|
|
TEST_F(SceneTest, Save_And_Load) {
|
|
|
|
|
GameObject* go = testScene->CreateGameObject("SavedObject");
|
|
|
|
|
go->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
|
|
|
go->SetActive(true);
|
|
|
|
|
|
|
|
|
|
testScene->Save("test_scene.scene");
|
|
|
|
|
|
|
|
|
|
Scene loadedScene;
|
|
|
|
|
loadedScene.Load("test_scene.scene");
|
|
|
|
|
|
|
|
|
|
GameObject* loadedGo = loadedScene.Find("SavedObject");
|
|
|
|
|
EXPECT_NE(loadedGo, nullptr);
|
|
|
|
|
EXPECT_EQ(loadedGo->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(SceneTest, Save_ContainsGameObjectData) {
|
|
|
|
|
testScene->CreateGameObject("Player");
|
|
|
|
|
testScene->CreateGameObject("Enemy");
|
|
|
|
|
|
|
|
|
|
testScene->Save("test_scene_multi.scene");
|
|
|
|
|
|
|
|
|
|
std::ifstream file("test_scene_multi.scene");
|
|
|
|
|
std::stringstream buffer;
|
|
|
|
|
buffer << file.rdbuf();
|
|
|
|
|
std::string content = buffer.str();
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(content.find("Player") != std::string::npos);
|
|
|
|
|
EXPECT_TRUE(content.find("Enemy") != std::string::npos);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 20:22:04 +08:00
|
|
|
} // namespace
|