Files
XCEngine/tests/Scene/test_runtime_loop.cpp

156 lines
4.1 KiB
C++

#include <gtest/gtest.h>
#include <XCEngine/Components/Component.h>
#include <XCEngine/Components/GameObject.h>
#include <XCEngine/Scene/RuntimeLoop.h>
#include <XCEngine/Scene/Scene.h>
#include <memory>
#include <string>
using namespace XCEngine::Components;
namespace {
struct RuntimeLoopCounters {
int startCount = 0;
int fixedUpdateCount = 0;
int updateCount = 0;
int lateUpdateCount = 0;
};
class RuntimeLoopObserverComponent : public Component {
public:
explicit RuntimeLoopObserverComponent(RuntimeLoopCounters* counters)
: m_counters(counters) {
}
std::string GetName() const override {
return "RuntimeLoopObserver";
}
void Start() override {
if (m_counters) {
++m_counters->startCount;
}
}
void FixedUpdate() override {
if (m_counters) {
++m_counters->fixedUpdateCount;
}
}
void Update(float deltaTime) override {
(void)deltaTime;
if (m_counters) {
++m_counters->updateCount;
}
}
void LateUpdate(float deltaTime) override {
(void)deltaTime;
if (m_counters) {
++m_counters->lateUpdateCount;
}
}
private:
RuntimeLoopCounters* m_counters = nullptr;
};
class RuntimeLoopTest : public ::testing::Test {
protected:
Scene* CreateScene(const std::string& name = "RuntimeLoopScene") {
m_scene = std::make_unique<Scene>(name);
return m_scene.get();
}
RuntimeLoopCounters counters;
std::unique_ptr<Scene> m_scene;
};
TEST_F(RuntimeLoopTest, AccumulatesFixedUpdatesAcrossFramesAndRunsVariableUpdatesEveryTick) {
RuntimeLoop loop({0.02f, 0.1f, 4});
Scene* scene = CreateScene();
GameObject* host = scene->CreateGameObject("Host");
host->AddComponent<RuntimeLoopObserverComponent>(&counters);
loop.Start(scene);
loop.Tick(0.01f);
EXPECT_EQ(counters.fixedUpdateCount, 0);
EXPECT_EQ(counters.startCount, 1);
EXPECT_EQ(counters.updateCount, 1);
EXPECT_EQ(counters.lateUpdateCount, 1);
loop.Tick(0.01f);
EXPECT_EQ(counters.fixedUpdateCount, 1);
EXPECT_EQ(counters.startCount, 1);
EXPECT_EQ(counters.updateCount, 2);
EXPECT_EQ(counters.lateUpdateCount, 2);
}
TEST_F(RuntimeLoopTest, ClampAndFixedStepLimitPreventExcessiveCatchUp) {
RuntimeLoop loop({0.02f, 0.05f, 2});
Scene* scene = CreateScene();
GameObject* host = scene->CreateGameObject("Host");
host->AddComponent<RuntimeLoopObserverComponent>(&counters);
loop.Start(scene);
loop.Tick(1.0f);
EXPECT_EQ(counters.fixedUpdateCount, 2);
EXPECT_EQ(counters.startCount, 1);
EXPECT_EQ(counters.updateCount, 1);
EXPECT_EQ(counters.lateUpdateCount, 1);
EXPECT_NEAR(loop.GetFixedAccumulator(), 0.01f, 1e-4f);
}
TEST_F(RuntimeLoopTest, PauseSkipsAutomaticTicksUntilStepFrameIsRequested) {
RuntimeLoop loop({0.02f, 0.1f, 4});
Scene* scene = CreateScene();
GameObject* host = scene->CreateGameObject("Host");
host->AddComponent<RuntimeLoopObserverComponent>(&counters);
loop.Start(scene);
loop.Pause();
loop.Tick(0.025f);
EXPECT_EQ(counters.fixedUpdateCount, 0);
EXPECT_EQ(counters.startCount, 0);
EXPECT_EQ(counters.updateCount, 0);
EXPECT_EQ(counters.lateUpdateCount, 0);
loop.StepFrame();
loop.Tick(0.025f);
EXPECT_EQ(counters.fixedUpdateCount, 1);
EXPECT_EQ(counters.startCount, 1);
EXPECT_EQ(counters.updateCount, 1);
EXPECT_EQ(counters.lateUpdateCount, 1);
EXPECT_TRUE(loop.IsPaused());
}
TEST_F(RuntimeLoopTest, ResumeRestoresAutomaticTickProgressionAfterPause) {
RuntimeLoop loop({0.02f, 0.1f, 4});
Scene* scene = CreateScene();
GameObject* host = scene->CreateGameObject("Host");
host->AddComponent<RuntimeLoopObserverComponent>(&counters);
loop.Start(scene);
loop.Pause();
loop.Tick(0.025f);
EXPECT_EQ(counters.updateCount, 0);
loop.Resume();
EXPECT_FALSE(loop.IsPaused());
loop.Tick(0.025f);
EXPECT_EQ(counters.startCount, 1);
EXPECT_EQ(counters.fixedUpdateCount, 1);
EXPECT_EQ(counters.updateCount, 1);
EXPECT_EQ(counters.lateUpdateCount, 1);
}
} // namespace