409 lines
9.9 KiB
C++
409 lines
9.9 KiB
C++
|
|
#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();
|
||
|
|
}
|