#include #include #include #include #include #include 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; bool m_onEnableCalled = false; bool m_onDisableCalled = false; bool m_onDestroyCalled = false; int m_startCount = 0; int m_updateCount = 0; int m_onEnableCount = 0; int m_onDisableCount = 0; int m_onDestroyCount = 0; void Awake() override { m_awakeCalled = true; } void Start() override { m_startCalled = true; ++m_startCount; } void Update(float deltaTime) override { m_updateCalled = true; ++m_updateCount; } void OnEnable() override { m_onEnableCalled = true; ++m_onEnableCount; } void OnDisable() override { m_onDisableCalled = true; ++m_onDisableCount; } void OnDestroy() override { m_onDestroyCalled = true; ++m_onDestroyCount; } private: std::string m_customName; }; class GameObjectTest : public ::testing::Test { protected: void SetUp() override { testScene = std::make_unique("TestScene"); } std::unique_ptr testScene; }; TEST(GameObject_Test, DefaultConstructor_DefaultValues) { GameObject go; EXPECT_EQ(go.GetName(), "GameObject"); EXPECT_EQ(go.GetTag(), "Untagged"); EXPECT_TRUE(go.CompareTag("Untagged")); EXPECT_TRUE(go.IsActive()); EXPECT_NE(go.GetID(), GameObject::INVALID_ID); } TEST(GameObject_Test, NamedConstructor) { GameObject go("TestObject"); EXPECT_EQ(go.GetName(), "TestObject"); } TEST(GameObject_Test, Layer_GetSetAndSerializeRoundTrip) { GameObject source("LayeredObject"); source.SetTag("Player"); source.SetLayer(7); std::stringstream stream; source.Serialize(stream); GameObject target; target.Deserialize(stream); EXPECT_EQ(source.GetTag(), "Player"); EXPECT_EQ(source.GetLayer(), 7u); EXPECT_EQ(target.GetTag(), "Player"); EXPECT_EQ(target.GetLayer(), 7u); } TEST(GameObject_Test, AddComponent_Single) { GameObject go; TestComponent* comp = go.AddComponent(); EXPECT_NE(comp, nullptr); EXPECT_EQ(go.GetComponent(), comp); } TEST(GameObject_Test, AddComponent_Multiple) { GameObject go; TestComponent* comp1 = go.AddComponent(); TestComponent* comp2 = go.AddComponent(); 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* found = go.GetComponent(); EXPECT_EQ(found, comp); } TEST(GameObject_Test, GetComponent_NotExists) { GameObject go; TestComponent* found = go.GetComponent(); EXPECT_EQ(found, nullptr); } TEST(GameObject_Test, GetComponents_Multiple) { GameObject go; go.AddComponent(); go.AddComponent(); go.AddComponent(); std::vector comps = go.GetComponents(); 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 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, SetActive_PropagatesEnableDisableToChildren) { GameObject parent("Parent"); GameObject child("Child"); TestComponent* comp = child.AddComponent(); child.SetParent(&parent); parent.SetActive(false); EXPECT_EQ(comp->m_onDisableCount, 1); parent.SetActive(true); EXPECT_EQ(comp->m_onEnableCount, 1); } TEST(GameObject_Test, SetParent_PropagatesActiveHierarchyChanges) { GameObject activeParent("ActiveParent"); GameObject inactiveParent("InactiveParent"); GameObject child("Child"); TestComponent* comp = child.AddComponent(); inactiveParent.SetActive(false); child.SetParent(&inactiveParent); EXPECT_EQ(comp->m_onDisableCount, 1); child.SetParent(&activeParent); EXPECT_EQ(comp->m_onEnableCount, 1); } 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(); go.Awake(); EXPECT_TRUE(comp->m_awakeCalled); } TEST(GameObject_Test, Lifecycle_Start) { GameObject go; TestComponent* comp = go.AddComponent(); go.Start(); EXPECT_TRUE(comp->m_startCalled); EXPECT_EQ(comp->m_startCount, 1); } TEST(GameObject_Test, Lifecycle_Start_IsOnlyCalledOnce) { GameObject go; TestComponent* comp = go.AddComponent(); go.Start(); go.Start(); EXPECT_EQ(comp->m_startCount, 1); } TEST(GameObject_Test, Lifecycle_Update) { GameObject go; TestComponent* comp = go.AddComponent(); go.Update(0.016f); EXPECT_TRUE(comp->m_updateCalled); EXPECT_EQ(comp->m_updateCount, 1); } TEST(GameObject_Test, Destroy_CallsOnDestroyOnce_WhenStandalone) { GameObject go; TestComponent* comp = go.AddComponent(); go.Destroy(); EXPECT_TRUE(comp->m_onDestroyCalled); EXPECT_EQ(comp->m_onDestroyCount, 1); } 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