tests: remove legacy test tree
This commit is contained in:
@@ -1,933 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Components/BoxColliderComponent.h>
|
||||
#include <XCEngine/Components/CapsuleColliderComponent.h>
|
||||
#include <XCEngine/Components/RigidbodyComponent.h>
|
||||
#include <XCEngine/Components/SphereColliderComponent.h>
|
||||
#include <XCEngine/Physics/PhysicsWorld.h>
|
||||
#include <XCEngine/Scene/SceneRuntime.h>
|
||||
#include <XCEngine/Scripting/IScriptRuntime.h>
|
||||
#include <XCEngine/Scripting/ScriptComponent.h>
|
||||
#include <XCEngine/Scripting/ScriptEngine.h>
|
||||
#include <XCEngine/UI/Runtime/UIScreenStackController.h>
|
||||
#include <XCEngine/UI/Runtime/UIScreenTypes.h>
|
||||
#include <XCEngine/UI/Runtime/UISystem.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace XCEngine::Components;
|
||||
using namespace XCEngine::Scripting;
|
||||
using XCEngine::UI::Runtime::UIScreenAsset;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string LifecycleMethodToString(ScriptLifecycleMethod method) {
|
||||
switch (method) {
|
||||
case ScriptLifecycleMethod::Awake: return "Awake";
|
||||
case ScriptLifecycleMethod::OnEnable: return "OnEnable";
|
||||
case ScriptLifecycleMethod::Start: return "Start";
|
||||
case ScriptLifecycleMethod::FixedUpdate: return "FixedUpdate";
|
||||
case ScriptLifecycleMethod::Update: return "Update";
|
||||
case ScriptLifecycleMethod::LateUpdate: return "LateUpdate";
|
||||
case ScriptLifecycleMethod::OnDisable: return "OnDisable";
|
||||
case ScriptLifecycleMethod::OnDestroy: return "OnDestroy";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
std::string PhysicsMessageToString(ScriptPhysicsMessage message) {
|
||||
switch (message) {
|
||||
case ScriptPhysicsMessage::CollisionEnter: return "CollisionEnter";
|
||||
case ScriptPhysicsMessage::CollisionStay: return "CollisionStay";
|
||||
case ScriptPhysicsMessage::CollisionExit: return "CollisionExit";
|
||||
case ScriptPhysicsMessage::TriggerEnter: return "TriggerEnter";
|
||||
case ScriptPhysicsMessage::TriggerStay: return "TriggerStay";
|
||||
case ScriptPhysicsMessage::TriggerExit: return "TriggerExit";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
class OrderedObserverComponent : public Component {
|
||||
public:
|
||||
explicit OrderedObserverComponent(std::vector<std::string>* events)
|
||||
: m_events(events) {
|
||||
}
|
||||
|
||||
std::string GetName() const override { return "OrderedObserver"; }
|
||||
|
||||
void Start() override {
|
||||
if (m_events) {
|
||||
m_events->push_back("NativeStart:" + GetGameObject()->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void FixedUpdate() override {
|
||||
if (m_events) {
|
||||
m_events->push_back("NativeFixedUpdate:" + GetGameObject()->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void Update(float deltaTime) override {
|
||||
(void)deltaTime;
|
||||
if (m_events) {
|
||||
m_events->push_back("NativeUpdate:" + GetGameObject()->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate(float deltaTime) override {
|
||||
(void)deltaTime;
|
||||
if (m_events) {
|
||||
m_events->push_back("NativeLateUpdate:" + GetGameObject()->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string>* m_events = nullptr;
|
||||
};
|
||||
|
||||
class PhysicsObserverComponent : public Component {
|
||||
public:
|
||||
explicit PhysicsObserverComponent(std::vector<std::string>* events)
|
||||
: m_events(events) {
|
||||
}
|
||||
|
||||
std::string GetName() const override { return "PhysicsObserver"; }
|
||||
|
||||
void OnCollisionEnter(GameObject* other) override { Record("NativeCollisionEnter", other); }
|
||||
void OnCollisionStay(GameObject* other) override { Record("NativeCollisionStay", other); }
|
||||
void OnCollisionExit(GameObject* other) override { Record("NativeCollisionExit", other); }
|
||||
void OnTriggerEnter(GameObject* other) override { Record("NativeTriggerEnter", other); }
|
||||
void OnTriggerStay(GameObject* other) override { Record("NativeTriggerStay", other); }
|
||||
void OnTriggerExit(GameObject* other) override { Record("NativeTriggerExit", other); }
|
||||
|
||||
private:
|
||||
void Record(const char* prefix, GameObject* other) {
|
||||
if (!m_events || !GetGameObject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_events->push_back(
|
||||
std::string(prefix)
|
||||
+ ":"
|
||||
+ GetGameObject()->GetName()
|
||||
+ ":"
|
||||
+ (other ? other->GetName() : std::string("null")));
|
||||
}
|
||||
|
||||
std::vector<std::string>* m_events = nullptr;
|
||||
};
|
||||
|
||||
class TempFileScope {
|
||||
public:
|
||||
TempFileScope(std::string stem, std::string extension, std::string contents) {
|
||||
const auto uniqueId = std::to_string(
|
||||
std::chrono::steady_clock::now().time_since_epoch().count());
|
||||
m_path = fs::temp_directory_path() / (std::move(stem) + "_" + uniqueId + std::move(extension));
|
||||
std::ofstream output(m_path, std::ios::binary | std::ios::trunc);
|
||||
output << contents;
|
||||
}
|
||||
|
||||
~TempFileScope() {
|
||||
std::error_code ec;
|
||||
fs::remove(m_path, ec);
|
||||
}
|
||||
|
||||
const fs::path& Path() const {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
fs::path m_path = {};
|
||||
};
|
||||
|
||||
std::string BuildViewMarkup(const char* heroTitle) {
|
||||
return
|
||||
"<View name=\"Runtime Screen\">\n"
|
||||
" <Column id=\"root\" padding=\"18\" gap=\"10\">\n"
|
||||
" <Card id=\"hero\" title=\"" + std::string(heroTitle) + "\" subtitle=\"Shared XCUI runtime layer\" />\n"
|
||||
" <Text id=\"status\" text=\"Ready for play\" />\n"
|
||||
" </Column>\n"
|
||||
"</View>\n";
|
||||
}
|
||||
|
||||
UIScreenAsset BuildScreenAsset(const fs::path& viewPath, const char* screenId) {
|
||||
UIScreenAsset screen = {};
|
||||
screen.screenId = screenId;
|
||||
screen.documentPath = viewPath.string();
|
||||
return screen;
|
||||
}
|
||||
|
||||
bool DrawDataContainsText(
|
||||
const XCEngine::UI::UIDrawData& drawData,
|
||||
const std::string& text) {
|
||||
for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) {
|
||||
for (const XCEngine::UI::UIDrawCommand& command : drawList.GetCommands()) {
|
||||
if (command.type == XCEngine::UI::UIDrawCommandType::Text &&
|
||||
command.text == text) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const XCEngine::UI::UIDrawCommand* FindFirstFilledRectCommand(
|
||||
const XCEngine::UI::UIDrawData& drawData) {
|
||||
for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) {
|
||||
for (const XCEngine::UI::UIDrawCommand& command : drawList.GetCommands()) {
|
||||
if (command.type == XCEngine::UI::UIDrawCommandType::FilledRect) {
|
||||
return &command;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const XCEngine::UI::Runtime::UISystemPresentedLayer* FindPresentedLayerById(
|
||||
const XCEngine::UI::Runtime::UISystemFrameResult& frame,
|
||||
XCEngine::UI::Runtime::UIScreenLayerId layerId) {
|
||||
for (const XCEngine::UI::Runtime::UISystemPresentedLayer& layer : frame.layers) {
|
||||
if (layer.layerId == layerId) {
|
||||
return &layer;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class RecordingScriptRuntime : public IScriptRuntime {
|
||||
public:
|
||||
explicit RecordingScriptRuntime(std::vector<std::string>* events)
|
||||
: m_events(events) {
|
||||
}
|
||||
|
||||
void OnRuntimeStart(Scene* scene) override {
|
||||
if (m_events) {
|
||||
m_events->push_back("RuntimeStart:" + (scene ? scene->GetName() : std::string("null")));
|
||||
}
|
||||
}
|
||||
|
||||
void OnRuntimeStop(Scene* scene) override {
|
||||
if (m_events) {
|
||||
m_events->push_back("RuntimeStop:" + (scene ? scene->GetName() : std::string("null")));
|
||||
}
|
||||
}
|
||||
|
||||
bool TryGetAvailableScriptClasses(
|
||||
std::vector<ScriptClassDescriptor>& outClasses) const override {
|
||||
outClasses.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryGetAvailableRenderPipelineAssetClasses(
|
||||
std::vector<ScriptClassDescriptor>& outClasses) const override {
|
||||
outClasses.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryGetClassFieldMetadata(
|
||||
const std::string& assemblyName,
|
||||
const std::string& namespaceName,
|
||||
const std::string& className,
|
||||
std::vector<ScriptFieldMetadata>& outFields) const override {
|
||||
(void)assemblyName;
|
||||
(void)namespaceName;
|
||||
(void)className;
|
||||
outFields.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryGetClassFieldDefaultValues(
|
||||
const std::string& assemblyName,
|
||||
const std::string& namespaceName,
|
||||
const std::string& className,
|
||||
std::vector<ScriptFieldDefaultValue>& outFields) const override {
|
||||
(void)assemblyName;
|
||||
(void)namespaceName;
|
||||
(void)className;
|
||||
outFields.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TrySetManagedFieldValue(
|
||||
const ScriptRuntimeContext& context,
|
||||
const std::string& fieldName,
|
||||
const ScriptFieldValue& value) override {
|
||||
(void)context;
|
||||
(void)fieldName;
|
||||
(void)value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryGetManagedFieldValue(
|
||||
const ScriptRuntimeContext& context,
|
||||
const std::string& fieldName,
|
||||
ScriptFieldValue& outValue) const override {
|
||||
(void)context;
|
||||
(void)fieldName;
|
||||
(void)outValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
void SyncManagedFieldsToStorage(const ScriptRuntimeContext& context) override {
|
||||
(void)context;
|
||||
}
|
||||
|
||||
bool CreateScriptInstance(const ScriptRuntimeContext& context) override {
|
||||
if (m_events) {
|
||||
m_events->push_back("Create:" + Describe(context));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DestroyScriptInstance(const ScriptRuntimeContext& context) override {
|
||||
if (m_events) {
|
||||
m_events->push_back("Destroy:" + Describe(context));
|
||||
}
|
||||
}
|
||||
|
||||
void InvokeMethod(
|
||||
const ScriptRuntimeContext& context,
|
||||
ScriptLifecycleMethod method,
|
||||
float deltaTime) override {
|
||||
(void)deltaTime;
|
||||
if (m_events) {
|
||||
m_events->push_back(LifecycleMethodToString(method) + ":" + Describe(context));
|
||||
}
|
||||
}
|
||||
|
||||
void InvokePhysicsMessage(
|
||||
const ScriptRuntimeContext& context,
|
||||
ScriptPhysicsMessage message,
|
||||
GameObject* other) override {
|
||||
if (m_events) {
|
||||
m_events->push_back(
|
||||
PhysicsMessageToString(message)
|
||||
+ ":"
|
||||
+ Describe(context)
|
||||
+ ":"
|
||||
+ (other ? other->GetName() : std::string("null")));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string Describe(const ScriptRuntimeContext& context) {
|
||||
const std::string gameObjectName = context.gameObject ? context.gameObject->GetName() : "null";
|
||||
const std::string className = context.component ? context.component->GetFullClassName() : "null";
|
||||
return gameObjectName + ":" + className;
|
||||
}
|
||||
|
||||
std::vector<std::string>* m_events = nullptr;
|
||||
};
|
||||
|
||||
class SceneRuntimeTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
scriptEngine = &ScriptEngine::Get();
|
||||
scriptEngine->OnRuntimeStop();
|
||||
runtimeImpl = std::make_unique<RecordingScriptRuntime>(&events);
|
||||
scriptEngine->SetRuntime(runtimeImpl.get());
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
runtime.Stop();
|
||||
scriptEngine->OnRuntimeStop();
|
||||
scriptEngine->SetRuntime(nullptr);
|
||||
runtimeImpl.reset();
|
||||
scene.reset();
|
||||
}
|
||||
|
||||
Scene* CreateScene(const std::string& sceneName) {
|
||||
scene = std::make_unique<Scene>(sceneName);
|
||||
return scene.get();
|
||||
}
|
||||
|
||||
ScriptComponent* AddScript(GameObject* gameObject, const std::string& namespaceName, const std::string& className) {
|
||||
ScriptComponent* component = gameObject->AddComponent<ScriptComponent>();
|
||||
component->SetScriptClass("GameScripts", namespaceName, className);
|
||||
return component;
|
||||
}
|
||||
|
||||
std::vector<std::string> events;
|
||||
std::unique_ptr<Scene> scene;
|
||||
std::unique_ptr<RecordingScriptRuntime> runtimeImpl;
|
||||
ScriptEngine* scriptEngine = nullptr;
|
||||
SceneRuntime runtime;
|
||||
};
|
||||
|
||||
TEST_F(SceneRuntimeTest, StartAndStopForwardToScriptEngine) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||
AddScript(host, "Gameplay", "Bootstrap");
|
||||
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.Stop();
|
||||
|
||||
const std::vector<std::string> expected = {
|
||||
"RuntimeStart:RuntimeScene",
|
||||
"Create:Host:Gameplay.Bootstrap",
|
||||
"Awake:Host:Gameplay.Bootstrap",
|
||||
"OnEnable:Host:Gameplay.Bootstrap",
|
||||
"OnDisable:Host:Gameplay.Bootstrap",
|
||||
"OnDestroy:Host:Gameplay.Bootstrap",
|
||||
"Destroy:Host:Gameplay.Bootstrap",
|
||||
"RuntimeStop:RuntimeScene"
|
||||
};
|
||||
EXPECT_EQ(events, expected);
|
||||
EXPECT_FALSE(runtime.IsRunning());
|
||||
EXPECT_EQ(runtime.GetScene(), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, StartCreatesPhysicsWorldAndScansScenePhysicsComponents) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
GameObject* ground = runtimeScene->CreateGameObject("Ground");
|
||||
ground->AddComponent<BoxColliderComponent>();
|
||||
|
||||
GameObject* player = runtimeScene->CreateGameObject("Player");
|
||||
player->AddComponent<RigidbodyComponent>();
|
||||
player->AddComponent<SphereColliderComponent>();
|
||||
|
||||
GameObject* trigger = runtimeScene->CreateGameObject("Trigger", player);
|
||||
trigger->AddComponent<CapsuleColliderComponent>();
|
||||
|
||||
runtime.Start(runtimeScene);
|
||||
|
||||
XCEngine::Physics::PhysicsWorld* physicsWorld = runtime.GetPhysicsWorld();
|
||||
ASSERT_NE(physicsWorld, nullptr);
|
||||
EXPECT_EQ(physicsWorld->GetCreateInfo().scene, runtimeScene);
|
||||
EXPECT_EQ(
|
||||
physicsWorld->IsInitialized(),
|
||||
XCEngine::Physics::PhysicsWorld::IsPhysXAvailable());
|
||||
EXPECT_EQ(physicsWorld->GetTrackedRigidbodyCount(), 1u);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedColliderCount(), 3u);
|
||||
|
||||
runtime.Stop();
|
||||
EXPECT_EQ(runtime.GetPhysicsWorld(), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, ReplaceSceneRebuildsPhysicsWorldAgainstNewScene) {
|
||||
Scene* firstScene = CreateScene("FirstScene");
|
||||
GameObject* firstActor = firstScene->CreateGameObject("FirstActor");
|
||||
firstActor->AddComponent<RigidbodyComponent>();
|
||||
firstActor->AddComponent<BoxColliderComponent>();
|
||||
|
||||
runtime.Start(firstScene);
|
||||
|
||||
auto secondScene = std::make_unique<Scene>("SecondScene");
|
||||
GameObject* secondActor = secondScene->CreateGameObject("SecondActor");
|
||||
secondActor->AddComponent<SphereColliderComponent>();
|
||||
secondActor->AddComponent<CapsuleColliderComponent>();
|
||||
|
||||
runtime.ReplaceScene(secondScene.get());
|
||||
|
||||
XCEngine::Physics::PhysicsWorld* physicsWorld = runtime.GetPhysicsWorld();
|
||||
ASSERT_NE(physicsWorld, nullptr);
|
||||
EXPECT_EQ(physicsWorld->GetCreateInfo().scene, secondScene.get());
|
||||
EXPECT_EQ(physicsWorld->GetTrackedRigidbodyCount(), 0u);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedColliderCount(), 2u);
|
||||
|
||||
runtime.Stop();
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, RuntimePhysicsWorldTracksComponentAndGameObjectRemoval) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
|
||||
XCEngine::Physics::PhysicsWorld* physicsWorld = runtime.GetPhysicsWorld();
|
||||
ASSERT_NE(physicsWorld, nullptr);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedRigidbodyCount(), 0u);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedColliderCount(), 0u);
|
||||
|
||||
GameObject* actor = runtimeScene->CreateGameObject("Actor");
|
||||
actor->AddComponent<RigidbodyComponent>();
|
||||
SphereColliderComponent* sphereCollider = actor->AddComponent<SphereColliderComponent>();
|
||||
actor->AddComponent<BoxColliderComponent>();
|
||||
|
||||
EXPECT_EQ(physicsWorld->GetTrackedRigidbodyCount(), 1u);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedColliderCount(), 2u);
|
||||
|
||||
actor->RemoveComponent(sphereCollider);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedRigidbodyCount(), 1u);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedColliderCount(), 1u);
|
||||
|
||||
runtimeScene->DestroyGameObject(actor);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedRigidbodyCount(), 0u);
|
||||
EXPECT_EQ(physicsWorld->GetTrackedColliderCount(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, FrameOrderRunsScriptLifecycleBeforeNativeComponents) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||
host->AddComponent<OrderedObserverComponent>(&events);
|
||||
AddScript(host, "Gameplay", "Mover");
|
||||
|
||||
runtime.Start(runtimeScene);
|
||||
events.clear();
|
||||
|
||||
runtime.FixedUpdate(0.02f);
|
||||
runtime.Update(0.016f);
|
||||
runtime.LateUpdate(0.016f);
|
||||
|
||||
const std::vector<std::string> expected = {
|
||||
"FixedUpdate:Host:Gameplay.Mover",
|
||||
"NativeFixedUpdate:Host",
|
||||
"Start:Host:Gameplay.Mover",
|
||||
"Update:Host:Gameplay.Mover",
|
||||
"NativeStart:Host",
|
||||
"NativeUpdate:Host",
|
||||
"LateUpdate:Host:Gameplay.Mover",
|
||||
"NativeLateUpdate:Host"
|
||||
};
|
||||
EXPECT_EQ(events, expected);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, FixedUpdateDispatchesTriggerPhysicsMessagesToNativeAndScripts) {
|
||||
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
|
||||
GTEST_SKIP() << "PhysX is not available in this configuration.";
|
||||
}
|
||||
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
GameObject* mover = runtimeScene->CreateGameObject("Mover");
|
||||
mover->AddComponent<PhysicsObserverComponent>(&events);
|
||||
mover->AddComponent<SphereColliderComponent>();
|
||||
RigidbodyComponent* rigidbody = mover->AddComponent<RigidbodyComponent>();
|
||||
rigidbody->SetUseGravity(false);
|
||||
AddScript(mover, "Gameplay", "TriggerProbe");
|
||||
mover->GetTransform()->SetPosition(XCEngine::Math::Vector3(-3.0f, 0.0f, 0.0f));
|
||||
|
||||
GameObject* triggerZone = runtimeScene->CreateGameObject("TriggerZone");
|
||||
BoxColliderComponent* triggerCollider = triggerZone->AddComponent<BoxColliderComponent>();
|
||||
triggerCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
||||
triggerCollider->SetTrigger(true);
|
||||
|
||||
runtime.Start(runtimeScene);
|
||||
events.clear();
|
||||
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
|
||||
runtime.FixedUpdate(0.02f);
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
||||
runtime.FixedUpdate(0.02f);
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
|
||||
runtime.FixedUpdate(0.02f);
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
||||
runtime.FixedUpdate(0.02f);
|
||||
|
||||
const std::vector<std::string> expected = {
|
||||
"FixedUpdate:Mover:Gameplay.TriggerProbe",
|
||||
"FixedUpdate:Mover:Gameplay.TriggerProbe",
|
||||
"NativeTriggerEnter:Mover:TriggerZone",
|
||||
"TriggerEnter:Mover:Gameplay.TriggerProbe:TriggerZone",
|
||||
"NativeTriggerStay:Mover:TriggerZone",
|
||||
"TriggerStay:Mover:Gameplay.TriggerProbe:TriggerZone",
|
||||
"FixedUpdate:Mover:Gameplay.TriggerProbe",
|
||||
"NativeTriggerStay:Mover:TriggerZone",
|
||||
"TriggerStay:Mover:Gameplay.TriggerProbe:TriggerZone",
|
||||
"FixedUpdate:Mover:Gameplay.TriggerProbe",
|
||||
"NativeTriggerExit:Mover:TriggerZone",
|
||||
"TriggerExit:Mover:Gameplay.TriggerProbe:TriggerZone"
|
||||
};
|
||||
EXPECT_EQ(events, expected);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, FixedUpdateDispatchesCollisionPhysicsMessagesToNativeAndScripts) {
|
||||
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
|
||||
GTEST_SKIP() << "PhysX is not available in this configuration.";
|
||||
}
|
||||
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
GameObject* mover = runtimeScene->CreateGameObject("Mover");
|
||||
mover->AddComponent<PhysicsObserverComponent>(&events);
|
||||
mover->AddComponent<SphereColliderComponent>();
|
||||
RigidbodyComponent* rigidbody = mover->AddComponent<RigidbodyComponent>();
|
||||
rigidbody->SetUseGravity(false);
|
||||
AddScript(mover, "Gameplay", "CollisionProbe");
|
||||
mover->GetTransform()->SetPosition(XCEngine::Math::Vector3(-3.0f, 0.0f, 0.0f));
|
||||
|
||||
GameObject* wall = runtimeScene->CreateGameObject("Wall");
|
||||
BoxColliderComponent* wallCollider = wall->AddComponent<BoxColliderComponent>();
|
||||
wallCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
||||
|
||||
runtime.Start(runtimeScene);
|
||||
events.clear();
|
||||
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
|
||||
runtime.FixedUpdate(0.02f);
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
||||
runtime.FixedUpdate(0.02f);
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(-150.0f, 0.0f, 0.0f));
|
||||
runtime.FixedUpdate(0.02f);
|
||||
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
||||
runtime.FixedUpdate(0.02f);
|
||||
|
||||
const std::vector<std::string> expected = {
|
||||
"FixedUpdate:Mover:Gameplay.CollisionProbe",
|
||||
"FixedUpdate:Mover:Gameplay.CollisionProbe",
|
||||
"NativeCollisionEnter:Mover:Wall",
|
||||
"CollisionEnter:Mover:Gameplay.CollisionProbe:Wall",
|
||||
"NativeCollisionStay:Mover:Wall",
|
||||
"CollisionStay:Mover:Gameplay.CollisionProbe:Wall",
|
||||
"FixedUpdate:Mover:Gameplay.CollisionProbe",
|
||||
"NativeCollisionStay:Mover:Wall",
|
||||
"CollisionStay:Mover:Gameplay.CollisionProbe:Wall",
|
||||
"FixedUpdate:Mover:Gameplay.CollisionProbe",
|
||||
"NativeCollisionExit:Mover:Wall",
|
||||
"CollisionExit:Mover:Gameplay.CollisionProbe:Wall"
|
||||
};
|
||||
EXPECT_EQ(events, expected);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, InactiveSceneSkipsFrameExecution) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
GameObject* host = runtimeScene->CreateGameObject("Host");
|
||||
host->AddComponent<OrderedObserverComponent>(&events);
|
||||
AddScript(host, "Gameplay", "PausedScript");
|
||||
runtimeScene->SetActive(false);
|
||||
|
||||
runtime.Start(runtimeScene);
|
||||
events.clear();
|
||||
|
||||
runtime.FixedUpdate(0.02f);
|
||||
runtime.Update(0.016f);
|
||||
runtime.LateUpdate(0.016f);
|
||||
|
||||
EXPECT_TRUE(events.empty());
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, StartingNewSceneStopsPreviousRuntimeFirst) {
|
||||
Scene* firstScene = CreateScene("FirstScene");
|
||||
GameObject* firstHost = firstScene->CreateGameObject("FirstHost");
|
||||
AddScript(firstHost, "Gameplay", "FirstScript");
|
||||
|
||||
runtime.Start(firstScene);
|
||||
|
||||
std::unique_ptr<Scene> secondScene = std::make_unique<Scene>("SecondScene");
|
||||
GameObject* secondHost = secondScene->CreateGameObject("SecondHost");
|
||||
ScriptComponent* secondScript = AddScript(secondHost, "Gameplay", "SecondScript");
|
||||
|
||||
events.clear();
|
||||
runtime.Start(secondScene.get());
|
||||
|
||||
const std::vector<std::string> expected = {
|
||||
"OnDisable:FirstHost:Gameplay.FirstScript",
|
||||
"OnDestroy:FirstHost:Gameplay.FirstScript",
|
||||
"Destroy:FirstHost:Gameplay.FirstScript",
|
||||
"RuntimeStop:FirstScene",
|
||||
"RuntimeStart:SecondScene",
|
||||
"Create:SecondHost:Gameplay.SecondScript",
|
||||
"Awake:SecondHost:Gameplay.SecondScript",
|
||||
"OnEnable:SecondHost:Gameplay.SecondScript"
|
||||
};
|
||||
EXPECT_EQ(events, expected);
|
||||
EXPECT_EQ(runtime.GetScene(), secondScene.get());
|
||||
EXPECT_TRUE(scriptEngine->HasRuntimeInstance(secondScript));
|
||||
|
||||
runtime.Stop();
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, UpdateTicksUiRuntimeAndClearsQueuedInputEvents) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f));
|
||||
runtime.SetUIFocused(true);
|
||||
|
||||
TempFileScope menuView("xcui_scene_runtime_menu", ".xcui", BuildViewMarkup("Runtime Menu"));
|
||||
const auto layerId = runtime.GetUIScreenStackController().PushMenu(
|
||||
BuildScreenAsset(menuView.Path(), "runtime.menu"),
|
||||
"menu");
|
||||
ASSERT_NE(layerId, 0u);
|
||||
|
||||
XCEngine::UI::UIInputEvent textEvent = {};
|
||||
textEvent.type = XCEngine::UI::UIInputEventType::Character;
|
||||
textEvent.character = 'A';
|
||||
runtime.QueueUIInputEvent(textEvent);
|
||||
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& firstFrame = runtime.GetLastUIFrame();
|
||||
ASSERT_EQ(firstFrame.presentedLayerCount, 1u);
|
||||
ASSERT_EQ(firstFrame.layers.size(), 1u);
|
||||
EXPECT_EQ(firstFrame.frameIndex, 1u);
|
||||
EXPECT_EQ(firstFrame.layers.front().stats.inputEventCount, 1u);
|
||||
EXPECT_TRUE(DrawDataContainsText(firstFrame.drawData, "Runtime Menu"));
|
||||
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& secondFrame = runtime.GetLastUIFrame();
|
||||
ASSERT_EQ(secondFrame.presentedLayerCount, 1u);
|
||||
ASSERT_EQ(secondFrame.layers.size(), 1u);
|
||||
EXPECT_EQ(secondFrame.frameIndex, 2u);
|
||||
EXPECT_EQ(secondFrame.layers.front().stats.inputEventCount, 0u);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, StopClearsUiRuntimeState) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f));
|
||||
runtime.SetUIFocused(true);
|
||||
|
||||
TempFileScope menuView("xcui_scene_runtime_pause", ".xcui", BuildViewMarkup("Pause Menu"));
|
||||
const auto layerId = runtime.GetUIScreenStackController().PushMenu(
|
||||
BuildScreenAsset(menuView.Path(), "runtime.pause"),
|
||||
"pause");
|
||||
ASSERT_NE(layerId, 0u);
|
||||
|
||||
runtime.Update(0.016f);
|
||||
ASSERT_EQ(runtime.GetUISystem().GetLayerCount(), 1u);
|
||||
ASSERT_EQ(runtime.GetUIScreenStackController().GetEntryCount(), 1u);
|
||||
ASSERT_EQ(runtime.GetLastUIFrame().presentedLayerCount, 1u);
|
||||
|
||||
runtime.Stop();
|
||||
|
||||
EXPECT_FALSE(runtime.IsRunning());
|
||||
EXPECT_EQ(runtime.GetScene(), nullptr);
|
||||
EXPECT_EQ(runtime.GetUISystem().GetLayerCount(), 0u);
|
||||
EXPECT_EQ(runtime.GetUIScreenStackController().GetEntryCount(), 0u);
|
||||
EXPECT_EQ(runtime.GetLastUIFrame().presentedLayerCount, 0u);
|
||||
EXPECT_TRUE(runtime.GetLastUIFrame().layers.empty());
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, ClearQueuedUiInputEventsPreventsPendingDelivery) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f));
|
||||
runtime.SetUIFocused(true);
|
||||
|
||||
TempFileScope menuView("xcui_scene_runtime_clear_input", ".xcui", BuildViewMarkup("Clear Input Menu"));
|
||||
const auto layerId = runtime.GetUIScreenStackController().PushMenu(
|
||||
BuildScreenAsset(menuView.Path(), "runtime.clear.input"),
|
||||
"clear-input");
|
||||
ASSERT_NE(layerId, 0u);
|
||||
|
||||
XCEngine::UI::UIInputEvent textEvent = {};
|
||||
textEvent.type = XCEngine::UI::UIInputEventType::Character;
|
||||
textEvent.character = 'A';
|
||||
runtime.QueueUIInputEvent(textEvent);
|
||||
|
||||
XCEngine::UI::UIInputEvent keyEvent = {};
|
||||
keyEvent.type = XCEngine::UI::UIInputEventType::KeyDown;
|
||||
keyEvent.keyCode = 13;
|
||||
runtime.QueueUIInputEvent(keyEvent);
|
||||
|
||||
runtime.ClearQueuedUIInputEvents();
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& clearedFrame = runtime.GetLastUIFrame();
|
||||
ASSERT_EQ(clearedFrame.presentedLayerCount, 1u);
|
||||
ASSERT_EQ(clearedFrame.layers.size(), 1u);
|
||||
EXPECT_EQ(clearedFrame.frameIndex, 1u);
|
||||
EXPECT_EQ(clearedFrame.layers.front().layerId, layerId);
|
||||
EXPECT_EQ(clearedFrame.layers.front().stats.inputEventCount, 0u);
|
||||
|
||||
runtime.QueueUIInputEvent(textEvent);
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& deliveredFrame = runtime.GetLastUIFrame();
|
||||
ASSERT_EQ(deliveredFrame.presentedLayerCount, 1u);
|
||||
ASSERT_EQ(deliveredFrame.layers.size(), 1u);
|
||||
EXPECT_EQ(deliveredFrame.frameIndex, 2u);
|
||||
EXPECT_EQ(deliveredFrame.layers.front().stats.inputEventCount, 1u);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, ViewportPersistsAcrossFramesAndResetsAfterStop) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIViewportRect(XCEngine::UI::UIRect(32.0f, 48.0f, 900.0f, 500.0f));
|
||||
runtime.SetUIFocused(true);
|
||||
|
||||
TempFileScope menuView("xcui_scene_runtime_viewport", ".xcui", BuildViewMarkup("Viewport Menu"));
|
||||
const UIScreenAsset screenAsset = BuildScreenAsset(menuView.Path(), "runtime.viewport.menu");
|
||||
ASSERT_NE(runtime.GetUIScreenStackController().PushMenu(screenAsset, "viewport-menu"), 0u);
|
||||
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& firstFrame = runtime.GetLastUIFrame();
|
||||
const auto* firstBackground = FindFirstFilledRectCommand(firstFrame.drawData);
|
||||
ASSERT_NE(firstBackground, nullptr);
|
||||
EXPECT_EQ(firstFrame.frameIndex, 1u);
|
||||
EXPECT_FLOAT_EQ(firstBackground->rect.x, 32.0f);
|
||||
EXPECT_FLOAT_EQ(firstBackground->rect.y, 48.0f);
|
||||
EXPECT_FLOAT_EQ(firstBackground->rect.width, 900.0f);
|
||||
EXPECT_FLOAT_EQ(firstBackground->rect.height, 500.0f);
|
||||
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& secondFrame = runtime.GetLastUIFrame();
|
||||
const auto* secondBackground = FindFirstFilledRectCommand(secondFrame.drawData);
|
||||
ASSERT_NE(secondBackground, nullptr);
|
||||
EXPECT_EQ(secondFrame.frameIndex, 2u);
|
||||
EXPECT_FLOAT_EQ(secondBackground->rect.x, 32.0f);
|
||||
EXPECT_FLOAT_EQ(secondBackground->rect.y, 48.0f);
|
||||
EXPECT_FLOAT_EQ(secondBackground->rect.width, 900.0f);
|
||||
EXPECT_FLOAT_EQ(secondBackground->rect.height, 500.0f);
|
||||
|
||||
runtime.Stop();
|
||||
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIFocused(true);
|
||||
ASSERT_NE(runtime.GetUIScreenStackController().PushMenu(screenAsset, "viewport-menu-reset"), 0u);
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& restartedFrame = runtime.GetLastUIFrame();
|
||||
const auto* restartedBackground = FindFirstFilledRectCommand(restartedFrame.drawData);
|
||||
ASSERT_NE(restartedBackground, nullptr);
|
||||
EXPECT_EQ(restartedFrame.frameIndex, 1u);
|
||||
EXPECT_FLOAT_EQ(restartedBackground->rect.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(restartedBackground->rect.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(restartedBackground->rect.width, 640.0f);
|
||||
EXPECT_FLOAT_EQ(restartedBackground->rect.height, 360.0f);
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, LayeredSceneUiRoutesInputOnlyToTopInteractivePresentedLayer) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 1280.0f, 720.0f));
|
||||
runtime.SetUIFocused(true);
|
||||
|
||||
TempFileScope gameplayView("xcui_scene_runtime_gameplay", ".xcui", BuildViewMarkup("Gameplay Layer"));
|
||||
TempFileScope overlayView("xcui_scene_runtime_overlay", ".xcui", BuildViewMarkup("Overlay Layer"));
|
||||
|
||||
XCEngine::UI::Runtime::UIScreenLayerOptions gameplayOptions = {};
|
||||
gameplayOptions.debugName = "gameplay";
|
||||
gameplayOptions.acceptsInput = true;
|
||||
gameplayOptions.blocksLayersBelow = false;
|
||||
|
||||
XCEngine::UI::Runtime::UIScreenLayerOptions overlayOptions = {};
|
||||
overlayOptions.debugName = "overlay";
|
||||
overlayOptions.acceptsInput = true;
|
||||
overlayOptions.blocksLayersBelow = false;
|
||||
|
||||
const auto gameplayLayerId = runtime.GetUIScreenStackController().PushScreen(
|
||||
BuildScreenAsset(gameplayView.Path(), "runtime.gameplay"),
|
||||
gameplayOptions);
|
||||
const auto overlayLayerId = runtime.GetUIScreenStackController().PushScreen(
|
||||
BuildScreenAsset(overlayView.Path(), "runtime.overlay"),
|
||||
overlayOptions);
|
||||
ASSERT_NE(gameplayLayerId, 0u);
|
||||
ASSERT_NE(overlayLayerId, 0u);
|
||||
|
||||
XCEngine::UI::UIInputEvent textEvent = {};
|
||||
textEvent.type = XCEngine::UI::UIInputEventType::Character;
|
||||
textEvent.character = 'I';
|
||||
runtime.QueueUIInputEvent(textEvent);
|
||||
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& frame = runtime.GetLastUIFrame();
|
||||
ASSERT_EQ(frame.presentedLayerCount, 2u);
|
||||
ASSERT_EQ(frame.skippedLayerCount, 0u);
|
||||
ASSERT_EQ(frame.layers.size(), 2u);
|
||||
|
||||
const auto* gameplayLayer = FindPresentedLayerById(frame, gameplayLayerId);
|
||||
const auto* overlayLayer = FindPresentedLayerById(frame, overlayLayerId);
|
||||
ASSERT_NE(gameplayLayer, nullptr);
|
||||
ASSERT_NE(overlayLayer, nullptr);
|
||||
EXPECT_EQ(gameplayLayer->stats.inputEventCount, 0u);
|
||||
EXPECT_EQ(overlayLayer->stats.inputEventCount, 1u);
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Gameplay Layer"));
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Overlay Layer"));
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, BlockingLayerSkipsLowerLayersAndOwnsQueuedInput) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 1280.0f, 720.0f));
|
||||
runtime.SetUIFocused(true);
|
||||
|
||||
TempFileScope gameplayView("xcui_scene_runtime_blocked_gameplay", ".xcui", BuildViewMarkup("Blocked Gameplay"));
|
||||
TempFileScope modalView("xcui_scene_runtime_modal", ".xcui", BuildViewMarkup("Pause Modal"));
|
||||
|
||||
XCEngine::UI::Runtime::UIScreenLayerOptions gameplayOptions = {};
|
||||
gameplayOptions.debugName = "gameplay";
|
||||
gameplayOptions.acceptsInput = true;
|
||||
gameplayOptions.blocksLayersBelow = false;
|
||||
|
||||
const auto gameplayLayerId = runtime.GetUIScreenStackController().PushScreen(
|
||||
BuildScreenAsset(gameplayView.Path(), "runtime.blocked.gameplay"),
|
||||
gameplayOptions);
|
||||
const auto modalLayerId = runtime.GetUIScreenStackController().PushModal(
|
||||
BuildScreenAsset(modalView.Path(), "runtime.pause.modal"),
|
||||
"pause-modal");
|
||||
ASSERT_NE(gameplayLayerId, 0u);
|
||||
ASSERT_NE(modalLayerId, 0u);
|
||||
|
||||
XCEngine::UI::UIInputEvent textEvent = {};
|
||||
textEvent.type = XCEngine::UI::UIInputEventType::Character;
|
||||
textEvent.character = 'P';
|
||||
runtime.QueueUIInputEvent(textEvent);
|
||||
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& frame = runtime.GetLastUIFrame();
|
||||
ASSERT_EQ(frame.presentedLayerCount, 1u);
|
||||
ASSERT_EQ(frame.skippedLayerCount, 1u);
|
||||
ASSERT_EQ(frame.layers.size(), 1u);
|
||||
|
||||
const auto* modalLayer = FindPresentedLayerById(frame, modalLayerId);
|
||||
ASSERT_NE(modalLayer, nullptr);
|
||||
EXPECT_EQ(modalLayer->stats.inputEventCount, 1u);
|
||||
EXPECT_EQ(FindPresentedLayerById(frame, gameplayLayerId), nullptr);
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Pause Modal"));
|
||||
EXPECT_FALSE(DrawDataContainsText(frame.drawData, "Blocked Gameplay"));
|
||||
}
|
||||
|
||||
TEST_F(SceneRuntimeTest, HiddenTopLayerDoesNotStealInputFromVisibleUnderlyingLayer) {
|
||||
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||
runtime.Start(runtimeScene);
|
||||
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 1280.0f, 720.0f));
|
||||
runtime.SetUIFocused(true);
|
||||
|
||||
TempFileScope gameplayView("xcui_scene_runtime_visible_gameplay", ".xcui", BuildViewMarkup("Visible Gameplay"));
|
||||
TempFileScope hiddenOverlayView("xcui_scene_runtime_hidden_overlay", ".xcui", BuildViewMarkup("Hidden Overlay"));
|
||||
|
||||
XCEngine::UI::Runtime::UIScreenLayerOptions gameplayOptions = {};
|
||||
gameplayOptions.debugName = "gameplay";
|
||||
gameplayOptions.acceptsInput = true;
|
||||
gameplayOptions.blocksLayersBelow = false;
|
||||
|
||||
XCEngine::UI::Runtime::UIScreenLayerOptions hiddenOverlayOptions = {};
|
||||
hiddenOverlayOptions.debugName = "hidden-overlay";
|
||||
hiddenOverlayOptions.visible = false;
|
||||
hiddenOverlayOptions.acceptsInput = true;
|
||||
hiddenOverlayOptions.blocksLayersBelow = false;
|
||||
|
||||
const auto gameplayLayerId = runtime.GetUIScreenStackController().PushScreen(
|
||||
BuildScreenAsset(gameplayView.Path(), "runtime.visible.gameplay"),
|
||||
gameplayOptions);
|
||||
const auto hiddenOverlayLayerId = runtime.GetUIScreenStackController().PushScreen(
|
||||
BuildScreenAsset(hiddenOverlayView.Path(), "runtime.hidden.overlay"),
|
||||
hiddenOverlayOptions);
|
||||
ASSERT_NE(gameplayLayerId, 0u);
|
||||
ASSERT_NE(hiddenOverlayLayerId, 0u);
|
||||
|
||||
XCEngine::UI::UIInputEvent textEvent = {};
|
||||
textEvent.type = XCEngine::UI::UIInputEventType::Character;
|
||||
textEvent.character = 'W';
|
||||
runtime.QueueUIInputEvent(textEvent);
|
||||
|
||||
runtime.Update(0.016f);
|
||||
|
||||
const auto& frame = runtime.GetLastUIFrame();
|
||||
ASSERT_EQ(frame.presentedLayerCount, 1u);
|
||||
ASSERT_EQ(frame.skippedLayerCount, 1u);
|
||||
ASSERT_EQ(frame.layers.size(), 1u);
|
||||
|
||||
const auto* gameplayLayer = FindPresentedLayerById(frame, gameplayLayerId);
|
||||
ASSERT_NE(gameplayLayer, nullptr);
|
||||
EXPECT_EQ(gameplayLayer->stats.inputEventCount, 1u);
|
||||
EXPECT_EQ(FindPresentedLayerById(frame, hiddenOverlayLayerId), nullptr);
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Visible Gameplay"));
|
||||
EXPECT_FALSE(DrawDataContainsText(frame.drawData, "Hidden Overlay"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user