324 lines
7.9 KiB
C++
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());
|
|
}
|