Extra2D/Tests/test_scene_graph.cpp

324 lines
7.9 KiB
C++

/**
* @file test_scene_graph.cpp
* @brief SceneGraph 单元测试
*
* 测试场景图的节点管理、遍历、查找等功能。
*/
#include "test_framework.h"
#include <extra2d/node/node.h>
#include <extra2d/node/scene_graph.h>
#include <extra2d/core/math_extended.h>
using namespace extra2d;
using namespace extra2d::test;
namespace {
constexpr f32 EPSILON = 0.0001f;
bool vec2Equal(const Vec2& a, const Vec2& b, f32 eps = EPSILON) {
return std::abs(a.x - b.x) < eps && std::abs(a.y - b.y) < eps;
}
}
// ---------------------------------------------------------------------------
// SceneGraph 基本测试
// ---------------------------------------------------------------------------
TEST(SceneGraph, Create) {
SceneGraph graph;
TEST_ASSERT_EQ(0u, graph.nodeCount());
TEST_ASSERT_TRUE(graph.roots().empty());
}
TEST(SceneGraph, CreateNode) {
SceneGraph graph;
auto node = graph.create<Node>();
TEST_ASSERT_NOT_NULL(node.get());
TEST_ASSERT_EQ(1u, graph.nodeCount());
TEST_ASSERT_EQ(node.get(), graph.roots()[0].get());
}
TEST(SceneGraph, AddNode) {
SceneGraph graph;
auto node = ptr::make<Node>();
graph.add(node);
TEST_ASSERT_EQ(1u, graph.nodeCount());
TEST_ASSERT_EQ(node.get(), graph.roots()[0].get());
}
TEST(SceneGraph, RemoveNode) {
SceneGraph graph;
auto node = graph.create<Node>();
graph.remove(node);
TEST_ASSERT_EQ(0u, graph.nodeCount());
}
TEST(SceneGraph, Clear) {
SceneGraph graph;
graph.create<Node>();
graph.create<Node>();
graph.create<Node>();
TEST_ASSERT_EQ(3u, graph.nodeCount());
graph.clear();
TEST_ASSERT_EQ(0u, graph.nodeCount());
}
// ---------------------------------------------------------------------------
// 节点查找测试
// ---------------------------------------------------------------------------
TEST(SceneGraph, FindByName) {
SceneGraph graph;
auto node1 = graph.create<Node>();
node1->setName("Node1");
auto node2 = graph.create<Node>();
node2->setName("Node2");
auto node3 = graph.create<Node>();
node3->setName("Node3");
Node* found = graph.find("Node2");
TEST_ASSERT_NOT_NULL(found);
TEST_ASSERT_EQ(node2.get(), found);
}
TEST(SceneGraph, FindByNameNotFound) {
SceneGraph graph;
graph.create<Node>()->setName("Node1");
graph.create<Node>()->setName("Node2");
Node* found = graph.find("NonExistent");
TEST_ASSERT_NULL(found);
}
TEST(SceneGraph, FindByTag) {
SceneGraph graph;
auto node1 = graph.create<Node>();
node1->setTag(1);
auto node2 = graph.create<Node>();
node2->setTag(2);
auto node3 = graph.create<Node>();
node3->setTag(3);
Node* found = graph.findByTag(2);
TEST_ASSERT_NOT_NULL(found);
TEST_ASSERT_EQ(node2.get(), found);
}
TEST(SceneGraph, FindByTagNotFound) {
SceneGraph graph;
graph.create<Node>()->setTag(1);
graph.create<Node>()->setTag(2);
Node* found = graph.findByTag(999);
TEST_ASSERT_NULL(found);
}
// ---------------------------------------------------------------------------
// 遍历测试
// ---------------------------------------------------------------------------
TEST(SceneGraph, Traverse) {
SceneGraph graph;
auto node1 = graph.create<Node>();
node1->setName("Node1");
auto node2 = graph.create<Node>();
node2->setName("Node2");
auto child = ptr::make<Node>();
child->setName("Child");
node1->addChild(child);
std::vector<std::string> visited;
graph.traverse([&visited](Node* node) {
visited.push_back(node->name());
});
TEST_ASSERT_EQ(3u, visited.size());
bool foundNode1 = false, foundNode2 = false, foundChild = false;
for (const auto& name : visited) {
if (name == "Node1") foundNode1 = true;
if (name == "Node2") foundNode2 = true;
if (name == "Child") foundChild = true;
}
TEST_ASSERT_TRUE(foundNode1);
TEST_ASSERT_TRUE(foundNode2);
TEST_ASSERT_TRUE(foundChild);
}
TEST(SceneGraph, TraverseRecursive) {
SceneGraph graph;
auto root = graph.create<Node>();
root->setName("Root");
auto child1 = ptr::make<Node>();
child1->setName("Child1");
root->addChild(child1);
auto grandchild = ptr::make<Node>();
grandchild->setName("Grandchild");
child1->addChild(grandchild);
std::vector<std::string> visited;
graph.traverseRecursive(root.get(), [&visited](Node* node) {
visited.push_back(node->name());
});
TEST_ASSERT_EQ(3u, visited.size());
TEST_ASSERT_EQ("Root", visited[0]);
TEST_ASSERT_EQ("Child1", visited[1]);
TEST_ASSERT_EQ("Grandchild", visited[2]);
}
// ---------------------------------------------------------------------------
// 更新测试
// ---------------------------------------------------------------------------
class TestUpdateNode : public Node {
public:
void onUpdate(f32 dt) override {
updateCount++;
lastDt = dt;
}
int updateCount = 0;
f32 lastDt = 0.0f;
};
TEST(SceneGraph, Update) {
SceneGraph graph;
auto node = graph.create<TestUpdateNode>();
graph.update(0.016f);
TEST_ASSERT_EQ(1, node->updateCount);
TEST_ASSERT_TRUE(std::abs(node->lastDt - 0.016f) < EPSILON);
}
TEST(SceneGraph, UpdateMultipleNodes) {
SceneGraph graph;
auto node1 = graph.create<TestUpdateNode>();
auto node2 = graph.create<TestUpdateNode>();
auto node3 = graph.create<TestUpdateNode>();
graph.update(0.033f);
TEST_ASSERT_EQ(1, node1->updateCount);
TEST_ASSERT_EQ(1, node2->updateCount);
TEST_ASSERT_EQ(1, node3->updateCount);
}
// ---------------------------------------------------------------------------
// 层级结构测试
// ---------------------------------------------------------------------------
TEST(SceneGraph, HierarchyWithChildren) {
SceneGraph graph;
auto parent = graph.create<Node>();
parent->setName("Parent");
auto child1 = ptr::make<Node>();
child1->setName("Child1");
parent->addChild(child1);
auto child2 = ptr::make<Node>();
child2->setName("Child2");
parent->addChild(child2);
TEST_ASSERT_EQ(1u, graph.nodeCount());
TEST_ASSERT_EQ(2u, parent->children().size());
}
TEST(SceneGraph, DeepHierarchy) {
SceneGraph graph;
auto root = graph.create<Node>();
root->setPos(100, 100);
auto level1 = ptr::make<Node>();
level1->setPos(50, 50);
root->addChild(level1);
auto level2 = ptr::make<Node>();
level2->setPos(25, 25);
level1->addChild(level2);
auto level3 = ptr::make<Node>();
level3->setPos(10, 10);
level2->addChild(level3);
Vec2 worldPos = level3->worldPos();
TEST_ASSERT_TRUE(vec2Equal(worldPos, Vec2(185, 185)));
}
// ---------------------------------------------------------------------------
// 自定义节点类型测试
// ---------------------------------------------------------------------------
class CustomNode : public Node {
public:
CustomNode() : value(0) {}
explicit CustomNode(int v) : value(v) {}
int value;
protected:
Vec2 defaultAnchor() const override { return Vec2(0.0f, 0.0f); }
};
TEST(SceneGraph, CreateCustomNode) {
SceneGraph graph;
auto node = graph.create<CustomNode>(42);
TEST_ASSERT_NOT_NULL(node.get());
TEST_ASSERT_EQ(42, node->value);
TEST_ASSERT_TRUE(vec2Equal(node->anchor, Vec2(0, 0)));
}
// ---------------------------------------------------------------------------
// 节点可见性测试
// ---------------------------------------------------------------------------
TEST(SceneGraph, NodeVisibility) {
SceneGraph graph;
auto node = graph.create<Node>();
TEST_ASSERT_TRUE(node->visible());
node->setVisible(false);
TEST_ASSERT_FALSE(node->visible());
}