refactor(tests): 移除测试套件及相关配置
移除整个tests目录及其内容,包括测试代码、构建脚本和文档 同时更新xmake.lua移除测试套件配置 重构src/resource/material.cpp代码格式
This commit is contained in:
parent
bdf78f5eca
commit
bbdc1435ce
|
|
@ -6,82 +6,86 @@ Material::Material() = default;
|
||||||
|
|
||||||
Material::~Material() = default;
|
Material::~Material() = default;
|
||||||
|
|
||||||
void Material::setShader(Ptr<Shader> shader) {
|
void Material::setShader(Ptr<Shader> shader) { shader_ = shader; }
|
||||||
shader_ = shader;
|
|
||||||
|
void Material::setBool(const std::string &name, bool value) {
|
||||||
|
properties_[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setBool(const std::string& name, bool value) {
|
void Material::setInt(const std::string &name, int value) {
|
||||||
properties_[name] = value;
|
properties_[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setInt(const std::string& name, int value) {
|
void Material::setFloat(const std::string &name, float value) {
|
||||||
properties_[name] = value;
|
properties_[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setFloat(const std::string& name, float value) {
|
void Material::setVec2(const std::string &name, const Vec2 &value) {
|
||||||
properties_[name] = value;
|
properties_[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setVec2(const std::string& name, const Vec2& value) {
|
void Material::setVec3(const std::string &name, const Vec3 &value) {
|
||||||
properties_[name] = value;
|
properties_[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setVec3(const std::string& name, const Vec3& value) {
|
void Material::setColor(const std::string &name, const Color &value) {
|
||||||
properties_[name] = value;
|
properties_[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setColor(const std::string& name, const Color& value) {
|
void Material::setTexture(const std::string &name, Ptr<Texture> texture) {
|
||||||
properties_[name] = value;
|
properties_[name] = texture;
|
||||||
}
|
|
||||||
|
|
||||||
void Material::setTexture(const std::string& name, Ptr<Texture> texture) {
|
|
||||||
properties_[name] = texture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::apply() {
|
void Material::apply() {
|
||||||
if (!shader_) return;
|
if (!shader_)
|
||||||
|
return;
|
||||||
|
|
||||||
shader_->use();
|
shader_->use();
|
||||||
applyProperties();
|
applyProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::applyProperties() {
|
void Material::applyProperties() {
|
||||||
if (!shader_) return;
|
if (!shader_)
|
||||||
|
return;
|
||||||
|
|
||||||
textureUnit_ = 0;
|
textureUnit_ = 0;
|
||||||
|
|
||||||
for (const auto& [name, prop] : properties_) {
|
for (const auto &pair : properties_) {
|
||||||
std::visit([this, &name](auto&& value) {
|
const auto &name = pair.first;
|
||||||
using T = std::decay_t<decltype(value)>;
|
const auto &prop = pair.second;
|
||||||
|
std::visit(
|
||||||
|
[this, &name](auto &&value) {
|
||||||
|
using T = std::decay_t<decltype(value)>;
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, bool>) {
|
if constexpr (std::is_same_v<T, bool>) {
|
||||||
shader_->setBool(name, value);
|
shader_->setBool(name, value);
|
||||||
} else if constexpr (std::is_same_v<T, int>) {
|
} else if constexpr (std::is_same_v<T, int>) {
|
||||||
shader_->setInt(name, value);
|
shader_->setInt(name, value);
|
||||||
} else if constexpr (std::is_same_v<T, float>) {
|
} else if constexpr (std::is_same_v<T, float>) {
|
||||||
shader_->setFloat(name, value);
|
shader_->setFloat(name, value);
|
||||||
} else if constexpr (std::is_same_v<T, Vec2>) {
|
} else if constexpr (std::is_same_v<T, Vec2>) {
|
||||||
shader_->setVec2(name, value);
|
shader_->setVec2(name, value);
|
||||||
} else if constexpr (std::is_same_v<T, Vec3>) {
|
} else if constexpr (std::is_same_v<T, Vec3>) {
|
||||||
shader_->setVec3(name, value);
|
shader_->setVec3(name, value);
|
||||||
} else if constexpr (std::is_same_v<T, Color>) {
|
} else if constexpr (std::is_same_v<T, Color>) {
|
||||||
shader_->setColor(name, value);
|
shader_->setColor(name, value);
|
||||||
} else if constexpr (std::is_same_v<T, Ptr<Texture>>) {
|
} else if constexpr (std::is_same_v<T, Ptr<Texture>>) {
|
||||||
if (value) {
|
if (value) {
|
||||||
value->bind(textureUnit_);
|
value->bind(textureUnit_);
|
||||||
shader_->setInt(name, textureUnit_);
|
shader_->setInt(name, textureUnit_);
|
||||||
textureUnit_++;
|
textureUnit_++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, prop);
|
}
|
||||||
}
|
},
|
||||||
|
prop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<Material> Material::clone() const {
|
Ptr<Material> Material::clone() const {
|
||||||
Ptr<Material> newMat = makePtr<Material>();
|
Ptr<Material> newMat = makePtr<Material>();
|
||||||
newMat->setShader(shader_);
|
newMat->setShader(shader_);
|
||||||
newMat->properties_ = properties_;
|
newMat->properties_ = properties_;
|
||||||
return newMat;
|
return newMat;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
139
tests/README.md
139
tests/README.md
|
|
@ -1,139 +0,0 @@
|
||||||
# Extra2D 测试套件
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
本测试套件包含对 Extra2D 引擎核心模块的单元测试和集成测试。
|
|
||||||
|
|
||||||
## 测试框架
|
|
||||||
|
|
||||||
使用自定义轻量级测试框架,位于 `test_framework.h`。
|
|
||||||
|
|
||||||
### 特性
|
|
||||||
- 自动测试注册和发现
|
|
||||||
- 支持测试套件组织
|
|
||||||
- 丰富的断言宏
|
|
||||||
- 详细的测试报告
|
|
||||||
|
|
||||||
## 测试文件说明
|
|
||||||
|
|
||||||
| 文件 | 描述 | 测试数量 |
|
|
||||||
|------|------|----------|
|
|
||||||
| `test_timer_module.cpp` | 定时器模块测试 | 20+ |
|
|
||||||
| `test_event_bus.cpp` | 事件总线测试 | 20+ |
|
|
||||||
| `test_module_registry.cpp` | 模块注册表测试 | 12+ |
|
|
||||||
| `test_plugin_loader.cpp` | 插件加载器测试 | 15+ |
|
|
||||||
| `test_context.cpp` | Context 集成测试 | 15+ |
|
|
||||||
|
|
||||||
## 构建和运行
|
|
||||||
|
|
||||||
### 构建测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 配置并构建测试
|
|
||||||
xmake f --tests=y
|
|
||||||
xmake
|
|
||||||
|
|
||||||
# 或者显式指定平台
|
|
||||||
xmake f -p mingw --tests=y
|
|
||||||
xmake
|
|
||||||
```
|
|
||||||
|
|
||||||
### 运行测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 运行所有测试
|
|
||||||
xmake run extra2d_tests
|
|
||||||
|
|
||||||
# 或者在构建目录中直接运行
|
|
||||||
./build/mingw/x86_64/debug/extra2d_tests.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
## 测试覆盖范围
|
|
||||||
|
|
||||||
### TimerModule 测试
|
|
||||||
- 基础属性和生命周期
|
|
||||||
- 一次性定时器(scheduleOnce)
|
|
||||||
- 重复定时器(scheduleRepeat)
|
|
||||||
- 每帧更新定时器(scheduleUpdate)
|
|
||||||
- 暂停和恢复功能
|
|
||||||
- 时间缩放效果
|
|
||||||
- 多定时器管理
|
|
||||||
- 边界条件处理
|
|
||||||
|
|
||||||
### EventBus 测试
|
|
||||||
- 基础事件广播
|
|
||||||
- 多监听器支持
|
|
||||||
- 监听器启用/禁用
|
|
||||||
- 带参数事件
|
|
||||||
- 不同事件类型
|
|
||||||
- 监听器生命周期(RAII)
|
|
||||||
- 输入事件(键盘、鼠标、触摸)
|
|
||||||
- 窗口事件
|
|
||||||
- 游戏手柄事件
|
|
||||||
- 性能测试
|
|
||||||
|
|
||||||
### ModuleRegistry 测试
|
|
||||||
- 基础注册/注销操作
|
|
||||||
- 重复注册处理
|
|
||||||
- 按类型获取模块
|
|
||||||
- 初始化和关闭顺序
|
|
||||||
- 优先级排序
|
|
||||||
- 空注册表处理
|
|
||||||
|
|
||||||
### PluginLoader 测试
|
|
||||||
- 基础注册/卸载操作
|
|
||||||
- 插件加载和卸载
|
|
||||||
- 依赖关系解析
|
|
||||||
- 加载顺序验证
|
|
||||||
- 缺失依赖处理
|
|
||||||
- 循环依赖处理
|
|
||||||
- 复杂依赖图
|
|
||||||
|
|
||||||
### Context 集成测试
|
|
||||||
- 创建和销毁
|
|
||||||
- 初始化和关闭
|
|
||||||
- 时间推进
|
|
||||||
- 停止请求
|
|
||||||
- 组件访问(模块、插件、定时器)
|
|
||||||
- 事件广播
|
|
||||||
- 多 Context 支持
|
|
||||||
- 定时器集成
|
|
||||||
- 完整生命周期
|
|
||||||
- 性能测试
|
|
||||||
|
|
||||||
## 添加新测试
|
|
||||||
|
|
||||||
使用 `TEST` 宏添加新测试:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include "test_framework.h"
|
|
||||||
#include <your_module.h>
|
|
||||||
|
|
||||||
using namespace extra2d;
|
|
||||||
using namespace extra2d::test;
|
|
||||||
|
|
||||||
TEST(SuiteName, TestName) {
|
|
||||||
// 测试代码
|
|
||||||
ASSERT_EQ(expected, actual);
|
|
||||||
ASSERT_TRUE(condition);
|
|
||||||
ASSERT_NOT_NULL(ptr);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 断言宏
|
|
||||||
|
|
||||||
- `ASSERT_TRUE(expr)` - 断言表达式为 true
|
|
||||||
- `ASSERT_FALSE(expr)` - 断言表达式为 false
|
|
||||||
- `ASSERT_EQ(expected, actual)` - 断言相等
|
|
||||||
- `ASSERT_NE(expected, actual)` - 断言不相等
|
|
||||||
- `ASSERT_NULL(ptr)` - 断言指针为 null
|
|
||||||
- `ASSERT_NOT_NULL(ptr)` - 断言指针不为 null
|
|
||||||
- `ASSERT_THROW(expr, ExceptionType)` - 断言抛出异常
|
|
||||||
- `ASSERT_NO_THROW(expr)` - 断言不抛出异常
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. 测试使用静态注册,自动发现并运行
|
|
||||||
2. 每个测试用例独立运行,互不影响
|
|
||||||
3. 测试框架会捕获异常并报告错误
|
|
||||||
4. 支持测试跳过(skip)功能
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
// GoogleTest 主入口
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
||||||
|
|
@ -1,408 +0,0 @@
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <context/context.h>
|
|
||||||
#include <module/module_registry.h>
|
|
||||||
#include <module/timer_module.h>
|
|
||||||
#include <plugin/plugin_loader.h>
|
|
||||||
#include <event/event_bus.h>
|
|
||||||
#include <event/events.h>
|
|
||||||
|
|
||||||
using namespace extra2d;
|
|
||||||
using extra2d::event::Listener;
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Context 基础测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, CreateAndDestroy) {
|
|
||||||
auto context = Context::create();
|
|
||||||
EXPECT_NE(nullptr, context.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, InitShutdown) {
|
|
||||||
auto context = Context::create();
|
|
||||||
|
|
||||||
EXPECT_FALSE(context->isRunning());
|
|
||||||
|
|
||||||
EXPECT_TRUE(context->init());
|
|
||||||
EXPECT_TRUE(context->isRunning());
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
EXPECT_FALSE(context->isRunning());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, TickAdvancesTime) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
EXPECT_EQ(0.0f, context->totalTime());
|
|
||||||
EXPECT_EQ(0, context->frameCount());
|
|
||||||
|
|
||||||
context->tick(0.016f);
|
|
||||||
EXPECT_NEAR(0.016f, context->totalTime(), 0.0001f);
|
|
||||||
EXPECT_EQ(1, context->frameCount());
|
|
||||||
|
|
||||||
context->tick(0.016f);
|
|
||||||
EXPECT_NEAR(0.032f, context->totalTime(), 0.0001f);
|
|
||||||
EXPECT_EQ(2, context->frameCount());
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, StopRequest) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
EXPECT_TRUE(context->isRunning());
|
|
||||||
|
|
||||||
context->stop();
|
|
||||||
EXPECT_FALSE(context->isRunning());
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Context 组件访问测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, AccessModules) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
// 访问模块注册表
|
|
||||||
ModuleRegistry& modules = context->modules();
|
|
||||||
// 至少应有 TimerModule
|
|
||||||
EXPECT_TRUE(modules.hasModule("Timer"));
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, AccessPlugins) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
// 访问插件加载器
|
|
||||||
PluginLoader& plugins = context->plugins();
|
|
||||||
// 初始时应该没有插件
|
|
||||||
EXPECT_EQ(0, plugins.getPluginCount());
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, AccessTimer) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
// 访问定时器模块
|
|
||||||
TimerModule& timer = context->timer();
|
|
||||||
EXPECT_STREQ("Timer", timer.name());
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 事件总线集成测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, EventsBroadcastOnTick) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
int updateCount = 0;
|
|
||||||
int lateUpdateCount = 0;
|
|
||||||
float receivedDt = 0.0f;
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> updateListener;
|
|
||||||
updateListener.bind([&](float dt) {
|
|
||||||
updateCount++;
|
|
||||||
receivedDt = dt;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnLateUpdate> lateUpdateListener;
|
|
||||||
lateUpdateListener.bind([&](float dt) {
|
|
||||||
lateUpdateCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
context->tick(0.016f);
|
|
||||||
|
|
||||||
EXPECT_EQ(1, updateCount);
|
|
||||||
EXPECT_EQ(1, lateUpdateCount);
|
|
||||||
EXPECT_NEAR(0.016f, receivedDt, 0.0001f);
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, InitEvent) {
|
|
||||||
auto context = Context::create();
|
|
||||||
|
|
||||||
bool initReceived = false;
|
|
||||||
Listener<events::OnInit> initListener;
|
|
||||||
initListener.bind([&]() {
|
|
||||||
initReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
context->init();
|
|
||||||
EXPECT_TRUE(initReceived);
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, ShutdownEvent) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
bool shutdownReceived = false;
|
|
||||||
Listener<events::OnShutdown> shutdownListener;
|
|
||||||
shutdownListener.bind([&]() {
|
|
||||||
shutdownReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
EXPECT_TRUE(shutdownReceived);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 多Context测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, MultipleContexts) {
|
|
||||||
auto context1 = Context::create();
|
|
||||||
auto context2 = Context::create();
|
|
||||||
|
|
||||||
EXPECT_NE(context1.get(), context2.get());
|
|
||||||
|
|
||||||
context1->init();
|
|
||||||
context2->init();
|
|
||||||
|
|
||||||
// 两个Context应该是独立的
|
|
||||||
EXPECT_EQ(0.0f, context1->totalTime());
|
|
||||||
EXPECT_EQ(0.0f, context2->totalTime());
|
|
||||||
|
|
||||||
context1->tick(0.016f);
|
|
||||||
EXPECT_NEAR(0.016f, context1->totalTime(), 0.0001f);
|
|
||||||
EXPECT_EQ(0.0f, context2->totalTime()); // context2 不应受影响
|
|
||||||
|
|
||||||
context1->shutdown();
|
|
||||||
context2->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 定时器集成测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, TimerIntegration) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
int timerCount = 0;
|
|
||||||
TimerId id = context->timer().scheduleOnce(0.05f, [&]() {
|
|
||||||
timerCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_NE(INVALID_TIMER_ID, id);
|
|
||||||
|
|
||||||
// 模拟多帧
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
context->tick(0.016f); // 约 16ms 每帧
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(1, timerCount);
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, TimerWithTimeScale) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
int timerCount = 0;
|
|
||||||
context->timer().setTimeScale(2.0f);
|
|
||||||
|
|
||||||
context->timer().scheduleOnce(0.1f, [&]() {
|
|
||||||
timerCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 实际经过 0.05 秒,但时间缩放为 2.0,效果相当于 0.1 秒
|
|
||||||
context->tick(0.05f);
|
|
||||||
|
|
||||||
EXPECT_EQ(1, timerCount);
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 完整生命周期测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, FullLifecycle) {
|
|
||||||
std::vector<std::string> eventLog;
|
|
||||||
|
|
||||||
auto context = Context::create();
|
|
||||||
|
|
||||||
// 注册事件监听器
|
|
||||||
Listener<events::OnInit> initListener;
|
|
||||||
initListener.bind([&]() {
|
|
||||||
eventLog.push_back("Init");
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> updateListener;
|
|
||||||
updateListener.bind([&](float dt) {
|
|
||||||
if (eventLog.empty() || eventLog.back() != "Update") {
|
|
||||||
eventLog.push_back("Update");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnLateUpdate> lateUpdateListener;
|
|
||||||
lateUpdateListener.bind([&](float dt) {
|
|
||||||
if (eventLog.empty() || eventLog.back() != "LateUpdate") {
|
|
||||||
eventLog.push_back("LateUpdate");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnShutdown> shutdownListener;
|
|
||||||
shutdownListener.bind([&]() {
|
|
||||||
eventLog.push_back("Shutdown");
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化
|
|
||||||
context->init();
|
|
||||||
EXPECT_EQ(1, eventLog.size());
|
|
||||||
EXPECT_EQ("Init", eventLog[0]);
|
|
||||||
|
|
||||||
// 运行几帧
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
context->tick(0.016f);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(7, eventLog.size()); // Init + 3*(Update + LateUpdate)
|
|
||||||
EXPECT_EQ("Update", eventLog[1]);
|
|
||||||
EXPECT_EQ("LateUpdate", eventLog[2]);
|
|
||||||
|
|
||||||
// 关闭
|
|
||||||
context->shutdown();
|
|
||||||
EXPECT_EQ(8, eventLog.size());
|
|
||||||
EXPECT_EQ("Shutdown", eventLog[7]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 模块和插件集成测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, ModuleRegistration) {
|
|
||||||
auto context = Context::create();
|
|
||||||
|
|
||||||
// 创建一个测试模块
|
|
||||||
class TestModule : public IModule {
|
|
||||||
public:
|
|
||||||
const char* name() const override { return "TestModule"; }
|
|
||||||
ModuleType type() const override { return ModuleType::Feature; }
|
|
||||||
|
|
||||||
bool init() override {
|
|
||||||
initCalled = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown() override {
|
|
||||||
shutdownCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool initCalled = false;
|
|
||||||
bool shutdownCalled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
TestModule testModule;
|
|
||||||
|
|
||||||
// 在初始化前注册模块
|
|
||||||
context->modules().registerModule(&testModule);
|
|
||||||
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
EXPECT_TRUE(testModule.initCalled);
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
|
|
||||||
EXPECT_TRUE(testModule.shutdownCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, PluginRegistration) {
|
|
||||||
auto context = Context::create();
|
|
||||||
|
|
||||||
// 创建一个测试插件
|
|
||||||
class TestPlugin : public IPlugin {
|
|
||||||
public:
|
|
||||||
const PluginInfo& getInfo() const override { return info_; }
|
|
||||||
|
|
||||||
bool load() override {
|
|
||||||
loadCalled = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload() override {
|
|
||||||
unloadCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginInfo info_{"TestPlugin", "1.0.0"};
|
|
||||||
bool loadCalled = false;
|
|
||||||
bool unloadCalled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
TestPlugin testPlugin;
|
|
||||||
|
|
||||||
// 在初始化前注册插件
|
|
||||||
context->plugins().registerPlugin(&testPlugin);
|
|
||||||
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
EXPECT_TRUE(testPlugin.loadCalled);
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
|
|
||||||
EXPECT_TRUE(testPlugin.unloadCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 性能测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(Context, HighFrequencyTick) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
int updateCount = 0;
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> listener;
|
|
||||||
listener.bind([&](float dt) {
|
|
||||||
updateCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
const int iterations = 1000;
|
|
||||||
for (int i = 0; i < iterations; i++) {
|
|
||||||
context->tick(0.016f);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(iterations, updateCount);
|
|
||||||
EXPECT_EQ(iterations, context->frameCount());
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Context, MultipleTimers) {
|
|
||||||
auto context = Context::create();
|
|
||||||
context->init();
|
|
||||||
|
|
||||||
int count1 = 0, count2 = 0, count3 = 0;
|
|
||||||
|
|
||||||
context->timer().scheduleOnce(0.05f, [&]() { count1++; });
|
|
||||||
context->timer().scheduleOnce(0.1f, [&]() { count2++; });
|
|
||||||
context->timer().scheduleRepeat(0.03f, 5, [&]() { count3++; });
|
|
||||||
|
|
||||||
// 运行足够长的时间
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
context->tick(0.016f);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(1, count1);
|
|
||||||
EXPECT_EQ(1, count2);
|
|
||||||
EXPECT_EQ(5, count3);
|
|
||||||
|
|
||||||
context->shutdown();
|
|
||||||
}
|
|
||||||
|
|
@ -1,534 +0,0 @@
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <event/event_bus.h>
|
|
||||||
#include <event/events.h>
|
|
||||||
|
|
||||||
using namespace extra2d;
|
|
||||||
using extra2d::event::broadcast;
|
|
||||||
using extra2d::event::Listener;
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 基础事件测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, BasicBroadcast) {
|
|
||||||
int received = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
Listener<events::OnInit> listener;
|
|
||||||
listener.bind([&received]() {
|
|
||||||
received++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 广播事件
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, received);
|
|
||||||
|
|
||||||
// 再次广播
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(2, received);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听器销毁后不应再接收
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(2, received);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, MultipleListeners) {
|
|
||||||
int count1 = 0, count2 = 0, count3 = 0;
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> listener1;
|
|
||||||
listener1.bind([&count1](float dt) {
|
|
||||||
count1++;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> listener2;
|
|
||||||
listener2.bind([&count2](float dt) {
|
|
||||||
count2++;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> listener3;
|
|
||||||
listener3.bind([&count3](float dt) {
|
|
||||||
count3++;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnUpdate>(0.016f);
|
|
||||||
|
|
||||||
EXPECT_EQ(1, count1);
|
|
||||||
EXPECT_EQ(1, count2);
|
|
||||||
EXPECT_EQ(1, count3);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, ListenerEnableDisable) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener;
|
|
||||||
listener.bind([&count]() {
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 默认启用
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count);
|
|
||||||
|
|
||||||
// 禁用
|
|
||||||
listener.disable();
|
|
||||||
EXPECT_FALSE(listener.isEnabled());
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count); // 不应增加
|
|
||||||
|
|
||||||
// 重新启用
|
|
||||||
listener.enable();
|
|
||||||
EXPECT_TRUE(listener.isEnabled());
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(2, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, ListenerReset) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener;
|
|
||||||
listener.bind([&count]() {
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count);
|
|
||||||
|
|
||||||
// 重置回调
|
|
||||||
listener.reset();
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count); // 不应增加
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 带参数事件测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, EventWithParameters) {
|
|
||||||
float receivedDt = 0.0f;
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> listener;
|
|
||||||
listener.bind([&receivedDt](float dt) {
|
|
||||||
receivedDt = dt;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnUpdate>(0.016f);
|
|
||||||
EXPECT_NEAR(0.016f, receivedDt, 0.0001f);
|
|
||||||
|
|
||||||
broadcast<events::OnUpdate>(0.033f);
|
|
||||||
EXPECT_NEAR(0.033f, receivedDt, 0.0001f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, EventWithMultipleParameters) {
|
|
||||||
int receivedWidth = 0, receivedHeight = 0;
|
|
||||||
|
|
||||||
Listener<events::OnResize> listener;
|
|
||||||
listener.bind([&receivedWidth, &receivedHeight](int32 width, int32 height) {
|
|
||||||
receivedWidth = width;
|
|
||||||
receivedHeight = height;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnResize>(1920, 1080);
|
|
||||||
EXPECT_EQ(1920, receivedWidth);
|
|
||||||
EXPECT_EQ(1080, receivedHeight);
|
|
||||||
|
|
||||||
broadcast<events::OnResize>(1280, 720);
|
|
||||||
EXPECT_EQ(1280, receivedWidth);
|
|
||||||
EXPECT_EQ(720, receivedHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 不同事件类型测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, DifferentEventTypes) {
|
|
||||||
int initCount = 0;
|
|
||||||
int updateCount = 0;
|
|
||||||
int shutdownCount = 0;
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener1;
|
|
||||||
listener1.bind([&initCount]() {
|
|
||||||
initCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> listener2;
|
|
||||||
listener2.bind([&updateCount](float dt) {
|
|
||||||
updateCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnShutdown> listener3;
|
|
||||||
listener3.bind([&shutdownCount]() {
|
|
||||||
shutdownCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 广播不同类型的事件
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, initCount);
|
|
||||||
EXPECT_EQ(0, updateCount);
|
|
||||||
EXPECT_EQ(0, shutdownCount);
|
|
||||||
|
|
||||||
broadcast<events::OnUpdate>(0.016f);
|
|
||||||
EXPECT_EQ(1, initCount);
|
|
||||||
EXPECT_EQ(1, updateCount);
|
|
||||||
EXPECT_EQ(0, shutdownCount);
|
|
||||||
|
|
||||||
broadcast<events::OnShutdown>();
|
|
||||||
EXPECT_EQ(1, initCount);
|
|
||||||
EXPECT_EQ(1, updateCount);
|
|
||||||
EXPECT_EQ(1, shutdownCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 监听器生命周期测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, ListenerAutoUnregister) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
Listener<events::OnInit> listener;
|
|
||||||
listener.bind([&count]() {
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count);
|
|
||||||
} // listener 在这里销毁
|
|
||||||
|
|
||||||
// 再次广播,不应触发
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, ListenerOrder) {
|
|
||||||
std::vector<int> order;
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener1;
|
|
||||||
listener1.bind([&order]() {
|
|
||||||
order.push_back(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener2;
|
|
||||||
listener2.bind([&order]() {
|
|
||||||
order.push_back(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener3;
|
|
||||||
listener3.bind([&order]() {
|
|
||||||
order.push_back(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
|
|
||||||
EXPECT_EQ(3, order.size());
|
|
||||||
EXPECT_EQ(1, order[0]);
|
|
||||||
EXPECT_EQ(2, order[1]);
|
|
||||||
EXPECT_EQ(3, order[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 复杂场景测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, AddListenerDuringBroadcast) {
|
|
||||||
// 这个测试验证在广播期间添加监听器的行为
|
|
||||||
int count1 = 0;
|
|
||||||
int count2 = 0;
|
|
||||||
|
|
||||||
Listener<events::OnInit>* listener2 = nullptr;
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener1;
|
|
||||||
listener1.bind([&]() {
|
|
||||||
count1++;
|
|
||||||
// 在回调中创建新监听器
|
|
||||||
if (!listener2) {
|
|
||||||
listener2 = new Listener<events::OnInit>();
|
|
||||||
listener2->bind([&count2]() {
|
|
||||||
count2++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count1);
|
|
||||||
// 新监听器在当前广播中不应被触发
|
|
||||||
EXPECT_EQ(0, count2);
|
|
||||||
|
|
||||||
// 下一次广播,新监听器应该被触发
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(2, count1);
|
|
||||||
EXPECT_EQ(1, count2);
|
|
||||||
|
|
||||||
delete listener2;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, RemoveListenerDuringBroadcast) {
|
|
||||||
int count1 = 0, count2 = 0, count3 = 0;
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener1;
|
|
||||||
listener1.bind([&]() {
|
|
||||||
count1++;
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
Listener<events::OnInit> listener2;
|
|
||||||
listener2.bind([&]() {
|
|
||||||
count2++;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnInit> listener3;
|
|
||||||
listener3.bind([&]() {
|
|
||||||
count3++;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(1, count1);
|
|
||||||
EXPECT_EQ(1, count2);
|
|
||||||
EXPECT_EQ(1, count3);
|
|
||||||
} // listener2 和 listener3 在这里销毁
|
|
||||||
|
|
||||||
// 再次广播
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(2, count1);
|
|
||||||
EXPECT_EQ(1, count2); // 不应增加
|
|
||||||
EXPECT_EQ(1, count3); // 不应增加
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 输入事件测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, InputEvents) {
|
|
||||||
int keyDownCode = -1;
|
|
||||||
int keyUpCode = -1;
|
|
||||||
int mouseButton = -1;
|
|
||||||
int mouseX = -1, mouseY = -1;
|
|
||||||
|
|
||||||
Listener<events::OnKeyDown> keyDownListener;
|
|
||||||
keyDownListener.bind([&keyDownCode](int32 key) {
|
|
||||||
keyDownCode = key;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnKeyUp> keyUpListener;
|
|
||||||
keyUpListener.bind([&keyUpCode](int32 key) {
|
|
||||||
keyUpCode = key;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnMouseDown> mouseDownListener;
|
|
||||||
mouseDownListener.bind([&mouseButton, &mouseX, &mouseY](int32 button, int32 x, int32 y) {
|
|
||||||
mouseButton = button;
|
|
||||||
mouseX = x;
|
|
||||||
mouseY = y;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 模拟输入事件
|
|
||||||
broadcast<events::OnKeyDown>(65); // 'A' key
|
|
||||||
EXPECT_EQ(65, keyDownCode);
|
|
||||||
|
|
||||||
broadcast<events::OnKeyUp>(65);
|
|
||||||
EXPECT_EQ(65, keyUpCode);
|
|
||||||
|
|
||||||
broadcast<events::OnMouseDown>(0, 100, 200); // 左键点击
|
|
||||||
EXPECT_EQ(0, mouseButton);
|
|
||||||
EXPECT_EQ(100, mouseX);
|
|
||||||
EXPECT_EQ(200, mouseY);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, MouseMoveEvent) {
|
|
||||||
int receivedX = -1, receivedY = -1;
|
|
||||||
|
|
||||||
Listener<events::OnMouseMove> listener;
|
|
||||||
listener.bind([&receivedX, &receivedY](int32 x, int32 y) {
|
|
||||||
receivedX = x;
|
|
||||||
receivedY = y;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnMouseMove>(500, 300);
|
|
||||||
EXPECT_EQ(500, receivedX);
|
|
||||||
EXPECT_EQ(300, receivedY);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, MouseWheelEvent) {
|
|
||||||
int receivedDelta = 0;
|
|
||||||
|
|
||||||
Listener<events::OnMouseWheel> listener;
|
|
||||||
listener.bind([&receivedDelta](int32 delta) {
|
|
||||||
receivedDelta = delta;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnMouseWheel>(120);
|
|
||||||
EXPECT_EQ(120, receivedDelta);
|
|
||||||
|
|
||||||
broadcast<events::OnMouseWheel>(-120);
|
|
||||||
EXPECT_EQ(-120, receivedDelta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 窗口事件测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, WindowEvents) {
|
|
||||||
bool focusReceived = false;
|
|
||||||
bool blurReceived = false;
|
|
||||||
bool showReceived = false;
|
|
||||||
bool hideReceived = false;
|
|
||||||
bool closeReceived = false;
|
|
||||||
int resizeWidth = 0, resizeHeight = 0;
|
|
||||||
|
|
||||||
Listener<events::OnFocus> focusListener;
|
|
||||||
focusListener.bind([&focusReceived]() {
|
|
||||||
focusReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnBlur> blurListener;
|
|
||||||
blurListener.bind([&blurReceived]() {
|
|
||||||
blurReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnShow> showListener;
|
|
||||||
showListener.bind([&showReceived]() {
|
|
||||||
showReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnHide> hideListener;
|
|
||||||
hideListener.bind([&hideReceived]() {
|
|
||||||
hideReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnClose> closeListener;
|
|
||||||
closeListener.bind([&closeReceived]() {
|
|
||||||
closeReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnResize> resizeListener;
|
|
||||||
resizeListener.bind([&resizeWidth, &resizeHeight](int32 width, int32 height) {
|
|
||||||
resizeWidth = width;
|
|
||||||
resizeHeight = height;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnFocus>();
|
|
||||||
EXPECT_TRUE(focusReceived);
|
|
||||||
|
|
||||||
broadcast<events::OnBlur>();
|
|
||||||
EXPECT_TRUE(blurReceived);
|
|
||||||
|
|
||||||
broadcast<events::OnShow>();
|
|
||||||
EXPECT_TRUE(showReceived);
|
|
||||||
|
|
||||||
broadcast<events::OnHide>();
|
|
||||||
EXPECT_TRUE(hideReceived);
|
|
||||||
|
|
||||||
broadcast<events::OnClose>();
|
|
||||||
EXPECT_TRUE(closeReceived);
|
|
||||||
|
|
||||||
broadcast<events::OnResize>(800, 600);
|
|
||||||
EXPECT_EQ(800, resizeWidth);
|
|
||||||
EXPECT_EQ(600, resizeHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 触摸事件测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, TouchEvents) {
|
|
||||||
int touchId = -1;
|
|
||||||
int touchX = -1, touchY = -1;
|
|
||||||
|
|
||||||
Listener<events::OnTouchBegin> touchBeginListener;
|
|
||||||
touchBeginListener.bind([&touchId, &touchX, &touchY](int32 id, int32 x, int32 y) {
|
|
||||||
touchId = id;
|
|
||||||
touchX = x;
|
|
||||||
touchY = y;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnTouchBegin>(0, 100, 200);
|
|
||||||
EXPECT_EQ(0, touchId);
|
|
||||||
EXPECT_EQ(100, touchX);
|
|
||||||
EXPECT_EQ(200, touchY);
|
|
||||||
|
|
||||||
Listener<events::OnTouchMove> touchMoveListener;
|
|
||||||
touchMoveListener.bind([&touchId, &touchX, &touchY](int32 id, int32 x, int32 y) {
|
|
||||||
touchId = id;
|
|
||||||
touchX = x;
|
|
||||||
touchY = y;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnTouchMove>(0, 150, 250);
|
|
||||||
EXPECT_EQ(0, touchId);
|
|
||||||
EXPECT_EQ(150, touchX);
|
|
||||||
EXPECT_EQ(250, touchY);
|
|
||||||
|
|
||||||
Listener<events::OnTouchEnd> touchEndListener;
|
|
||||||
touchEndListener.bind([&touchId, &touchX, &touchY](int32 id, int32 x, int32 y) {
|
|
||||||
touchId = id;
|
|
||||||
touchX = x;
|
|
||||||
touchY = y;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnTouchEnd>(0, 150, 250);
|
|
||||||
EXPECT_EQ(0, touchId);
|
|
||||||
EXPECT_EQ(150, touchX);
|
|
||||||
EXPECT_EQ(250, touchY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 游戏手柄事件测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, GamepadEvents) {
|
|
||||||
int connectedDeviceId = -1;
|
|
||||||
int disconnectedDeviceId = -1;
|
|
||||||
|
|
||||||
Listener<events::OnGamepadConnect> connectListener;
|
|
||||||
connectListener.bind([&connectedDeviceId](int32 deviceId) {
|
|
||||||
connectedDeviceId = deviceId;
|
|
||||||
});
|
|
||||||
|
|
||||||
Listener<events::OnGamepadDisconnect> disconnectListener;
|
|
||||||
disconnectListener.bind([&disconnectedDeviceId](int32 deviceId) {
|
|
||||||
disconnectedDeviceId = deviceId;
|
|
||||||
});
|
|
||||||
|
|
||||||
broadcast<events::OnGamepadConnect>(0);
|
|
||||||
EXPECT_EQ(0, connectedDeviceId);
|
|
||||||
|
|
||||||
broadcast<events::OnGamepadDisconnect>(0);
|
|
||||||
EXPECT_EQ(0, disconnectedDeviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 性能测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(EventBus, HighFrequencyBroadcast) {
|
|
||||||
const int iterations = 10000;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
Listener<events::OnUpdate> listener;
|
|
||||||
listener.bind([&count](float dt) {
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (int i = 0; i < iterations; i++) {
|
|
||||||
broadcast<events::OnUpdate>(0.016f);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(iterations, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(EventBus, ManyListeners) {
|
|
||||||
const int listenerCount = 100;
|
|
||||||
int totalCount = 0;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Listener<events::OnInit>>> listeners;
|
|
||||||
for (int i = 0; i < listenerCount; i++) {
|
|
||||||
auto listener = std::make_unique<Listener<events::OnInit>>();
|
|
||||||
listener->bind([&totalCount]() {
|
|
||||||
totalCount++;
|
|
||||||
});
|
|
||||||
listeners.push_back(std::move(listener));
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcast<events::OnInit>();
|
|
||||||
EXPECT_EQ(listenerCount, totalCount);
|
|
||||||
}
|
|
||||||
|
|
@ -1,322 +0,0 @@
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <module/module_registry.h>
|
|
||||||
#include <module/imodule.h>
|
|
||||||
#include <types/const/priority.h>
|
|
||||||
|
|
||||||
using namespace extra2d;
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 测试用模块类
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
class TestModule : public IModule {
|
|
||||||
public:
|
|
||||||
TestModule(const char* name, ModuleType type, int priority)
|
|
||||||
: name_(name), type_(type), priority_(priority) {}
|
|
||||||
|
|
||||||
const char* name() const override { return name_.c_str(); }
|
|
||||||
ModuleType type() const override { return type_; }
|
|
||||||
int priority() const override { return priority_; }
|
|
||||||
|
|
||||||
bool init() override {
|
|
||||||
initCalled = true;
|
|
||||||
return initResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown() override {
|
|
||||||
shutdownCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_;
|
|
||||||
ModuleType type_;
|
|
||||||
int priority_;
|
|
||||||
bool initCalled = false;
|
|
||||||
bool shutdownCalled = false;
|
|
||||||
bool initResult = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ModuleRegistry 基础测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, BasicOperations) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
TestModule module1("Module1", ModuleType::Feature, 0);
|
|
||||||
TestModule module2("Module2", ModuleType::System, 10);
|
|
||||||
|
|
||||||
// 注册模块
|
|
||||||
registry.registerModule(&module1);
|
|
||||||
EXPECT_EQ(1, registry.getModuleCount());
|
|
||||||
EXPECT_TRUE(registry.hasModule("Module1"));
|
|
||||||
|
|
||||||
registry.registerModule(&module2);
|
|
||||||
EXPECT_EQ(2, registry.getModuleCount());
|
|
||||||
EXPECT_TRUE(registry.hasModule("Module2"));
|
|
||||||
|
|
||||||
// 获取模块
|
|
||||||
EXPECT_EQ(&module1, registry.getModule("Module1"));
|
|
||||||
EXPECT_EQ(&module2, registry.getModule("Module2"));
|
|
||||||
EXPECT_EQ(nullptr, registry.getModule("NonExistent"));
|
|
||||||
|
|
||||||
// 注销模块
|
|
||||||
registry.unregisterModule("Module1");
|
|
||||||
EXPECT_EQ(1, registry.getModuleCount());
|
|
||||||
EXPECT_FALSE(registry.hasModule("Module1"));
|
|
||||||
EXPECT_TRUE(registry.hasModule("Module2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, DuplicateRegistration) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
TestModule module1("SameName", ModuleType::Feature, 0);
|
|
||||||
TestModule module2("SameName", ModuleType::System, 10);
|
|
||||||
|
|
||||||
registry.registerModule(&module1);
|
|
||||||
EXPECT_EQ(1, registry.getModuleCount());
|
|
||||||
|
|
||||||
// 重复注册同名模块,后注册的应覆盖
|
|
||||||
registry.registerModule(&module2);
|
|
||||||
EXPECT_EQ(1, registry.getModuleCount());
|
|
||||||
EXPECT_EQ(&module2, registry.getModule("SameName"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, GetAllModules) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
TestModule module1("Module1", ModuleType::Feature, 0);
|
|
||||||
TestModule module2("Module2", ModuleType::System, 10);
|
|
||||||
TestModule module3("Module3", ModuleType::Core, 5);
|
|
||||||
|
|
||||||
registry.registerModule(&module1);
|
|
||||||
registry.registerModule(&module2);
|
|
||||||
registry.registerModule(&module3);
|
|
||||||
|
|
||||||
auto allModules = registry.getAllModules();
|
|
||||||
EXPECT_EQ(3, allModules.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, GetModulesByType) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
TestModule coreModule("CoreModule", ModuleType::Core, 0);
|
|
||||||
TestModule systemModule1("SystemModule1", ModuleType::System, 10);
|
|
||||||
TestModule systemModule2("SystemModule2", ModuleType::System, 20);
|
|
||||||
TestModule featureModule("FeatureModule", ModuleType::Feature, 5);
|
|
||||||
|
|
||||||
registry.registerModule(&coreModule);
|
|
||||||
registry.registerModule(&systemModule1);
|
|
||||||
registry.registerModule(&systemModule2);
|
|
||||||
registry.registerModule(&featureModule);
|
|
||||||
|
|
||||||
auto coreModules = registry.getModulesByType(ModuleType::Core);
|
|
||||||
EXPECT_EQ(1, coreModules.size());
|
|
||||||
|
|
||||||
auto systemModules = registry.getModulesByType(ModuleType::System);
|
|
||||||
EXPECT_EQ(2, systemModules.size());
|
|
||||||
|
|
||||||
auto featureModules = registry.getModulesByType(ModuleType::Feature);
|
|
||||||
EXPECT_EQ(1, featureModules.size());
|
|
||||||
|
|
||||||
auto extensionModules = registry.getModulesByType(ModuleType::Extension);
|
|
||||||
EXPECT_EQ(0, extensionModules.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 初始化和关闭测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, InitShutdown) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
TestModule module1("Module1", ModuleType::Feature, 0);
|
|
||||||
TestModule module2("Module2", ModuleType::System, 10);
|
|
||||||
|
|
||||||
registry.registerModule(&module1);
|
|
||||||
registry.registerModule(&module2);
|
|
||||||
|
|
||||||
// 初始化所有模块
|
|
||||||
EXPECT_TRUE(registry.initAll());
|
|
||||||
EXPECT_TRUE(module1.initCalled);
|
|
||||||
EXPECT_TRUE(module2.initCalled);
|
|
||||||
|
|
||||||
// 关闭所有模块
|
|
||||||
registry.shutdownAll();
|
|
||||||
EXPECT_TRUE(module1.shutdownCalled);
|
|
||||||
EXPECT_TRUE(module2.shutdownCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, InitFailure) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
TestModule module1("Module1", ModuleType::Feature, 0);
|
|
||||||
TestModule module2("Module2", ModuleType::System, 10);
|
|
||||||
module2.initResult = false; // 模拟初始化失败
|
|
||||||
|
|
||||||
registry.registerModule(&module1);
|
|
||||||
registry.registerModule(&module2);
|
|
||||||
|
|
||||||
// 初始化应失败
|
|
||||||
EXPECT_FALSE(registry.initAll());
|
|
||||||
EXPECT_TRUE(module1.initCalled);
|
|
||||||
EXPECT_TRUE(module2.initCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, PriorityOrder) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
std::vector<std::string> initOrder;
|
|
||||||
std::vector<std::string> shutdownOrder;
|
|
||||||
|
|
||||||
class OrderTrackingModule : public IModule {
|
|
||||||
public:
|
|
||||||
OrderTrackingModule(const char* name, int priority,
|
|
||||||
std::vector<std::string>& initVec,
|
|
||||||
std::vector<std::string>& shutdownVec)
|
|
||||||
: name_(name), priority_(priority),
|
|
||||||
initOrder_(initVec), shutdownOrder_(shutdownVec) {}
|
|
||||||
|
|
||||||
const char* name() const override { return name_.c_str(); }
|
|
||||||
int priority() const override { return priority_; }
|
|
||||||
|
|
||||||
bool init() override {
|
|
||||||
initOrder_.push_back(name_);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown() override {
|
|
||||||
shutdownOrder_.push_back(name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_;
|
|
||||||
int priority_;
|
|
||||||
std::vector<std::string>& initOrder_;
|
|
||||||
std::vector<std::string>& shutdownOrder_;
|
|
||||||
};
|
|
||||||
|
|
||||||
OrderTrackingModule module1("LowPriority", 100, initOrder, shutdownOrder);
|
|
||||||
OrderTrackingModule module2("HighPriority", 10, initOrder, shutdownOrder);
|
|
||||||
OrderTrackingModule module3("MediumPriority", 50, initOrder, shutdownOrder);
|
|
||||||
|
|
||||||
registry.registerModule(&module1);
|
|
||||||
registry.registerModule(&module2);
|
|
||||||
registry.registerModule(&module3);
|
|
||||||
|
|
||||||
registry.initAll();
|
|
||||||
|
|
||||||
// 初始化应按优先级顺序(数值小的先初始化)
|
|
||||||
EXPECT_EQ(3, initOrder.size());
|
|
||||||
EXPECT_EQ("HighPriority", initOrder[0]);
|
|
||||||
EXPECT_EQ("MediumPriority", initOrder[1]);
|
|
||||||
EXPECT_EQ("LowPriority", initOrder[2]);
|
|
||||||
|
|
||||||
registry.shutdownAll();
|
|
||||||
|
|
||||||
// 关闭应按优先级逆序(数值大的先关闭)
|
|
||||||
EXPECT_EQ(3, shutdownOrder.size());
|
|
||||||
EXPECT_EQ("LowPriority", shutdownOrder[0]);
|
|
||||||
EXPECT_EQ("MediumPriority", shutdownOrder[1]);
|
|
||||||
EXPECT_EQ("HighPriority", shutdownOrder[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 空注册表测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, EmptyRegistry) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
EXPECT_EQ(0, registry.getModuleCount());
|
|
||||||
EXPECT_FALSE(registry.hasModule("AnyModule"));
|
|
||||||
EXPECT_EQ(nullptr, registry.getModule("AnyModule"));
|
|
||||||
|
|
||||||
auto allModules = registry.getAllModules();
|
|
||||||
EXPECT_EQ(0, allModules.size());
|
|
||||||
|
|
||||||
// 空注册表的初始化和关闭不应崩溃
|
|
||||||
EXPECT_TRUE(registry.initAll());
|
|
||||||
registry.shutdownAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, UnregisterNonExistent) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
// 注销不存在的模块不应崩溃
|
|
||||||
EXPECT_NO_THROW(registry.unregisterModule("NonExistent"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 模块生命周期测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, ModuleLifecycle) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
enum class State { Created, Initialized, Running, Shutdown };
|
|
||||||
|
|
||||||
class LifecycleModule : public IModule {
|
|
||||||
public:
|
|
||||||
LifecycleModule(const char* name, State& state)
|
|
||||||
: name_(name), state_(state) {
|
|
||||||
state_ = State::Created;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* name() const override { return name_.c_str(); }
|
|
||||||
|
|
||||||
bool init() override {
|
|
||||||
state_ = State::Initialized;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown() override {
|
|
||||||
state_ = State::Shutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_;
|
|
||||||
State& state_;
|
|
||||||
};
|
|
||||||
|
|
||||||
State state = State::Created;
|
|
||||||
LifecycleModule module("Lifecycle", state);
|
|
||||||
|
|
||||||
EXPECT_EQ(State::Created, state);
|
|
||||||
|
|
||||||
registry.registerModule(&module);
|
|
||||||
registry.initAll();
|
|
||||||
EXPECT_EQ(State::Initialized, state);
|
|
||||||
|
|
||||||
registry.shutdownAll();
|
|
||||||
EXPECT_EQ(State::Shutdown, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 混合类型模块测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(ModuleRegistry, MixedModuleTypes) {
|
|
||||||
ModuleRegistry registry;
|
|
||||||
|
|
||||||
TestModule coreModule("Core", ModuleType::Core, Pri::System);
|
|
||||||
TestModule systemModule("System", ModuleType::System, Pri::Window);
|
|
||||||
TestModule featureModule("Feature", ModuleType::Feature, 0);
|
|
||||||
TestModule extensionModule("Extension", ModuleType::Extension, 100);
|
|
||||||
|
|
||||||
registry.registerModule(&coreModule);
|
|
||||||
registry.registerModule(&systemModule);
|
|
||||||
registry.registerModule(&featureModule);
|
|
||||||
registry.registerModule(&extensionModule);
|
|
||||||
|
|
||||||
EXPECT_EQ(4, registry.getModuleCount());
|
|
||||||
|
|
||||||
EXPECT_TRUE(registry.initAll());
|
|
||||||
EXPECT_TRUE(coreModule.initCalled);
|
|
||||||
EXPECT_TRUE(systemModule.initCalled);
|
|
||||||
EXPECT_TRUE(featureModule.initCalled);
|
|
||||||
EXPECT_TRUE(extensionModule.initCalled);
|
|
||||||
|
|
||||||
registry.shutdownAll();
|
|
||||||
EXPECT_TRUE(coreModule.shutdownCalled);
|
|
||||||
EXPECT_TRUE(systemModule.shutdownCalled);
|
|
||||||
EXPECT_TRUE(featureModule.shutdownCalled);
|
|
||||||
EXPECT_TRUE(extensionModule.shutdownCalled);
|
|
||||||
}
|
|
||||||
|
|
@ -1,413 +0,0 @@
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <plugin/plugin_loader.h>
|
|
||||||
#include <plugin/iplugin.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
using namespace extra2d;
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 测试用插件类
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
class TestPlugin : public IPlugin {
|
|
||||||
public:
|
|
||||||
TestPlugin(const char* name, const char* version = "1.0.0",
|
|
||||||
const std::vector<std::string>& deps = {})
|
|
||||||
: name_(name), version_(version), dependencies_(deps) {
|
|
||||||
info_.name = name;
|
|
||||||
info_.version = version;
|
|
||||||
info_.dependencies = deps;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PluginInfo& getInfo() const override { return info_; }
|
|
||||||
|
|
||||||
bool load() override {
|
|
||||||
loadCalled = true;
|
|
||||||
return loadResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload() override {
|
|
||||||
unloadCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> getDependencies() const override {
|
|
||||||
return dependencies_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_;
|
|
||||||
std::string version_;
|
|
||||||
std::vector<std::string> dependencies_;
|
|
||||||
PluginInfo info_;
|
|
||||||
bool loadCalled = false;
|
|
||||||
bool unloadCalled = false;
|
|
||||||
bool loadResult = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// PluginLoader 基础测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, BasicOperations) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
TestPlugin plugin1("Plugin1");
|
|
||||||
TestPlugin plugin2("Plugin2", "2.0.0");
|
|
||||||
|
|
||||||
// 注册插件
|
|
||||||
loader.registerPlugin(&plugin1);
|
|
||||||
EXPECT_EQ(1, loader.getPluginCount());
|
|
||||||
EXPECT_TRUE(loader.hasPlugin("Plugin1"));
|
|
||||||
|
|
||||||
loader.registerPlugin(&plugin2);
|
|
||||||
EXPECT_EQ(2, loader.getPluginCount());
|
|
||||||
EXPECT_TRUE(loader.hasPlugin("Plugin2"));
|
|
||||||
|
|
||||||
// 获取插件
|
|
||||||
EXPECT_EQ(&plugin1, loader.getPlugin("Plugin1"));
|
|
||||||
EXPECT_EQ(&plugin2, loader.getPlugin("Plugin2"));
|
|
||||||
EXPECT_EQ(nullptr, loader.getPlugin("NonExistent"));
|
|
||||||
|
|
||||||
// 卸载插件
|
|
||||||
loader.unloadPlugin("Plugin1");
|
|
||||||
EXPECT_EQ(1, loader.getPluginCount());
|
|
||||||
EXPECT_FALSE(loader.hasPlugin("Plugin1"));
|
|
||||||
EXPECT_TRUE(loader.hasPlugin("Plugin2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PluginLoader, DuplicateRegistration) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
TestPlugin plugin1("SameName", "1.0.0");
|
|
||||||
TestPlugin plugin2("SameName", "2.0.0");
|
|
||||||
|
|
||||||
loader.registerPlugin(&plugin1);
|
|
||||||
EXPECT_EQ(1, loader.getPluginCount());
|
|
||||||
|
|
||||||
// 重复注册同名插件,后注册的应覆盖
|
|
||||||
loader.registerPlugin(&plugin2);
|
|
||||||
EXPECT_EQ(1, loader.getPluginCount());
|
|
||||||
|
|
||||||
IPlugin* retrieved = loader.getPlugin("SameName");
|
|
||||||
EXPECT_NE(nullptr, retrieved);
|
|
||||||
EXPECT_EQ("2.0.0", retrieved->getInfo().version);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PluginLoader, GetAllPlugins) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
TestPlugin plugin1("Plugin1");
|
|
||||||
TestPlugin plugin2("Plugin2");
|
|
||||||
TestPlugin plugin3("Plugin3");
|
|
||||||
|
|
||||||
loader.registerPlugin(&plugin1);
|
|
||||||
loader.registerPlugin(&plugin2);
|
|
||||||
loader.registerPlugin(&plugin3);
|
|
||||||
|
|
||||||
auto allPlugins = loader.getAllPlugins();
|
|
||||||
EXPECT_EQ(3, allPlugins.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 加载和卸载测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, LoadUnload) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
TestPlugin plugin1("Plugin1");
|
|
||||||
TestPlugin plugin2("Plugin2");
|
|
||||||
|
|
||||||
loader.registerPlugin(&plugin1);
|
|
||||||
loader.registerPlugin(&plugin2);
|
|
||||||
|
|
||||||
// 加载所有插件
|
|
||||||
EXPECT_TRUE(loader.initAll());
|
|
||||||
EXPECT_TRUE(plugin1.loadCalled);
|
|
||||||
EXPECT_TRUE(plugin2.loadCalled);
|
|
||||||
|
|
||||||
// 卸载所有插件
|
|
||||||
loader.shutdownAll();
|
|
||||||
EXPECT_TRUE(plugin1.unloadCalled);
|
|
||||||
EXPECT_TRUE(plugin2.unloadCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PluginLoader, LoadFailure) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
TestPlugin plugin1("Plugin1");
|
|
||||||
TestPlugin plugin2("Plugin2");
|
|
||||||
plugin2.loadResult = false; // 模拟加载失败
|
|
||||||
|
|
||||||
loader.registerPlugin(&plugin1);
|
|
||||||
loader.registerPlugin(&plugin2);
|
|
||||||
|
|
||||||
// 加载应失败
|
|
||||||
EXPECT_FALSE(loader.initAll());
|
|
||||||
EXPECT_TRUE(plugin1.loadCalled);
|
|
||||||
EXPECT_TRUE(plugin2.loadCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 依赖关系测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, Dependencies) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
// PluginC 依赖 PluginB,PluginB 依赖 PluginA
|
|
||||||
TestPlugin pluginA("PluginA");
|
|
||||||
TestPlugin pluginB("PluginB", "1.0.0", {"PluginA"});
|
|
||||||
TestPlugin pluginC("PluginC", "1.0.0", {"PluginB"});
|
|
||||||
|
|
||||||
loader.registerPlugin(&pluginC);
|
|
||||||
loader.registerPlugin(&pluginB);
|
|
||||||
loader.registerPlugin(&pluginA);
|
|
||||||
|
|
||||||
// 所有插件都应能加载(依赖会自动解析)
|
|
||||||
EXPECT_TRUE(loader.initAll());
|
|
||||||
EXPECT_TRUE(pluginA.loadCalled);
|
|
||||||
EXPECT_TRUE(pluginB.loadCalled);
|
|
||||||
EXPECT_TRUE(pluginC.loadCalled);
|
|
||||||
|
|
||||||
loader.shutdownAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PluginLoader, MissingDependency) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
// PluginB 依赖不存在的 PluginA
|
|
||||||
TestPlugin pluginB("PluginB", "1.0.0", {"PluginA"});
|
|
||||||
|
|
||||||
loader.registerPlugin(&pluginB);
|
|
||||||
|
|
||||||
// 加载应失败(依赖未满足)
|
|
||||||
EXPECT_FALSE(loader.initAll());
|
|
||||||
EXPECT_FALSE(pluginB.loadCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PluginLoader, CircularDependency) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
// A 依赖 B,B 依赖 A(循环依赖)
|
|
||||||
TestPlugin pluginA("PluginA", "1.0.0", {"PluginB"});
|
|
||||||
TestPlugin pluginB("PluginB", "1.0.0", {"PluginA"});
|
|
||||||
|
|
||||||
loader.registerPlugin(&pluginA);
|
|
||||||
loader.registerPlugin(&pluginB);
|
|
||||||
|
|
||||||
// 循环依赖应能处理(目前实现可能不检测循环,但至少不应崩溃)
|
|
||||||
// 注意:根据实现不同,这可能成功或失败
|
|
||||||
loader.initAll();
|
|
||||||
loader.shutdownAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PluginLoader, DependencyLoadOrder) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
std::vector<std::string> loadOrder;
|
|
||||||
std::vector<std::string> unloadOrder;
|
|
||||||
|
|
||||||
class OrderTrackingPlugin : public IPlugin {
|
|
||||||
public:
|
|
||||||
OrderTrackingPlugin(const char* name,
|
|
||||||
const std::vector<std::string>& deps,
|
|
||||||
std::vector<std::string>& loadVec,
|
|
||||||
std::vector<std::string>& unloadVec)
|
|
||||||
: name_(name), loadOrder_(loadVec), unloadOrder_(unloadVec) {
|
|
||||||
info_.name = name;
|
|
||||||
info_.dependencies = deps;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PluginInfo& getInfo() const override { return info_; }
|
|
||||||
|
|
||||||
bool load() override {
|
|
||||||
loadOrder_.push_back(name_);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload() override {
|
|
||||||
unloadOrder_.push_back(name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_;
|
|
||||||
PluginInfo info_;
|
|
||||||
std::vector<std::string>& loadOrder_;
|
|
||||||
std::vector<std::string>& unloadOrder_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// C 依赖 B,B 依赖 A
|
|
||||||
OrderTrackingPlugin pluginC("PluginC", {"PluginB"}, loadOrder, unloadOrder);
|
|
||||||
OrderTrackingPlugin pluginA("PluginA", {}, loadOrder, unloadOrder);
|
|
||||||
OrderTrackingPlugin pluginB("PluginB", {"PluginA"}, loadOrder, unloadOrder);
|
|
||||||
|
|
||||||
loader.registerPlugin(&pluginC);
|
|
||||||
loader.registerPlugin(&pluginA);
|
|
||||||
loader.registerPlugin(&pluginB);
|
|
||||||
|
|
||||||
loader.initAll();
|
|
||||||
|
|
||||||
// 加载顺序应满足依赖关系
|
|
||||||
EXPECT_EQ(3, loadOrder.size());
|
|
||||||
// A 必须在 B 之前加载
|
|
||||||
auto aPos = std::find(loadOrder.begin(), loadOrder.end(), "PluginA");
|
|
||||||
auto bPos = std::find(loadOrder.begin(), loadOrder.end(), "PluginB");
|
|
||||||
auto cPos = std::find(loadOrder.begin(), loadOrder.end(), "PluginC");
|
|
||||||
EXPECT_TRUE(aPos < bPos);
|
|
||||||
EXPECT_TRUE(bPos < cPos);
|
|
||||||
|
|
||||||
loader.shutdownAll();
|
|
||||||
|
|
||||||
// 卸载顺序应与加载顺序相反
|
|
||||||
EXPECT_EQ(3, unloadOrder.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 空加载器测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, EmptyLoader) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
EXPECT_EQ(0, loader.getPluginCount());
|
|
||||||
EXPECT_FALSE(loader.hasPlugin("AnyPlugin"));
|
|
||||||
EXPECT_EQ(nullptr, loader.getPlugin("AnyPlugin"));
|
|
||||||
|
|
||||||
auto allPlugins = loader.getAllPlugins();
|
|
||||||
EXPECT_EQ(0, allPlugins.size());
|
|
||||||
|
|
||||||
// 空加载器的初始化和关闭不应崩溃
|
|
||||||
EXPECT_TRUE(loader.initAll());
|
|
||||||
loader.shutdownAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PluginLoader, UnloadNonExistent) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
// 卸载不存在的插件不应崩溃
|
|
||||||
EXPECT_NO_THROW(loader.unloadPlugin("NonExistent"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 搜索路径测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, SearchPaths) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
// 添加搜索路径
|
|
||||||
loader.addSearchPath("/usr/lib/extra2d/plugins");
|
|
||||||
loader.addSearchPath("/home/user/.extra2d/plugins");
|
|
||||||
loader.addSearchPath("./plugins");
|
|
||||||
|
|
||||||
// 搜索路径功能主要用于动态库加载,这里只验证不崩溃
|
|
||||||
EXPECT_NO_THROW(loader.loadPluginsFromDirectory("./non_existent_dir"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 插件信息测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, PluginInfo) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
TestPlugin plugin("TestPlugin", "1.2.3");
|
|
||||||
plugin.info_.author = "Test Author";
|
|
||||||
plugin.info_.description = "Test Description";
|
|
||||||
plugin.info_.dependencies = {"Dep1", "Dep2"};
|
|
||||||
|
|
||||||
loader.registerPlugin(&plugin);
|
|
||||||
|
|
||||||
IPlugin* retrieved = loader.getPlugin("TestPlugin");
|
|
||||||
EXPECT_NE(nullptr, retrieved);
|
|
||||||
|
|
||||||
const PluginInfo& info = retrieved->getInfo();
|
|
||||||
EXPECT_EQ("TestPlugin", info.name);
|
|
||||||
EXPECT_EQ("1.2.3", info.version);
|
|
||||||
EXPECT_EQ("Test Author", info.author);
|
|
||||||
EXPECT_EQ("Test Description", info.description);
|
|
||||||
EXPECT_EQ(2, info.dependencies.size());
|
|
||||||
EXPECT_EQ("Dep1", info.dependencies[0]);
|
|
||||||
EXPECT_EQ("Dep2", info.dependencies[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 插件生命周期测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, PluginLifecycle) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
enum class State { Created, Loaded, Running, Unloaded };
|
|
||||||
|
|
||||||
class LifecyclePlugin : public IPlugin {
|
|
||||||
public:
|
|
||||||
LifecyclePlugin(const char* name, State& state)
|
|
||||||
: name_(name), state_(state) {
|
|
||||||
state_ = State::Created;
|
|
||||||
info_.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PluginInfo& getInfo() const override { return info_; }
|
|
||||||
|
|
||||||
bool load() override {
|
|
||||||
state_ = State::Loaded;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload() override {
|
|
||||||
state_ = State::Unloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_;
|
|
||||||
State& state_;
|
|
||||||
PluginInfo info_;
|
|
||||||
};
|
|
||||||
|
|
||||||
State state = State::Created;
|
|
||||||
LifecyclePlugin plugin("Lifecycle", state);
|
|
||||||
|
|
||||||
EXPECT_EQ(State::Created, state);
|
|
||||||
|
|
||||||
loader.registerPlugin(&plugin);
|
|
||||||
loader.initAll();
|
|
||||||
EXPECT_EQ(State::Loaded, state);
|
|
||||||
|
|
||||||
loader.shutdownAll();
|
|
||||||
EXPECT_EQ(State::Unloaded, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 复杂依赖测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(PluginLoader, ComplexDependencies) {
|
|
||||||
PluginLoader loader;
|
|
||||||
|
|
||||||
// 创建复杂的依赖图:
|
|
||||||
// A -> B -> D
|
|
||||||
// A -> C -> D
|
|
||||||
// E (独立)
|
|
||||||
|
|
||||||
TestPlugin pluginA("A", "1.0.0", {});
|
|
||||||
TestPlugin pluginB("B", "1.0.0", {"A"});
|
|
||||||
TestPlugin pluginC("C", "1.0.0", {"A"});
|
|
||||||
TestPlugin pluginD("D", "1.0.0", {"B", "C"});
|
|
||||||
TestPlugin pluginE("E", "1.0.0", {});
|
|
||||||
|
|
||||||
// 乱序注册
|
|
||||||
loader.registerPlugin(&pluginD);
|
|
||||||
loader.registerPlugin(&pluginB);
|
|
||||||
loader.registerPlugin(&pluginE);
|
|
||||||
loader.registerPlugin(&pluginC);
|
|
||||||
loader.registerPlugin(&pluginA);
|
|
||||||
|
|
||||||
// 所有插件都应能加载
|
|
||||||
EXPECT_TRUE(loader.initAll());
|
|
||||||
EXPECT_TRUE(pluginA.loadCalled);
|
|
||||||
EXPECT_TRUE(pluginB.loadCalled);
|
|
||||||
EXPECT_TRUE(pluginC.loadCalled);
|
|
||||||
EXPECT_TRUE(pluginD.loadCalled);
|
|
||||||
EXPECT_TRUE(pluginE.loadCalled);
|
|
||||||
|
|
||||||
loader.shutdownAll();
|
|
||||||
}
|
|
||||||
|
|
@ -1,405 +0,0 @@
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <module/timer_module.h>
|
|
||||||
|
|
||||||
using namespace extra2d;
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// TimerModule 基础测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, BasicProperties) {
|
|
||||||
TimerModule timer;
|
|
||||||
|
|
||||||
// 测试基本属性
|
|
||||||
EXPECT_STREQ("Timer", timer.name());
|
|
||||||
EXPECT_EQ(ModuleType::Core, timer.type());
|
|
||||||
EXPECT_EQ(Pri::System, timer.priority());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, InitShutdown) {
|
|
||||||
TimerModule timer;
|
|
||||||
|
|
||||||
// 测试初始化和关闭
|
|
||||||
EXPECT_TRUE(timer.init());
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, TimeScale) {
|
|
||||||
TimerModule timer;
|
|
||||||
|
|
||||||
// 测试时间缩放默认值
|
|
||||||
EXPECT_FLOAT_EQ(1.0f, timer.getTimeScale());
|
|
||||||
|
|
||||||
// 测试设置时间缩放
|
|
||||||
timer.setTimeScale(2.0f);
|
|
||||||
EXPECT_FLOAT_EQ(2.0f, timer.getTimeScale());
|
|
||||||
|
|
||||||
timer.setTimeScale(0.5f);
|
|
||||||
EXPECT_FLOAT_EQ(0.5f, timer.getTimeScale());
|
|
||||||
|
|
||||||
// 测试边界值
|
|
||||||
timer.setTimeScale(0.0f);
|
|
||||||
EXPECT_FLOAT_EQ(0.0f, timer.getTimeScale());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 一次性定时器测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, ScheduleOnce) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
bool called = false;
|
|
||||||
TimerId id = timer.scheduleOnce(0.1f, [&called]() {
|
|
||||||
called = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_NE(INVALID_TIMER_ID, id);
|
|
||||||
EXPECT_EQ(1, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 模拟时间流逝(未到达触发时间)
|
|
||||||
timer.update(0.05f);
|
|
||||||
EXPECT_FALSE(called);
|
|
||||||
EXPECT_EQ(1, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 模拟时间流逝(到达触发时间)
|
|
||||||
timer.update(0.05f);
|
|
||||||
EXPECT_TRUE(called);
|
|
||||||
EXPECT_EQ(0, timer.getActiveCount());
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, ScheduleOnceImmediate) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
bool called = false;
|
|
||||||
TimerId id = timer.scheduleOnce(0.0f, [&called]() {
|
|
||||||
called = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_NE(INVALID_TIMER_ID, id);
|
|
||||||
|
|
||||||
// 立即触发
|
|
||||||
timer.update(0.0f);
|
|
||||||
EXPECT_TRUE(called);
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, CancelTimer) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
bool called = false;
|
|
||||||
TimerId id = timer.scheduleOnce(0.1f, [&called]() {
|
|
||||||
called = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 取消定时器
|
|
||||||
timer.cancel(id);
|
|
||||||
|
|
||||||
// 模拟时间流逝
|
|
||||||
timer.update(0.2f);
|
|
||||||
EXPECT_FALSE(called);
|
|
||||||
EXPECT_EQ(0, timer.getActiveCount());
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, CancelInvalidTimer) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
// 取消不存在的定时器不应崩溃
|
|
||||||
EXPECT_NO_THROW(timer.cancel(99999));
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 重复定时器测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, ScheduleRepeat) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
int callCount = 0;
|
|
||||||
TimerId id = timer.scheduleRepeat(0.1f, 3, [&callCount]() {
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_NE(INVALID_TIMER_ID, id);
|
|
||||||
|
|
||||||
// 第一次触发
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(1, callCount);
|
|
||||||
|
|
||||||
// 第二次触发
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(2, callCount);
|
|
||||||
|
|
||||||
// 第三次触发
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(3, callCount);
|
|
||||||
|
|
||||||
// 定时器应已自动移除
|
|
||||||
EXPECT_EQ(0, timer.getActiveCount());
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, ScheduleRepeatInfinite) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
int callCount = 0;
|
|
||||||
TimerId id = timer.scheduleRepeat(0.1f, 0, [&callCount]() {
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 触发多次
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
timer.update(0.1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(10, callCount);
|
|
||||||
EXPECT_EQ(1, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 取消无限定时器
|
|
||||||
timer.cancel(id);
|
|
||||||
EXPECT_EQ(0, timer.getActiveCount());
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 每帧更新定时器测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, ScheduleUpdate) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
float totalDt = 0.0f;
|
|
||||||
int callCount = 0;
|
|
||||||
TimerId id = timer.scheduleUpdate([&](float dt) {
|
|
||||||
totalDt += dt;
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_NE(INVALID_TIMER_ID, id);
|
|
||||||
|
|
||||||
// 模拟多帧
|
|
||||||
timer.update(0.016f);
|
|
||||||
timer.update(0.016f);
|
|
||||||
timer.update(0.016f);
|
|
||||||
|
|
||||||
EXPECT_EQ(3, callCount);
|
|
||||||
EXPECT_FLOAT_EQ(0.048f, totalDt);
|
|
||||||
|
|
||||||
// 取消更新定时器
|
|
||||||
timer.cancel(id);
|
|
||||||
EXPECT_EQ(0, timer.getActiveCount());
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 暂停和恢复测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, PauseResume) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
int callCount = 0;
|
|
||||||
TimerId id = timer.scheduleRepeat(0.1f, 0, [&callCount]() {
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 正常触发一次
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(1, callCount);
|
|
||||||
|
|
||||||
// 暂停
|
|
||||||
timer.pause(id);
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(1, callCount); // 不应增加
|
|
||||||
|
|
||||||
// 恢复
|
|
||||||
timer.resume(id);
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(2, callCount);
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, PauseInvalidTimer) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
// 暂停不存在的定时器不应崩溃
|
|
||||||
EXPECT_NO_THROW(timer.pause(99999));
|
|
||||||
EXPECT_NO_THROW(timer.resume(99999));
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 时间缩放测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, TimeScaleEffect) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
int callCount = 0;
|
|
||||||
timer.scheduleRepeat(1.0f, 0, [&callCount]() {
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 设置2倍速
|
|
||||||
timer.setTimeScale(2.0f);
|
|
||||||
|
|
||||||
// 实际经过0.5秒,但效果相当于1秒
|
|
||||||
timer.update(0.5f);
|
|
||||||
EXPECT_EQ(1, callCount);
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, TimeScaleZero) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
int callCount = 0;
|
|
||||||
timer.scheduleRepeat(0.1f, 0, [&callCount]() {
|
|
||||||
callCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 暂停时间
|
|
||||||
timer.setTimeScale(0.0f);
|
|
||||||
|
|
||||||
// 无论经过多少时间,都不会触发
|
|
||||||
timer.update(1.0f);
|
|
||||||
timer.update(1.0f);
|
|
||||||
EXPECT_EQ(0, callCount);
|
|
||||||
|
|
||||||
// 恢复时间
|
|
||||||
timer.setTimeScale(1.0f);
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(1, callCount);
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 多定时器测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, MultipleTimers) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
int count1 = 0, count2 = 0, count3 = 0;
|
|
||||||
|
|
||||||
TimerId id1 = timer.scheduleOnce(0.1f, [&count1]() { count1++; });
|
|
||||||
TimerId id2 = timer.scheduleOnce(0.2f, [&count2]() { count2++; });
|
|
||||||
TimerId id3 = timer.scheduleOnce(0.3f, [&count3]() { count3++; });
|
|
||||||
|
|
||||||
EXPECT_EQ(3, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 经过0.15秒
|
|
||||||
timer.update(0.15f);
|
|
||||||
EXPECT_EQ(1, count1);
|
|
||||||
EXPECT_EQ(0, count2);
|
|
||||||
EXPECT_EQ(0, count3);
|
|
||||||
EXPECT_EQ(2, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 再经过0.1秒
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(1, count1);
|
|
||||||
EXPECT_EQ(1, count2);
|
|
||||||
EXPECT_EQ(0, count3);
|
|
||||||
EXPECT_EQ(1, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 再经过0.1秒
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_EQ(1, count1);
|
|
||||||
EXPECT_EQ(1, count2);
|
|
||||||
EXPECT_EQ(1, count3);
|
|
||||||
EXPECT_EQ(0, timer.getActiveCount());
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, CancelAll) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
timer.scheduleOnce(0.1f, [&count]() { count++; });
|
|
||||||
timer.scheduleOnce(0.2f, [&count]() { count++; });
|
|
||||||
timer.scheduleOnce(0.3f, [&count]() { count++; });
|
|
||||||
|
|
||||||
EXPECT_EQ(3, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 取消所有
|
|
||||||
timer.cancelAll();
|
|
||||||
EXPECT_EQ(0, timer.getActiveCount());
|
|
||||||
|
|
||||||
// 确认都不会触发
|
|
||||||
timer.update(0.5f);
|
|
||||||
EXPECT_EQ(0, count);
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 边界条件测试
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
TEST(TimerModule, SelfCancelInCallback) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
TimerId id = INVALID_TIMER_ID;
|
|
||||||
bool called = false;
|
|
||||||
|
|
||||||
// 在回调中取消自己
|
|
||||||
id = timer.scheduleOnce(0.1f, [&]() {
|
|
||||||
called = true;
|
|
||||||
timer.cancel(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_TRUE(called);
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TimerModule, ScheduleInCallback) {
|
|
||||||
TimerModule timer;
|
|
||||||
timer.init();
|
|
||||||
|
|
||||||
bool secondCalled = false;
|
|
||||||
|
|
||||||
// 在回调中创建新定时器
|
|
||||||
timer.scheduleOnce(0.1f, [&]() {
|
|
||||||
timer.scheduleOnce(0.1f, [&]() {
|
|
||||||
secondCalled = true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_FALSE(secondCalled);
|
|
||||||
|
|
||||||
timer.update(0.1f);
|
|
||||||
EXPECT_TRUE(secondCalled);
|
|
||||||
|
|
||||||
timer.shutdown();
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
-- Extra2D 测试套件构建脚本
|
|
||||||
-- 使用 GoogleTest 标准测试框架
|
|
||||||
|
|
||||||
-- 添加 gtest 包依赖
|
|
||||||
add_requires("gtest")
|
|
||||||
|
|
||||||
target("extra2d_tests")
|
|
||||||
set_kind("binary")
|
|
||||||
set_languages("c++17")
|
|
||||||
|
|
||||||
-- 添加 gtest 包
|
|
||||||
add_packages("gtest")
|
|
||||||
|
|
||||||
-- 添加测试源文件
|
|
||||||
add_files("*.cpp")
|
|
||||||
|
|
||||||
-- 添加头文件搜索路径
|
|
||||||
add_includedirs(".")
|
|
||||||
add_includedirs("../include")
|
|
||||||
|
|
||||||
-- 添加引擎源文件(仅核心模块,避免平台依赖)
|
|
||||||
add_files("../src/context/context.cpp")
|
|
||||||
add_files("../src/module/module_registry.cpp")
|
|
||||||
add_files("../src/module/timer_module.cpp")
|
|
||||||
add_files("../src/plugin/plugin_loader.cpp")
|
|
||||||
add_files("../src/event/event_bus.cpp")
|
|
||||||
add_files("../src/utils/random.cpp")
|
|
||||||
-- 注意:logger.cpp 在 TEST_BUILD 模式下使用条件编译避免 SDL 依赖
|
|
||||||
|
|
||||||
-- 第三方库
|
|
||||||
add_includedirs("../third_party/nlohmann")
|
|
||||||
add_includedirs("../third_party/stb")
|
|
||||||
|
|
||||||
-- 定义 TEST_BUILD 宏,用于条件编译
|
|
||||||
add_defines("TEST_BUILD")
|
|
||||||
|
|
||||||
-- 编译选项
|
|
||||||
if is_mode("debug") then
|
|
||||||
add_defines("DEBUG")
|
|
||||||
set_symbols("debug")
|
|
||||||
set_optimize("none")
|
|
||||||
else
|
|
||||||
add_defines("NDEBUG")
|
|
||||||
set_optimize("fastest")
|
|
||||||
set_strip("all")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- 警告设置
|
|
||||||
set_warnings("all")
|
|
||||||
|
|
||||||
-- 平台特定设置
|
|
||||||
if is_plat("windows") then
|
|
||||||
add_cxflags("/wd4267", "/wd4244", "/wd4996", "/wd4100", "/wd4458")
|
|
||||||
add_syslinks("kernel32", "user32")
|
|
||||||
elseif is_plat("mingw") then
|
|
||||||
add_cxflags("-Wno-unused-parameter", "-Wno-unused-variable")
|
|
||||||
else
|
|
||||||
add_cxflags("-Wno-unused-parameter", "-Wno-unused-variable")
|
|
||||||
end
|
|
||||||
|
|
@ -97,10 +97,6 @@ if is_config("examples","true") then
|
||||||
includes("examples/resource_loading", {rootdir = "examples/resource_loading"})
|
includes("examples/resource_loading", {rootdir = "examples/resource_loading"})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 测试套件目标(作为子项目)
|
|
||||||
if is_config("tests","true") then
|
|
||||||
includes("tests")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ==============================================
|
-- ==============================================
|
||||||
-- 项目信息输出
|
-- 项目信息输出
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue